Python Tutorial
Introduction to pandas: Series and DataFrames
pandas is the most important library for working with tabular data in Python — think of it as a programmable spreadsheet. It reads data from CSV, Excel, JSON, SQL databases and more into a DataFrame: a table with labelled columns (each with its own type) and a labelled row index. It then lets you inspect, clean, filter, transform, group, join and export that data in a few lines.
This lesson introduces the two core objects, Series and DataFrame, shows how to create them and load files, and how to take a first look at a new dataset. The examples use pandas 3, which enables Copy-on-Write and a dedicated string type by default.
Series and DataFrame
A Series is a one-dimensional labelled array — one column — with an index (labels), values and a dtype. A DataFrame is a collection of Series sharing the same index. Create one from a dict of lists (column by column), a list of dicts (row by row, like JSON records), or a NumPy array with column names. Selecting df["price"] returns a Series; df[["name", "price"]] returns a smaller DataFrame. Install with pip install pandas and import as import pandas as pd.
Reading and Writing Data
pd.read_csv("sales.csv")— options includesep,usecols,dtype,parse_dates,index_col,na_valuesandnrows.pd.read_excel("report.xlsx", sheet_name="2026")(needsopenpyxl),pd.read_json(),pd.read_sql(query, connection),pd.read_parquet().df.to_csv("out.csv", index=False),to_excel,to_json(orient="records"),to_sql,to_parquet.
First Look at a Dataset
head(n)/tail(n) show the first/last rows, shape gives (rows, columns), columns and dtypes list columns and types, info() summarises types, non-null counts and memory, describe() gives count, mean, std, min, quartiles and max for numeric columns, and value_counts() counts categories. Doing this first on every new dataset reveals wrong types, missing values and surprises before you analyse anything.
Examples
Creating Series and DataFrames
import pandas as pd
prices = pd.Series([450, 899, 350], index=["Python Basics", "Deep Python", "SQL Basics"], name="price")
print(prices)
print(prices["Deep Python"], prices.mean(), prices.index.tolist())
# From a dict of columns
df = pd.DataFrame({
"product": ["Pen", "Notebook", "Backpack", "Mouse"],
"category": ["stationery", "stationery", "bags", "electronics"],
"price": [20, 60, 1250, 699],
"in_stock": [True, True, False, True],
})
print(df)
print(df.shape, list(df.columns))
print(df.dtypes)
# From a list of dicts (like JSON records)
orders = pd.DataFrame([{"id": 1, "total": 540.5}, {"id": 2, "total": 120.0}])
print(orders)
Python Basics 450
Deep Python 899
SQL Basics 350
Name: price, dtype: int64
899 566.3333333333334 ['Python Basics', 'Deep Python', 'SQL Basics']
product category price in_stock
0 Pen stationery 20 True
1 Notebook stationery 60 True
2 Backpack bags 1250 False
3 Mouse electronics 699 True
(4, 4) ['product', 'category', 'price', 'in_stock']
product str
category str
price int64
in_stock bool
dtype: object
id total
0 1 540.5
1 2 120.0
Reading a CSV and taking a first look
import io
import pandas as pd
csv_text = """order_id,date,city,product,quantity,unit_price
1001,2026-01-05,Pune,Pen,10,20
1002,2026-01-05,Mumbai,Notebook,4,60
1003,2026-01-06,Pune,Backpack,1,1250
1004,2026-01-07,Delhi,Mouse,2,699
1005,2026-01-07,Mumbai,Pen,25,20
1006,2026-01-08,Pune,Notebook,,60
"""
# With a real file: pd.read_csv("sales.csv", parse_dates=["date"])
sales = pd.read_csv(io.StringIO(csv_text), parse_dates=["date"])
print(sales.head(3))
print(sales.shape)
sales.info()
print(sales.describe().round(1))
print(sales["city"].value_counts())
order_id date city product quantity unit_price
0 1001 2026-01-05 Pune Pen 10.0 20
1 1002 2026-01-05 Mumbai Notebook 4.0 60
2 1003 2026-01-06 Pune Backpack 1.0 1250
(6, 6)
<class 'pandas.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 order_id 6 non-null int64
1 date 6 non-null datetime64[us]
2 city 6 non-null str
3 product 6 non-null str
4 quantity 5 non-null float64
5 unit_price 6 non-null int64
dtypes: datetime64[us](1), float64(1), int64(2), str(2)
memory usage: 420.0 bytes
order_id date quantity unit_price
count 6.0 6 5.0 6.0
mean 1003.5 2026-01-06 08:00:00 8.4 351.5
min 1001.0 2026-01-05 00:00:00 1.0 20.0
25% 1002.2 2026-01-05 06:00:00 2.0 30.0
50% 1003.5 2026-01-06 12:00:00 4.0 60.0
75% 1004.8 2026-01-07 00:00:00 10.0 539.2
max 1006.0 2026-01-08 00:00:00 25.0 1250.0
std 1.9 NaN 9.9 513.4
city
Pune 3
Mumbai 2
Delhi 1
Name: count, dtype: int64
Writing data back out
import pandas as pd
report = pd.DataFrame({"city": ["Pune", "Mumbai"], "revenue": [1890, 740]})
report.to_csv("report.csv", index=False) # index=False: no extra index column
print(open("report.csv").read())
print(report.to_json(orient="records"))
print(pd.read_csv("report.csv").equals(report))
city,revenue
Pune,1890
Mumbai,740
[{"city":"Pune","revenue":1890},{"city":"Mumbai","revenue":740}]
True
Common Mistakes
- Forgetting index=False in to_csv and getting an unwanted "Unnamed: 0" column when reading it back.
- Not using parse_dates, so dates stay as text and date operations fail.
- Skipping info()/describe() and missing wrong types or missing values.
- Confusing df["col"] (a Series) with df[["col"]] (a DataFrame).
- Loading an entire huge file when usecols or nrows would do.
Key Points to Remember
- A Series is one labelled column; a DataFrame is a table of Series sharing an index.
- Create DataFrames from dicts of lists, lists of dicts or NumPy arrays.
- read_csv/read_excel/read_json/read_sql load data; to_csv and friends save it.
- head, shape, dtypes, info, describe and value_counts give a first look.
- pandas 3 uses Copy-on-Write and a dedicated string dtype by default.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Use your local project environment for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.