Skip to content

Pandas 完整实战手册

导航目录

1. 前言(用途、安装、导入惯例)

1.1 Pandas 是什么

Pandas 是 Python 生态中最常用的数据处理库,适合做以下工作:

  • 结构化数据清洗(缺失值、重复值、异常值)
  • 表格数据分析(统计、分组、透视、时间序列)
  • 多数据源读写(CSV/Excel/JSON/TXT)
  • 特征工程前的数据准备

1.2 安装

bash
pip install pandas

1.3 导入惯例

python
import pandas as pd

关键注意点:

  • 全文统一使用 import pandas as pd
  • 示例尽量用小数据集,便于理解和复现

2. 基础数据结构:Series、DataFrame

2.1 Series(一维带索引数组)

python
import pandas as pd

s = pd.Series([95, 88, 76], index=["张三", "李四", "王五"], name="score")
print(s)
# 输出解释:
# 张三    95
# 李四    88
# 王五    76
# Name: score, dtype: int64

关键注意点:

  • Series = 值 + 索引
  • 可通过索引标签直接访问元素,如 s["张三"]

2.2 DataFrame(二维表格)

python
import pandas as pd

df = pd.DataFrame(
    {
        "name": ["张三", "李四", "王五"],
        "age": [23, 25, 22],
        "city": ["北京", "上海", "深圳"],
    }
)
print(df)
# 输出解释:
#   name  age city
# 0  张三   23   北京
# 1  李四   25   上海
# 2  王五   22   深圳

关键注意点:

  • DataFrame 由行索引 + 列索引组成
  • 每一列本质上是一个 Series

3. 文件读写:csv、excel、txt、json

3.1 CSV 读写

python
import pandas as pd

df = pd.DataFrame({"name": ["A", "B"], "score": [90, 85]})
df.to_csv("students.csv", index=False)

df2 = pd.read_csv("students.csv")
print(df2)
# 输出解释:成功读取 CSV 文件内容

3.2 Excel 读写

python
import pandas as pd

df = pd.DataFrame({"month": ["Jan", "Feb"], "sales": [100, 120]})
df.to_excel("sales.xlsx", index=False)

df2 = pd.read_excel("sales.xlsx")
print(df2)
# 输出解释:成功读取 Excel 内容

3.3 TXT 读写(分隔符文本)

python
import pandas as pd

df = pd.DataFrame({"id": [1, 2], "value": [10, 20]})
df.to_csv("data.txt", sep="\t", index=False)

df2 = pd.read_csv("data.txt", sep="\t")
print(df2)
# 输出解释:按制表符分割读取成功

3.4 JSON 读写

python
import pandas as pd

df = pd.DataFrame({"name": ["Tom", "Jerry"], "age": [20, 21]})
df.to_json("users.json", orient="records", force_ascii=False)

df2 = pd.read_json("users.json")
print(df2)
# 输出解释:读取 JSON 成功,得到 DataFrame

关键注意点:

  • 写文件建议 index=False,避免额外索引列
  • JSON 常用 orient="records",更接近接口返回格式

4. 数据查看与基础信息:info、describe、head、tail、shape、dtypes

python
import pandas as pd

df = pd.DataFrame(
    {"name": ["A", "B", "C"], "age": [20, 21, 22], "score": [88.5, 92.0, 79.5]}
)

print(df.head(2))      # 查看前 2 行
print(df.tail(2))      # 查看后 2 行
print(df.shape)        # 输出解释:(3, 3) -> 3 行 3 列
print(df.dtypes)       # 查看每列数据类型
print(df.info())       # 列信息 + 非空数量 + 内存
print(df.describe())   # 数值列统计摘要

关键注意点:

  • info() 是排查缺失值和类型问题的第一步
  • describe() 默认只统计数值列,可用 include="all" 查看全部列

5. 索引与选取:loc、iloc、[]、at、iat

python
import pandas as pd

df = pd.DataFrame(
    {"name": ["A", "B", "C"], "score": [90, 85, 88]}, index=["s1", "s2", "s3"]
)

print(df["name"])
# 输出解释:取出单列,返回 Series

print(df.loc["s1"])
# 输出解释:按标签取行(s1)

print(df.iloc[0])
# 输出解释:按位置取第 0 行

print(df.at["s2", "score"])
# 输出解释:按标签快速取单个标量,返回 85

print(df.iat[2, 1])
# 输出解释:按位置快速取单个标量,返回 88

关键注意点:

  • loc 用标签,iloc 用整数位置
  • at/iat 适合单值读写,速度更快

6. 数据筛选:条件过滤、多条件、isin、query、between

python
import pandas as pd

df = pd.DataFrame(
    {
        "name": ["A", "B", "C", "D"],
        "age": [18, 25, 30, 22],
        "city": ["北京", "上海", "北京", "深圳"],
        "score": [70, 88, 95, 82],
    }
)

print(df[df["age"] > 20])
# 输出解释:筛选年龄大于 20 的行

print(df[(df["age"] > 20) & (df["score"] >= 85)])
# 输出解释:多条件与(&)

print(df[df["city"].isin(["北京", "深圳"])])
# 输出解释:城市在给定列表中的行

print(df.query("age >= 22 and score >= 80"))
# 输出解释:query 写法更接近 SQL 条件表达式

print(df[df["score"].between(80, 90)])
# 输出解释:分数在 [80, 90] 区间内

关键注意点:

  • 多条件必须加括号:(cond1) & (cond2)
  • and/or 不能直接用于 Series 条件,需用 & / |

7. 新增/修改/删除列:assign、drop、del、map、apply

python
import pandas as pd

df = pd.DataFrame({"name": ["A", "B"], "score": [80, 95]})

df = df.assign(pass_flag=df["score"] >= 60)
# 输出解释:新增 pass_flag 列

df["level"] = df["score"].map(lambda x: "A" if x >= 90 else "B")
# 输出解释:map 用于逐元素映射

df["score2"] = df["score"].apply(lambda x: x + 5)
# 输出解释:apply 逐元素处理,得到新列

del df["score2"]
# 输出解释:删除列 score2

df = df.drop(columns=["pass_flag"])
# 输出解释:删除 pass_flag 列,返回新 DataFrame

print(df)

关键注意点:

  • assign() 链式调用友好
  • map() 常用于单列映射;apply() 更灵活
  • drop() 默认返回新对象,若原地可用 inplace=True

8. 缺失值处理:isna、fillna、dropna、interpolate

python
import pandas as pd

df = pd.DataFrame({"name": ["A", "B", "C"], "score": [90, None, 80], "age": [20, None, 22]})

print(df.isna())
# 输出解释:True 表示该位置是缺失值

print(df.fillna({"score": df["score"].mean(), "age": 0}))
# 输出解释:score 用均值填充,age 用 0 填充

print(df.dropna(subset=["score"]))
# 输出解释:删除 score 缺失的行

ts = pd.Series([1.0, None, None, 4.0])
print(ts.interpolate())
# 输出解释:线性插值 -> [1.0, 2.0, 3.0, 4.0]

关键注意点:

  • 先判断缺失比例,再决定“删”还是“补”
  • 时间序列数据经常用 interpolate()

9. 重复值:duplicated、drop_duplicates

python
import pandas as pd

df = pd.DataFrame({"name": ["A", "A", "B"], "score": [90, 90, 80]})

print(df.duplicated())
# 输出解释:第 2 行是重复行 -> True

print(df.drop_duplicates())
# 输出解释:去重后只保留首次出现记录

print(df.drop_duplicates(subset=["name"], keep="last"))
# 输出解释:按 name 去重,保留最后一次出现

关键注意点:

  • subset 指定按哪些列判重
  • keep=False 可删除所有重复项(包括第一次)

10. 数据类型转换:astype、to_numeric、to_datetime

python
import pandas as pd

df = pd.DataFrame({"age": ["20", "21", "x"], "date": ["2026-01-01", "2026-01-02", "2026-01-03"]})

df["age_num"] = pd.to_numeric(df["age"], errors="coerce")
# 输出解释:无法转换的 "x" 变为 NaN

df["date"] = pd.to_datetime(df["date"])
# 输出解释:date 列变成 datetime64[ns]

df["age_int"] = df["age_num"].fillna(0).astype("int64")
# 输出解释:先填补 NaN,再转换整数

print(df.dtypes)

关键注意点:

  • errors="coerce" 是清洗脏数据的常见手法
  • 整数列含 NaN 时,不能直接转 int64

11. 排序:sort_values、sort_index

python
import pandas as pd

df = pd.DataFrame({"name": ["A", "B", "C"], "score": [88, 95, 88]}, index=[2, 0, 1])

print(df.sort_values(by="score", ascending=False))
# 输出解释:按 score 降序排序

print(df.sort_values(by=["score", "name"], ascending=[False, True]))
# 输出解释:先按 score,再按 name 排序

print(df.sort_index())
# 输出解释:按行索引升序

关键注意点:

  • 多列排序时,ascending 可传列表
  • sort_index(axis=1) 可按列名排序

12. 分组聚合:groupby、agg、transform、apply

python
import pandas as pd

df = pd.DataFrame(
    {
        "dept": ["技术", "技术", "销售", "销售"],
        "name": ["A", "B", "C", "D"],
        "salary": [12000, 15000, 9000, 11000],
    }
)

print(df.groupby("dept")["salary"].mean())
# 输出解释:按部门求平均工资

print(df.groupby("dept").agg(avg_salary=("salary", "mean"), max_salary=("salary", "max")))
# 输出解释:一次聚合多个指标

df["dept_avg"] = df.groupby("dept")["salary"].transform("mean")
# 输出解释:transform 返回与原表等长结果,可直接回填新列

result = df.groupby("dept").apply(lambda g: g.nlargest(1, "salary"))
print(result)
# 输出解释:每个部门取工资最高的一行

关键注意点:

  • agg 适合“汇总表”
  • transform 适合“回写原表”
  • apply 灵活但通常更慢,优先考虑向量化/内建聚合

13. 表连接:merge、concat、join、append

python
import pandas as pd

left = pd.DataFrame({"id": [1, 2, 3], "name": ["A", "B", "C"]})
right = pd.DataFrame({"id": [2, 3, 4], "score": [80, 90, 85]})

print(pd.merge(left, right, on="id", how="inner"))
# 输出解释:内连接,只保留 id=2,3

print(pd.merge(left, right, on="id", how="left"))
# 输出解释:左连接,以 left 为主

df1 = pd.DataFrame({"a": [1, 2]})
df2 = pd.DataFrame({"a": [3, 4]})
print(pd.concat([df1, df2], axis=0, ignore_index=True))
# 输出解释:纵向拼接,重置索引

left_i = left.set_index("id")
right_i = right.set_index("id")
print(left_i.join(right_i, how="left"))
# 输出解释:基于索引 join

关键注意点:

  • append 已不推荐,使用 pd.concat
  • 连接后注意检查行数变化,防止一对多导致数据膨胀

14. 透视表与交叉表:pivot_table、crosstab

python
import pandas as pd

df = pd.DataFrame(
    {
        "city": ["北京", "北京", "上海", "上海"],
        "gender": ["男", "女", "男", "女"],
        "sales": [100, 120, 90, 130],
    }
)

pt = pd.pivot_table(df, index="city", columns="gender", values="sales", aggfunc="mean")
print(pt)
# 输出解释:按城市和性别统计平均销售额

ct = pd.crosstab(df["city"], df["gender"])
print(ct)
# 输出解释:城市 * 性别 的频数统计表

关键注意点:

  • pivot_table 支持聚合,pivot 不支持重复键
  • crosstab(..., normalize="index") 可计算行占比

15. 字符串处理:str 方法大全

python
import pandas as pd

s = pd.Series(["  Alice_01  ", "Bob_02", "Cathy_03"])

print(s.str.strip())
# 输出解释:去除首尾空格

print(s.str.lower())
# 输出解释:转小写

print(s.str.replace("_", "-"))
# 输出解释:替换字符

print(s.str.split("_", expand=True))
# 输出解释:按 "_" 拆分为多列

print(s.str.contains("Bob"))
# 输出解释:是否包含子串 Bob

print(s.str.extract(r"([A-Za-z]+)_(\d+)"))
# 输出解释:正则提取名字和编号

关键注意点:

  • 字符串列存在缺失值时,许多 str 方法会返回 NaN
  • 正则处理优先使用 extract / contains / replace

16. 时间序列:dt 访问器、重采样 resample

python
import pandas as pd

df = pd.DataFrame(
    {
        "date": pd.to_datetime(["2026-01-01", "2026-01-02", "2026-01-08", "2026-01-09"]),
        "value": [10, 12, 15, 13],
    }
)

df["year"] = df["date"].dt.year
df["weekday"] = df["date"].dt.day_name()
print(df[["date", "year", "weekday"]])
# 输出解释:dt 访问器提取日期特征

ts = df.set_index("date")
print(ts.resample("W")["value"].mean())
# 输出解释:按周重采样并计算均值

关键注意点:

  • 使用 resample 前,索引必须是 DatetimeIndex
  • 常见频率:D 日、W 周、M 月、Q 季、Y

17. 数据导出与优化:to_csv、to_excel、减少内存

17.1 导出数据

python
import pandas as pd

df = pd.DataFrame({"name": ["A", "B"], "score": [90, 85]})
df.to_csv("result.csv", index=False, encoding="utf-8-sig")
df.to_excel("result.xlsx", index=False)
# 输出解释:生成 CSV 和 Excel 文件

17.2 内存优化示例

python
import pandas as pd

df = pd.DataFrame(
    {
        "id": [1, 2, 3],
        "score": [100, 90, 80],
        "city": ["北京", "上海", "北京"],
    }
)

print(df.memory_usage(deep=True))
# 输出解释:查看每列内存占用

df["id"] = df["id"].astype("int32")
df["score"] = df["score"].astype("int16")
df["city"] = df["city"].astype("category")

print(df.dtypes)
# 输出解释:整数降位 + 分类类型可减少内存

关键注意点:

  • 大表优先优化整数/浮点类型位宽
  • 低基数字符串列可转 category

18. 高频实战技巧(一行代码完成常用操作)

python
import pandas as pd

df = pd.DataFrame(
    {
        "name": ["A", "B", "C", "D"],
        "score": [90, 75, 88, 92],
        "city": ["北京", "上海", "北京", "深圳"],
    }
)

top3 = df.nlargest(3, "score")  # 分数 Top3
city_count = df["city"].value_counts()  # 城市频次
has_null = df.isna().any().any()  # 是否存在缺失值
renamed = df.rename(columns={"score": "final_score"})  # 重命名列
unique_city = df["city"].nunique()  # 不重复城市数
score_rank = df["score"].rank(method="dense", ascending=False)  # 密集排名

print(top3)
print(city_count)
print(has_null)
print(renamed.head(1))
print(unique_city)
print(score_rank)
# 输出解释:以上是一行即可完成的高频分析操作

关键注意点:

  • 掌握 nlargest/value_counts/rank/rename/nunique 能显著提升效率
  • 对大数据优先使用向量化方法,少写 Python 循环

19. 常见坑与避坑指南

19.1 链式赋值警告(SettingWithCopyWarning)

python
import pandas as pd

df = pd.DataFrame({"score": [60, 90, 75]})
sub = df[df["score"] >= 80]
sub.loc[:, "level"] = "A"  # 推荐写法:明确 loc
print(sub)
# 输出解释:避免不明确的链式赋值

关键注意点:

  • 尽量使用 df.loc[行条件, 列名] = 值 进行赋值

19.2 布尔条件优先级错误

python
import pandas as pd

df = pd.DataFrame({"a": [1, 2, 3], "b": [3, 2, 1]})
ok = df[(df["a"] > 1) & (df["b"] < 3)]  # 每个条件都加括号
print(ok)
# 输出解释:正确筛选出满足复合条件的行

关键注意点:

  • 直接写 df["a"] > 1 & df["b"] < 3 会出错或结果错误

19.3 datetime 未转换导致 dt 报错

python
import pandas as pd

df = pd.DataFrame({"date": ["2026-01-01", "2026-01-02"]})
df["date"] = pd.to_datetime(df["date"])
print(df["date"].dt.month)
# 输出解释:必须先转 datetime 才能使用 dt

关键注意点:

  • 时间处理前统一 pd.to_datetime

19.4 merge 后行数异常膨胀

python
import pandas as pd

left = pd.DataFrame({"id": [1, 1], "x": [10, 20]})
right = pd.DataFrame({"id": [1, 1], "y": [100, 200]})
merged = pd.merge(left, right, on="id", how="inner")
print(merged)
# 输出解释:一对多/多对多会产生笛卡尔扩张

关键注意点:

  • 连接前先检查键是否唯一:df["key"].is_unique
  • 必要时先 drop_duplicates 再连接

19.5 读写中文乱码

python
import pandas as pd

df = pd.DataFrame({"城市": ["北京", "上海"]})
df.to_csv("cn.csv", index=False, encoding="utf-8-sig")
df2 = pd.read_csv("cn.csv", encoding="utf-8-sig")
print(df2)
# 输出解释:使用 utf-8-sig 可减少 Excel 打开乱码问题

关键注意点:

  • 团队内统一编码规范,优先 utf-8 / utf-8-sig

结语

掌握本手册中的核心 API 后,你已经具备了完整的 Pandas 数据处理能力。建议后续按以下路径持续提升:

  • 先把“清洗 + 分组 + 连接 + 时间序列”练熟
  • 再结合真实业务数据进行端到端分析
  • 最后形成自己的可复用数据处理模板

持续实践,Pandas 会成为你最稳定、最高效的数据工程工具之一。