Bar & Distribution Charts
It is Saturday and Maya the bakery owner has logged every order, 30 of them, into a little table: the headline item the customer came for, what they spent, and whether it was the morning rush or a quiet afternoon. Two questions are nagging her. Which pastry is pulling its weight? and what does a typical order actually look like? A scatter or a line, the charts from Lesson 2, answer neither. These questions need bars and distributions.
In Lesson 2 you used geom_point() for a relationship and geom_line() for a value moving through time. Now you meet the charts that compare categories and reveal the shape of a single column of numbers.
By the end of this lesson you will be able to:
- Use
geom_col()to compare an amount across categories, andgeom_bar()to compare counts, and say exactly which one does the counting - Draw a histogram with
geom_histogram()to see how one continuous variable is spread, and choose a sensible bin width - Read and draw a boxplot with
geom_boxplot()to compare a distribution across groups - Match a question to the chart that answers it
Prerequisites: Lesson 1, the grammar of graphics (data, aes(), geoms, layering with +) and Lesson 2, scatter and line charts. Every new term is defined as it appears.
Bars compare categories
A bar chart answers one question: how do categories compare on some number? Each category gets its own bar, and the bar's height is the number. Maya's pastries are the categories; the dollars each one earned is the number.
Start, as always, by building Maya's Saturday right here. Each lesson runs in a fresh R session, so the data lives on this page (run this once):
Maya already knows her per-pastry revenue is worth plotting, so first she totals it up, one row per pastry, with the dplyr verbs from earlier in the course:
Now revenue is an amount she already has, one value per category. The geom that draws a bar at a height you supply is geom_col() ("col" for column): map pastry to x and revenue to y, and it does no arithmetic, it just draws your numbers.
The bars rank the pastries by money. Sourdough wins, on only 3 orders, while Coffee, ordered far more often, comes second. Hold on to that surprise.
geom_col gives the amount, geom_bar counts the rows
There are two bar geoms, and the difference is the single most common confusion in ggplot2. It comes down to one question: who computes the bar height, you or ggplot?
geom_col()uses youryexactly as given. It is the right tool when you have already worked out one number per category (Maya'srevenue). In grammar terms it usesstat = "identity": the height is the data.geom_bar()is for when you have not summarised yet. Hand it the raworderstable, map onlyx, and it counts the rows in each category for you. That isstat = "count": the height is a tally ggplot computes.
So to ask "how many orders did each pastry get?" you do not summarise at all, you let geom_bar() tally the 30 raw rows:
No y in the mapping, because the count is invented by the geom. Coffee towers here with 14 orders; Sourdough is the shortest bar at 3. This is the mirror image of the revenue chart: most ordered is not most valuable.
geom_col() plots an amount you supply (you give x and y). geom_bar() plots a count it computes (you give only x). Reach for geom_bar() on raw rows, geom_col() on a summary table.Which bar geom?
Maya has just built by_pastry, a four-row table with one revenue total per pastry, and she wants a bar chart of those revenue totals. Which geom draws it correctly?
geom_col() draws each bar at the height she supplies. No counting needed.geom_bar() computes a count (stat = "count"); geom_col() uses your value (stat = "identity"). On a pre-summarised table only geom_col() shows the revenue.Histograms: where do the values pile up?
Bars compared categories. Now switch questions entirely: Maya wants to understand one continuous column, order_value, on its own. Not per pastry, not over time, just: across all 30 orders, what does a typical order look like, and how spread out are they?
A histogram answers exactly that. It chops the range of the numbers into equal-width slices called bins, then counts how many orders fall in each bin and draws a bar for the count. Crucially you map only x (the continuous variable); like geom_bar(), the height is a count ggplot computes. The control that matters is binwidth, the width of each slice in the data's own units (dollars here):
Read the shape. Most orders cluster low, between about 4 and 9 dollars, a single coffee or a pastry. Then a long, thin tail stretches out to the right: a handful of big basket orders worth 22, 24 and 30 dollars. That lopsided shape, a tall pile on the left with a tail to the right, is called a right skew, and you can confirm it with one line: the mean is dragged above the median by those large orders.
The mean (8.93) sits above the median (7) precisely because the big orders pull the average up while the median, the middle order, ignores them. A histogram makes a skew like this jump off the page.
geom_histogram() with no binwidth and ggplot picks 30 bins and prints a message nudging you to choose your own. With only 30 orders that default leaves most bins empty and the shape looks ragged. Always set binwidth (or bins) to something the data justifies: too wide hides the shape, too narrow turns it into spiky noise.Set the bin width
Below is the histogram of order_value, but the bin width is missing. Fill in the blank so each bar covers a 4-dollar-wide slice of order value.
Show answer
ggplot(orders, aes(x = order_value)) +
geom_histogram(binwidth = 4)Bar chart or histogram?
Two charts both draw rectangles, so they are easy to confuse. A bar chart has one bar per category (Coffee, Croissant, ...). A histogram has one bar per bin of a single continuous number. Maya wants to see how her order_value (a continuous dollar amount) is distributed across all 30 orders. Which chart, and why?
geom_histogram() slices its range into bins and counts orders per bin, revealing the right-skewed shape a bar chart cannot.geom_point() needs an x and a y to relate two variables. Here there is only one variable and one question, its distribution, which is the histogram's whole job.Boxplots: a distribution squeezed into a box
A histogram shows one distribution beautifully, but stack several histograms to compare groups and they overlap into mush. When Maya asks are morning orders bigger than afternoon orders, and which is more variable? she wants two distributions side by side. That is the boxplot's job: it compresses each group's distribution into a compact box you can line up.
A boxplot is a picture of the five-number summary. Sort a group's order values and read off five landmarks: the minimum, the first quartile \(Q_1\) (the value below which 25% of orders fall), the median \(Q_2\) (the middle order, half below and half above), the third quartile \(Q_3\) (75% of orders fall below it), and the maximum.
The box is drawn from \(Q_1\) to \(Q_3\), so it holds the middle half of the orders; its height is the interquartile range \(\text{IQR} = Q_3 - Q_1\), a measure of spread that a few unusually large orders cannot inflate. The line across the box is the median. The whiskers reach out to the most extreme order still within \(1.5 \times \text{IQR}\) of the box; any order beyond \(Q_3 + 1.5\,\text{IQR}\) or below \(Q_1 - 1.5\,\text{IQR}\) is drawn as a lone point, a candidate outlier.
Map the grouping column to x and the number to y:
Now the comparison is immediate. The Morning box sits higher (median 9 dollars, box running from about 6 to 12) than the Afternoon box (median 6, a tighter 5-to-8 box), and Morning's two fattest baskets, 24 and 30 dollars, sit past the whisker as outlier points near the top. Morning orders are both larger and more spread out, the whole answer in one glance.
Compare the two dayparts
You have the mapping: daypart on x, order_value on y. Add the layer that draws one box per daypart so Maya can compare the two distributions.
Show answer
ggplot(orders, aes(x = daypart, y = order_value)) +
geom_boxplot()Which chart answers which question?
You now have four tools, and choosing well is mostly about naming your question first. Here is the map:
| Your question | The chart | The geom |
|---|---|---|
| Compare categories on an amount I already computed | bar chart | geom_col() |
| How many rows fall in each category? | bar chart | geom_bar() |
| How is one continuous variable spread out (shape, skew)? | histogram | geom_histogram() |
| How does a distribution differ across a few groups? | boxplot | geom_boxplot() |
Maya has a new question: are weekend baskets typically bigger than weekday baskets, and which day type has the more variable order value? She wants to compare the center and the spread of order_value across two groups, weekday and weekend. Which chart should she reach for?
order_value to y draws one box per group, so she reads and compares both the median (center) and the box height (spread) at a glance, which is precisely the question.References
A few authoritative places to take this further:
- ggplot2 reference: geom_bar() and geom_col() - the official explanation of why one counts (
stat = "count") and the other does not (stat = "identity"). - ggplot2 reference: geom_histogram() - bins, binwidth, and the choices behind that 30-bin default message.
- ggplot2 reference: geom_boxplot() - exactly how the box, whiskers and outlier points are computed.
- R for Data Science (2e): Exploratory Data Analysis - when to reach for a bar, a histogram or a boxplot while exploring, with worked examples.
Lesson 3 complete
You can now pick a chart by the question it answers. geom_col() draws an amount you supply; geom_bar() counts raw rows for you, and confusing the two is the classic bar-chart trap you have now dodged. geom_histogram() bins one continuous variable to reveal its shape and skew, with binwidth as the dial that matters. geom_boxplot() squeezes each group's distribution into a five-number-summary box so you can compare center and spread across groups at a glance, and you know it trades shape for that comparability.
Next, Lesson 4: A ggplot2 gallery and publication figures. You will tour the common chart types as a quick chooser, then take one plain plot and polish it to publication quality with titles, labels, and a clean theme.