📊 第3章 Pandas 入门
📄 一句话概括

改列名用 rename 传字典,旧名对新名,不改的列不用写。 改某一格的值用 loc 定位再赋值,不要用链式赋值。 删列删行都用 drop,用 columns= 和 index= 指定,别再用 axis。

📝 本章和前面几章的关系

上一篇往表里加东西,这一篇反过来:改掉、删掉。三种动作合起来,一张表就管住了。 改和删最容易出的错不是写错函数,是忘了接返回值。

一、改列名:rename

df.rename(columns={'语文': '语文成绩', '数学': '数学成绩'}),改完只动这两列。 字典的键是旧名,值是旧名。不改的列不用写,原样保留。 改完的列名:姓名、班级、语文成绩、数学成绩、英语、总分。

rename 默认返回新表。不接住返回值,等于什么都没改。 非要原地改就加 inplace=True,但新版更推荐写 df = df.rename(...)。

二、改值:loc 定位再赋值

黄磊原来英语 60 分,录错了,要改成 75。 df.loc[df['姓名'] == '黄磊', '英语'] = 75,一行搞定。

姓名 英语
黄磊 75

句式拆开看:loc[行条件, 列名] = 新值,行和列各管各的,互不影响。 改单格一定要用 loc。用别的写法可能改的是副本,原表一点没动。

三、链式赋值:pandas 3 最想让你戒掉的习惯

df[df['姓名'] == '黄磊']['英语'] = 75 这种写法叫链式赋值。 它中间取了一次子表,赋的是子表的值。原表没变,也不报错。 pandas 3 起不保证生效,可能改得进去,也可能改不进去——最坏的一种错。

同样的道理,df['英语'][7] = 75 也是链式赋值,也要改成 df.loc[7, '英语'] = 75。

四、删列删行:drop

删列:df.drop(columns=['总分']),列数从 6 变 5。 删行:df.drop(index=[0, 1]),行数从 10 变 8。

姓名 语文
李娜 75
刘洋 85
陈静 90

删多列传列表,删多行也传列表。都不改原表,除非加 inplace=True。 注意删行是按行标签删,不是按位置删。标签被改过之后,别按「第几行」去想。 老写法是 axis=1 删列、axis=0 删行。新版推荐直接写 columns= 和 index=。

五、删完之后行标签断了一段

删掉标签 0 和 1 之后,剩下的标签从 2 起,前面空出两个位置。 删中间的更明显:删掉 3 和 7,剩下 0、1、2、4、5、6、8、9,中间断两截。 这时候 df.iloc[5] 拿到的是第 6 个还在的行,不是标签 5 那行。

位置和标签从这一刻起就对不上了。要连续就 reset_index(drop=True)。 drop=True 的意思是旧标签直接丢掉,不变成一列叫 index 的列。

六、动手 Demo:修改与删除

先改两个列名,再改一个录错的分数,然后删一列、删两行。 最后演示链式赋值为什么改不动原表。

import pandas as pd

DF = pd.DataFrame({
    "姓名": ["张伟", "王芳", "李娜", "刘洋", "陈静",
             "杨帆", "赵敏", "黄磊", "周涛", "吴霞"],
    "班级": ["一班"] * 5 + ["二班"] * 5,
    "语文": [88, 92, 75, 85, 90, 68, 95, 72, 83, 79],
    "数学": [92, 95, 68, 88, 73, 82, 91, 65, 86, 90],
    "英语": [85, 90, 80, 79, 95, 71, 88, 60, 84, 87],
})
DF["总分"] = DF["语文"] + DF["数学"] + DF["英语"]

DF = DF.rename(columns={"语文": "语文成绩", "数学": "数学成绩"})
print("列名:%s" % list(DF.columns))

DF.loc[DF["姓名"] == "黄磊", "英语"] = 75

print("删列后列数 %d" % len(DF.drop(columns=["总分"]).columns))
print("删行后行数 %d" % len(DF.drop(index=[0, 1])))

CP = DF.copy()
CP[CP["姓名"] == "张伟"]["语文成绩"] = 0
print("链式赋值之后张伟的分数:%d" % CP.loc[0, "语文成绩"])

实跑输出:

--------------------------------------------------------------------
修改与删除
--------------------------------------------------------------------

  ① 改列名:rename
  --------------------------------------------------------------------
  改完的列名:姓名、班级、语文成绩、数学成绩、英语、总分。
  rename 要传一个字典:旧名对旧名。不改的列不用写。
  ! rename 默认返回新表,不接住返回值等于没改。要原地改加 inplace=True。

  ② 改值:loc 定位再赋值
  --------------------------------------------------------------------
  黄磊原来英语 60 分,改成 75。

| 行 | 姓名 | 英语 |
|---|---:|---:|
| 7 | 黄磊 | 75 |

  句式是 df.loc[行条件, 列名] = 新值。行和列各管各的,互不影响。
  ! 改值一定要用 loc,不要用链式赋值(df[条件]['英语'] = 75)。
  链式赋值在 pandas 3 里不保证生效,因为它改的是副本。

  ③ 删列删行:drop
  --------------------------------------------------------------------
  删列:姓名、班级、语文、数学、英语、总分 → 姓名、班级、语文、数学、英语。
  删行:10 行 → 8 行,按行标签删,不是按位置。

| 行 | 姓名 | 语文 |
|---|---:|---:|
| 2 | 李娜 | 75 |
| 3 | 刘洋 | 85 |
| 4 | 陈静 | 90 |

  删多列传列表,删多行也传列表。都不改原表,除非加 inplace=True。
  ! drop 的 axis 参数老写法是 axis=1 删列、axis=0 删行;现在推荐直接用 columns= 和 index=。

  ④ 改和删的注意事项
  --------------------------------------------------------------------

| 动作 | 写法 | 最容易错的地方 |
|---|---|---|
| 改列名 | rename(columns={}) | 忘记接返回值 |
| 改值 | loc[条件, 列] = 值 | 用了链式赋值 |
| 删列 | drop(columns=[...]) | 漏了 columns= |
| 删行 | drop(index=[...]) | 把行号当成行标签 |

  最后一条要留意:删完行之后,剩下的行标签是不连续的(比如 0、2、3…)。
  ! 要连续就 reset_index(drop=True),这也是下一篇「索引设置」要讲的第一件事。

最后那两行是本篇的重点:链式赋值跑完不报错,但张伟的分数还是 88。 改单格只有 loc 一种可靠写法。其余写法在 pandas 3 里都算「不一定生效」。

30 秒实验

  1. 把链式赋值那行改成 CP.loc[0, '语文成绩'] = 0,看结果变不变。
  2. 删行时把 index=[0, 1] 改成 index=[0, 1, 2, 3],看表会不会空。
  3. 想一想:删完行不 reset_index,接着写 df.iloc[-1],取到的是哪行?

常见误区

  1. rename 不接返回值:以为改好了,打出来还是旧列名。
  2. 用链式赋值改单格:不报错、不生效,最难查。
  3. drop 忘了写 columns= 或 index=:报错说找不到标签。
  4. 删完行直接按位置取:标签断了,位置和标签对不上。
自测三问(点开对答案) 1. rename 和 drop 有没有改原表? —— 默认都不改,返回新表。要改原表得接住返回值,或者加 inplace=True。 2. 为什么链式赋值危险? —— 它改的是中间那张临时子表,原表可能一点没动,但也不报错,看起来像成功了。 3. 删完行之后行标签为什么会断? —— drop 只删行不动标签,被删掉那几行的标签位置空出来,剩下的标签就不连续了。

改和删都只是手艺。真正让数据能用的,是下一篇:缺失、重复、异常怎么处理。

相关笔记