Histogram gives a wrong result?
显示 更早的评论
I was debugging the codes to find what was wrong, but I just found histogram can give wrong results.
This is an example. Accoring to the document (Histogram - Histogram plot - MATLAB), it should give the all statistical values as 5 except the last one, but instead, there are wrong variances. (Due to the floating number?)
Is there an accurate way to do the histogram?
a=0:0.01:1; figure; h=histogram(a,0:0.05:1);
回答(2 个)
Due to the floating number?
Correct. There is no reason to expect exact boundaries, or for data to fall exactly within those boundaries, when the inputs are generated with floating point math. One solution would be to recast the operation in terms of integers,
a=0:100; figure; h=histogram(a,0:5:100);
xticklabels(str2double(xticklabels)/100)
4 个评论
Tianlun
about 2 hours 前
In case anyone is wondering, that last bin being higher than the rest is not a bug. Every bin except for the last bin includes its left edge but excludes its right edge. The last bin includes both its left and right edges. That way each point in the range covered by the edges is part of one and only one bin.
The first bin covers [0, 5) and so will count a = 0, a = 1, a = 2, a = 3, and a = 4.
The last bin covers [95, 100] and so will count a = 95, a = 96, a = 97, a = 98, a = 99, and a = 100.
If you update the edges list so 100 is its own bin:
a = 0:100;
histogram(a, 0:5:105)
So there is no accurate way to do a histogram.
Sure there is, as long as you realize that many, many numbers you can type cannot be stored in double precision. Do you expect a = 0:0.1:1 to exactly contain the numbers one-tenth, two-tenths, three-tenths, etc.?
a = 0:0.1:1;
tentimes = 10*a;
shouldThisBeAllZero = tentimes - (0:10)
If you believe the 4th element of shouldThisBeAllZero not being 0 is incorrect, please try this little experiment. Find something to write with and something to write on (ideally compatible things; pencil and paper not pencil and whiteboard.)
Step 1: Using long division (like you learned in school) divide 1 by 3. Call the result x. You are allowed to write as many decimal places of the result as you want, but only those you explicitly write can be used in step 2. No using 0.3 repeating to get "an infinite" number of places.
Step 2: Multiply x by 3. Call the result y.
In exact arithmetic we know (1/3)*3 is exactly 1. But the x value you defined in step 1 is not one third. It is slightly smaller than one third because you rounded off one third to fit it into x. If you've written one more decimal place in step 1 you'd have an x that's closer to one third than the x you actually used in step 2. Therefore y will not be 1. The value stored in y will be slightly smaller than 1.
This is exactly the same scenario, only the number in the computer is stored in base 2 and the computer stores the number to a certain number of binary places. Integer values (up to a certain point) can be exactly represented. So can numbers that are [waves hands a bit] "the sum of a small number of relatively closely spaced powers of 2". 0.5, 0.25, and 0.75 (2^-1+2^-2) are examples of those types of numbers; 0.1 and 0.3 aren't.
So there is no accurate way to do a histogram?
histogram(a,e) is giving you perfectly accurate results. It is your input data (both a and e) that are inaccurate, because of floating point approximation. To avoid this issue, choose edge data e(i) that are well-separated from your a(j). By well-separated, I mean, greater than floating point precision thresholds.
John D'Errico
about 2 hours 前
编辑:John D'Errico
about 2 hours 前
Never assume that two mathematically identical floating point numbers will, if computed using different expressions, produce the the identical result. You tripped over that when you assumed 0:.01:1 and 0:0.05:1 would in fact "line up".
a=0:0.01:1; figure; h=histogram(a,0:0.05:1);
The crux of the problem can be seen in this call to intersect.
intersect(a,0:0.05:1)
setdiff(0:0.05:1,a)
Do you see that 0.15 is not in the intersection, that is, it did not appear in both vectors? As well, 0.30 is also missing.
b = 0:0.05:1;
a([16,31])
b([4,7])
It certainly looks as if both numbers appear in each vector. And they look identical. But they are not in fact identical. Again, compute them in different ways, and you run the risk they will not be the same.
num2str(a([16,31]),55)
num2str(b([4,7]),55)
It looks like in all other cases, the two vector computations did generate the same set of numbers, since intersect would have caught more cases otherwise. But there are tiny, imperceptible differences. How far apart are those numbers?
num2hex(a([16,31]))
num2hex(b([4,7]))
As you can see, they actually differ by one single bit, down at the level of the least significant bit.
Can you "fix" that? Not easily in some automatic way. Yes, you can use tolerances. But histogram does not employ tolerances when it computes frequencies of elements landing in bins, nor should it do so. And there is no flag you can set in the call to histogram to fix it. Histogram cannot understand the subtle difference here, nor can/should it worry about least significant bits.
I suppose, by understanding what is happening, in this specific case we might have tried this, which offsets the bin boundaries subtly by just enough the counts come out "correct".
h=histogram(a,-1e-12:0.05:1.05);
And indeed, now the counts per bin are correct, with the final bin catching only that last element at 1. It worked because I understood the root cause of the problem, and that led me to a solution which would fix it for this specific case.
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!




