Pandas 入门到实用:数据读取、清洗、合并、聚合与项目流程

Pandas 是 Python 中最常用的数据分析库之一。它适合处理表格数据,例如 CSV、Excel、实验记录、Kaggle 数据集、推荐系统日志和机器学习训练数据。

如果说 NumPy 更像“高效数组计算工具”,那么 Pandas 更像“带行列标签的表格处理工具”。它最适合做数据分析项目的前半段:读取数据、检查质量、清洗字段、构造特征、合并多张表、聚合统计,最后把干净的数据交给模型或可视化工具。

1. Pandas 的核心对象

Pandas 里最重要的是两个对象:

  • Series:一列数据,类似带索引的一维数组。
  • DataFrame:二维表格,类似 Excel 表或数据库表。
1
2
3
4
5
6
7
8
9
10
import pandas as pd

s = pd.Series([1, 2, 3, 4])
print(s)

df = pd.DataFrame({
"name": ["Alice", "Bob", "Cathy"],
"score": [92, 85, 96]
})
print(df)

可以这样理解:

1
DataFrame = 多个 Series 按列拼在一起

所以 Pandas 的很多操作,本质是在回答三个问题:

  1. 我要选哪些行?
  2. 我要选哪些列?
  3. 我要对这些行列做什么变换或统计?

2. 读取数据

机器学习项目里最常见的数据入口是 CSV:

1
df = pd.read_csv("train.csv")

如果文件是 tab 分隔,例如 .tsv

1
df = pd.read_csv("data.tsv", sep="\t")

如果中文乱码,可以指定编码:

1
df = pd.read_csv("data.csv", encoding="utf-8")

有些中文 Windows 文件可能是 gbk

1
df = pd.read_csv("data.csv", encoding="gbk")

读取 Excel:

1
df = pd.read_excel("data.xlsx", sheet_name="Sheet1")

读取 JSON:

1
df = pd.read_json("data.json")

读取大 CSV 时,可以先只读部分行:

1
df_sample = pd.read_csv("large.csv", nrows=1000)

这样能先看字段结构,避免一开始就把大文件全部读进内存。

3. 数据开箱检查

拿到一个 DataFrame 后,不要马上写复杂逻辑。先做基础检查:

1
2
3
4
5
6
df.head()
df.tail()
df.shape
df.columns
df.info()
df.describe()

这些方法分别回答:

方法 用途
head() 看前几行,确认数据长什么样
tail() 看后几行,检查尾部是否异常
shape 看行数和列数
columns 看列名
info() 看字段类型、非空数量、内存占用
describe() 看数值列统计信息

更实用的检查清单:

1
2
3
4
df.isna().sum()          # 每列缺失值数量
df.duplicated().sum() # 重复行数量
df.nunique() # 每列唯一值数量
df.dtypes # 每列类型

如果某个分类字段想看分布:

1
df["label"].value_counts()

如果想看比例:

1
df["label"].value_counts(normalize=True)

这是分类任务里非常常见的一步,可以快速判断类别是否不平衡。

4. 选择列:单中括号和双中括号

按列名取一列:

1
df["score"]

返回的是 Series

取多列:

1
df[["name", "score"]]

返回的是 DataFrame

即使只取一列,双中括号仍然返回 DataFrame:

1
df[["score"]]

这个区别很重要:

1
2
type(df["score"])     # pandas.Series
type(df[["score"]]) # pandas.DataFrame

实际项目里,如果你要传给模型,通常希望保持二维结构,可以用双中括号:

1
2
X = df[["age", "score"]]
y = df["label"]

5. 选择行:条件筛选

按条件筛选:

1
high_score = df[df["score"] >= 90]

多个条件要用 &|,每个条件要加括号:

1
filtered = df[(df["score"] >= 90) & (df["name"] != "Bob")]

不要写成:

1
2
# 错误示范
df[df["score"] >= 90 and df["name"] != "Bob"]

因为 Pandas 的条件不是单个 True/False,而是一整列 boolean values。

常见筛选写法:

1
2
3
df[df["city"].isin(["Hong Kong", "Shenzhen"])]
df[df["text"].notna()]
df[df["score"].between(60, 90)]

6. loc 和 iloc

loc 按标签选:

1
df.loc[0, "name"]

iloc 按位置选:

1
df.iloc[0, 1]

简单记忆:

  • loc 看名字。
  • iloc 看数字位置。

选择多行多列:

1
2
df.loc[0:5, ["name", "score"]]
df.iloc[0:5, 0:2]

如果要修改某些行的值,推荐用 loc

1
2
df.loc[df["score"] < 60, "passed"] = False
df.loc[df["score"] >= 60, "passed"] = True

这样比链式赋值更稳。

7. 类型转换

很多 bug 来自字段类型不对。比如数字被读成字符串,日期被读成普通文本。

查看类型:

1
df.dtypes

转成数字:

1
df["score"] = pd.to_numeric(df["score"], errors="coerce")

errors="coerce" 表示无法转换的值变成 NaN

转成字符串:

1
df["text"] = df["text"].astype(str)

转成日期:

1
df["date"] = pd.to_datetime(df["date"], errors="coerce")

日期转好后,可以提取年月日:

1
2
3
df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df["dayofweek"] = df["date"].dt.dayofweek

这在日志分析、交易数据、用户行为数据里非常常用。

8. 缺失值处理

查看缺失值:

1
df.isna().sum()

查看缺失比例:

1
df.isna().mean().sort_values(ascending=False)

填充缺失值:

1
df["score"] = df["score"].fillna(0)

用均值填充:

1
df["score"] = df["score"].fillna(df["score"].mean())

用众数填充分类字段:

1
df["city"] = df["city"].fillna(df["city"].mode()[0])

删除缺失行:

1
df = df.dropna()

只根据某些关键列删除:

1
df = df.dropna(subset=["text", "label"])

实用经验:

  • 标签列缺失,通常直接删除。
  • 文本列缺失,通常删除或填空字符串。
  • 数值特征缺失,可以用均值、中位数、业务默认值填充。
  • 缺失本身可能有信息时,可以新增一列记录是否缺失。
1
df["score_missing"] = df["score"].isna()

9. 重复值和异常值

检查重复行:

1
df.duplicated().sum()

删除完全重复的行:

1
df = df.drop_duplicates()

按某些列去重:

1
df = df.drop_duplicates(subset=["user_id", "item_id"])

异常值可以先用 describe() 粗看:

1
df["score"].describe()

也可以看分位数:

1
df["score"].quantile([0.01, 0.05, 0.5, 0.95, 0.99])

如果发现年龄为 999、价格为负数、评分超过上限,就要回到业务含义判断是删除、截断还是修正。

10. 新增列和 apply

如果列之间有计算关系,可以直接生成新列:

1
df["passed"] = df["score"] >= 60

简单运算尽量用向量化写法:

1
df["score_10"] = df["score"] / 10

不要写 for loop:

1
2
3
# 不推荐
for i in range(len(df)):
df.loc[i, "score_10"] = df.loc[i, "score"] / 10

复杂逻辑可以用 apply

1
2
3
4
5
6
7
8
9
10
def grade(score):
if score >= 90:
return "A"
if score >= 80:
return "B"
if score >= 60:
return "C"
return "D"

df["grade"] = df["score"].apply(grade)

如果要按行读取多个列:

1
2
3
4
def combine_name(row):
return f"{row['first_name']} {row['last_name']}"

df["full_name"] = df.apply(combine_name, axis=1)

但要注意:apply(axis=1) 在大数据上可能慢。能用向量化、np.wheremapreplace 的时候,优先不用逐行 apply。

11. 字符串处理

NLP 和日志分析里,字符串处理非常常见。

Pandas 的字符串方法通常通过 .str 调用:

1
2
3
df["text"] = df["text"].astype(str)
df["text_lower"] = df["text"].str.lower()
df["text_len"] = df["text"].str.len()

去掉前后空格:

1
df["text"] = df["text"].str.strip()

判断是否包含关键词:

1
df[df["text"].str.contains("spark", case=False, na=False)]

替换文本:

1
df["text"] = df["text"].str.replace(r"\s+", " ", regex=True)

拆分:

1
df["tokens"] = df["text"].str.split()

如果是情感分析数据,常见格式是:

1
2
3
text,label
I love this movie,positive
This is terrible,negative

可以这样读取:

1
2
3
4
5
6
df = pd.read_csv("sentiment.csv")
df = df.dropna(subset=["text", "label"])
df["text"] = df["text"].astype(str).str.strip()

texts = df["text"]
labels = df["label"]

12. 排序、排名和抽样

排序:

1
df.sort_values("score", ascending=False)

按多个字段排序:

1
df.sort_values(["class", "score"], ascending=[True, False])

取前几行:

1
2
df.nlargest(10, "score")
df.nsmallest(10, "score")

随机抽样:

1
df_sample = df.sample(n=100, random_state=42)

按比例抽样:

1
df_sample = df.sample(frac=0.1, random_state=42)

random_state 可以保证每次抽样结果一致,方便复现实验。

13. merge:合并多张表

真实项目里,数据通常不在一张表里。例如:

  • 用户表:user_id, age, gender
  • 物品表:item_id, category, price
  • 行为表:user_id, item_id, rating

merge 合并:

1
2
3
4
5
6
merged = pd.merge(
left=ratings,
right=movies,
on="movie_id",
how="inner"
)

常见 join 类型:

join 类型 含义
inner 只保留两边都匹配的行
left 保留左表全部行,右表匹配不到填 NaN
right 保留右表全部行,左表匹配不到填 NaN
outer 左右两边全部保留,匹配不到填 NaN

如果左右表的 key 名字不同:

1
2
3
4
5
6
7
pd.merge(
left=df1,
right=df2,
left_on="user_id",
right_on="id",
how="left"
)

merge 后一定要检查行数:

1
2
3
print(df1.shape)
print(df2.shape)
print(merged.shape)

如果行数突然暴涨,可能是 key 不唯一导致 many-to-many join。

14. groupby:从明细到统计

groupby 是 Pandas 里最实用的功能之一。

基本模式:

1
df.groupby("label").size()

按类别求平均:

1
df.groupby("label")["score"].mean()

多个聚合:

1
2
3
4
5
6
summary = df.groupby("label").agg(
count=("score", "size"),
mean_score=("score", "mean"),
max_score=("score", "max"),
min_score=("score", "min")
).reset_index()

按多个字段分组:

1
df.groupby(["gender", "label"])["score"].mean().reset_index()

推荐系统里常见问题:计算每部电影的平均评分。

1
2
3
4
5
6
7
8
9
10
movie_rating = (
ratings
.groupby("movie_id")
.agg(
rating_count=("rating", "size"),
rating_mean=("rating", "mean")
)
.reset_index()
.sort_values("rating_mean", ascending=False)
)

实用提醒:只看平均分可能会误导。如果一部电影只有 1 个评分且是 5 分,它会排很高。所以通常要加评分数量门槛:

1
movie_rating = movie_rating[movie_rating["rating_count"] >= 20]

15. pivot_table:做交叉统计

pivot_table 可以做类似 Excel 透视表的统计。

例如按性别和标签统计平均分:

1
2
3
4
5
6
7
table = pd.pivot_table(
df,
values="score",
index="gender",
columns="label",
aggfunc="mean"
)

如果想统计数量:

1
2
3
4
5
6
7
8
table = pd.pivot_table(
df,
values="user_id",
index="gender",
columns="label",
aggfunc="count",
fill_value=0
)

这在做 EDA 时很好用,可以快速看不同群体之间的差异。

16. 时间序列基础

如果有时间字段,先转成 datetime:

1
df["timestamp"] = pd.to_datetime(df["timestamp"], errors="coerce")

按日期统计数量:

1
2
3
4
5
6
daily_count = (
df
.set_index("timestamp")
.resample("D")
.size()
)

按月统计:

1
2
3
4
5
6
monthly_count = (
df
.set_index("timestamp")
.resample("M")
.size()
)

如果是用户行为日志,这些操作可以用来观察活跃趋势、季节性和异常峰值。

17. 链式写法:让清洗流程更清楚

简单清洗可以写成链式:

1
2
3
4
5
6
7
8
9
clean_df = (
df
.drop_duplicates()
.dropna(subset=["text", "label"])
.assign(
text=lambda x: x["text"].astype(str).str.strip(),
text_len=lambda x: x["text"].astype(str).str.len()
)
)

优点是每一步都清楚,适合写成可复现的数据处理流程。

如果步骤太复杂,可以拆成多个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def clean_text_columns(df):
df = df.copy()
df["text"] = df["text"].astype(str).str.strip()
df["text_len"] = df["text"].str.len()
return df

def remove_bad_rows(df):
return df.dropna(subset=["text", "label"]).drop_duplicates()

clean_df = (
df
.pipe(remove_bad_rows)
.pipe(clean_text_columns)
)

pipe 适合把数据清洗流程模块化。

18. 保存结果

保存 CSV:

1
df.to_csv("output.csv", index=False)

index=False 很常用,表示不要把 DataFrame 的行索引额外保存成一列。

保存 Excel:

1
df.to_excel("output.xlsx", index=False)

保存 Parquet:

1
df.to_parquet("output.parquet", index=False)

Parquet 比 CSV 更适合大数据和机器学习中间结果,因为它保留列类型,读取速度也通常更快。

19. 和机器学习的关系

Pandas 经常用在机器学习流程的前半段:

  1. 读取原始数据。
  2. 查看缺失值、重复值和异常值。
  3. 清洗文本或数值。
  4. 合并多张表。
  5. 构造特征。
  6. 划分训练集和测试集。
  7. 保存训练数据或预测结果。

例如一个简单的 NLP 分类数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
df = pd.read_csv("sentiment.csv")

df = (
df
.dropna(subset=["text", "label"])
.drop_duplicates()
)

df["text"] = df["text"].astype(str).str.strip()
df["text_len"] = df["text"].str.len()

texts = df["text"]
labels = df["label"]

再交给 scikit-learn 或 PyTorch:

1
2
3
4
5
6
7
8
from sklearn.model_selection import train_test_split

train_df, test_df = train_test_split(
df,
test_size=0.2,
random_state=42,
stratify=df["label"]
)

stratify=df["label"] 可以尽量保持训练集和测试集类别比例一致。

20. 性能和常见坑

不要在大 DataFrame 上频繁 for loop。

优先使用向量化、groupbymergemapreplace

避免链式赋值。

不要写:

1
df[df["score"] > 90]["grade"] = "A"

推荐写:

1
df.loc[df["score"] > 90, "grade"] = "A"

注意 copy 和 view。

如果从原 DataFrame 切出一部分后要修改,可以明确 .copy()

1
sub_df = df[df["score"] > 90].copy()

大文件先读样本。

1
sample = pd.read_csv("large.csv", nrows=1000)

合适时指定 dtype。

1
df = pd.read_csv("data.csv", dtype={"user_id": "string"})

ID 字段不一定要读成数字,尤其是有前导 0 的 ID。

21. 一个实用项目模板

以后做数据分析或机器学习项目,可以按这个模板走:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd

# 1. Read
df = pd.read_csv("data.csv")

# 2. Inspect
print(df.shape)
print(df.info())
print(df.isna().sum())

# 3. Clean
df = df.drop_duplicates()
df = df.dropna(subset=["text", "label"])
df["text"] = df["text"].astype(str).str.strip()

# 4. Feature engineering
df["text_len"] = df["text"].str.len()

# 5. Basic EDA
print(df["label"].value_counts(normalize=True))

# 6. Save
df.to_csv("clean_data.csv", index=False)

如果有多张表:

1
2
3
4
5
6
7
8
9
users = pd.read_csv("users.csv")
items = pd.read_csv("items.csv")
events = pd.read_csv("events.csv")

data = (
events
.merge(users, on="user_id", how="left")
.merge(items, on="item_id", how="left")
)

如果要做 groupby 特征:

1
2
3
4
5
6
7
8
9
user_features = (
data
.groupby("user_id")
.agg(
event_count=("item_id", "size"),
avg_price=("price", "mean")
)
.reset_index()
)

这就是很多推荐系统、用户画像、数据挖掘项目的雏形。

22. 小结

Pandas 初学阶段要掌握:

  • read_csv
  • head / info / describe
  • 选列和条件筛选
  • loc / iloc
  • 类型转换
  • 缺失值处理
  • 字符串处理
  • 新增列
  • merge
  • groupby
  • pivot_table
  • to_csv

更进一步,要形成项目意识:每次拿到数据,先检查质量,再清洗,再构造特征,再保存可复现结果。

后面学 Dask 和 Spark 时,你会发现很多概念都来自 Pandas:DataFrame、filter、select、groupby、join。Pandas 学扎实了,大数据工具就不会显得那么陌生。

Pandas 入门到实用:数据读取、清洗、合并、聚合与项目流程

https://richardf123.github.io/2024/02/02/Pandas/

作者

RichardF

发布于

2024-02-02

更新于

2026-06-24

许可协议