Pandas: DataFrame Core
Last updated: 2026-08-26
A DataFrame is the core data structure in Pandas — a labeled 2D table where each column is a Series with its own dtype and name. Think of a DataFrame as an Excel sheet or SQL table in code, but more powerful: programmable, extensible, and version-controllable. This section takes you deep into the essential structure of a DataFrame.
1. What You'll Learn
- ❶ The essence of a DataFrame (a collection of Series)
- ❷ The row/column index system (Index + Columns)
- ❸ Multiple ways to create a DataFrame
- ❹ Inspecting data (info / describe / head / tail)
- ❺ Core properties (shape / index / columns / dtypes / values)
2. Charlie's E-Commerce Product Table
(1) Pain Point: Managing Multiple Columns Together
Charlie runs an e-commerce website and needs to manage product data: name, category, price, stock, and rating — 5 columns of different types. With Series alone, he would need to maintain 5 separate Series objects and ensure their indexes stay aligned.
import pandas as pd
# 5 separate Series — must keep index aligned manually
names = pd.Series(['Laptop', 'Phone', 'Tablet', 'Monitor', 'Keyboard'])
categories = pd.Series(['Electronics', 'Electronics', 'Electronics', 'Electronics', 'Accessories'])
prices = pd.Series([999.99, 699.99, 349.99, 449.99, 79.99])
stocks = pd.Series([50, 120, 80, 35, 200])
ratings = pd.Series([4.7, 4.5, 4.3, 4.6, 4.2])
# Which price goes with which product? Hope the index stays aligned!
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) Solution: DataFrame Binds Columns Together
▶ Example: Managing Product Data with a DataFrame (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
df = pd.DataFrame({
'name': ['Laptop', 'Phone', 'Tablet', 'Monitor', 'Keyboard'],
'category': ['Electronics', 'Electronics', 'Electronics', 'Electronics', 'Accessories'],
'price': [999.99, 699.99, 349.99, 449.99, 79.99],
'stock': [50, 120, 80, 35, 200],
'rating': [4.7, 4.5, 4.3, 4.6, 4.2]
})
print(df)
# name category price stock rating
# 0 Laptop Electronics 999.99 50 4.7
# 1 Phone Electronics 699.99 120 4.5
# 2 Tablet Electronics 349.99 80 4.3
# 3 Monitor Electronics 449.99 35 4.6
# 4 Keyboard Accessories 79.99 200 4.2
print(df['name']) # a Series
print(df.loc[0]) # first row as a Series
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
All 5 columns are bound together in a single DataFrame, with self-descriptive column names and automatic row alignment.
(3) Benefit: A DataFrame Is a "Collection of Columns"
A DataFrame is not a "2D ndarray" — it is an ordered dictionary of Series. Each column is an independent Series with its own dtype, but all columns share the same row Index.
3. The Essential Structure of a DataFrame
(1) Internal Structure Diagram
graph TB
subgraph DataFrame
direction TB
COL["Columns Index<br/>(name, category, price, stock, rating)"]
ROW["Row Index<br/>(0, 1, 2, 3, 4)"]
S1["Series: name<br/>dtype: object"]
S2["Series: category<br/>dtype: object"]
S3["Series: price<br/>dtype: float64"]
S4["Series: stock<br/>dtype: int64"]
S5["Series: rating<br/>dtype: float64"]
end
ROW --> S1
ROW --> S2
ROW --> S3
ROW --> S4
ROW --> S5
COL --> S1
COL --> S2
COL --> S3
COL --> S4
COL --> S5
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
| Concept | Description | Analogy |
|---|---|---|
| Row Index | Row labels shared by all columns | Excel row numbers |
| Columns | Name label for each column | Excel column headers |
| Column Series | Each column as an independent Series | A single Excel column |
| values | The underlying 2D ndarray | Raw data matrix |
(2) DataFrame vs 2D ndarray
| Feature | NumPy 2D ndarray | Pandas DataFrame |
|---|---|---|
| Type constraint | Single global dtype | Independent dtype per column |
| Row labels | None (integer positions) | Index (customizable) |
| Column labels | None (integer positions) | Columns (column names) |
| Column selection | arr[:, 2] |
df['price'] |
| Row selection | arr[0, :] |
df.loc[0] |
| Mixed types | Not supported | Natively supported |
| Missing values | np.nan (float only) | NaN / NaT / pd.NA |
values property of a DataFrame returns a 2D ndarray, but when you have mixed types it becomes object dtype — performance drops significantly. For pure numeric operations, select numeric columns first, then call .values.
4. Multiple Ways to Create a DataFrame
(1) From a Dictionary (Most Common)
▶ Example: Creating a DataFrame from a Dictionary (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
# Dict of lists: keys = column names, values = column data
df = pd.DataFrame({
'product': ['Coffee', 'Tea', 'Juice', 'Water'],
'price': [4.50, 3.80, 5.20, 1.50],
'calories': [5, 2, 120, 0]
})
print(df)
# product price calories
# 0 Coffee 4.50 5
# 1 Tea 3.80 2
# 2 Juice 5.20 120
# 3 Water 1.50 0
# Custom row index
df2 = pd.DataFrame({
'product': ['Coffee', 'Tea', 'Juice'],
'price': [4.50, 3.80, 5.20]
}, index=['A001', 'A002', 'A003'])
print(df2.index) # Index(['A001', 'A002', 'A003'])
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) From a List of Lists
▶ Example: Creating a DataFrame from a List of Lists (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
# List of lists: each inner list = one row
data = [
['Coffee', 4.50, 5],
['Tea', 3.80, 2],
['Juice', 5.20, 120],
['Water', 1.50, 0]
]
df = pd.DataFrame(data, columns=['product', 'price', 'calories'])
print(df)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(3) From a List of Dictionaries
▶ Example: Creating a DataFrame from a List of Dictionaries (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
# List of dicts: each dict = one row
data = [
{'product': 'Coffee', 'price': 4.50, 'calories': 5},
{'product': 'Tea', 'price': 3.80, 'calories': 2},
{'product': 'Juice', 'price': 5.20, 'calories': 120}
]
df = pd.DataFrame(data)
print(df)
# Missing keys become NaN
data2 = [
{'product': 'Coffee', 'price': 4.50},
{'product': 'Tea', 'calories': 2}
]
df2 = pd.DataFrame(data2)
print(df2)
# product price calories
# 0 Coffee 4.50 NaN
# 1 Tea NaN 2.0
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(4) From a NumPy ndarray
▶ Example: Creating a DataFrame from an ndarray (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import numpy as np
import pandas as pd
arr = np.random.randn(4, 3) # 4 rows, 3 columns of random numbers
df = pd.DataFrame(arr, columns=['A', 'B', 'C'])
print(df.shape) # (4, 3)
print(df.dtypes) # all float64
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(5) Comparison of Creation Methods
| Method | Syntax | Characteristics | Use Case |
|---|---|---|---|
| Dictionary | pd.DataFrame({'col': [data]}) |
Column-oriented, most natural | Most common |
| List of lists | pd.DataFrame([[r1c1, ...], ...]) |
Row-oriented | Building row by row |
| List of dicts | pd.DataFrame([{'col': val, ...}, ...]) |
Row-oriented, missing keys auto-NaN | API/JSON data |
| ndarray | pd.DataFrame(np.array(...)) |
Homogeneous data | Converting NumPy data |
| read_csv | pd.read_csv('file.csv') |
Load from file | Most common in real projects |
5. Inspecting and Exploring Data
(1) Quick Preview: head / tail / sample
▶ Example: Previewing a DataFrame (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
df = pd.DataFrame({
'name': ['Laptop', 'Phone', 'Tablet', 'Monitor', 'Keyboard',
'Mouse', 'Headset', 'Speaker', 'Camera', 'Charger'],
'price': [999.99, 699.99, 349.99, 449.99, 79.99,
29.99, 149.99, 89.99, 599.99, 19.99],
'stock': [50, 120, 80, 35, 200, 300, 150, 180, 25, 500]
})
print(df.head()) # first 5 rows
print(df.head(3)) # first 3 rows
print(df.tail(2)) # last 2 rows
print(df.sample(3)) # random 3 rows (for inspection)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) Data Overview: info
▶ Example: The info Method in Detail (Difficulty ⭐⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
import numpy as np
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'Carol', 'David'],
'age': [28, 34, 25, 30, 45],
'salary': [75000.0, 92000.0, np.nan, 88000.0, 105000.0],
'department': ['Sales', 'Engineering', 'Marketing', 'Sales', 'Management']
})
df.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 5 entries, 0 to 4
# Data columns (total 4 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 name 5 non-null object
# 1 age 5 non-null int64
# 2 salary 4 non-null float64 ← 1 missing value!
# 3 department 5 non-null object
# dtypes: float64(1), int64(1), object(2)
# memory usage: 288.0+ bytes
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
info() is the first step in data analysis — it tells you: how many rows there are, the dtype of each column, whether there are missing values, and memory usage.
(3) Statistical Summary: describe
▶ Example: Statistical Summary with describe (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
df = pd.DataFrame({
'price': [999.99, 699.99, 349.99, 449.99, 79.99],
'stock': [50, 120, 80, 35, 200],
'rating': [4.7, 4.5, 4.3, 4.6, 4.2]
})
# Numeric columns only (default)
print(df.describe())
# price stock rating
# count 5.00 5.00 5.00
# mean 515.79 97.00 4.46
# std 347.31 69.72 0.21
# min 79.99 35.00 4.20
# 25% 349.99 50.00 4.30
# 50% 449.99 80.00 4.50
# 75% 699.99 120.00 4.60
# max 999.99 200.00 4.70
# Include all columns
print(df.describe(include='all'))
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
| Method | What It Shows | Use Case |
|---|---|---|
df.head() |
First 5 rows | Quick look at data format |
df.info() |
Row count / column dtypes / missing values / memory | Must-check first step |
df.describe() |
Statistical summary of numeric columns | Understanding distribution |
df.shape |
(rows, columns) | Data dimensions |
df.dtypes |
Dtype of each column | Type checking |
6. Core DataFrame Properties
(1) Property Quick Reference
| Property | Return Type | Description | Example |
|---|---|---|---|
df.shape |
tuple | (rows, columns) | (5, 4) |
df.index |
Index | Row labels | RangeIndex(0, 5) |
df.columns |
Index | Column names | Index(['name', 'age', ...]) |
df.dtypes |
Series | Dtype of each column | name: object, age: int64 |
df.values |
ndarray | Underlying data | 2D array |
df.ndim |
int | Number of dimensions | 2 |
df.size |
int | Total number of elements | 20 (5 rows x 4 cols) |
df.T |
DataFrame | Transpose | Rows and columns swapped |
▶ Example: Core Properties at a Glance (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [28, 34, 25],
'salary': [75000.0, 92000.0, 68000.0]
})
print(f"Shape: {df.shape}") # (3, 3)
print(f"Index: {df.index}") # RangeIndex(0, 3)
print(f"Columns: {df.columns}") # Index(['name', 'age', 'salary'])
print(f"Dtypes:\n{df.dtypes}")
print(f"Size: {df.size}") # 9
print(f"Ndim: {df.ndim}") # 2
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(2) Column Selection and Addition
▶ Example: Column Operations (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [28, 34, 25],
'salary': [75000.0, 92000.0, 68000.0]
})
# Select single column → Series
print(type(df['name'])) # <class 'pandas.core.series.Series'>
# Select multiple columns → DataFrame
print(df[['name', 'salary']])
# Add new column
df['bonus'] = df['salary'] * 0.1
df['level'] = df['age'].apply(lambda x: 'Senior' if x >= 30 else 'Junior')
print(df)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
(3) Transpose
▶ Example: Transposing a DataFrame (Difficulty ⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
df = pd.DataFrame({
'Q1': [320, 280, 350],
'Q2': [410, 390, 420]
}, index=['Product A', 'Product B', 'Product C'])
print(df.T)
# Product A Product B Product C
# Q1 320 280 350
# Q2 410 390 420
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
7. Complete Example: E-Commerce Product Table End-to-End
▶ Example: Product Data Creation and Exploration (Difficulty ⭐⭐⭐)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: E-commerce product
# table creation and exploration
# ============================================
# 1. Create DataFrame
products = pd.DataFrame({
'name': ['Laptop', 'Phone', 'Tablet', 'Monitor', 'Keyboard',
'Mouse', 'Headset', 'Speaker'],
'category': ['Electronics', 'Electronics', 'Electronics',
'Electronics', 'Accessories', 'Accessories',
'Accessories', 'Accessories'],
'price': [999.99, 699.99, 349.99, 449.99, 79.99,
29.99, 149.99, 89.99],
'stock': [50, 120, 80, 35, 200, 300, 150, 180],
'rating': [4.7, 4.5, 4.3, 4.6, 4.2, 4.0, 4.4, 4.1]
})
# 2. Overview
print("=== Product Catalog Overview ===")
print(f"Products: {len(products)}")
print(f"Categories: {products['category'].unique().tolist()}")
print(f"Columns: {products.columns.tolist()}")
# 3. Data types and memory
print(f"\n=== Data Types ===")
print(products.dtypes)
print(f"\nMemory usage: {products.memory_usage(deep=True).sum() / 1024:.1f} KB")
# 4. Quick statistics
print(f"\n=== Price Statistics ===")
print(products['price'].describe())
# 5. Inventory value per product
products['inventory_value'] = products['price'] * products['stock']
print(f"\n=== Top 3 by Inventory Value ===")
print(products.nlargest(3, 'inventory_value')[['name', 'inventory_value']])
# 6. Category breakdown
print(f"\n=== Category Summary ===")
cat_summary = products.groupby('category').agg(
count=('name', 'count'),
avg_price=('price', 'mean'),
total_stock=('stock', 'sum')
)
print(cat_summary)
> **Output:** Run this in a local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed — install it locally (`pip install pandas`) to follow along. Actual values may vary slightly depending on your pandas version.
Expected output (excerpt):
=== Product Catalog Overview ===
Products: 8
Categories: ['Electronics', 'Accessories']
Columns: ['name', 'category', 'price', 'stock', 'rating']
=== Price Statistics ===
count 8.00
mean 349.99
std 339.32
min 29.99
max 999.99
=== Top 3 by Inventory Value ===
name inventory_value
0 Laptop 49999.50
1 Phone 83998.80
3 Monitor 15749.65
=== Category Summary ===
count avg_price total_stock
category
Accessories 4 87.24 830
Electronics 4 624.99 285
❓ FAQ
0, 1, 2), using df[0] is ambiguous — Pandas treats it as a column name, not a position. In that case, use df.iloc[:, 0] for positional access or df.loc[:, 0] for label-based access. It is best to use string column names to avoid confusion.df['new_col'] = values (appended at the end) or df.insert(pos, 'new_col', values) (at a specific position). To remove a column, use df.drop('col', axis=1) or del df['col']. drop returns a new DataFrame (the original is unchanged), while del modifies in place.df.values returns the underlying 2D NumPy ndarray. If the DataFrame has mixed types (e.g., object + int64), the ndarray dtype is upcast to object, losing vectorized performance. For purely numeric DataFrames, values retains the numeric dtype.df.T or df.transpose(). After transposing, rows become columns and columns become rows. Note: if the original DataFrame has mixed types, all columns become object dtype after transposition (because ndarray requires homogeneous types).pd.read_csv() to load from a file.📖 Summary
- A DataFrame is essentially an ordered collection of Series — each column has an independent dtype, and all share the same row Index
- Row Index + Columns form a 2D labeling system, making data self-descriptive
- Creation methods: dictionary (most common) / list of lists / list of dicts / ndarray / read_csv
- Data exploration trio: head() for format → info() for structure → describe() for distribution
- Core properties: shape / index / columns / dtypes / values / T
- Column selection:
df['col']returns a Series,df[['c1','c2']]returns a DataFrame - Add columns with
df['new'] = ..., remove columns withdf.drop('col', axis=1)
📝 Exercises
- Basic (Difficulty ⭐): Create a student table (name / math / english / science) using 3 different methods, then print the info and describe results.
- Intermediate (Difficulty ⭐⭐): Create an employee table with missing values (name / age / salary / department, where salary has 2 NaN entries), then: use info to check for missing values → calculate the non-null ratio for each column → add a salary_level column (salary > 80000 is "High", otherwise "Low", NaN is "Unknown").
- Challenge (Difficulty ⭐⭐⭐): Build a DataFrame from a list of dictionaries (5 records, some keys missing), then: check for missing values → fill with column mean/mode → count non-null fields per row → add a completeness column (non-null ratio), and finally output a full describe report.
← Previous: Introduction to Series · Next: The Index System →