7
\$\begingroup\$

Your task is to make a histogram given a sample of arbitrary size.

Input

A float array or any other reasonable form of input with an arbitrary number of elements.

Output

The histogram; more to follow.

How to make a histogram

We'll use the sample: [1.1, 3, 5, 13, 15.5, 21, 29.7, 63, 16, 5, 5.1, 5.01, 51, 61, 13]

To make a histogram, you first separate your observations into classes. The ideal number of classes is the lowest k such that 2^k > n, where k is the number of classes, and n is the number of observations. In our case, k = 4. The number of bars is not final. This k value's purpose is only to find the width of each class.

The width of each class is the range of your data set divided by the number of classes. The range of our data set is 63 - 1.1 = 61.9, so the width of each class is 15.475. This means that the first class is [1.1, 16.575), the second is [16.575, 32.05) and so on. The last class is [63.0, 78.475). The second value of the last class (b in [a,b)) must be greater than the max value of the set.

Now we rearrange our data into the classes; for us:

[1.1, 16.575): [1.1, 3, 5, 5, 5.01, 5.1, 13, 13, 15.5, 16]
[16.575, 32.05): [21, 29.7]
[32.05, 47.525): []
[47.525, 63.0): [51, 61]
[63.0, 78.475): [63]

And finally, we graph!

For our purposes, a bar will be a stacked for the number of observations in the class (because I think that looks good). You can count the as one byte in your source code. There should be no space between bars (unless there is an empty bar), nor do you need to label axes or any of that fancy stuff. There can be trailing whitespace at the end of your output. In our case, that would be:

▉
▉
▉
▉
▉
▉
▉▉ 
▉▉ ▉
▉▉ ▉▉

This is , so shortest code in bytes wins!

\$\endgroup\$
8
  • \$\begingroup\$ The specifications are inconsistent. You take k=4, but there are 5 bars in your histogram; and you say that b must be larger than the max value, but in your answer it equals the last value (thus causing an extra bar). \$\endgroup\$ Commented Oct 7, 2016 at 3:00
  • \$\begingroup\$ @GregMartin, I have edited the question. Is it ok now? \$\endgroup\$
    – Daniel
    Commented Oct 7, 2016 at 4:08
  • 2
    \$\begingroup\$ It's clear now, but you always have exactly 1 value in the last class, no less no more. It seems wrong \$\endgroup\$
    – edc65
    Commented Oct 7, 2016 at 7:47
  • \$\begingroup\$ @edc65, how would that be true? I only said that the last class must contain the max value of the set, just in other words. This means that b must be greater than the max value if b is the (open) upper bound of the histogram \$\endgroup\$
    – Daniel
    Commented Oct 7, 2016 at 11:37
  • \$\begingroup\$ The width of each class is the range of your data set divided by the number of classes so the only value that can be inside the last class is the max value. If I'm wrong, could you please give me an example? \$\endgroup\$
    – edc65
    Commented Oct 7, 2016 at 12:25

5 Answers 5

3
\$\begingroup\$

Python 3, 139

Generates histogram with numpy, takes on account the strange last column rule and prints.

from numpy import*
def f(l):
 h,_=histogram(l,'fd');h[-1]-=1
 for i in zip(*((max(h)-i)*' '+i*'▉'for i in append(h,1))):print(''.join(i))
\$\endgroup\$
2
\$\begingroup\$

05AB1E, 50 49 52 bytes

g.²òD>Ýs¹ZsWs\©-s/*®+v¹y.S>_O})¥ZUv'▉y×ðXy-׫S})øRJ»

Try it online!

Explanation

First we calculate k as the inclusive round up of log2 of the input length.

g.²ò

We then construct a list of upper bounds of the classes.
This is constructerd as range[0 .. k+1]*((max(input) - min(input))/k)+min(input) using the code:

D>Ýs¹ZsWs\©-s/*®+

Now we loop over this list counting the numbers of the input under these upper bounds. As we only want the numbers between each lower and upper bound we reduce this list by subtraction. Unfortunately due to a bug when comparing floats this becomes longer than it should be.

v¹y.S>_O})¥

Now that we have a list of the observation within each class we loop over these creating a matrix of the special char and fill them out with spaces so that each row in the matrix will have equal length.

v'▉y×ðXy-׫S})

This matrix has the staples of the histogram horizontally and we want them vertically. To remedy this, we transpose the matrix. This is the reason we filled the rows with spaces (as transpose need equal length lists). This gives us vertical bars starting from the top running down, so we reverse the order of the rows in the matrix. Now that we have the data in the correct order we print the matrix by joining the rows and joining the columns on newlines.

øRJ»

\$\endgroup\$
2
\$\begingroup\$

CJam, 72 71 bytes

l',-~$__,2b,\)\(:A\;-\d/:B;{AB+:A;_{A<},,_@>}h;]_:e>:M;{'▉*MSe[}%NM*a+z

Try it online!


l',-~$     e# read and sorts the list
__,2b,     e# find k
\)\(:A     e# find max and min (keep min as A)
\;-\d/:B;  e# find width (keep as B)
{          e# while entries remain
  AB+:A;   e#    find intervals upper bound
  _{A<},,  e#    count entries below bound
  _@>      e#    remove entries below bound
}h;        e# end while
]_:e>:M;   e# find maximum size
{'▉*MSe[}% e# convert to bars and pad columns
NM*a+z     e# add newlines and transpose to display upright
\$\endgroup\$
1
\$\begingroup\$

Pyth, 76 bytes

This code is absolutely disgusting. I've mentioned before how bad I am at golfing string-output challenges, right?

J_Mr8Smf>+*T/-h.MZQKh.mbQ.EllQKd)Q)=Y_.tUMme@+J]0xhMJdSheJ;jmj""m@"▉ "qk\ dY

Explanation to follow.

Test it out online!

\$\endgroup\$
1
\$\begingroup\$

R, 105 95 bytes (non-competing)

Takes input from stdin, prints to stdout. As a bonus, outputs an actual histogram as well as the ASCII art. Was able to golf away 10 bytes thanks to partial argument matching (c instead of counts) and reworking the output so as to avoid using apply.

cat(t(cbind(sapply(h<-hist(scan())$c,function(i)c(rep(" ",max(h)-i),rep("▉",i))),"\n")),sep="")

Input:

1.1, 3, 5, 13, 15.5, 21, 29.7, 63, 16, 5, 5.1, 5.01, 51, 61, 1

Output:

▉      
▉      
▉      
▉      
▉▉     
▉▉▉   ▉
▉▉▉  ▉▉

Graphics output; a histogram

This version is non-competing because it just lets R decide what the best number of bins is, using Sturge's formula. (Sturges, H. A. (1926) The choice of a class interval. Journal of the American Statistical Association 21, 65–66.) The OP uses a specific method, which has the last bar reserved exclusively for the maximum value, which is bizarre. R makes it hard to do it this way (perhaps because it was built by statisticians), but it's still possible, in 165 bytes:

k=ceiling(log2(length(i<-scan())))
cat(t(cbind(sapply(h<-hist(i,br=min(i)+diff(range(i))/k*0:(k+1)-1e-5)$c,function(i)c(rep(" ",max(h)-i),rep("▉",i))),"\n")),sep="")

Example output:

▉    
▉    
▉    
▉    
▉    
▉    
▉    
▉    
▉▉ ▉ 
▉▉ ▉▉

enter image description here

\$\endgroup\$
4
  • \$\begingroup\$ I have specified what k is. It is the lowest value for which 2^k>n \$\endgroup\$
    – Daniel
    Commented Oct 7, 2016 at 15:55
  • \$\begingroup\$ @Dopapp Ah, I misinterpreted "the number of bars is not final". I disagree that your definition of k is anything close to "ideal" but it's your challenge so your rules :) I'll update my code when I'm back at my desk. \$\endgroup\$
    – rturnbull
    Commented Oct 7, 2016 at 21:01
  • \$\begingroup\$ Honestly, I agree that the way k is defined is not ideal, but it got a little overly complicated otherwise in my opinion \$\endgroup\$
    – Daniel
    Commented Oct 7, 2016 at 22:14
  • \$\begingroup\$ Right, k isn't ideal, but it's workable. But why the weird bin definition? Why not have k be the number of bins, and have the ranges be [1.04,16.6) [16.6,32) [32,47.5) [47.5,63.1) or something similar? Having the last bar reserved exclusively for max values is really bizarre. Anyway, I've updated my post to meet your original specifications. \$\endgroup\$
    – rturnbull
    Commented Oct 8, 2016 at 8:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.