Pandas DataFrames: Tables in Python

A spreadsheet you can ask questions

Advanced 15 min

In this lesson

NumPy handles columns of numbers. Real data is a whole table of names, dates, categories and numbers together, and that is what pandas is for.

This is the library you will use most. If you take one thing from this unit, take the DataFrame.

Explain it like I’m 5

A DataFrame is a spreadsheet that Python can ask questions about — and unlike a spreadsheet, the questions are written down and repeatable.

The whole table as one object

A DataFrame holds the entire table: columns with names, rows with positions, and different types in different columns. Building one from a dictionary is the quickest way to start: each key becomes a column.

Printing it shows the table, and the leading numbers are the index: pandas' label for each row, starting at 0 unless you give it something better.

Example
import pandas as pd

grades = pd.DataFrame({
    "student": ["Ada", "Sam", "Rae", "Kit", "Joe"],
    "subject": ["math", "math", "art", "art", "math"],
    "score":   [91, 78, 84, 66, 95],
})

print(grades)
print()
print("shape:", grades.shape)
Output
  student subject  score
0     Ada    math     91
1     Sam    math     78
2     Rae     art     84
3     Kit     art     66
4     Joe    math     95

shape: (5, 3)
Three columns, five rows, one object.

Reading a real file, and looking before you leap

In practice the data comes from a file. pd.read_csv() handles the CSV parsing you did by hand in Unit 3, working out the column names from the header row.

And then .head(), which shows the first few rows. This is the habit to build: look at the table before calculating anything. It takes one line and catches wrong delimiters, missing headers, and shifted columns immediately.

Example
import io
import pandas as pd

# Normally this is a filename: pd.read_csv("grades.csv")
# Here the CSV is written inline so the example runs anywhere.
CSV = """student,subject,score
Ada,math,91
Sam,math,78
Rae,art,84
Kit,art,66
Joe,math,95
"""

grades = pd.read_csv(io.StringIO(CSV))
print(grades.head(3))
Output
  student subject  score
0     Ada    math     91
1     Sam    math     78
2     Rae     art     84
head(3) shows the first three rows. The default is five.

Selecting columns and filtering rows

Two operations do most of the work. Selecting picks columns; filtering picks rows.

One column comes back as a Series, a single labeled column, which is essentially a NumPy array that remembers its index. Several columns come back as another DataFrame, and that needs double brackets, which is one of the two things everyone gets wrong at first.

Filtering uses the boolean indexing you met in NumPy: write a condition, and pandas keeps the rows where it was true.

Example
import pandas as pd

grades = pd.DataFrame({
    "student": ["Ada", "Sam", "Rae", "Kit", "Joe"],
    "subject": ["math", "math", "art", "art", "math"],
    "score":   [91, 78, 84, 66, 95],
})

# One column is a Series.
print(type(grades["score"]).__name__)
print(grades["score"].mean())
print()

# Two or more columns is a DataFrame - note the double brackets.
print(grades[["student", "score"]].head(2))
print()

# Filtering: a condition gives True/False, and that picks the rows.
print(grades[grades["score"] >= 85])
Output
Series
82.8

  student  score
0     Ada     91
1     Sam     78

  student subject  score
0     Ada    math     91
4     Joe    math     95
Single brackets for one column, double for several.

Summarizing by group

The question is rarely “what is the average score?” It is usually “what is the average score per subject?” That is groupby, and it is the point at which pandas starts saving you real work.

Read it as a sentence: group by subject, take the score column, give me the mean of each group.

Example
import pandas as pd

grades = pd.DataFrame({
    "student": ["Ada", "Sam", "Rae", "Kit", "Joe"],
    "subject": ["math", "math", "art", "art", "math"],
    "score":   [91, 78, 84, 66, 95],
})

print(grades.groupby("subject")["score"].mean())
print()
print(grades["subject"].value_counts())
Output
subject
art     75.0
math    88.0
Name: score, dtype: float64

subject
math    3
art     2
Name: count, dtype: int64
Two summaries, one line each. The equivalent loops would be a dozen lines.

Find the students who scored above 80. Filter the table with a condition, then report how many there were and their average score rounded to one decimal place.

import pandas as pd

grades = pd.DataFrame({
    "student": ["Ada", "Sam", "Rae", "Kit", "Joe"],
    "subject": ["math", "math", "art", "art", "math"],
    "score":   [91, 78, 84, 66, 95],
})

# TODO: keep only the rows where score is above 80
passed = grades

print(passed)
print("how many:", len(passed))
print("their average:", round(passed["score"].mean(), 1))

Common mistake: Forgetting the second pair of brackets for several columns

Why it happens:

One column needs single brackets, so two columns look like they should too.

How to fix it:

Selecting several columns means passing a list: df[["a", "b"]]. The inner brackets are the list.

Common mistake: Confusing a Series with a DataFrame

Why it happens:

They print similarly and share many method names.

How to fix it:

One column is a Series; two or more is a DataFrame. Check with type(x).__name__ when a method is unexpectedly missing.

Common mistake: Using = instead of == inside a filter

Why it happens:

The condition reads like plain English, where “is” is one word.

How to fix it:

== compares, = assigns. df[df["subject"] == "art"].

Common mistake: Chaining conditions with and / or

Why it happens:

Those are the Python keywords for combining truth values everywhere else.

How to fix it:

Use & and |, and bracket each condition: df[(df["a"] > 1) & (df["b"] < 5)].

What does head() show?

How do you select a single column called score?

What does df[df["score"] > 80] do?

Mini exercise (medium)

A small table of books with title, year, and rating. Show the title and year of every book published in 1980 or later, find the two highest-rated books by name, and print the average rating across all five rounded to two decimals.

Now you. Edit the starter code below, then Run it, everything happens in the browser.

import pandas as pd

books = pd.DataFrame({
    "title":  ["Dune", "Neuromancer", "Snow Crash", "Ubik", "Solaris"],
    "year":   [1965, 1984, 1992, 1969, 1961],
    "rating": [4.6, 4.1, 4.3, 3.9, 4.4],
})

modern = books          # TODO: only books from 1980 onwards
best = books            # TODO: the two highest-rated books

print(modern[["title", "year"]])
print()
print("top two by rating:", list(best["title"]))
print("average rating:", round(books["rating"].mean(), 2))

What to learn next

You now have the tool you will reach for most: building a DataFrame, reading a CSV, head() before anything else, single brackets for a Series and double for several columns, filtering with a condition, and groupby turning a page of loops into one readable line.

Every table so far has been suspiciously tidy. Cleaning Data deals with what real files are actually like, and it is the lesson you will use every single time.