Machine Learning: Pandas数据处理 — 数据加载清洗转换聚合完全指南
Pandas是数据科学的瑞士军刀——加载、清洗、转换、聚合,一条龙搞定。
1. 你将学到
- DataFrame与Series核心操作:创建、索引、选择、过滤
- 数据清洗实战:缺失值处理、重复值去除、异常值检测
- 数据转换:apply/map/transform、分组聚合groupby、透视表pivot_table
- 多数据源合并:merge/join/concat,Alice的美国订单与Bob的中国订单合并分析
- 时间序列处理:日期解析、resample重采样、滚动窗口rolling
2. 一个数据分析师的真实故事
(1) 痛点:CSV数据脏乱差,清洗占80%时间
Bob拿到了SalesPredict的原始订单数据:200 thousand条记录中,5%的金额为空值,3%有重复订单,还有用户ID格式不统一的问题。Alice的美国数据字段名和Bob的不一致,Charlie的EUR金额需要汇率转换。数据清洗成了ML项目最大的时间黑洞。
(2) Pandas的解法
Pandas提供一整套数据清洗工具链——缺失值处理、重复检测、类型转换、多源合并——把数据清洗从手动操作变成可复现的代码。
PYTHON
import pandas as pd
# Load and clean in a pipeline
df = pd.read_csv("orders.csv")
df_clean = (df
.drop_duplicates()
.fillna({"amount": df["amount"].median()})
.assign(amount_usd=lambda x: x["amount"] * x["exchange_rate"])
)
print(f"Clean rows: {len(df_clean)}, Original: {len(df)}")
(3) 收益:清洗时间从3天降到30分钟
Bob用Pandas将数据清洗流程代码化后,原本3天的手动清洗降到30分钟自动执行,且每次新数据进来都能复用。
3. DataFrame与Series核心操作
(1) 创建DataFrame
PYTHON
import pandas as pd
import numpy as np
# From dictionary
sales_data = pd.DataFrame({
"date": pd.date_range("2024-01-01", periods=5, freq="D"),
"category": ["Electronics", "Clothing", "Food", "Books", "Home"],
"revenue_k_usd": [250, 145, 90, 70, 400],
"orders": [1200, 800, 3000, 500, 600],
})
# From NumPy array
arr = np.random.rand(3, 4)
df_from_arr = pd.DataFrame(arr, columns=["A", "B", "C", "D"])
print(sales_data)
print(f"\nShape: {sales_data.shape}")
print(f"Dtypes:\n{sales_data.dtypes}")
▶ 示例:加载SalesPredict真实数据
PYTHON
import pandas as pd
# Load CSV with type hints
df = pd.read_csv("sales_data.csv", parse_dates=["order_date"])
# Quick overview
print(f"Shape: {df.shape}")
print(f"\nFirst 5 rows:\n{df.head()}")
print(f"\nData types:\n{df.dtypes}")
print(f"\nMemory usage:\n{df.memory_usage(deep=True)}")
输出:
TEXT
📖 仅展示
# 执行成功
(2) 选择与过滤
▶ 示例:多种数据选择方式
PYTHON
import pandas as pd
df = pd.DataFrame({
"product": ["Laptop", "Phone", "Tablet", "Monitor", "Keyboard"],
"category": ["Electronics", "Electronics", "Electronics", "Electronics", "Accessories"],
"price": [999, 699, 399, 299, 49],
"stock": [50, 200, 100, 80, 500],
})
# Column selection
prices = df["price"] # Series
subset = df[["product", "price"]] # DataFrame
# Row selection with loc (label) and iloc (position)
row = df.loc[0] # First row by label
rows = df.iloc[1:3] # Rows 1-2 by position
# Conditional filtering
expensive = df[df["price"] > 400]
elec_cheap = df[(df["category"] == "Electronics") & (df["price"] < 500)]
print(f"Expensive items:\n{expensive}")
输出:
TEXT
📖 仅展示
# 执行成功
| 选择方式 | 语法 | 适用场景 |
|---|---|---|
| 列选择 | df["col"] / df[["c1","c2"]] |
取一列或多列 |
| loc | df.loc[row, col] |
按标签索引 |
| iloc | df.iloc[r, c] |
按位置索引 |
| 条件过滤 | df[df["col"] > val] |
按条件筛选 |
| query | df.query("price > 400") |
SQL风格筛选 |
4. 数据清洗实战
Pandas数据清洗是一个流水线过程——每一步解决一类问题,逐步将脏数据变为干净数据:
graph LR
RAW[Raw Data<br/>5% Missing, 3% Dupes] --> DEDUP[Deduplicate<br/>drop_duplicates]
DEDUP --> FILL[Fill Missing<br/>fillna median/mode]
FILL --> OUTLIER[Remove Outliers<br/>IQR clipping]
OUTLIER --> CONVERT[Type Convert<br/>astype / to_datetime]
CONVERT --> CLEAN[Clean Data<br/>Ready for ML]
(1) 缺失值处理
▶ 示例:SalesPredict缺失值诊断与处理
PYTHON
import pandas as pd
import numpy as np
# Simulate data with missing values
df = pd.DataFrame({
"order_id": [1001, 1002, 1003, 1004, 1005, 1006],
"amount_usd": [150, np.nan, 280, np.nan, 95, 320],
"category": ["Electronics", "Clothing", np.nan, "Food", "Books", "Electronics"],
"user_rating": [4.5, 3.8, np.nan, 4.2, np.nan, 5.0],
})
# Diagnose missing values
print(f"Missing count:\n{df.isnull().sum()}")
print(f"\nMissing ratio:\n{df.isnull().mean().round(3)}")
# Strategy 1: Drop rows with any missing
df_drop = df.dropna()
# Strategy 2: Fill with median/mode
df_fill = df.fillna({
"amount_usd": df["amount_usd"].median(),
"category": df["category"].mode()[0],
"user_rating": df["user_rating"].mean(),
})
print(f"\nFilled data:\n{df_fill}")
输出:
TEXT
📖 仅展示
# 执行成功
| 策略 | 方法 | 适用场景 | 风险 |
|---|---|---|---|
| 删除 | dropna() |
缺失比例<5% | 丢失信息 |
| 均值填充 | fillna(df["col"].mean()) |
数值型,近似正态 | 降低方差 |
| 中位数填充 | fillna(df["col"].median()) |
数值型,有异常值 | 保守估计 |
| 众数填充 | fillna(df["col"].mode()[0]) |
类别型 | 可能放大主流 |
| 前向/后向填充 | fillna(method="ffill") |
时间序列 | 传播偏差 |
(2) 重复值与异常值
▶ 示例:检测与处理重复和异常
PYTHON
import pandas as pd
import numpy as np
df = pd.DataFrame({
"order_id": [1001, 1002, 1002, 1003, 1004],
"amount": [150, 280, 280, 95000, 95],
})
# Duplicate detection
dupes = df.duplicated(subset=["order_id", "amount"])
print(f"Duplicated rows:\n{df[dupes]}")
# Remove duplicates
df_clean = df.drop_duplicates(subset=["order_id"], keep="first")
# Outlier detection with IQR method
def detect_outliers_iqr(series, factor=1.5):
q1, q3 = series.quantile([0.25, 0.75])
iqr = q3 - q1
lower, upper = q1 - factor * iqr, q3 + factor * iqr
return (series < lower) | (series > upper)
outlier_mask = detect_outliers_iqr(df_clean["amount"])
print(f"\nOutliers:\n{df_clean[outlier_mask]}")
# Cap outliers at 99th percentile
cap = df_clean["amount"].quantile(0.99)
df_capped = df_clean.assign(amount=df_clean["amount"].clip(upper=cap))
输出:
TEXT
📖 仅展示
# 函数定义成功
5. 数据转换与聚合
(1) apply/map/transform
▶ 示例:特征转换
PYTHON
import pandas as pd
df = pd.DataFrame({
"product": ["Laptop", "Phone", "Tablet"],
"price_usd": [999, 699, 399],
"cost_usd": [600, 350, 180],
})
# map: element-wise transform on Series
df["price_level"] = df["price_usd"].map(
lambda x: "High" if x > 700 else ("Mid" if x > 400 else "Low")
)
# apply: row/column-wise transform
df["margin_pct"] = df.apply(
lambda row: (row["price_usd"] - row["cost_usd"]) / row["price_usd"] * 100,
axis=1
)
# transform: same-shape output
df["price_zscore"] = df["price_usd"].transform(
lambda x: (x - x.mean()) / x.std()
)
print(df)
输出:
TEXT
📖 仅展示
# 执行成功
(2) 分组聚合groupby
▶ 示例:按品类聚合销售指标
PYTHON
import pandas as pd
df = pd.DataFrame({
"category": ["Elec", "Elec", "Cloth", "Cloth", "Food", "Food"],
"month": ["Jan", "Feb", "Jan", "Feb", "Jan", "Feb"],
"revenue_k": [250, 260, 145, 150, 90, 95],
"orders": [1200, 1250, 800, 820, 3000, 3100],
})
# Single aggregation
cat_revenue = df.groupby("category")["revenue_k"].sum()
print(f"Revenue by category:\n{cat_revenue}")
# Multiple aggregations
cat_stats = df.groupby("category").agg({
"revenue_k": ["sum", "mean", "std"],
"orders": ["sum", "mean"],
})
print(f"\nCategory stats:\n{cat_stats}")
# Named aggregations
cat_named = df.groupby("category").agg(
total_revenue=("revenue_k", "sum"),
avg_revenue=("revenue_k", "mean"),
total_orders=("orders", "sum"),
avg_order_value=("revenue_k", lambda x: x.sum() / df.loc[x.index, "orders"].sum() * 1000),
)
输出:
TEXT
📖 仅展示
# 执行成功
(3) 透视表pivot_table
▶ 示例:月度品类销售透视
PYTHON
import pandas as pd
df = pd.DataFrame({
"category": ["Elec"]*3 + ["Cloth"]*3 + ["Food"]*3,
"month": ["Jan", "Feb", "Mar"]*3,
"revenue_k": [250, 260, 270, 145, 150, 155, 90, 95, 100],
})
# Pivot: categories as rows, months as columns
pivot = df.pivot_table(
values="revenue_k",
index="category",
columns="month",
aggfunc="sum",
margins=True, # Add row/column totals
)
print(pivot)
输出:
TEXT
📖 仅展示
# 执行成功
| 维度 | groupby | pivot_table |
|---|---|---|
| 输出形状 | 长(Long) | 宽(Wide) |
| 多指标 | ✅ agg多列 | ✅ values多列 |
| 小计 | ❌ 需手动 | ✅ margins=True |
| 灵活度 | 高(任意agg) | 中(固定aggfunc) |
6. 多数据源合并与时间序列
(1) 合并操作
▶ 示例:Alice的美国数据 + Bob的中国数据合并
PYTHON
import pandas as pd
# Alice's US orders
us_orders = pd.DataFrame({
"order_id": ["US001", "US002", "US003"],
"amount_usd": [150, 280, 95],
"product": ["Laptop", "Phone", "Book"],
})
# Bob's China orders (need currency conversion)
cn_orders = pd.DataFrame({
"order_id": ["CN001", "CN002"],
"amount_cny": [1200, 3500],
"product": ["Phone", "Laptop"],
})
# Concat vertically (append rows)
cn_orders_usd = cn_orders.assign(
amount_usd=cn_orders["amount_cny"] * 0.14 # CNY to USD
).drop(columns=["amount_cny"])
all_orders = pd.concat([us_orders, cn_orders_usd], ignore_index=True)
print(f"Combined orders:\n{all_orders}")
# Merge with product catalog
catalog = pd.DataFrame({
"product": ["Laptop", "Phone", "Book", "Tablet"],
"category": ["Electronics", "Electronics", "Books", "Electronics"],
"margin_pct": [35, 45, 20, 30],
})
enriched = all_orders.merge(catalog, on="product", how="left")
print(f"\nEnriched orders:\n{enriched}")
输出:
TEXT
📖 仅展示
# 执行成功
| 合并方式 | 语法 | 类似SQL | 说明 |
|---|---|---|---|
| Inner | merge(how="inner") |
INNER JOIN | 交集 |
| Left | merge(how="left") |
LEFT JOIN | 保留左表全部 |
| Outer | merge(how="outer") |
FULL JOIN | 并集 |
| Concat | concat(axis=0) |
UNION | 上下拼接行 |
| Concat | concat(axis=1) |
— | 左右拼接列 |
(2) 时间序列处理
▶ 示例:SalesPredict日度数据重采样与滚动统计
PYTHON
import pandas as pd
import numpy as np
# Daily sales data
rng = pd.date_range("2024-01-01", periods=90, freq="D")
daily = pd.DataFrame({
"date": rng,
"revenue": np.random.normal(5000, 1000, 90).cumsum(),
}, index=rng)
# Resample to monthly
monthly = daily["revenue"].resample("M").agg(["first", "last", "mean", "sum"])
print(f"Monthly stats:\n{monthly}")
# Rolling window: 7-day moving average
daily["ma_7d"] = daily["revenue"].rolling(window=7).mean()
daily["ma_30d"] = daily["revenue"].rolling(window=30).mean()
# Percentage change
daily["pct_change"] = daily["revenue"].pct_change()
print(f"\nLast 5 rows with rolling stats:\n{daily.tail()}")
输出:
TEXT
📖 仅展示
# 执行成功
❓ 常见问题
Q loc和iloc有什么区别?
A loc按标签(label)索引,包含末端;iloc按位置(position)索引,不包含末端。如df.loc[0:3]取4行,df.iloc[0:3]取3行。
Q SettingWithCopyWarning怎么解决?
A 这是链式赋值警告。用
.copy()明确创建副本,或用.loc[]单次赋值替代链式操作。例如用df.loc[mask, "col"] = value替代df[mask]["col"] = value。Q groupby后如何保持分组列为普通列?
A 用
as_index=False参数:df.groupby("category", as_index=False).agg(...),或groupby后调用.reset_index()。Q merge时出现意外多行怎么办?
A 检查合并键是否有重复。用
df.duplicated(subset=key).sum()检查。如果有重复,先决定保留策略(去重或聚合)再merge。Q 大数据集内存不够怎么办?
A 三种策略:1) 指定dtypes减少内存(如int64→int32);2) 使用
chunksize参数分块读取;3) 使用Dask库处理超大数据集。Q 时间序列的freq参数报错怎么办?
A 使用
df.asfreq("D")确保规则频率,或用df.resample("D").asfreq()。不规则时间序列需要先重采样再分析。📖 小节
- DataFrame是二维表格数据结构,Series是一维数组,两者构成Pandas核心
- 缺失值处理三大策略:删除(dropna)、填充(fillna)、插值(interpolate),根据场景选择
- groupby + agg实现分组聚合,pivot_table实现宽表透视,各有适用场景
- merge实现SQL风格的表连接,concat实现行/列拼接,注意how参数选择
- 时间序列处理:parse_dates解析日期、resample重采样、rolling滚动统计、pct_change计算变化率
📝 作业
- 基础题(难度⭐):加载一个CSV文件,用
info()和describe()查看概况,统计缺失值数量。提示:df.isnull().sum()。 - 进阶题(难度⭐⭐):创建两个DataFrame(Alice的美国销售和Charlie的欧洲销售),用merge按product合并,计算每个产品的全球总销售额。提示:先统一金额单位为USD,再merge。
- 挑战题(难度⭐⭐⭐):对日度销售数据执行:1) 重采样为周度;2) 计算4周滚动平均;3) 检测异常值(偏离滚动平均2个标准差以上的点)。提示:
resample("W")+rolling(4)+ 布尔索引。