Command Palette

Search for a command to run...

Hectal
PHASE 7Intermediate ~22 min· topic 4 of 7

Topic 7.4

Histograms and Distributions

In one line

A histogram counts how many values fall in each range of numbers, so you see the shape of the data instead of single values. d3.bin makes the ranges. The width of the bins can change the story, so you check more than one.

Think of it like this

Sorting coins into a stack of jars labelled 0–10, 10–20, 20–30 and so on. When you are done, the height of each stack tells you how many coins fall in that range. The jars are the bins and the stacks are the bars.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

distribution
How the values of one measurement are spread: where most of them are, and how far they go.
bin
One range of values in a histogram, such as 40 up to (not including) 50.
threshold
The edge between two bins.
histogram
Bars whose heights are the number of values in each bin.
quartile
A value that splits the sorted data: 25% of values lie below the first quartile, 75% below the third.
median
The middle value of the sorted data: half the values are below it and half above.
whisker and outlier
In a box plot, a whisker is the line out to the furthest value that is not far from the box. An outlier is a value beyond the whisker limits, drawn as its own mark.

Step by step

01Bin the batteries and print the edges

The first example prints the bins for the 120 e-bikes. By default: 0-10:4 10-20:1 20-30:3 30-40:11 … up to 100-110:11. The scale of the chart must go to 110 even though no battery is above 100.

With the domain fixed to [0, 100], the bins end at 100 and the last one is 90-100:23. The count there jumped from 12 to 23 because the 11 full batteries moved into it.

02Know the empty cases

d3.bin() on no values gives one empty bin, not an empty array, so check the total before drawing. One value or equal values give a single bin from the value to itself.

03Change the bin width

The three small histograms use bins of 5, 10 and 20. The tallest bar is 13, 23 or 37 depending on the width. All three charts use the same y axis (0 to 40), so the heights can be compared. Every bar for a battery under 10% is red. In the 20-wide chart there is no red bar at all, because the first bin (0–20) holds 5 bikes and is not entirely under 10.

04Draw the box and the dots

The second example draws every e-bike as a dot (the wobble up and down is only for visibility, and uses a seeded random source so the picture is the same each time) and a box plot below. The box runs from 49 to 83.25, the line in it is the median 65, and the whiskers reach from 3 to 100. No dot is outside them, but the four orange dots on the left are the bikes to charge.

Try it yourself

  1. 1

    Let D3 choose the number

    In the first example, in the 0-100 line, replace d3.range(10, 100, 10) by 25. The console prints 20 bins: the hint is rounded to a 1-2-5 step (steps of 5), exactly as in the thresholds hint 25 line.

  2. 2

    Give the full batteries their own bin

    In the 0-100 line change d3.range(10, 100, 10) to d3.range(10, 101, 10). The threshold 100 now exists, and the last two bins are 90-100:12 100-100:11: the 11 full batteries are separated from the rest.

Code & diagrams

Three bin widths for the same 120 batteries New tab

Orange bars are bins that end at 10% or lower. The pedal bikes have `battery: null`, so the example keeps the e-bikes only, and `d3.bin` would skip the nulls without any message anyway.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

default: 11 bins -> 0-10:4 10-20:1 20-30:3 30-40:11 40-50:12 50-60:18 60-70:20 70-80:14 80-90:14 90-100:12 100-110:11
0-100 : 10 bins -> 0-10:4 10-20:1 20-30:3 30-40:11 40-50:12 50-60:18 60-70:20 70-80:14 80-90:14 90-100:23
counted from all 320 bikes: 120
thresholds hint 25 gives 20 bins (D3 rounds to 1-2-5 steps)
e-bikes under 10%: 4 | at exactly 100%: 11
A box plot, every bike as a dot, and bin edge cases New tab

The box plot's whiskers reach from 3 to 100 and no point is an outlier, so the box alone would hide the four low bikes. The dots keep them visible.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

quartiles: 49 65 83.25 | mean: 64.5
whiskers: 3 to 100 | outliers: none
no values      [[null,null,0]]
one value      [[7,7,1]]
all equal      [[4,4,3]]
with undefined [[0,5,1],[5,10,1]]

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Bin the objects instead of the numbers

In the first example's histogram function replace the bins line by const bins = d3.bin()(bikes);: the 320 bike objects, with no .value(...).

terminal
$ Console
── what you'll see ──
Error: <rect> attribute x: Expected length, "NaN".
Error: <rect> attribute width: Expected length, "NaN".

Myth vs fact

Myth

The average battery level tells you which bikes need charging.

Fact

The mean is 64.5% and says nothing about the 4 bikes under 10%. Only the shape shows them.

Myth

A box plot shows everything about a distribution.

Fact

It shows five numbers. Here it hides the four low bikes, because none counts as an outlier by the 1.5 rule.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    d3.bin uses d3.ticks for numeric thresholds, which is why the hint is rounded to a nice step. When the thresholds are evenly spaced it finds each value's bin by arithmetic instead of searching (see the "quantization" comment in d3-array/src/bin.js), which is faster on large arrays and gives the same result. A threshold equal to the domain's end is kept, which is what makes the 100-100 bin in the second Try it possible.

  • ▸

    d3.thresholdSturges, Scott and FreedmanDiaconis pick the number of bins by a formula. On these 120 values they give 11, 6 and 6 bins. A formula is a starting point: check the picture against the question.

Remember this

  1. 1

    The problem this solves: Priya wants to know how charged the e-bikes are, and an average (64.5%) hides the interesting part: are most half-full, or are some almost empty? A histogram shows how the 120 batteries are spread, and where the few problem bikes are.

  2. 2

    **d3.bin returns an array of bins** (recall Topic 4.5: bin is one of the summarising tools of d3-array). Each bin is an array of the data items that fall in it, with two extra numbers: x0 (the start, included) and x1 (the end, excluded). The bin's count is its length. A bar is a rectangle from x(x0) to x(x1) with the height of length.

  3. 3

    Thresholds are the edges between bins. You can pass a number (a hint: D3 rounds it to 1-2-5 steps, so 25 gives 20 bins in the first example), an array of exact edges such as d3.range(10, 100, 10), or a rule like d3.thresholdSturges. By default D3 uses a "nice" domain that runs beyond the data: for batteries from 3 to 100 it makes 11 bins from 0 to 110, and the last one holds the 11 bikes at exactly 100%.

  4. 4

    The last bin includes its end. With the fixed domain [0, 100] and ten bins the last bin is 90 to 100 and it holds 12 bikes under 100% plus 11 at exactly 100%: 23. The bar looks like "bikes that are 90 to 99% charged", but it is really "90 to 100% inclusive". A bin edge always needs a rule for the edge value, and you should write it on the chart.

  5. 5

    Bin width changes the story. The second example draws the same 120 values with bins 5, 10 and 20 wide. With 5-wide bins you see the pile at 100% and the four low bikes in the first two bins. With 10-wide bins the low ones are one red bar of 4. With 20-wide bins they melt into a bar of 5 and look like every other bin. Try several widths and keep the one that does not hide what the question needs.

  6. 6

    Other views of a distribution. A box plot shows the middle (median 65) and the spread (quartiles 49 and 83.25) in one small shape, but it hid the four problem bikes: by the usual rule none of the 120 is an outlier. A dot strip draws every value as a dot, so nothing is hidden. Use a histogram for shape, a box to compare many groups, and dots for a few hundred points.

  7. 7

    Edge cases. No values gives one empty bin [null, null, 0]. One value gives a single bin from 7 to 7. All equal values do the same. null, undefined and NaN are skipped silently: the pedal bikes have battery: null, so binning all 320 bikes counts only 120. Always know how many values you did not count.

Explain it without notes

01

What is in a bin that d3.bin returns?

02

Why does the default bin range run to 110 when no battery is above 100?

03

Why can the width of the bins change what you conclude?

Practice

01

Write histogram(values, width) that returns the bins of values from 0 to 100 in steps of width, and prints how many values fell outside 0–100.

02

Draw the bikes' km_total as a histogram with 8 bins. Hint: look at d3.extent(bikes, (d) => d.km_total) first.

Trade-offs

  • ↔

    Wide bins give a calm shape and hide small groups. Narrow bins show detail and noise. Show two widths when the small group matters.

Done when you can

  • I can bin data with d3.bin().value(...) and read x0, x1 and length.

  • I can say how the first and last edge behave and write the rule on a chart.

  • I can show that a different bin width changes the picture.

  • I know what d3.bin does with null, empty and equal values.