5.数据筛选与修改

本文最后更新于 2025年7月27日 晚上

初始化与加载数据

首先,我们导入 pandas 并加载东京奥运会的奖牌数据。

1
2
3
4
5
6
7
8
9
import pandas as pd
import numpy as np # 导入numpy,后面会用到

# 从网页直接读取数据,Pandas会自动抓取页面上的表格
# read_html返回一个列表,我们取第一个表格[0]
df = pd.read_html("https://olympics.com/tokyo-2020/olympic-games/zh/results/all-sports/medal-standings.htm")[0]

# 如果网络不稳定,也可以加载本地文件
# df = pd.read_csv("东京奥运会奖牌数据.csv")

5-1 数据修改

1. 数据修改|列名

  • 题目: 将原 df 列名 Unnamed: 2Unnamed: 3Unnamed: 4 修改为 金牌数银牌数铜牌数
  • 答案:
    1
    2
    3
    4
    5
    df_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
    4
    df_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.nanNumPy 库中表示标准缺失值的对象 (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
    3
    df_calc_col = df_new_col.copy()
    df_calc_col['金银牌总数'] = df_calc_col['金牌数'] + df_calc_col['银牌数']
    df_calc_col.head()
  • 解释:
    • 新增的列可以是现有列之间计算的结果。Pandas 支持列与列之间的向量化运算,非常高效。

11. 数据增加|新增列(比较值)

  • 题目: 新增一列 最多奖牌数量 列,值为该过金银牌数量种最多的一个奖牌数量。
  • 答案:
    1
    2
    3
    df_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
    3
    df_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
    6
    df_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开始的连续索引。

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 “粘合”起来。

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() 方法删除。

19. 数据删除|删除列

  • 题目: 删除刚刚新增的 比赛地点 列。
  • 答案:
    1
    2
    3
    # 使用上一个结果
    df_dropped_col = df_dropped_cond.drop(columns=['比赛地点'])
    df_dropped_col.head()
  • 解释:
    • drop() 方法的 columns=... 参数用于指定要删除的列的名称。

20. 数据删除|删除列(按列号)

  • 题目: 删除 df7、8、9、10 列。
  • 答案:
    1
    2
    3
    cols_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
    2
    df = 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
    2
    df_selected_cols = df.iloc[:, [0, 1, 2, 3]]
    df_selected_cols.head()
  • 解释:
    • iloc 是基于整数位置进行索引的。: 表示选择所有行,[0, 1, 2, 3] 表示选择指定的列。

23. 筛选列|通过列名

  • 题目: 提取 金牌数、银牌数、铜牌数 三列。
  • 答案:
    1
    2
    df_selected_names = df[['金牌数', '银牌数', '铜牌数']]
    df_selected_names.head()
  • 解释:
    • 当你向 df[...] 中传入一个列名列表时,就可以选取多列。使用双中括号 [[...]] 会确保返回的是一个 DataFrame,而不是 Series。

24. 筛选列|条件(列号)

  • 题目: 筛选全部 奇数列
  • 答案:
    1
    2
    df_odd_cols = df.iloc[:, 1::2]
    df_odd_cols.head()
  • 解释:
    • iloc 支持 Python 的切片语法。1::2 的意思是:从索引 1 开始,到结尾为止,步长为 2。

25. 筛选列|条件(列名)

  • 题目: 提取全部列名中包含 的列。
  • 答案:
    1
    2
    df_filtered_names = df.filter(like='数')
    df_filtered_names.head()
  • 解释:
    • filter() 方法是筛选列名的利器。like='数' 会返回所有列名中包含“数”这个字的列。

26. 筛选列|组合(行号+列名)

  • 题目: 提取倒数后三列的10-20行。
  • 答案:
    1
    2
    df_sliced = df.iloc[10:21, -3:]
    df_sliced
  • 解释:
    • iloc 非常适合这种行列组合切片。10:21 选取第10到20行(不含21),-3: 选取倒数第3列到最后一列。

27. 筛选行|通过行号

  • 题目: 提取第 10 行。
  • 答案:
    1
    2
    df_row_10 = df.iloc[[9]] # 索引从0开始,所以第10行是iloc[9]
    df_row_10
  • 解释:
    • 同样,使用双中括号 [[9]] 会返回一个单行 DataFrame。如果用 df.iloc[9] 则会返回一个 Series。

28. 筛选行|通过行号(多行)

  • 题目: 提取第 10 行之后的全部行。
  • 答案:
    1
    2
    df_after_10 = df.iloc[10:]
    df_after_10.head()
  • 解释:
    • 切片 10: 表示从索引为10的行开始,一直到 DataFrame 的末尾。

29. 筛选行|固定间隔

  • 题目: 提取 0-50 行,间隔为 3。
  • 答案:
    1
    2
    df_strided = df.iloc[0:51:3]
    df_strided.head()
  • 解释:
    • Python 标准的切片 [start:stop:step]iloc 中同样适用。

30. 筛选行|判断(大于)

  • 题目: 提取 金牌数 大于 30 的行。
  • 答案:
    1
    2
    df_gold_30 = df[df['金牌数'] > 30]
    df_gold_30
  • 解释:
    • 这是 Pandas 中最核心的布尔索引df['金牌数'] > 30 会生成一个布尔值的 Series,用它来过滤 dfdf 会只保留那些对应值为 True 的行。

31. 筛选行 |判断(等于)

  • 题目: 提取 金牌数 等于 10 的行。
  • 答案:
    1
    2
    df_gold_10 = df[df['金牌数'] == 10]
    df_gold_10
  • 解释:
    • 与上一题同理,只是比较运算符从 > 变成了 ==

32. 筛选行|判断(不等于)

  • 题目: 提取 金牌数 不等于 10 的行。
  • 答案:
    1
    2
    df_not_gold_10 = df[df['金牌数'] != 10]
    df_not_gold_10.head()
  • 解释:
    • 不等于的运算符是 !=

33. 筛选行|条件(指定行号)

  • 题目: 提取全部 奇数行
  • 答案:
    1
    2
    df_odd_rows = df.iloc[1::2]
    df_odd_rows.head()
  • 解释:
    • 同样是利用 iloc 的切片步长功能。

34. 筛选行|条件(指定值)

  • 题目: 提取 中国、美国、英国 三行数据。
  • 答案:
    1
    2
    3
    countries = ['中华人民共和国', '美国', '英国']
    df_selected_countries = df[df['国家奥委会'].isin(countries)]
    df_selected_countries
  • 解释:
    • isin() 方法非常适合筛选“等于多个值中任意一个”的情况。它接收一个列表,返回一个布尔 Series。

35. 筛选行|多条件

  • 题目: 在上一题的条件下,新增一个条件:金牌数小于30
  • 答案:
    1
    2
    3
    countries = ['中华人民共和国', '美国', '英国']
    df_multi_cond = df[(df['国家奥委会'].isin(countries)) & (df['金牌数'] < 30)]
    df_multi_cond
  • 解释:
    • 多个条件组合时,使用 & 代表“与”(and),| 代表“或”(or)。
    • 务必用括号 () 将每个独立的条件括起来,这是因为 Python 运算符优先级的原因。

36. 筛选行|条件(包含指定值)

  • 题目: 提取 国家奥委会 列中,所有包含 的行。
  • 答案:
    1
    2
    df_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
    2
    df_block = df.iloc[0:3, 0:3]
    df_block
  • 解释:
    • 行和列都使用切片语法,可以方便地提取出一个矩形区块。

39. 筛选值|组合(行号+列号)

  • 题目: 提取第 4 行,第 4 列的值。
  • 答案:
    1
    2
    value_4_4 = df.iloc[4, 4] # 第5行是索引4,第5列是索引4
    value_4_4
  • 解释:
    • 与37题同理,使用 iloc 进行精确定位。

40. 筛选值|组合(行号+列名)

  • 题目: 提取行索引为 4 ,列名为 金牌数 的值。
  • 答案:
    1
    2
    value_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
    2
    df_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-数据筛选与修改/
作者
lwy
发布于
2025年7月27日
许可协议