5.数据筛选与修改
本文最后更新于 2025年7月27日 晚上
初始化与加载数据
首先,我们导入 pandas
并加载东京奥运会的奖牌数据。
1 |
|
5-1 数据修改
1. 数据修改|列名
- 题目: 将原
df
列名Unnamed: 2
、Unnamed: 3
、Unnamed: 4
修改为金牌数
、银牌数
、铜牌数
。 - 答案:
1
2
3
4
5df_renamed = df.rename(columns={'Unnamed: 2': '金牌数',
'Unnamed: 3': '银牌数',
'Unnamed: 4': '铜牌数'})
# 查看修改后的前几行
df_renamed.head() - 解释:
rename()
是用于修改轴标签(行索引或列名)的函数。columns={...}
参数接收一个字典,key
是原始列名,value
是你想要的新列名。
2. 数据修改|行索引
- 题目: 将第一列(排名)设置为索引。
- 答案:
1
2
3# 在上一步重命名后的df_renamed上操作
df_indexed = df_renamed.set_index('排名')
df_indexed.head() - 解释:
set_index()
方法可以将 DataFrame 的某一列或多列设置为新的行索引。这在后续使用.loc
按标签查找时非常方便。
3. 数据修改|修改索引名
- 题目: 修改索引名为 金牌排名。
- 答案:
1
2
3
4# 同样在上一步的结果上操作
df_named_index = df_indexed.copy() # 创建副本以避免修改df_indexed
df_named_index.index.name = '金牌排名'
df_named_index.head() - 解释:
- 通过访问
df.index.name
属性,可以直接为其赋值,从而修改索引的名称。
- 通过访问
4. 数据修改|修改值
- 题目: 将 ROC(第一列第五行)修改为 俄奥委会。
- 答案:
1
2
3
4df_modified_value = df_named_index.copy()
# 使用 .loc[行标签, 列标签] 定位并修改
df_modified_value.loc[5, '国家奥委会'] = '俄奥委会'
df_modified_value.head() - 解释:
.loc
是基于标签进行索引的,是我们修改值的首选方法。df.loc[行标签, 列标签] = 新值
可以精确地定位到单元格并赋予新值。
5. 数据修改|替换值(单值)
- 题目: 将金牌数列的数字
0
替换为无
。 - 答案:
1
2
3
4# 创建一个副本进行操作
df_replaced = df_modified_value.copy()
df_replaced['金牌数'] = df_replaced['金牌数'].replace(0, '无')
df_replaced.tail() # 查看尾部数据确认替换 - 解释:
replace()
方法可以替换 Series 或 DataFrame 中的值。replace(要被替换的值, 新值)
是其基本用法。
6. 数据修改|替换值(多值)
- 题目: 同时替换,将
无
替换为缺失值,将0
替换为None
。 - 答案:
1
2
3# 使用上一步的 df_replaced 进行操作
df_multi_replaced = df_replaced.replace({'无': np.nan, 0: None})
df_multi_replaced.tail() - 解释:
replace()
也可以接收一个字典来进行多值替换。key
是待替换的值,value
是新值。np.nan
是NumPy
库中表示标准缺失值的对象 (Not a Number)。Pandas 中None
也会被视作缺失值。
7. 数据查看
- 题目: 查看各列数据类型。
- 答案:
1
df_multi_replaced.dtypes
- 解释:
.dtypes
属性可以快速返回 DataFrame 中每一列的数据类型。
8. 数据修改|修改类型
- 题目: 将
金牌数
列类型修改为int
。 - 答案:
1
2
3
4
5
6
7
8# 由于上一步我们引入了np.nan(浮点型),直接转int会报错
# 需要先填充缺失值
df_temp = df_multi_replaced.copy()
df_temp['金牌数'] = df_temp['金牌数'].fillna(0) # 将缺失值填充为0
# 现在可以安全地转换类型了
df_temp['金牌数'] = df_temp['金牌数'].astype(int)
print(df_temp.dtypes) - 解释:
.astype()
是转换数据类型的核心方法。- 注意:标准的
int
类型不支持缺失值np.nan
。在转换前,必须确保该列不含缺失值(可以填充或删除)。如果你想保留缺失值的同时使用整数类型,可以使用 Pandas 提供的 nullable integer 类型:astype('Int64')
(注意大写的’I’)。
9. 数据增加|新增列(固定值)
- 题目: 重新加载数据 并 新增一列
比赛地点
,值为东京
。 - 答案:
1
2
3
4
5
6# 重新加载并重命名
df_reloaded = df.rename(columns={'Unnamed: 2': '金牌数', 'Unnamed: 3': '银牌数', 'Unnamed: 4': '铜牌数'})
df_new_col = df_reloaded.copy()
df_new_col['比赛地点'] = '东京'
df_new_col.head() - 解释:
- 新增一列最简单的方法就是像字典一样直接赋值:
df['新列名'] = 值
。Pandas 会自动将这个值广播到每一行。
- 新增一列最简单的方法就是像字典一样直接赋值:
10. 数据增加|新增列(计算值)
- 题目: 新增一列
金银牌总数
列,值为该国家金银牌总数。 - 答案:
1
2
3df_calc_col = df_new_col.copy()
df_calc_col['金银牌总数'] = df_calc_col['金牌数'] + df_calc_col['银牌数']
df_calc_col.head() - 解释:
- 新增的列可以是现有列之间计算的结果。Pandas 支持列与列之间的向量化运算,非常高效。
11. 数据增加|新增列(比较值)
- 题目: 新增一列
最多奖牌数量
列,值为该过金银牌数量种最多的一个奖牌数量。 - 答案:
1
2
3df_max_col = df_calc_col.copy()
df_max_col['最多奖牌数量'] = df_max_col[['金牌数', '银牌数', '铜牌数']].max(axis=1)
df_max_col.head() - 解释:
.max()
方法可以求最大值。当应用于多列组成的 DataFrame 时,axis=1
参数表示按行操作,即计算每一行中指定列的最大值。
12. 数据增加|新增列(判断值)
- 题目: 新增一列
金牌大于30
,如果金牌数大于 30 则值为是
,反之为否
。 - 答案:
1
2
3df_cond_col = df_max_col.copy()
df_cond_col['金牌大于30'] = np.where(df_cond_col['金牌数'] > 30, '是', '否')
df_cond_col.head() - 解释:
np.where(condition, value_if_true, value_if_false)
是实现条件赋值的绝佳工具。它会根据condition
的布尔结果,从后面两个参数中选择对应的值,效率远高于.apply
。
13. 数据增加|增加多列
- 题目: 新增
金铜牌总数
和银铜牌总数
两列。 - 答案:
1
2
3
4
5
6df_multi_add = df_cond_col.copy()
df_assigned = df_multi_add.assign(
金铜牌总数 = df_multi_add['金牌数'] + df_multi_add['铜牌数'],
银铜牌总数 = df_multi_add['银牌数'] + df_multi_add['铜牌数']
)
df_assigned.head() - 解释:
assign()
方法允许链式调用,一次性创建多个新列,代码更整洁。它会返回一个包含新列的新 DataFrame。
14. 数据增加|新增列(引用变量)
- 题目: 新增一列
金牌占比
,为各国金牌数除以总金牌数。 - 答案:
1
2
3
4
5
6
7# 先计算总金牌数
gold_sum = df_assigned['金牌数'].sum()
# 再计算占比
df_ratio = df_assigned.copy()
df_ratio['金牌占比'] = df_ratio['金牌数'] / gold_sum
df_ratio.head() - 解释:
- 新增列的计算过程可以引用外部变量(如
gold_sum
),这在进行标准化或计算占比等场景中非常常见。
- 新增列的计算过程可以引用外部变量(如
15. 数据增加|新增行(末尾追加)
- 题目: 在 df 末尾追加一行,内容为 0,1,2,3…
- 答案:
1
2
3
4
5
6
7
8# 创建新行的数据
new_row_data = list(range(df_ratio.shape[1]))
# 将新行包装成一个DataFrame
new_row_df = pd.DataFrame([new_row_data], columns=df_ratio.columns)
# 使用pd.concat合并
df_appended = pd.concat([df_ratio, new_row_df], ignore_index=True)
df_appended.tail() - 解释:
- 自 Pandas 2.0 起,官方推荐使用
pd.concat
来合并 DataFrame,以取代已弃用的append
方法。 ignore_index=True
会创建一个新的、从0开始的连续索引。
- 自 Pandas 2.0 起,官方推荐使用
16. 数据增加|新增行(指定位置)
- 题目: 在第 2 行(美国和中国之间)新增一行数据。
- 答案:
1
2
3
4
5
6
7
8
9
10# 定义新行
new_row_df = pd.DataFrame([list(range(df_ratio.shape[1]))], columns=df_ratio.columns)
# 将原DataFrame拆分为上下两部分
df_top = df_ratio.iloc[:2]
df_bottom = df_ratio.iloc[2:]
# 按顺序合并
df_inserted = pd.concat([df_top, new_row_df, df_bottom], ignore_index=True)
df_inserted.head(4) - 解释:
- 在 DataFrame 中间插入行的标准做法是将其“切开”,然后将顶部、新行、底部按顺序用
pd.concat
“粘合”起来。
- 在 DataFrame 中间插入行的标准做法是将其“切开”,然后将顶部、新行、底部按顺序用
17. 数据删除|删除行
- 题目: 删除
df
第一行。 - 答案:
1
2
3# 使用插入后的df_inserted
df_dropped_row = df_inserted.drop(index=0)
df_dropped_row.head() - 解释:
drop()
是通用的删除函数。index=...
参数用于指定要删除的行的索引标签。
18. 数据删除|删除行(条件)
- 题目: 删除金牌数为 0 的所有行。
- 答案:
1
2
3
4
5
6# 先筛选出金牌数为0的行的索引
indices_to_drop = df_dropped_row[df_dropped_row['金牌数'] == 0].index
# 再根据索引删除
df_dropped_cond = df_dropped_row.drop(index=indices_to_drop)
df_dropped_cond.tail() - 解释:
- 条件删除通常分两步:1. 根据你的条件找到需要删除的行的索引。2. 将这些索引用
drop()
方法删除。
- 条件删除通常分两步:1. 根据你的条件找到需要删除的行的索引。2. 将这些索引用
19. 数据删除|删除列
- 题目: 删除刚刚新增的
比赛地点
列。 - 答案:
1
2
3# 使用上一个结果
df_dropped_col = df_dropped_cond.drop(columns=['比赛地点'])
df_dropped_col.head() - 解释:
drop()
方法的columns=...
参数用于指定要删除的列的名称。
20. 数据删除|删除列(按列号)
- 题目: 删除
df
的7、8、9、10
列。 - 答案:
1
2
3cols_to_drop = df_dropped_col.columns[[7, 8, 9, 10]]
df_dropped_by_pos = df_dropped_col.drop(columns=cols_to_drop)
df_dropped_by_pos.head() - 解释:
drop
不直接支持按列号删除。你需要先通过df.columns[...]
按位置获取列名,然后再按列名删除。
5-2 数据筛选
21. 重新加载数据
- 操作: 为了这部分的练习,我们重新加载一份干净的数据。注意: 以下操作均基于这份新加载的
1
2df = pd.read_html("https://olympics.com/tokyo-2020/olympic-games/zh/results/all-sports/medal-standings.htm")[0]
df.rename(columns={'Unnamed: 2':'金牌数','Unnamed: 3':'银牌数','Unnamed: 4':'铜牌数'}, inplace=True)df
。
22. 筛选列|通过列号
- 题目: 提取第 1、2、3、4 列。
- 答案:
1
2df_selected_cols = df.iloc[:, [0, 1, 2, 3]]
df_selected_cols.head() - 解释:
iloc
是基于整数位置进行索引的。:
表示选择所有行,[0, 1, 2, 3]
表示选择指定的列。
23. 筛选列|通过列名
- 题目: 提取
金牌数、银牌数、铜牌数
三列。 - 答案:
1
2df_selected_names = df[['金牌数', '银牌数', '铜牌数']]
df_selected_names.head() - 解释:
- 当你向
df[...]
中传入一个列名列表时,就可以选取多列。使用双中括号[[...]]
会确保返回的是一个 DataFrame,而不是 Series。
- 当你向
24. 筛选列|条件(列号)
- 题目: 筛选全部 奇数列。
- 答案:
1
2df_odd_cols = df.iloc[:, 1::2]
df_odd_cols.head() - 解释:
iloc
支持 Python 的切片语法。1::2
的意思是:从索引 1 开始,到结尾为止,步长为 2。
25. 筛选列|条件(列名)
- 题目: 提取全部列名中包含
数
的列。 - 答案:
1
2df_filtered_names = df.filter(like='数')
df_filtered_names.head() - 解释:
filter()
方法是筛选列名的利器。like='数'
会返回所有列名中包含“数”这个字的列。
26. 筛选列|组合(行号+列名)
- 题目: 提取倒数后三列的10-20行。
- 答案:
1
2df_sliced = df.iloc[10:21, -3:]
df_sliced - 解释:
iloc
非常适合这种行列组合切片。10:21
选取第10到20行(不含21),-3:
选取倒数第3列到最后一列。
27. 筛选行|通过行号
- 题目: 提取第 10 行。
- 答案:
1
2df_row_10 = df.iloc[[9]] # 索引从0开始,所以第10行是iloc[9]
df_row_10 - 解释:
- 同样,使用双中括号
[[9]]
会返回一个单行 DataFrame。如果用df.iloc[9]
则会返回一个 Series。
- 同样,使用双中括号
28. 筛选行|通过行号(多行)
- 题目: 提取第 10 行之后的全部行。
- 答案:
1
2df_after_10 = df.iloc[10:]
df_after_10.head() - 解释:
- 切片
10:
表示从索引为10的行开始,一直到 DataFrame 的末尾。
- 切片
29. 筛选行|固定间隔
- 题目: 提取 0-50 行,间隔为 3。
- 答案:
1
2df_strided = df.iloc[0:51:3]
df_strided.head() - 解释:
- Python 标准的切片
[start:stop:step]
在iloc
中同样适用。
- Python 标准的切片
30. 筛选行|判断(大于)
- 题目: 提取 金牌数 大于 30 的行。
- 答案:
1
2df_gold_30 = df[df['金牌数'] > 30]
df_gold_30 - 解释:
- 这是 Pandas 中最核心的布尔索引。
df['金牌数'] > 30
会生成一个布尔值的 Series,用它来过滤df
,df
会只保留那些对应值为True
的行。
- 这是 Pandas 中最核心的布尔索引。
31. 筛选行 |判断(等于)
- 题目: 提取
金牌数
等于 10 的行。 - 答案:
1
2df_gold_10 = df[df['金牌数'] == 10]
df_gold_10 - 解释:
- 与上一题同理,只是比较运算符从
>
变成了==
。
- 与上一题同理,只是比较运算符从
32. 筛选行|判断(不等于)
- 题目: 提取
金牌数
不等于10
的行。 - 答案:
1
2df_not_gold_10 = df[df['金牌数'] != 10]
df_not_gold_10.head() - 解释:
- 不等于的运算符是
!=
。
- 不等于的运算符是
33. 筛选行|条件(指定行号)
- 题目: 提取全部 奇数行。
- 答案:
1
2df_odd_rows = df.iloc[1::2]
df_odd_rows.head() - 解释:
- 同样是利用
iloc
的切片步长功能。
- 同样是利用
34. 筛选行|条件(指定值)
- 题目: 提取 中国、美国、英国 三行数据。
- 答案:
1
2
3countries = ['中华人民共和国', '美国', '英国']
df_selected_countries = df[df['国家奥委会'].isin(countries)]
df_selected_countries - 解释:
isin()
方法非常适合筛选“等于多个值中任意一个”的情况。它接收一个列表,返回一个布尔 Series。
35. 筛选行|多条件
- 题目: 在上一题的条件下,新增一个条件:金牌数小于30
- 答案:
1
2
3countries = ['中华人民共和国', '美国', '英国']
df_multi_cond = df[(df['国家奥委会'].isin(countries)) & (df['金牌数'] < 30)]
df_multi_cond - 解释:
- 多个条件组合时,使用
&
代表“与”(and),|
代表“或”(or)。 - 务必用括号
()
将每个独立的条件括起来,这是因为 Python 运算符优先级的原因。
- 多个条件组合时,使用
36. 筛选行|条件(包含指定值)
- 题目: 提取
国家奥委会
列中,所有包含国
的行。 - 答案:
1
2df_contains_guo = df[df['国家奥委会'].str.contains('国', na=False)]
df_contains_guo - 解释:
.str
访问器允许我们对整列字符串进行操作。.str.contains('国')
会检查每个字符串是否包含“国”字。na=False
表示如果遇到缺失值,则直接返回False
,避免出错。
37. 筛选某行某列
- 题目: 提取
第 0 行第 2 列
。 - 答案:
1
2
3# .iloc[行位置, 列位置]
value_0_2 = df.iloc[0, 2]
value_0_2 - 解释:
iloc
中用逗号分隔行和列的位置,可以直接定位到单个值。
38. 筛选多行多列
- 题目: 提取 第 0-2 行第 0-2 列。
- 答案:
1
2df_block = df.iloc[0:3, 0:3]
df_block - 解释:
- 行和列都使用切片语法,可以方便地提取出一个矩形区块。
39. 筛选值|组合(行号+列号)
- 题目: 提取第 4 行,第 4 列的值。
- 答案:
1
2value_4_4 = df.iloc[4, 4] # 第5行是索引4,第5列是索引4
value_4_4 - 解释:
- 与37题同理,使用
iloc
进行精确定位。
- 与37题同理,使用
40. 筛选值|组合(行号+列名)
- 题目: 提取行索引为 4 ,列名为 金牌数 的值。
- 答案:
1
2value_loc = df.loc[4, '金牌数']
value_loc - 解释:
loc
用于基于标签的索引。这里行标签是4
,列标签是'金牌数'
。
41. 筛选值|条件
- 题目: 提取 国家奥委会 为
中华人民共和国
的金牌数。 - 答案:
1
2
3
4# 先筛选行,再选择列
china_gold = df.loc[df['国家奥委会'] == '中华人民共和国', '金牌数']
# 因为结果只有一个值,它会是一个Series,可以用.item()或.values[0]提取纯数值
china_gold.item() - 解释:
- 这是一种非常常见的组合用法:
df.loc[行过滤条件, '目标列名']
。
- 这是一种非常常见的组合用法:
42. 筛选值|query
- 题目: 使用
query
提取 金牌数 + 银牌数 大于 70 的国家。 - 答案:
1
2df_query_1 = df.query('金牌数 + 银牌数 > 70')
df_query_1 - 解释:
query()
方法允许你使用字符串表达式来筛选数据,当条件复杂时,代码可读性比布尔索引更好。可以直接在字符串中书写列名和计算。
43. 筛选值|query(引用变量)
- 题目: 使用
query
提取 金牌数 大于 金牌均值的国家。 - 答案:
1
2
3
4
5
6# 先计算均值
gold_mean = df['金牌数'].mean()
# 在query字符串中使用@符号引用外部变量
df_query_2 = df.query('金牌数 > @gold_mean')
df_query_2 - 解释:
- 在
query
表达式字符串中,你可以在变量名前加上@
符号,来引用在代码环境中已定义的外部变量。
- 在
5.数据筛选与修改
https://blog.wyyy.dpdns.org/2025/5-数据筛选与修改/