DataLens

Series and DataFrame basics

Meet pandas' two building blocks, the Series (one labelled column) and the DataFrame (a whole table).

Level: Foundations · 10 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

pandas is the Python library data analysts use to work with tables. Think of it as a spreadsheet you control with code: you can load data, clean it, calculate new columns and summarise it, and then run the exact same steps again next month with one click. No more redoing a report by hand.

Everything in pandas is built from two objects:

Learn these two well and the rest of pandas becomes "which method do I call on my DataFrame?".

By convention, everyone imports pandas with the short name pd. You'll see this line at the top of almost every data script in the world:

import pandas as pd

The Series: one labelled column

A Series is one column of data, like the price column of a product list. The easiest way to make one is from a plain Python list of values:

import pandas as pd

prices = pd.Series([95000, 65000, 110000])
print(prices)
0     95000
1     65000
2    110000
dtype: int64

There are two columns in that output, but only one holds your data. The numbers on the left (0, 1, 2) are the index: the label of each value. pandas adds a numbered index for you if you don't give one. At the bottom, dtype: int64 is the data type, meaning every value is a 64-bit whole number. A Series always has one dtype for all its values, which is what makes it fast.

Numbered labels are fine, but meaningful labels are better. Pass your own with index=:

prices = pd.Series(
    [95000, 65000, 110000],
    index=["Arabica Gayo", "Robusta Lampung", "Toraja Kalosi"],
)
print(prices)
print(prices["Toraja Kalosi"])
Arabica Gayo        95000
Robusta Lampung     65000
Toraja Kalosi      110000
dtype: int64
110000

Now you can look up a value by its label, like a dictionary. This is the key difference from a Python list: a Series knows what each value means.

Maths on the whole column at once

The real power shows up when you do maths. An operation on a Series applies to every value, no loop needed:

with_tax = prices * 1.11
print(with_tax)
Arabica Gayo       105450.0
Robusta Lampung     72150.0
Toraja Kalosi      122100.0
dtype: float64

This is called a vectorised operation. It's shorter than a loop, easier to read, and much faster on large data. Notice the dtype changed to float64, since multiplying by a decimal gives decimals.

The DataFrame: a whole table

A DataFrame is what you'll use most of the time. The easiest way to build one by hand is from a dictionary of lists: each key becomes a column name, and each list becomes that column's values.

orders = pd.DataFrame({
    "product": ["Arabica Gayo", "Ceramic Mug", "French Press", "Robusta Lampung"],
    "quantity": [3, 2, 1, 4],
    "unit_price": [95000, 55000, 185000, 65000],
})
print(orders)
           product  quantity  unit_price
0     Arabica Gayo         3       95000
1      Ceramic Mug         2       55000
2     French Press         1      185000
3  Robusta Lampung         4       65000

Every list must have the same length, because each position is one row. A few attributes tell you about the table's shape:

print(orders.shape)     # (rows, columns)
print(orders.columns)   # the column names
print(orders.dtypes)    # the type of each column
(4, 3)
Index(['product', 'quantity', 'unit_price'], dtype='object')
product       object
quantity       int64
unit_price     int64
dtype: object

object is pandas' name for "general Python objects", which for now means text. Notice that shape has no brackets after it: it's an attribute (a stored fact), not a method you call.

Tip

On this site you can use display(orders) instead of print(orders) to show a DataFrame as a proper table, the way Jupyter notebooks do. Both work.

A column is a Series

Pick one column with square brackets and its name. What comes back is a Series:

print(orders["quantity"])
0    3
1    2
2    1
3    4
Name: quantity, dtype: int64

That's the mental model: a DataFrame is a set of Series glued together by a shared index. Everything you can do to a Series, you can do to a column.

Adding a calculated column

To add a column, assign to a new name. Because columns are Series, the maths is vectorised across every row:

orders["revenue"] = orders["quantity"] * orders["unit_price"]
print(orders)
           product  quantity  unit_price  revenue
0     Arabica Gayo         3       95000   285000
1      Ceramic Mug         2       55000   110000
2     French Press         1      185000   185000
3  Robusta Lampung         4       65000   260000

pandas multiplied row 0 by row 0, row 1 by row 1, and so on. In a spreadsheet you'd write a formula in the first cell and drag it down. Here, one line does every row.

Quick summaries

Once you have a column, the first questions are usually the same: what's the total, what's the average, what's the biggest? Every Series has built-in methods that answer them directly, with no loop needed:

print(orders["revenue"].sum())       # 840000
print(orders["revenue"].mean())      # 210000.0
print(orders["unit_price"].max())    # 185000

Other useful ones are .min(), .count() and .round(1), which rounds every value to one decimal place. You'll meet many more in the coming modules.

Watch out

Column names are case-sensitive and must match exactly. orders["Quantity"] raises a KeyError because the column is called quantity. When you get a KeyError, print orders.columns and compare.

Why this matters for clean code

Two habits start here. First, name DataFrames after what they hold: orders, customers, daily_sales. Code full of df, df2 and df_final is impossible to follow a week later. Second, prefer vectorised operations over loops. orders["quantity"] * orders["unit_price"] says exactly what you mean in one line, and runs fast on a million rows.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: Creating DataFrames and reading CSVs →