跳转至

数据处理

在实际的数据处理工作中,经常需要同时处理多张表,需要对多张表的字段进行合并、提取等操作。例如,在审批贷款时,需要结合用户的基本信息进行信用评估。本章主要介绍数据处理的基本方法,包括数据读取、数据整合及数据清洗。本章涉及的数据处理软件是Python。

考点

考点 1:文件读取 read_csv(高频)

核心知识点

pd.read_csv () 读取本地 CSV 文件,考试常考三大参数:

  • na_values:将指定字符串识别为缺失值 NaN(最常考 \N)
  • header:指定第几行为表头,默认 header=0
  • index_col:指定某一列作为行索引

易错点

na_rep 是写入 to_csv参数,读取文件无效;encoding 只能填编码格式,不能处理缺失值。

✅ 标准示例代码

# 将CSV中的 \N 识别为缺失值
df = pd.read_csv("data.csv", na_values=["\\N"])

考点 2:缺失值处理 NaN(超高频|含删行 / 删列判断口诀)

核心知识点

  • fillna():填充缺失值,均值 / 固定值填充
  • dropna():删除含缺失值的行 / 列(选择题超级高频)
  • isna()/notna():判断缺失值(禁止用 == np.nan)

🔥 必考重点:dropna 如何判断删行还是删列?(独家口诀)

只看 axis! how 不决定行 / 列,只决定 “什么时候删”

  • axis = 0 → 删除 整行(默认值)
  • axis = 1 → 删除 整列

记忆无敌口诀:0 行 1 列

how 参数作用(仅控制删除条件)

  • how="any":任意一个 NaN 就删(默认)
  • how="all":全部都是 NaN 才删

真题语义秒懂

  • dropna(axis=0, how='any') → 一行只要有空值,删掉整行
  • dropna(axis=1, how='all') → 一列全部为空,才删掉整列

超级易错大坑

fillna/dropna 设置 inplace=True 时,返回值为 None!不能赋值给新变量

✅ 标准示例代码

# 均值填充缺失值(真题同款)
df.fillna(df["A"].mean(), inplace=True)

# 判断缺失值筛选
df[df["A"].isna()]

# 删除含NaN的整行(默认最常用)
df = df.dropna()

# 删除含NaN的整列
df = df.dropna(axis=1)

考点 3:loc 与 iloc 索引切片(必考天花板|补充多行多列、零散位置选取;数字标签特殊情况)

核心区别

  • loc [行标签,列标签]:按名字取值,闭区间(首尾都取)

💡特殊情况:如果行索引本身就是数字(数字标签),loc 里面直接写数字(不要引号!),这是标签,不是下标!

  • iloc [行位置,列位置]:按 0 开始数字下标,左闭右开

📎【多行、多列、零散位置选取(考试高频)】

书写格式:逗号分隔行维度与列维度;连续用冒号:;零散不连续使用列表 [ ]

iloc(数字下标,永远代表位置,从 0 开始)

1.连续多行多列切片

df.iloc[1:3, 0:2]

2.零散不连续多行多列(传入数字列表)

df.iloc[[0,2,4],[1,3]]

3.零散行,全部列

df.iloc[[1,3], :]

4.连续行,零散列

df.iloc[0:3, [2,4]]

loc(标签 / 列名;索引恰好是数字时直接写数字,不加引号!)

📌示例:行索引为数字:1,2,3,4(数字标签)

df.loc[1:3] # 这里 1、3 是标签值,闭区间,会取行标签 1、2、3;不是下标!不要和 iloc 混淆!

1.连续多行多列切片(普通字符串标签‑闭区间)

df.loc['a':'c', 'math':'english']

2.连续多行多列切片(数字行标签‑闭区间,不加引号)

df.loc[1:3, 'math':'english']

3.零散不连续多行多列(字符串标签)

df.loc[['a','c','e'],['math','chinese']]

4.零散不连续多行多列(数字行标签,直接写数字进列表,不加引号)

df.loc[[1,3,5],['math','chinese']]

5.零散行,全部列(数字标签)

df.loc[[2,4], :]

6.全部行,零散列

df.loc[:, ['math','chinese']]

⚠️易错总结(选择题超级坑!)

1.iloc 里面只能写数字下标(代表位置),不能写列名字!

2.loc 里面:

  • 如果索引是字符串标签 → 写字符串 'a';
  • 如果索引本身就是数字标签 → loc 直接写数字(不加引号!),此时这个数字是标签名字,不是下标!切片依旧是闭区间!

3.零散选取一定要套方括号列表 [x1,x2,x3],不要直接逗号隔开写!

❌错误写法:df.iloc[0,2,4 , 1,3]

✅正确写法:df.iloc[[0,2,4], [1,3]]

✅ 标准示例代码

# loc 按字符串标签取 y 行
df.loc["y"]

# iloc 按位置取第2行第2列(下标1,1)
df.iloc[1,1]

# iloc:零散下标位置选取多行多列
df.iloc[[0,2,4],[1,3]]

# loc:零散字符串标签选取多行多列
df.loc[['a','c','e'],['math','chinese']]

# loc:零散【数字行标签】选取多行多列(不加引号!高频考题)
df.loc[[1,3,5],["math","chinese"]]

# iloc连续切片(左闭右开,下标)
df.iloc[1:3,0:2]

# loc连续切片,数字行标签(闭区间!标签1‑3全部取出,重点易错!)
df.loc[1:3,"math":"english"]

考点 4:新增列、删除行列 drop

核心知识点

  • 新增列:df ["新列名"] = 数据(最标准写法)
  • drop 删除口诀通用:0 行 1 列
  • inplace=True:原地修改、返回 None;inplace=False(默认值):返回新表

✅ 标准示例代码

# 新增列
df["C"] = [7,8,9]

# 原地删除B列(真题标准答案)
df.drop(columns="B", inplace=True)

# 删除行标签为 y 的行
df.drop("y", axis=0)

考点 5:布尔筛选 & 多条件筛选(超级高频)

核心规则

  • 多条件禁止 and/or,必须用 & |
  • 每个条件必须单独括号包裹
  • between (a,b):默认闭区间 [a,b],包含首尾

✅ 标准示例代码

# 单条件筛选
df[df["age"] > 20]

# 多条件筛选(真题标准格式)
df[(df["city"]=="Shanghai") & (df["price"]>100)]

# 区间筛选 60~80 闭区间
df[df["score"].between(60,80)]

考点 6:数据合并 merge(JOIN 连接)

核心知识点

merge 用来实现两个 DataFrame 的表连接;how 参数控制连接方式。

  • inner(内连接,默认):只保留两张表匹配成功的行(取交集)
  • left(左连接):以左边表全部行为基准,右边匹配不上填充 NaN
  • right(右连接):以右边表全部行为基准,左边匹配不上填充 NaN
  • outer(外连接 / 全外连接):保留左右两边所有行,匹配不上填充 NaN
  • ⭐重名列:两张表有相同列名(非关联键),合并后自动生成 _x、_y后缀!(考试高频坑)
  • 支持多列复合匹配 on=[col1,col2]

💡先看原始测试数据

import pandas as pd
#员工基本信息表
df1 = pd.DataFrame({
    "id":[1,2,3],
    "name":["张三","李四","王五"],
    "dept":["技术部","财务部","人事部"]
})
#员工绩效表
df2 = pd.DataFrame({
    "id":[2,3,4],
    "dept":["财务","人事","运营部"],
    "score":[88,92,76]
})

df1(左表‑员工基础)

id name dept
1 张三 技术部
2 李四 财务部
3 王五 人事部

df2(右表‑员工绩效)

id dept score
2 财务 88
3 人事 92
4 运营部 76

❗注意:两张表都存在dept列!id 是连接键,dept 不是连接键;合并之后会自动重命名为 dept_x(来自左表 df1),dept_y(来自右表 df2)

① inner 内连接(交集,how="inner",默认)

代码:

res_inner = pd.merge(df1, df2, on="id", how="inner")

结果(只保留 id 同时出现在两张表里:2、3)

id name dept_x dept_y score
2 李四 财务部 财务 88
3 王五 人事部 人事 92

② left 左连接(how="left")

代码:

res_left = pd.merge(df1, df2, on="id", how="left")

结果:保留左表 df1 全部 id;右表没有的数据填 NaN

id name dept_x dept_y score
1 张三 技术部 NaN NaN
2 李四 财务部 财务 88
3 王五 人事部 人事 92

③ right 右连接(how="right")

代码:

res_right = pd.merge(df1, df2, on="id", how="right")

结果:保留右表 df2 全部 id;左表没有的数据填 NaN

id name dept_x dept_y score
2 李四 财务部 财务 88
3 王五 人事部 人事 92
4 NaN NaN 运营部 76

④ outer 全外连接(how="outer")

代码:

res_outer = pd.merge(df1, df2, on="id", how="outer")

结果:保留两边全部 id,哪边缺失哪边填充 NaN

id name dept_x dept_y score
1 张三 技术部 NaN NaN
2 李四 财务部 财务 88
3 王五 人事部 人事 92
4 NaN NaN 运营部 76

💡简易文字释义(考试背诵)

1.how="inner":只留下两边 id 都有的数据;

2.how="left":左表全部留下;右边找不到的数据填 NaN;

3.how="right":右表全部留下;左边找不到的数据填 NaN;

4.how="outer":左右两边全部保留,哪边缺失哪边填 NaN;

5.⚠️两张表存在重名列(不是连接键),自动生成 _x(左) _y(右)后缀!选择题高频坑!

✅ 更多标准示例代码

# inner 内连接(交集,默认)
pd.merge(df1, df2, on="id", how="inner")

# left 左连接,保留左表全部
pd.merge(df1, df2, on="id", how="left")

# right 右连接,保留右表全部
pd.merge(df1, df2, on="id", how="right")

# outer 全外连接,保留两边所有行
pd.merge(df1, df2, on="id", how="outer")

# 多列内连接(根据两列同时匹配)
pd.merge(df1, df2, on=["id","dept"], how="inner")

# 索引外连接
pd.merge(df1, df2, left_index=True, right_index=True, how="outer")

考点 7:数据拼接 concat(堆叠)

核心口诀(必考区分)

concat = 直接粘贴堆叠,不匹配关键字!按索引对齐!

  • axis=0(默认):纵向拼接(上下堆叠,增加行数)
  • axis=1:横向拼接(左右平铺,增加列数)
  • ignore_index=True:重置0、1、2新连续索引(超级高频)
  • 索引对不上的位置,自动填充 NaN

沿用和考点6一模一样的原始数据(方便对比记忆)

import pandas as pd
df1 = pd.DataFrame({
    "id":[1,2,3],
    "name":["张三","李四","王五"],
    "dept":["技术部","财务部","人事部"]
})

df2 = pd.DataFrame({
    "id":[2,3,4],
    "dept":["财务","人事","运营部"],
    "score":[88,92,76]
})

① 纵向拼接 axis=0(上下堆行)

方式1:不重置索引(默认状态,考试高频坑点)

不设置 ignore_index=True,会保留两张表原始行索引,拼接后会出现重复索引,后续取值极易出错。

# 纵向拼接,不重置索引(默认)
res_concat0_no = pd.concat([df1,df2],axis=0)

运行结果(存在重复索引 0、1、2)

行索引 id name dept score
0 1 张三 技术部 NaN
1 2 李四 财务部 NaN
2 3 王五 人事部 NaN
0 2 NaN 财务 88
1 3 NaN 人事 92
2 4 NaN 运营部 76

⚠️ 致命坑点:此时执行 res_concat0_no.loc[0],会取出两行数据,造成数据混乱、统计错误!

方式2:重置索引(标准写法,考试推荐)

添加 ignore_index=True,清除旧索引,生成 0-5 连续全新索引,无重复、无错误。

# 纵向拼接+重置索引(考试标准答案)
res_concat0 = pd.concat([df1,df2],axis=0,ignore_index=True)

运行结果(索引连续无重复)

行索引 id name dept score
0 1 张三 技术部 NaN
1 2 李四 财务部 NaN
2 3 王五 人事部 NaN
3 2 NaN 财务 88
4 3 NaN 人事 92
5 4 NaN 运营部 76

② 横向拼接 axis=1(左右拼列)

✅ 关键知识点:ignore_index 仅对纵向拼接(axis=0)生效,对横向拼接(axis=1)无效,横向拼接永远按原索引对齐,不会重复行索引。

res_concat1 = pd.concat([df1,df2],axis=1)

运行结果

id name dept id dept score
1 张三 技术部 2 财务 88
2 李四 财务部 3 人事 92
3 王五 人事部 4 运营部 76

🔥 考试终极区分:merge VS concat(必背)

  • merge:按关键字id匹配关联,行自动对齐、去重融合,适合联表查询
  • concat:纯堆叠粘贴,不做匹配,行索引对不上就空值,适合合并同类数据表
  • 重名列:merge生成_x/_y;concat直接保留两列不改名
  • 索引核心考点:concat纵向不重置索引会产生重复索引,取值报错;横向拼接无需重置索引

考点 8:数据排序 sort_values

基础语法

df.sort_values(by, axis=0, ascending=True, inplace=False, na_position='last')

重点参数列表(考试全部有可能考)

1.by:指定排序依据(必写)

- 单列排序:`by="列名"`
- **多列排序:`by=["列1","列2"]`,先按列1排;列1值相等,再依据列2继续排**

2.ascending:升序还是降序

- `ascending=True` **升序(从小到大,默认)**
- `ascending=False` **降序(从大到小)**
- ⭐**多列排序的时候,ascending要传入列表!列表顺序必须和by里面排序列一一对应!**
> `by=["A","B"], ascending=[True,False]`
> 含义:**A升序;A相同的时候,B降序**(历年真题!)
  1. inplace:是否原地修改(和drop、fillna一致)

    • inplace=False(默认):返回排序后的新DataFrame,原表不变,需要赋值接收结果
    df_sorted = df.sort_values(by="score")
    
    • inplace=True:直接修改原来的df,返回None,千万不要再赋值!
    df.sort_values(by="score", inplace=True)
    

❗易错坑:不要写 df = df.sort_values(by="score",inplace=True) inplace=True返回None,写完之后df会变成None,整张表直接丢失!(选择题陷阱)

  1. na_position:缺失值NaN的摆放位置(容易被忽略的考点)

    • na_position='last'(默认):排序的时候,所有NaN放到最后面
    • na_position='first':所有NaN放到最前面
  2. axis 参数(sort_values几乎只用axis=0!)

  3. axis=0:按行方向排序(对数据行重新排列,绝大多数场景,默认)

  4. axis=1:按列方向排序!是横向排序(交换列的左右顺序,非常少见,选择题会出干扰选项)

    ⚠️千万不要混淆!sort_values(axis=1)不是对每一行内部数字排序;而是依据表头标签字符串对列名排序,调换各列的左右位置,日常极少使用。考试经常拿axis=1做错误选项。

🧪 构造示例数据用于演示

name class score
张三 1 85
李四 1 92
王五 2 76
赵六 2 92
孙七 1 NaN

①单列排序示例

# score升序(从小到大,NaN默认放末尾)
df.sort_values(by="score")

# score降序,NaN依旧放末尾
df.sort_values(by="score", ascending=False)

# 降序,把缺失值放到最前面
df.sort_values(by="score", ascending=False, na_position="first")

②多列排序真题示例(最重要!必背)

需求:先按班级class升序;同一个班级内部,按分数score降序

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

执行之后结果:

先全部1班在上;1班内部分数从高到低;然后2班;2班内部分数从高到低;最后孙七(NaN)。

💡对应选择题套路: by里面列的顺序决定排序优先级!写在前边的列优先! ascending列表长度必须和by列表长度保持一致,数量不能多也不能少!如果数量不一致直接报错。

🚩 易混辨析:sort_index() 和 sort_values()(干扰选项常客)

  • df.sort_values(by="xxx"):按照某一列的数据值来对行排序(业务最常用)
  • df.sort_index():按照行索引(index标签)本身的值排序,不是按数据内容排序!没有by参数!

选择题坑:看到题目想根据某一列里面的数据大小排序,选sort_values,不要选sort_index!

📝 考点8背诵笔记

💡sort_values()排序: 1. by指定排序列;单列写字符串;多列写列表by=["A","B"],前边的列优先级更高,先排序;值相等时再以后边列为依据继续排。 2. ascending:单列传布尔值;多列排序时必须传入布尔列表,顺序与by列表一一对应,长度必须相等。True升序,False降序。 3. inplace=False默认返回新对象;inplace=True原地修改,返回None,禁止再赋值,否则变量变为None丢失数据。 4. na_position控制NaN位置;默认na_position='last'缺失值放末尾;na_position='first'放开头。 5. axis=0默认对行重新排序;axis=1横向对列名排序(极少使用,常作为错误选项)。 6. 和sort_index()区分:sort_index依据索引标签排序;sort_values依据单元格里面的数据值排序。

✅ 标准代码汇总

#单列升序,返回新表
df.sort_values(by="score")

#单列降序,原地修改
df.sort_values(by="score",ascending=False,inplace=True)

#多列排序真题写法:A升序,A相同B降序
df.sort_values(by=["A","B"],ascending=[True,False])

#缺失值放到最前面
df.sort_values(by="score",na_position="first")

#按索引排序(sort_index,无by)
df.sort_index()

考点 9:分组聚合 groupby + agg

groupby之后,分组字段默认变成行索引(考试高频坑)

一句话结论

使用 df.groupby(【分组列名】) 之后,用来分组的那一列,默认不再是普通数据列,升级成为DataFrame的行索引!

✨先看示例原始表 df

name team score
张三 A 85
李四 A 90
王五 B 78
赵六 B 82
res = df.groupby("team")["score"].mean()
print(res)

输出结果:

team
A    87.5
B    80.0
Name: score, dtype: float64

👉观察这里: team 不在普通列里面了!team现在是行索引(index);只剩下score的均值这一列。

🚩两个关键参数(选择题经常考)

① as_index=True 【默认!】分组字段 →行索引

# 默认 as_index=True,可以不写出来
res = df.groupby("team", as_index=True)["score"].mean()

效果:team成为行索引,不再是普通列。

❗坑点:此时你不能写 res["team"] 去取这一列!会报错,因为它已经不是列,是索引。 如果想拿到team的值,只能读取索引:res.index

② as_index=False 禁止升级为索引,保留普通数据列!

res = df.groupby("team", as_index=False)["score"].mean()
print(res)

输出表格(team仍然是实实在在的数据列)

team score
A 87.5
B 80.0

✅这时 res["team"] 就可以正常访问这一列,很多业务数据分析的时候就会写 as_index=False。


🚩 还有第二种方案:分组之后调用 .reset_index()

如果已经默认分组(as_index=True),结果的分组字段是行索引;后续想把索引还原回普通列,可以调用重置索引。

res = df.groupby("team")["score"].mean()
df_res = res.reset_index()   # 把行索引team还原成普通数据列

执行完reset_index之后,结果就和 as_index=False 的效果一模一样!

📝 考试背诵笔记

💡重点:groupby默认as_index=True,分组字段会升级成为行索引,不再属于普通数据列,此时无法通过df[分组字段名]取值;

两种方式让分组字段保留普通列: 1.分组的时候设置 as_index=False 2.先分组聚合,之后调用 .reset_index(),将索引还原回普通列。

🧪 真题易错选择题套路

题目:df.groupby("team")["score"].mean()之后,是否还可以df_res["team"]?

👉答案:不可以,team已经变成行索引;要么加as_index=False,要么后面reset_index()。

考点 10:透视表 pivot_table

基础回顾

pd.pivot_table() 默认aggfunc="mean",做均值计算,结果经常是浮点数!

pd.pivot_table(df, values="Salary", index="Dept", columns="Gender", aggfunc="mean")

✅ 重点:aggfunc 参数可以接收:字符串、列表、字典

方式1:单个聚合(修改aggfunc字符串,更换运算)

支持常用聚合名字字符串:

  • aggfunc="sum" 求和
  • aggfunc="max" 最大值
  • aggfunc="min" 最小值
  • aggfunc="count" 计数(统计记录行数,不是非空值数量)
  • aggfunc="size" 计数(size是分组元素个数,包含NaN所在行;pivot_table里面一般不用size)
  • aggfunc="median" 中位数

示例代码‑求和:

# 按部门做行、性别做列,薪资求和
pd.pivot_table(df, values="Salary", index="Dept", columns="Gender", aggfunc="sum")

方式2:aggfunc传列表,一次性执行多种聚合!(选择题高频!)

同一数值列,同时求求和、均值、最大值;透视表的列会产生多层列索引!

pd.pivot_table(df,
               values="Salary",
               index="Dept",
               columns="Gender",
               aggfunc=["sum","mean","max"])

⚠️考试坑:结果会出现双层列索引!不再是单层表头。

方式3:aggfunc传字典:不同列使用不同的聚合函数(非常容易考)

key:列名;value:聚合方法。不同数值字段各自指定运算

pd.pivot_table(df,
               index="Dept",
               columns="Gender",
               aggfunc={"Salary":"mean",  #薪资求平均
                        "Score":"max"}) #分数取最大值

👉不需要写values参数了!字典已经指定哪些列参与聚合。

🚩 补充两个重要参数(margins!考试选择题常客)

margins=True:开启合计行、合计列;margins_name="总计" 修改合计的名字,默认名字"All"

pd.pivot_table(df,
               values="Salary",
               index="Dept",
               columns="Gender",
               aggfunc="sum",
               margins=True)

运行之后表格右下角会多出All的总计单元格。

❗易错坑:margins计算是独立重新聚合,不是直接对现有单元格再求和!不要误以为等于上面数字相加。

🚩 辨析 pivot_table(透视表) vs pivot(简单旋转)【易混考点】

df.pivot():只做重塑表格,不能做聚合!如果相同行列存在多条记录,直接报错!

pivot没有aggfunc参数;只要数据里面出现重复(index‑column)组合直接抛异常。 只要题目提到聚合、求和求平均,一定选pivot_table,不要选pivot!

📝 直接追加进笔记的背诵段落(考点10)

💡拓展:透视表aggfunc支持多种聚合。

1.单个聚合:aggfunc="sum"/"max"/"min"/"median";默认aggfunc="mean"求均值。

2.传入列表如aggfunc=["sum","mean","max"],对同一字段执行多项聚合,结果生成双层列索引。

3.传入字典aggfunc={"A":"mean","B":"max"},实现不同列使用不同聚合运算,此时不需要写values参数。

4.margins=True打开总计行与总计列,默认总计名称为All;注意margin是重新聚合,不是现有单元格相加。

5.⚠️区分pivot():pivot仅用于表格重塑,没有聚合能力,遇到重复组合直接报错;需要聚合只能使用pivot_table。

考点 11:去重 drop_duplicates

核心参数

  • subset:指定判断重复的列
  • keep="first" 保留首行 / keep="last" 保留尾行

核心结论

keep 的默认值是 keep="first"——默认保留重复组里的第一行(首行),后面的重复行直接删除。

keep 的三种取值

  • keep="first"(默认,不写就是这个):保留重复组的第一行(首行),删除后面的重复行
  • keep="last":保留重复组的最后一行(尾行),删除前面的重复行
  • keep=False:全部重复行都删掉,只要出现重复,这一组整组删除

subset:指定依据哪些列判断重复

  • 不写 subset:整行所有字段完全一样才算重复
  • subset=["A","B"]:只根据 A、B 两列的值判定重复,其他列不管

🧪 小示例

原始数据:

id name
1 张三
2 李四
2 李四
3 王五
  • 默认 keep="first":df.drop_duplicates(subset="id") → 保留 id=2 的第一行李四,第二条李四被删掉
  • keep="last":保留 id=2 靠后面那一条李四,前面那条被删掉
  • keep=False:id 重复的两行全部删除,只剩 id=1、id=3 两行

🚨 考试坑选择题:题目没写 keep 参数、直接调用 drop_duplicates,默认保留第一行,不要误以为保留最后一行。

✅ 标准示例代码

# 根据A列去重,重复保留最后一行(真题原题)
df.drop_duplicates(subset="A", keep="last")

# 不写 keep:默认 keep="first",保留重复组的第一行
df.drop_duplicates(subset="A")

# keep=False:只要重复就全部删除
df.drop_duplicates(subset="A", keep=False)

# 不写 subset:整行所有字段完全一致才算重复
df.drop_duplicates()