Count the values that are actually there — which is not the same as counting rows.
Have you ever called count on a data frame expecting one number and gotten back a whole column of them? That surprise is the entire story of this method, and it is worth sitting with, because nearly every count bug I have seen starts right there.
count reports how many non-missing values there are. It looks at each column, skips anything absent — NaN, None, NaT, pd.NA — and tells you how many real values survive. On a data frame that means one number per column. On a series it means a single integer. What it never tells you is how many rows you have. That is len(df), which is instant, exact, and does not care about missing data.
Once you stop reading count as "how many rows" and start reading it as "how many values did I actually get," it turns into one of the best diagnostics in Pandas. The gap between the row count and the count is the number of holes, and the holes are usually where the interesting questions hide.
Official documentation: DataFrame.count and Series.count
The arguments that earn their keep
There are two, and both of them belong to the data frame version:
df.count() # non-null values per column — the default
df.count(axis='columns') # non-null values per row
df.count(numeric_only=True) # only the numeric columns
Series.count() takes no arguments whatsoever — one axis, nothing to restrict, nothing to configure. That is a short parameter list for a method this widely used, and it is a good sign: count does one thing.
A worked example, on real data
Bamboo Weekly #15 worked with a data set of every Eurovision Song Contest entrant from 1956 through 2020 — one row per country per year, with the performer, the song, where it placed, and how many points it collected along the way. It is a good data set for this method because the contest kept changing its rules, which means whole columns exist only for some of those years.
import pandas as pd
url = ('https://github.com/Spijkervet/eurovision-dataset/releases/download/'
'2020.0/contestants.csv')
df = pd.read_csv(url)
len(df)
1603
That is the row count, and len is how you get it. Now ask count the same question and watch it answer a different one:
df.count()
year 1603
to_country_id 1603
to_country 1603
performer 1603
song 1600
place_contest 1562
sf_num 557
running_final 1321
running_sf 522
place_final 1320
points_final 1308
place_sf 522
points_sf 522
points_tele_final 104
points_jury_final 104
points_tele_sf 144
points_jury_sf 144
composers 1561
lyricists 930
lyrics 1603
youtube_url 1603
dtype: int64
Twenty-one numbers, one per column, and only five of them equal 1603. That is the whole method in one output. points_tele_final has 104 values, because the televote and jury scores were reported separately for only four contests. sf_num has 557, because semi-finals did not exist until 2004.
Flip that around by subtracting from the row count and you have a missing-data audit in one line, which is how I most often use this method:
(len(df) - df.count()).sort_values(ascending=False).head(8)
points_tele_final 1499
points_jury_final 1499
points_jury_sf 1459
points_tele_sf 1459
running_sf 1081
place_sf 1081
points_sf 1081
sf_num 1046
dtype: int64
Before writing a line of analysis, I know which columns are nearly empty and which ones I can trust. Every one of these gaps turns out to be a rule change rather than a data-quality problem — but I would rather learn that in one line than in a debugging session an hour later.
Turn the axis and you count across each row instead of down each column:
df.count(axis='columns').head()
0 11
1 12
2 12
3 11
4 12
dtype: int64
Eleven or twelve populated fields out of 21 for those 1956 entries. Later rows score higher, which is another way of seeing the same story.
count is also an aggregation, so it works after a groupby. Here is the query from Bamboo Weekly #15, counting entrants per year:
df.groupby('year')['to_country'].count().tail()
year
2016 42
2017 42
2018 43
2019 41
2020 41
Name: to_country, dtype: int64
count or size?
This is where count earns its reputation for confusing people, and it is also where it becomes genuinely powerful. On a groupby, size counts rows and count counts values, so the difference between them is exactly the number of missing values in whatever column you chose:
df.groupby('year').size().tail(6)
year
2015 40
2016 42
2017 42
2018 43
2019 41
2020 41
dtype: int64
df.groupby('year')['points_final'].count().tail(6)
year
2015 27
2016 26
2017 26
2018 26
2019 26
2020 0
Name: points_final, dtype: int64
Forty-one countries entered in 2019 and 26 of them scored points in the final, because the other 15 were knocked out in the semi-finals. And 2020 shows 41 entrants and zero finalists, because that contest was cancelled. Neither fact required a filter, an isna, or a comparison. Subtracting one aggregation from the other was enough.
So: reach for size when you want rows, and for count when the presence of a value is itself what you are measuring.
Four mistakes people make
Expecting a row count. df.count() gives you a series with one entry per column, not a number, and no argument changes that. Worse, the per-column answers are often close enough to the row count to look right. Use len(df) for rows, or df.shape if you want the column count too. Both read the data frame's structure instead of scanning its contents, so they are also far faster.
Treating groupby(...).count() and groupby(...).size() as synonyms. They differ by exactly the missing values, as above, and that difference is silent. This is why so many Bamboo Weekly solutions count a column chosen specifically because it has no gaps — an ID column, usually. If your grouped counts are mysteriously low, the column you counted has holes in it.
Reaching for count when you meant value_counts. They sound like the same idea and they answer completely different questions. df['to_country'].count() returns 1603: how many rows have a country. But df['to_country'].value_counts() returns a series telling you Germany appears 65 times, France 64, Belgium 63. One counts the values; the other counts each distinct value. If you want a breakdown, you want value_counts.
Confusing count with .str.count(). These share a name and nothing else. df['composers'].count() is 1561 — the number of songs with a composer credited. df['composers'].str.count(';') counts semicolons inside each string, one result per row:
(df['composers'].str.count(';') + 1).value_counts().sort_index()
composers
1.0 1082
2.0 273
3.0 129
4.0 52
5.0 18
6.0 6
11.0 1
Name: count, dtype: int64
Composers are separated by semicolons, so adding one turns the semicolon count into a count of people. Most Eurovision songs have a single composer; one has eleven. Note the float64 dtype, too — .str.count() returns NaN for the 42 rows with no composer listed, where plain count would simply have skipped them.
Where it shows up in Bamboo Weekly
Thirty-six Bamboo Weekly exercises use count on real data. Four worth studying, all free to read:
Bamboo Weekly #13: Python developers asks how many people responded to the JetBrains survey, then spends the answer talking readers out of count — and times the alternatives to show what the method costs you as well as what it gets wrong.
Bamboo Weekly #73: Avocado hand groups emergency room visits by month and counts the case-number column, saying why that column in particular: it has few or no missing values. That is the habit worth copying whenever you count after a groupby.
Bamboo Weekly #51: Academy Awards starts with groupby('Film')['FilmId'].count(), discovers it answers the wrong question — films get nominated several times in one year — and switches to nunique. A clean demonstration of counting values when you meant to count distinct ones.
Bamboo Weekly #4: Eating well uses count as a denominator, dividing the count of children eating fewer than one vegetable a day by the count of the whole column, then repeating the trick per state with two groupby calls. When both halves of a fraction skip missing values the same way, the percentage stays honest.
Practice it
Work through a .count() exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/count/
Go deeper
count sits at the front of a small family of missing-data methods. isna marks the gaps, dropna removes the rows that have them, and fillna replaces them; count is how you find out whether any of that is necessary. On the other side, groupby is where count does most of its work. The Pandas user guide on working with missing data explains what Pandas considers absent in the first place, which is the assumption underneath every number count returns.
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.
Related methods
.isna()— when you want the missing values themselves rather than a count of what is present.mean()— which is the number the count belongs next to, since both skip missing values
See it on real data
Below are the 36 Bamboo Weekly exercises that use count on real-world data — try each one, then study the worked solution.
- Bamboo Weekly #182: Surveillance technology
- Bamboo Weekly #180: Movies
- Bamboo Weekly #178: Harmful algal bloom
- Bamboo Weekly #173: IPOs
- Bamboo Weekly #161: Missiles in Israel
- Bamboo Weekly #156: Winter Olympics
- Bamboo Weekly #142: Hurricanes
- Bamboo Weekly #137: UN Security Council
- Bamboo Weekly #132: JetBrains survey
- Bamboo Weekly #124: NATO Spending
- Bamboo Weekly #119: Python conferences
- Bamboo Weekly #116: Philadelphia Fed survey
- Bamboo Weekly #111: State taxes
- Bamboo Weekly #104: Aviation accidents
- Bamboo Weekly #102: WordPress
- Bamboo Weekly #96: Taylor Swift
- Bamboo Weekly #94: Strategic Wine Reserve
- Bamboo Weekly #88: Hot summers
- Bamboo Weekly #87: Nuclear power
- Bamboo Weekly #86: FEMA
- Bamboo Weekly #84: Central banks
- Bamboo Weekly #79: Cyber attacks
- Bamboo Weekly #73: Avocado hand
- Bamboo Weekly #72: City travel
- Bamboo Weekly #71: Holidays
- Bamboo Weekly #70: Moon missions
- Bamboo Weekly #69: Election participation
- Bamboo Weekly #65: Microplastics
- Bamboo Weekly #58: NATO
- Bamboo Weekly #51: Academy Awards
- Bamboo Weekly #46: Pedestrians
- Bamboo Weekly #34: House of Representatives
- Bamboo Weekly #15: Eurovision
- Bamboo Weekly #14: JOLTS
- Bamboo Weekly #13: Python developers
- Bamboo Weekly #4: Eating well
Part of the Pandas Methods Index. See also practice by skill.