Skip to content

pandas head

The first look at a data frame — and why it should never be the only look.

What is actually in this file? head is the first thing nearly everyone types after a read_csv or read_excel call, and it does exactly what you would guess: it returns the first five rows. One parameter, n, defaulting to 5. It works on a series too, and asking for more rows than exist returns all of them rather than an error.

That is the entire method. The interesting question is not how head works but what you do with what it shows you, because five rows off the top of a file is a small sample and not a random one. Somebody chose that order. Reading those rows as a description of the data set is the most common way I see an analysis start out wrong.

Official documentation: DataFrame.head and Series.head

The one argument

df.head()      # the first 5 rows
df.head(20)    # the first 20
df.head(-3)    # everything EXCEPT the last 3 rows
s.head(2)      # the first 2 values of a series

The third line is the one almost nobody knows about. A negative n means "leave off this many from the end," so head(-3) keeps everything but the final three:

pd.Series([10, 20, 30, 40, 50]).head(-2)
0    10
1    20
2    30
dtype: int64

That is how you drop a "Source: …" footer, a totals row, or the incomplete current month at the end of a time series, without counting the good rows first. tail plays the same trick from the other end.

A worked example, on real data

Here is the Netflix engagement report from Bamboo Weekly #45 — every title the service streamed in the first half of 2023:

import pandas as pd

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

netflix = pd.read_excel(url, header=5,
                        usecols=['Title', 'Available Globally?',
                                 'Release Date', 'Hours Viewed'])

netflix.head()
                                 Title Available Globally? Release Date  Hours Viewed
0            The Night Agent: Season 1                 Yes   2023-03-23     812100000
1            Ginny & Georgia: Season 2                 Yes   2023-01-05     665100000
2   The Glory: Season 1 // 더 글로리: 시즌 1                 Yes   2022-12-30     622800000
3                  Wednesday: Season 1                 Yes   2022-11-23     507700000
4  Queen Charlotte: A Bridgerton Story                 Yes   2023-05-04     503000000

English-language hits, released globally, complete release dates, hundreds of millions of hours apiece. Every one of those impressions is wrong, and three more lines say so:

netflix.shape
netflix.dtypes
netflix.isna().sum()
(18214, 4)

Title                             str
Available Globally?               str
Release Date           datetime64[us]
Hours Viewed                    int64
dtype: object

Title                      0
Available Globally?        0
Release Date           13359
Hours Viewed               0
dtype: int64

Eighteen thousand rows, not five. Release Date arrived as a real datetime rather than text — worth confirming every time, since a column that comes back as str is a job for astype or pd.to_datetime. And 73 percent of those dates are missing, which the first five rows gave no hint of; dropna is the next stop if that matters to your question.

Then ask for five rows nobody arranged:

netflix.sample(5, random_state=0)
                                                   Title Available Globally? Release Date  Hours Viewed
3539                         The Dragon Prince: Season 1                 Yes   2018-09-14       5100000
10964                             Miss Butcher // 미스 푸줏간                  No          NaT        400000
10123                                Il principe abusivo                  No          NaT        500000
14385                                       Yuva // युवा                  No          NaT        200000
6850   A Familiar Stranger: Season 1 // L'Absente: Se...                  No          NaT       1400000

That is the real data set: mostly non-English catalog titles, mostly not available globally, mostly undated, mostly small. The file is sorted by hours viewed, so head was handing me the five biggest rows in it. (random_state is there only so you can reproduce my output.)

Three mistakes people make

Judging a data set from its first five rows. The median title here was watched for 700,000 hours; the median of the five rows head showed me is 622,800,000, nearly a thousand times larger. All five are available globally, while 13,700 of the 18,214 rows are not. Nothing warns you, because a sorted file looks exactly like an unsorted one. head tells you the column names, the dtypes and the shape of a row; .sample(5) tells you what a row usually looks like. Run both.

Expecting groupby(...).head(n) to return the first n groups. It returns the first n rows of every group, stacked back together, so two groups and n=2 give you four rows:

netflix.groupby('Available Globally?').head(2)
                                                Title Available Globally? Release Date  Hours Viewed
0                           The Night Agent: Season 1                 Yes   2023-03-23     812100000
1                           Ginny & Georgia: Season 2                 Yes   2023-01-05     665100000
6                          La Reina del Sur: Season 3                  No   2022-12-30     429600000
20  Fake Profile: Season 1 // Perfil falso: Tempor...                  No   2023-05-31     206500000

Sort first and that is a fine way to get the top n within each group. It is just never what people thought they had asked for.

Assuming you can write to what head returns. netflix.head()['Hours Viewed'] = 0 looks like an edit and is not one. Under Pandas 3's copy-on-write rules you get a ChainedAssignmentError warning rather than an exception, the original data frame is untouched, and the intermediate object you did change is discarded on the next line. If you mean to assign, do it in a single step with .loc[].

Where it shows up in Bamboo Weekly

Bamboo Weekly #45: Netflix is the file above, and the exercise really begins at the moment df.isna().sum() reports 13,359 missing release dates.

Government corruption is head doing the job it is best at. Transparency International's spreadsheet puts two rows of titling above the real column names, so the first read produces nonsense; pd.read_excel(filename, header=2) fixes it, and df.head() is how you confirm the fix at a glance.

Bamboo Weekly #33: Fracking makes the case on a much bigger file: six million records, and df.isna().sum() finds 60,468 missing values concentrated in TotalBaseWaterVolume — the one column every question depends on.

Bamboo Weekly #79: Cyber attacks uses the other head: df['country'].value_counts().head(5) for the five most-affected countries. On a series that is already sorted, that is head at its most honest.

Practice it

Work through a .head() exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/head/

Go deeper

tail is the same method aimed at the bottom of the frame, and needs more care than this one. describe and isna round out the honest first look, sort_values is what makes head mean "the top ten" rather than "the first ten", and value_counts usually answers what people hoped head would.

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

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