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

加一列直接赋值:df['新列名'] = 一个能算出整列的式子,这是最常用的一种加。 要插到指定位置用 insert,位置从 0 数起。 加行用 concat,记得 ignore_index=True。df.append 在 pandas 2.0 就删了。

📝 本章和前面几章的关系

前面三篇在「取」,这一篇开始「改」。取是看,改才是把结果留下来。 算总分、算平均分、补一列序号,都是这个动作。加列便宜,加行麻烦。

一、加一列:直接赋值

df['平均分'] = df['总分'] / 3 一行就多一列,值自动按行算。

姓名 总分 平均分
张伟 265 88.33
王芳 277 92.33
李娜 223 74.33

等号左边给新列名,右边给一个 Series 或能算出整列的表达式。 右边可以直接用已有列做四则运算——这是 Pandas 最省事的地方。 新列默认加在最后。要插在中间得用 insert,下面一段讲。

二、算出来的列,值会是小数

265 / 3 在 Python 里是 88.33333333333333,不是 88.33。 要两位小数就 round(2),或者干脆先算总分再除以人数的时候用整数除法。 注意除法出来的是 float,这一列原来的 int64 变成了 float64。

类型变了要心里有数:后面按整数分组、按整数取模,都会因为这个变慢或者报错。

三、插在指定位置:insert

df.insert(1, '学号', ['S01', 'S02', 'S03']) 把学号插到第 2 列。

姓名 学号 语文
张伟 S01 88
王芳 S02 92
李娜 S03 75

第一个参数是列号,从 0 数起:0 是第一列,1 就是第二列。 insert 是原地改,不返回新表。这一点和 rename、drop 都不一样。 新版 Pandas 越来越不推荐 inplace 参数,能用「接住返回值」就别用 inplace。

四、加一行:concat

pd.concat([df, 新行], ignore_index=True) 拼完 11 行 8 列,原来 10 行 8 列。 8 列是因为前面已经加过学号和平均分。只拿原始 6 列的表去拼,拼完就是 11 行 6 列。 新行要写成 DataFrame,最省事的写法是把原来某行挑出来改几个值。

ignore_index=True 让行标签重排成 0 到 10;不写的话新行会带着旧标签进来。 老教程里的 df.append(row) 在 pandas 2.0 就被删了。现在只有 concat。

五、别在循环里一行一行加

想在循环里逐行追加,很容易写成每轮一次 concat。这在几十行上看不出问题。 到几万行就是灾难:每 concat 一次都要复制一整张表。 正确做法:把每行先攒成列表,循环结束拼一次。

import pandas as pd

rows = []
for i in range(3):
    rows.append({"编号": i, "值": i * i})
df = pd.DataFrame(rows)      # 只建一次

加行比加列麻烦,这是 Pandas 的设计取向:它按列存,加列天然便宜。

六、动手 Demo:增加数据:加列与加行

先加一列平均分,再用 insert 把学号插到第二列,最后用 concat 加一行。 每一步都打 shape,看表是变宽了还是变长了。

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["总分"] / 3.0
DF.insert(1, "学号", ["S%02d" % (i + 1) for i in range(len(DF))])
print("列顺序:%s" % list(DF.columns))

ROW = pd.DataFrame([{"姓名": "孙悦", "学号": "S11", "语文": 86,
                     "数学": 79, "英语": 82, "总分": 247, "平均分": 82.33}])
BIG = pd.concat([DF, ROW], ignore_index=True)
print("加行前 %s,加行后 %s" % (DF.shape, BIG.shape))

实跑输出:

--------------------------------------------------------------------
增加数据:加列与加行
--------------------------------------------------------------------

  ① 加一列:直接赋值
  --------------------------------------------------------------------

| 行 | 姓名 | 总分 | 平均分 |
|---|---:|---:|---:|
| 0 | 张伟 | 265 | 88.33333333333333 |
| 1 | 王芳 | 277 | 92.33333333333333 |
| 2 | 李娜 | 223 | 74.33333333333333 |

  等号左边给一个新列名,右边给一个能算出整列的东西。
  右边可以直接用已有的列做四则运算——这是 Pandas 最省事的地方。
  ! 新列加在最后。要插在指定位置得用 insert,下一个 Demo 有。

  ② 加列放在指定位置
  --------------------------------------------------------------------

| 行 | 姓名 | 学号 | 语文 |
|---|---:|---:|---:|
| 0 | 张伟 | S01 | 88 |
| 1 | 王芳 | S02 | 92 |
| 2 | 李娜 | S03 | 75 |

  insert(位置, 列名, 值):位置是列号,从 0 数起,1 就是第二列。
  ! insert 不改原表,要接住返回值或用 inplace——但 inplace 在新版里越来越不推荐。

  ③ 加一行:concat
  --------------------------------------------------------------------
  concat 之后的形状:(11, 6)(原来是 (10, 6))。

| 行 | 姓名 | 总分 |
|---|---:|---:|
| 8 | 周涛 | 253 |
| 9 | 吴霞 | 256 |
| 10 | 孙悦 | 247 |

  ignore_index=True 让行标签重排成 0 到 10,否则新行会沿用原来的标签。
  ! 老教程里写 df.append(row),这个方法在 pandas 2.0 就被删了。现在只有 concat。

  ④ 三种加法怎么选
  --------------------------------------------------------------------

| 要加什么 | 用什么 | 坑 |
|---|---|---|
| 新的一列(算出来的) | df[新列名] = 式子 | 无 |
| 新的一列(插中间) | df.insert(位置, 名, 值) | 位置从 0 数 |
| 新的一行 | pd.concat | 记得 ignore_index |

  加行比加列麻烦,这是 Pandas 的设计取向:它是按列存的,加列天然便宜。
  ! 如果发现自己在循环里一行一行加,先停下来——把行收集成列表,最后 concat 一次,快得多。

insert 不返回新表,所以直接调用来改 DF 本身;concat 返回新表,必须接住。 一个字判断要不要接返回值:改了 DF 自己就是原地,没改就得接。查一下类型最保险。

30 秒实验

  1. 把平均分那一列 round(2),看小数位变成什么。
  2. 把 concat 的 ignore_index 去掉,打一下 index,看新行的标签是几。
  3. 想一想:给 10 万行的表用循环 concat 逐行加,为什么慢?

常见误区

  1. 还在用 df.append:pandas 2.0 已删除,报 AttributeError。
  2. concat 忘了 ignore_index:新行沿用旧标签,和已有行标签撞车。
  3. 以为 insert 返回新表:它原地改,接返回值会拿到 None。
  4. 循环里逐行 concat:几十行看不出,几万行慢到不可用。
自测三问(点开对答案) 1. 加一列和加一行,哪个便宜?为什么? —— 加列便宜。Pandas 按列存数据,加一列只是多挂一列数组;加行要把每列都延长。 2. insert 和 rename 在「要不要接返回值」上有什么不同? —— insert 原地改,不用接;rename 默认返回新表,不接等于没改。 3. concat 的 ignore_index=True 在做什么? —— 丢掉原来的行标签,重新按 0、1、2 编号,避免新旧标签重复。

会加了,还得会改和删——列名写错了、某一格录错了、整行不要了,都是日常。

相关笔记