Skip to content

pandas value_counts

Count how often each distinct value appears in a column.

value_counts is usually the second thing you run on an unfamiliar dataset, right after head. It tells you what is in a column, how lopsided it is, and whether the categories are as tidy as you assumed — which, with real data, they rarely are.

Official documentation: Series.value_counts

The forms worth knowing

# Counts, largest first (the default)
df['Status'].value_counts()

# Proportions instead of raw counts
df['Status'].value_counts(normalize=True)

# Include missing values, which are excluded by default
df['Status'].value_counts(dropna=False)

# Ordered by value rather than by frequency
df['Status'].value_counts().sort_index()

# Combinations across several columns
df.value_counts(['Region', 'Status'])

That last form — calling it on the data frame rather than a column — counts unique combinations, which is often what you actually want.

A worked example, on real data

Netflix published an engagement report listing every title watched over six months. Bamboo Weekly #45 used it.

A natural first question: how much of the catalog is actually available worldwide?

import pandas as pd

url = ('https://www.bambooweekly.com/content/files/4cd45et68cgf/1HyknFM84ISQpeua6TjM7A/'
       '97a0a393098937a8f29c9d29c48dbfa8/'
       'what_we_watched_a_netflix_engagement_report_2023jan-jun.xlsx')

(
    pd.read_excel(url, skiprows=5, usecols=['Available Globally?'])
    ['Available Globally?']
    .value_counts()
)

Which gives:

Available Globally?
No     13700
Yes     4514

Three quarters of the titles are region-locked. That is a one-line finding from 18,214 rows, and it reframes any analysis that follows — comparing global hours to regional hours without knowing this split would be misleading.

Add normalize=True and you get 0.752 and 0.248 instead, which is often the number you actually want to quote.

Three mistakes people make

Forgetting that missing values are dropped by default. value_counts() silently excludes NaN, so the counts may not sum to len(df). If the gaps matter — and in real data they usually do — pass dropna=False and see them.

Reading it as a data frame when it is a series. The result is a series indexed by the distinct values, so result['Yes'] works but result['Available Globally?'] raises a KeyError. Chain .reset_index() if you want a two-column frame.

Trusting the categories to be clean. Real data gives you Yes, yes, and Y as three separate entries. value_counts is precisely the tool that reveals this — treat an unexpectedly long result as a finding about your data, not a nuisance.

Practice it

Work through a value_counts exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/value-counts/

Go deeper

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 71 Bamboo Weekly exercises that use value_counts on real-world data — try each one, then study the worked solution.

Part of the Pandas Methods Index. See also practice by skill.