Skip to content

pandas info

The structural survey: what your columns are, rather than what your values are.

Which of these columns can I actually use? That is the first honest question after reading a file, and it is not about the numbers. It is about the container: how many rows arrived, what each column is called, what type Pandas decided it is, and how many of its cells are empty. info prints that and nothing else.

It is the twin of describe, and the pair is argued for on the describe page, along with the memory story. This page is the rest of what info knows.

Official documentation: DataFrame.info and Series.info.

The arguments that earn their keep

df.info()                        # the whole survey
df.info(show_counts=True)        # insist on the non-null column
df.info(verbose=True)            # insist on per-column detail
df.info(memory_usage='deep')     # measure strings, not pointers
df.info(buf=open('schema.txt', 'w'))   # send it somewhere other than the screen

A worked example, on real data

OurAirports publishes every airfield, heliport and seaplane base it knows about, and rebuilds the file every day, so your counts will differ a little from mine:

import pandas as pd

url = 'https://davidmegginson.github.io/ourairports-data/airports.csv'

df = pd.read_csv(url)

df.info()
<class 'pandas.DataFrame'>
RangeIndex: 85944 entries, 0 to 85943
Data columns (total 19 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   id                 85944 non-null  int64  
 1   ident              85944 non-null  str    
 2   type               85944 non-null  str    
 3   name               85944 non-null  str    
 4   latitude_deg       85944 non-null  float64
 5   longitude_deg      85944 non-null  float64
 6   elevation_ft       71042 non-null  float64
 7   continent          46206 non-null  str    
 8   iso_country        85641 non-null  str    
 9   iso_region         85944 non-null  str    
 10  municipality       81225 non-null  str    
 11  scheduled_service  85944 non-null  str    
 12  icao_code          10467 non-null  str    
 13  iata_code          9054 non-null   str    
 14  gps_code           44397 non-null  str    
 15  local_code         36033 non-null  str    
 16  home_link          4752 non-null   str    
 17  wikipedia_link     16734 non-null  str    
 18  keywords           21667 non-null  str    
dtypes: float64(3), int64(1), str(15)
memory usage: 19.1 MB

That middle column is a map of what you are allowed to ask. Anything keyed on iata_code covers 9,054 of 85,944 rows, so a join against airline data will drop 89 percent of this file and will not mention it. elevation_ft has 14,902 gaps, so a mean elevation is a mean over the airports that bothered to report one.

And continent is missing 39,738 times, which is 46 percent of the world's airfields and cannot possibly be true. It is not:

df.loc[df['continent'].isna(), 'iso_country'].value_counts().head(3)
iso_country
US    32494
CA     3359
MX     2696
Name: count, dtype: int64

North America. Its code in this file is NA, which is on the list of strings read_csv reads as missing. Load it with keep_default_na=False, na_values=[''] and the column is complete: 39,738 rows marked NA, no gaps at all. One number in the info block was the only sign.

The first two lines are the part nothing else prints. <class 'pandas.DataFrame'> says you have a frame and not a series, which matters when a long chain has quietly collapsed to one column. RangeIndex: 85944 entries, 0 to 85943 says the index is still the row numbers Pandas invented at load time. Run df.set_index('ident').info() and that line becomes Index: 85944 entries, 00A to ZZ-0004 — the type changed, and so did the labels .loc[] accepts.

The trap: the counts vanish on big data

show_counts defaults to counting, but only up to a point. Above pd.options.display.max_info_rows, which is 1,690,785, Pandas decides the walk is too expensive and drops the column — silently, with no note, no ellipsis, nothing:

big = pd.DataFrame({'a': range(1_700_000), 'b': 1.0})

big.info()
<class 'pandas.DataFrame'>
RangeIndex: 1700000 entries, 0 to 1699999
Data columns (total 2 columns):
 #   Column  Dtype  
---  ------  -----  
 0   a       int64  
 1   b       float64
dtypes: float64(1), int64(1)
memory usage: 25.9 MB

The output still looks complete, and it is missing the one thing you opened it for — on exactly the files where the gaps are hardest to spot by eye. Pass show_counts=True and the column comes back. Width throws the same switch: past 100 columns you lose the per-column block entirely, and it takes verbose=True, show_counts=True together to get it back.

Three mistakes people make

Expecting a value back. info returns None. It prints. A chain ending in .info() ends in nothing at all, and df.info().shape raises AttributeError. When you want these facts as data rather than as text, they are three ordinary calls: df.dtypes, df.count(), and df.memory_usage(deep=True). When you want the text itself — to log it, or to diff today's schema against last week's — pass buf=.

Reading a non-null count as a row count. They agree only for columns with no gaps. len(df) is the row count, and the interesting thing about the block above is that a dozen different numbers appear in one column.

Assuming the memory line is undercounting. It used to be, always. With PyArrow installed, df.info() and df.info(memory_usage='deep') both report 19.1 MB here, because Arrow-backed strings know their own size. Pass deep anyway: it is either free or it is the only correct answer. The per-column version is on memory_usage.

Where it shows up in Bamboo Weekly

Bamboo Weekly #2: Egg prices is info doing its actual job, in one sentence: the Date column "contains text, as you'll see if you run either df.info() or df.dtypes." Everything that follows — pd.to_datetime, then the date arithmetic — starts from that one glance. Free to read.

Bamboo Weekly #10: Oil prices walks through the memory line in the old world and the new one: 822.6+ KB with the plus sign, 4.7 MB once memory_usage='deep' counts the Python strings, and 728.3 KB reading the same file through PyArrow, where "we don't need to do anything special to get the true memory usage." Also free.

Bamboo Weekly #140: Stack Overflow survey is the width limit in the wild. With 170 columns the printed output is the collapsed form — Columns: 170 entries, ResponseId to JobSat, no per-column detail at all. What it does show is 356.8 MB, which astype('category') then cuts to 66.4 MB.

Practice it

Work through an info exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/info/

Go deeper

describe covers the values, memory_usage breaks the last line down per column, and select_dtypes acts on the dtype column rather than reading it. When the dtypes are wrong the fix usually belongs in read_csv rather than in astype afterward, and when the gaps are the story, that is isna.

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