One bar per row, one color per column.
How does Pandas decide what a bar chart's bars are? The same way it decides anything about .plot: by looking at the shape. Every row of the data frame becomes a position on the x axis, and every column becomes a series drawn at that position. With one column you get one bar per row. With four columns you get four bars per row, side by side — or four segments of one bar, if you pass stacked=True.
That makes the reshape the entire design decision. Choosing what goes in the index and what goes in the columns is choosing what the chart compares.
Official documentation: pandas.DataFrame.plot.bar.
The arguments that earn their keep
s.plot.bar() # a Series: one bar per index entry
df.plot.bar() # grouped bars, one color per column
df.plot.bar(stacked=True) # segments of one bar instead
df.plot.bar(figsize=(10, 10)) # almost always needed for real labels
df.plot.bar(rot=0) # stop rotating the tick labels
df.plot.bar(y='co2') # one column out of many
stacked=True and figsize are the two that change outcomes. Stacking answers a different question from grouping — stacked shows the total and its composition, grouped shows the members against each other — so pick the one that matches the sentence you would write underneath. figsize matters more here than on a line chart, because bar charts carry a text label under every bar and the default canvas runs out of room fast.
A worked example, on real data
A Series is the simplest case, and it is worth starting there because the index does the labeling:
import pandas as pd
url = 'https://raw.githubusercontent.com/owid/co2-data/master/owid-co2-data.csv'
df = pd.read_csv(url, usecols=['country', 'iso_code', 'year', 'co2'])
(df
.loc[(df['year'] == 2023) & (df['iso_code'].str.len() == 3)]
.nlargest(8, 'co2')
.set_index('country')['co2']
.plot.bar())
The Series going in:
country
China 12172.0
United States 4918.0
India 3063.0
Russia 1733.0
Japan 987.0
Iran 790.0
Indonesia 762.0
Saudi Arabia 677.0
Eight rows, so eight bars, labeled from the index. The iso_code filter is doing quiet work: without it, "World", "Asia" and "High-income countries" would take the top places, because Our World in Data mixes aggregates and countries in the same column. Any bar chart of a country ranking needs that filter or it is a chart of nothing.
Note that set_index('country') is what produces the labels. Skip it and the bars are numbered 0 through 7, which is the single most common way a Pandas bar chart comes out unreadable.
Three mistakes people make
Leaving the labels to chance. Bars are labeled from the index, so the index has to be the thing you want written under each bar. If the chart shows integers along the bottom, you plotted a default RangeIndex and the names are still sitting in a column.
Stacking things that do not add up. stacked=True draws a total, which means the segments must be parts of one whole. Stacking percentages that each run to 100, or counts from overlapping categories, produces a bar whose height means nothing. Grouped bars are the honest default; stacking is the special case that has to be earned.
Plotting too many bars. A bar chart is a table you can look at, and it stops working somewhere around thirty bars. If the answer is "the top few", say so in the code with nlargest rather than drawing two hundred bars and hoping the reader finds them.
Where it shows up in Bamboo Weekly
Bamboo Weekly #66: Pittsburgh is the clearest progression on the site. It starts with pivot_table(index=df['CREATED_ON'].dt.year, columns='REQUEST_ORIGIN', aggfunc='count', values='REQUEST_ID').plot.bar(), then runs the same chain with stacked=True, figsize=(10, 10), then again after collapsing a family of similar categories with replace. Three versions of one chart, each fixing what the last one made obvious.
Bamboo Weekly #70: Moon missions counts launches per five-year bucket with pd.Grouper and plots the resulting Series directly, then switches to a pivot_table by operator to get plot.bar(stacked=True) — the same data, once as a total and once as a composition.
Bamboo Weekly #72: City travel sorts before it stacks: .groupby('Country')[['Active', 'Bus', 'Car']].mean() .sort_values('Car').plot.bar(stacked=True). Sorting a stacked bar chart by one of its segments is a small thing that makes the pattern legible.
Practice it
Work through a plot.bar exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/plot-bar/
Go deeper
plot.barh is the same chart on its side, and the better choice whenever the category names are words. plot.line is for time rather than categories. pivot_table and groupby build the shape, and nlargest keeps the bar count honest. For a version a reader can hover over, see plotly express bar.
More Pandas videos on Python and Pandas with Reuven Lerner.
Bamboo Weekly is the practice. If you want the structured version — full courses with downloadable Jupyter notebooks, plus live Pandas office hours when you get stuck — that is LernerPython+Data. A paid Bamboo Weekly subscription is included with it.
See it on real data
Below are the 30 Bamboo Weekly exercises that use plot.bar on real-world data — try each one, then study the worked solution.
- Bamboo Weekly #131: Canadian border crossings
- Bamboo Weekly #128: Extreme heat
- Bamboo Weekly #126: EV sales
- Bamboo Weekly #124: NATO Spending
- Bamboo Weekly #119: Python conferences
- Bamboo Weekly #117: Electricity
- Bamboo Weekly #116: Philadelphia Fed survey
- Bamboo Weekly #114: International trade
- Bamboo Weekly #113: US airport traffic
- Bamboo Weekly #112: Programming jobs
- Bamboo Weekly #111: State taxes
- Bamboo Weekly #108: Measles
- Bamboo Weekly #106: Flu season
- Bamboo Weekly #105: Federal employees
- Bamboo Weekly #104: Aviation accidents
- Bamboo Weekly #103: CDC data
- Bamboo Weekly #102: WordPress
- Bamboo Weekly #99: Literacy and numeracy
- Bamboo Weekly #97: Drones
- Bamboo Weekly #94: Strategic Wine Reserve
- Bamboo Weekly #88: Hot summers
- Bamboo Weekly #86: FEMA
- Bamboo Weekly #72: City travel
- Bamboo Weekly #70: Moon missions
- Bamboo Weekly #66: Pittsburgh
- Bamboo Weekly #62: Economic report card
- Bamboo Weekly #59: Long covid
- Bamboo Weekly #58: NATO
- Bamboo Weekly #54: Household debt
- Bamboo Weekly #46: Pedestrians
Part of the Pandas Methods Index. See also practice by skill.