Two columns, one point per row.
Why does this one refuse to run without arguments, when .plot.line() and .plot.bar() are happy on their own? Because a scatter plot has no default. Line and bar both read the index for one axis and the columns for the other, so the shape tells them everything. A scatter plot has two axes and no index involvement at all — it needs to be told which column is horizontal and which is vertical, and there is no sensible guess.
So .plot.scatter(x='...', y='...') is the minimum, and every row of the frame becomes one point.
Official documentation: pandas.DataFrame.plot.scatter.
The arguments that earn their keep
df.plot.scatter(x='population', y='co2_per_capita')
df.plot.scatter(x='population', y='co2_per_capita', logx=True) # skewed x
df.plot.scatter(x='a', y='b', alpha=0.3) # see through overlapping points
df.plot.scatter(x='a', y='b', s='population') # size by a third column
df.plot.scatter(x='a', y='b', c='continent') # color by a third column
df.plot.scatter(x='a', y='b', figsize=(9, 7))
alpha is the one that rescues most real scatter plots. Anywhere past a few hundred points the marks pile up and a dense region looks identical to a moderately busy one; dropping opacity to 0.3 turns overlap back into information. logx and logy come next, because so many interesting quantities — population, income, company size — are spread across orders of magnitude, and on a linear axis every point but the outliers ends up in the corner.
A worked example, on real data
Population against emissions per person, one point per country:
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_per_capita', 'population'])
(df
.loc[(df['year'] == 2023) & (df['iso_code'].str.len() == 3)]
.dropna(subset=['co2_per_capita', 'population'])
.plot.scatter(x='population', y='co2_per_capita', logx=True, alpha=0.5))
That gives 213 points. The distribution behind them explains both extra arguments:
count 213.00
mean 4.61
std 5.51
min 0.06
25% 0.99
50% 3.14
75% 5.76
max 40.13
Per-capita emissions run from 0.06 to 40 tonnes, and population from tens of thousands to over a billion. On linear axes almost every country sits crushed against the left edge while China and India occupy the rest of the canvas, so logx=True is doing the real work here. alpha=0.5 handles the cluster of small, low-emitting countries that would otherwise be one solid blob.
The dropna is not optional. plot.scatter silently skips rows where either coordinate is missing, so without it you get a chart drawn from an unknown number of countries.
Three mistakes people make
Forgetting that it drops missing rows quietly. Line charts leave a visible gap; scatter plots just have fewer points, and nothing tells you how many. Run .isna().sum() on both columns, or dropna(subset=[...]) explicitly, so the point count is a decision rather than an accident.
Plotting on linear axes out of habit. If a column's maximum is a thousand times its median, the linear chart is a picture of the outliers and nothing else. Check with .describe() before you draw.
Reading a shape as a cause. A scatter plot is very good at suggesting relationships that turn out to be population size in disguise. If both axes scale with something else, corr on the residuals is a better next step than another chart.
Where it shows up in Bamboo Weekly
Bamboo Weekly #49: Campaign finance is the method at its plainest — df.plot.scatter(x='TTL_DISB', y='TTL_RECEIPTS'), money in against money out, one point per campaign. When the two axes are the same kind of quantity, the interesting thing is the diagonal.
Bamboo Weekly #60: Iceland and Bamboo Weekly #72: City travel both build the frame at some length before scattering it — cleaning a price column in one case, stacking and joining in the other. That is typical: the scatter call is one line and the reshape above it is ten.
Practice it
Work through a plot.scatter exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/plot-scatter/
Go deeper
corr puts a number on what the scatter plot suggests. plot.hist is worth drawing first on each axis, to see whether a log scale is needed. dropna makes the missing rows explicit. For hover labels telling you which country a point is, plotly express scatter is the better tool.
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 9 Bamboo Weekly exercises that use plot.scatter on real-world data — try each one, then study the worked solution.
- Bamboo Weekly #108: Measles
- Bamboo Weekly #94: Strategic Wine Reserve
- Bamboo Weekly #92: Climate disaster costs
- Bamboo Weekly #72: City travel
- Bamboo Weekly #64: Coal power
- Bamboo Weekly #61: Solar eclipse
- Bamboo Weekly #60: Iceland
- Bamboo Weekly #49: Campaign finance
- Bamboo Weekly #42: Plant hardiness
Part of the Pandas Methods Index. See also practice by skill.