01-从零入门教程

知识库
知识库文档
/tech-stacks/pandas/tutorial/01-从零入门教程.md

文档

Pandas 入门教程 —— 从 Excel 思维到 DataFrame 思维

本章目标

  • 理解 Series 和 DataFrame 的数据结构
  • 掌握数据清洗的标准流程(ETL)
  • 学会用 groupby+agg 替代 Excel 透视表
  • 理解链式操作与管道编程风格

1. Pandas 的核心数据结构

Series —— 带索引的一维数组

import pandas as pd

s = pd.Series([10, 20, 30, 40], index=["a", "b", "c", "d"])
print(s)
# a    10
# b    20
# c    30
# d    40

# Series 同时具备 dict 和 ndarray 的特性
print(s["b"])        # 像 dict
print(s[s > 20])     # 像 ndarray 布尔索引
print(s.values)      # 底层 NumPy 数组

DataFrame —— 带行列标签的二维表

df = pd.DataFrame({
    "name": ["Alice", "Bob"],
    "age": [25, 30],
    "salary": [50000, 60000],
})
# 每列是一个 Series
# 行索引 (index) 默认 0, 1, 2...
# 列索引 (columns) 是字典的 key

2. 数据清洗标准流程(EDA Pipeline)

# 第1步:加载
df = pd.read_csv("raw_data.csv")

# 第2步:表面检查
print(df.shape)
print(df.head())
print(df.info())
print(df.describe())
print(df.isnull().sum())

# 第3步:缺失值处理
df["age"].fillna(df["age"].median(), inplace=True)   # 数值:中位数
df["city"].fillna("Unknown", inplace=True)            # 分类:特定值

# 第4步:重复值检查
df.drop_duplicates(subset=["email"], keep="first", inplace=True)

# 第5步:类型转换
df["date"] = pd.to_datetime(df["date"])
df["category"] = df["category"].astype("category")

# 第6步:异常值处理
q1 = df["price"].quantile(0.25)
q3 = df["price"].quantile(0.75)
iqr = q3 - q1
df = df[(df["price"] >= q1 - 1.5 * iqr) & (df["price"] <= q3 + 1.5 * iqr)]

# 第7步:保存
df.to_parquet("clean_data.parquet")

3. groupby 深度指南

groupby 三部曲:split → apply → combine

原始数据            Split               Apply            Combine
┌──┬──┬──┐     ┌──┬──┬──┐
│A│X│10│     │A│X│10│  → mean() →  A: 20
│B│Y│30│  →  │A│X│30│  → sum()  →  B: 105
│A│Z│20│     │B│Y│30│
│B│Y│75│     │B│Z│75│
└──┴──┴──┘     └──┴──┴──┘

agg vs transform vs apply

df = pd.DataFrame({
    "team": ["A", "A", "B", "B"],
    "score": [10, 20, 30, 40],
})

# agg: 聚合为一行(shape 缩小)
print(df.groupby("team").agg(avg_score=("score", "mean")))
#       avg_score
# team
# A          15.0
# B          35.0

# transform: 保持原形状(广播回来)
df["team_avg"] = df.groupby("team")["score"].transform("mean")
#   team  score  team_avg
# 0    A     10      15.0
# 1    A     20      15.0
# 2    B     30      35.0
# 3    B     40      35.0

# apply: 最灵活,但最慢
def top_score(group):
    return group.nlargest(1, "score")

print(df.groupby("team").apply(top_score))

4. 链式操作与管道

方法链(Method Chaining)

# ❌ 传统风格 —— 中间变量地狱
df1 = df[df["age"] > 25]
df2 = df1.groupby("city")
df3 = df2["salary"].mean()
df4 = df3.sort_values(ascending=False)
result = df4.head(5)

# ✅ 链式风格 —— 清晰的数据流
result = (
    df
    .query("age > 25")
    .groupby("city")["salary"]
    .mean()
    .sort_values(ascending=False)
    .head(5)
)

pipe —— 自定义函数链入管道

def remove_outliers(df, column):
    q1, q3 = df[column].quantile([0.25, 0.75])
    iqr = q3 - q1
    return df[df[column].between(q1 - 1.5*iqr, q3 + 1.5*iqr)]

def add_features(df):
    df["income_per_age"] = df["salary"] / df["age"]
    return df

clean_df = (
    df
    .pipe(remove_outliers, "salary")
    .pipe(add_features)
    .dropna()
)

5. Pandas 与 SQL 对照表

SQL Pandas
SELECT col1, col2 df[["col1", "col2"]]
WHERE condition df[df["col"] > 10]df.query()
GROUP BY df.groupby("col")
HAVING .filter(lambda g: g["val"].sum() > 100)
ORDER BY df.sort_values("col")
LIMIT n df.head(n)
JOIN ... ON pd.merge(left, right, on="key")
UNION ALL pd.concat([df1, df2])
COUNT(DISTINCT) df["col"].nunique()
CASE WHEN np.select()pd.cut()

思考题

  1. 什么情况下 df.groupby().apply()agg() 更好?性能上有何代价?
  2. pd.cutpd.qcut 的区别是什么?各自适用什么场景?
  3. 为什么 pd.concat([df1, df2]) 有时会导致索引重复?如何修复?
  4. Pandas 的 inplace=True 参数有什么优缺点?为什么很多开发者建议避免使用?

信息

路径
/tech-stacks/pandas/tutorial/01-从零入门教程.md
更新时间
2026/5/30