Lesson 2 of 16
Series and DataFrames
The two building blocks of pandas are the Series (a single column) and the DataFrame (a full table). Understanding how they relate — a DataFrame is just columns of Series — makes everything else click. This lesson covers Series and DataFrames. Let's build both — live:
Click Run to execute the code.
Run it. Notice the Series is one column (values + an index), and the DataFrame is several columns sharing an index — and pulling one column out (people["age"]) gives you a Series.
How Series and DataFrames relate
THE SERIES — a 1D labelled array (one COLUMN of data):
- VALUES + an INDEX — every Series has data values AND an index (row labels; default 0,1,2,...).
The index lines rows up across columns.
- Has a dtype (the type of its values: int64, float64, object/string, bool, datetime).
- Series have methods: .mean(), .sum(), .max(), .min(), .value_counts(), .unique(), etc.
- Create: pd.Series([values]) or from one column of a DataFrame (df["col"]).
THE DATAFRAME — a 2D table (multiple Series/COLUMNS sharing one index):
- COLUMNS — each column is a Series (with its own dtype). df.columns lists them.
- ROWS — indexed by the shared index (df.index). Each row spans all columns.
- Think of a DataFrame as a DICT of Series (column name -> Series), all aligned on the index.
- Create: pd.DataFrame({"col1": [...], "col2": [...]}) — a dict of columns.
KEY RELATIONSHIP: a DATAFRAME is made of SERIES. df["age"] returns the 'age' column as a Series;
df[["age", "city"]] returns a smaller DataFrame (note the double brackets — a LIST of columns).
INSPECTING THEM (essential first steps on any dataset):
- df.head(n) / df.tail(n) — first / last n rows (default 5). Peek at the data.
- df.shape — (rows, columns). df.info() — columns, dtypes, non-null counts. df.describe() —
summary stats for numeric columns.
- df.columns, df.index, df.dtypes — the structure.
Always LOOK at your data first (head/info/describe) before analysing.
The Series is a 1D labelled array (one column of data): values + an index (row labels; default 0,1,2,…), a dtype (int64, float64, object/string, bool, datetime), and methods (.mean(), .sum(), .max(), .value_counts(), .unique()) — create with pd.Series([values]) or from a DataFrame column (df["col"]). The DataFrame is a 2D table (multiple Series/columns sharing one index): columns (each a Series with its own dtype), rows (indexed by the shared index) — think of it as a dict of Series (column name → Series), all aligned on the index — create with pd.DataFrame({"col1": [...], "col2": [...]}). Key relationship: a DataFrame is made of Series — df["age"] returns the age column as a Series; df[["age", "city"]] returns a smaller DataFrame (note the double brackets — a list of columns). Inspecting them (essential first steps): df.head(n) / df.tail(n) (first/last n rows — default 5), df.shape ((rows, columns)), df.info() (columns, dtypes, non-null counts), df.describe() (summary stats for numeric columns), and df.columns / df.index / df.dtypes — always look at your data first before analysing.
The mistake beginners make
The first mistake is single vs double brackets confusion — df["col"] returns a Series, df[["col"]] returns a DataFrame; use double brackets (a list) to select multiple columns. The second mistake is not inspecting the data first — analysing before running head()/info()/describe() to see the shape, types, and missing values; always look first. The third mistake is ignoring the index — not realising the index aligns rows across columns (and across DataFrames in operations); understand the index. And wrong dtypes — numbers stored as strings (object) so math fails; check df.dtypes. And confusing Series methods with DataFrame methods — most work on both, but shapes differ; know whether you have a Series (1 column) or DataFrame (table). Use brackets correctly, inspect first, mind the index, check dtypes, and know Series vs DataFrame.
Your turn
Your turn
- Build both: create a Series with pd.Series([...]) and a DataFrame with pd.DataFrame({...}), print both, and see that a Series is one column while a DataFrame is a table.
- Pull a column: select one column with df["age"] (a Series) and multiple columns with df[["age", "city"]] (a DataFrame) - noting single vs double brackets.
- Inspect the data: run df.head(), df.shape, df.info(), and df.describe() on the DataFrame - the essential first look at any dataset (rows, dtypes, missing values, summary stats).
- Use Series methods: try df["age"].mean(), .max(), .min(), and df["city"].value_counts() to summarise single columns.
- Check dtypes + index: run df.dtypes to see each column's type and df.index to see the row labels - understanding the index aligns rows across columns.
Key points
- SERIES = a 1D labelled array (one COLUMN): values + an INDEX (row labels, default 0,1,2...) + a dtype (int64/float64/object/bool/datetime). Methods: .mean()/.sum()/.max()/.value_counts()/.unique(). Create: pd.Series([...]) or df['col'].
- DATAFRAME = a 2D table (multiple Series/COLUMNS sharing one index). A DataFrame is a DICT of Series (column -> Series, aligned on the index). Create: pd.DataFrame({'col1':[...], 'col2':[...]}).
- A DataFrame is made of Series: df['age'] -> a SERIES (one column); df[['age','city']] -> a DataFrame (double brackets = a LIST of columns). Single bracket = Series, double = DataFrame.
- INSPECT first (always): df.head(n)/df.tail(n) (first/last rows), df.shape ((rows,cols)), df.info() (columns/dtypes/non-null counts), df.describe() (numeric summary stats), df.columns/df.index/df.dtypes.
- The mistakes: single vs double bracket confusion (Series vs DataFrame), not inspecting first (head/info/describe), ignoring the index (it aligns rows), wrong dtypes (numbers as strings — check df.dtypes), and confusing Series vs DataFrame shapes.
Q&A · 0
Enrol to ask questions and join the discussion.
No questions yet — be the first to ask.