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

抽一列用单方括号拿 Series,抽多列用双方括号拿 DataFrame,差一个括号差很多。 条件筛选的本质是造一列 True/False,再拿它当行号用。 多条件必须用 & 和 |,每个条件各自加括号,不能用 and 和 or。

📝 本章和前面几章的关系

上一篇按行抽,这一篇按列抽,再按条件抽——条件筛选是实际用得最多的一种挑法。 十行数据里挑「数学高于 88」的四个人,手工数得过来,一万行就只能靠它。

一、抽一列:一个方括号

df['语文'] 拿到一个 Series,长度和表一样,标签是行标签。 它没有 columns 属性,只有 name——名字就是「语文」。 这个区别很要紧:后面能调什么方法,取决于拿到的是 Series 还是 DataFrame。

df['语文'].columns 会报 AttributeError,正确写法是 df[ ['语文'] ].columns。

二、抽多列:两个方括号

df[ ['姓名', '语文'] ] 拿到的是 DataFrame,列数 2。

姓名 语文
张伟 88
王芳 92
李娜 75

两个方括号里放的其实是「列名列表」,所以写法是列表套列表。 少写一层括号,拿到的就是单个 Series,后面的 .columns 直接报错。 记法:要几列就用列表,一列也可以写成列表,行为统一成 DataFrame。

三、条件筛选:先造一列布尔值

df['数学'] > 88 得到一串 True/False,和表一样长。 把它塞回方括号:df[df['数学'] > 88],只留下 True 那些行。

姓名 数学
张伟 92
王芳 95
赵敏 91
吴霞 90

数学高于 88 的有 4 人。李娜 68、刘洋 88 都没进来——88 不算高于 88。 本质:先用条件造一列布尔值,再拿它当行号。顺序不能反,先想条件再筛行。

四、多条件:& 和 | 的用法

两科都高于 85 的写法:df[(df['语文'] > 85) & (df['数学'] > 85)]。

姓名 语文 数学
张伟 88 92
王芳 92 95
赵敏 95 91

符合的有 3 人。三个符号要记牢:& 是并且,| 是或者,~ 是取反。 每个条件都要各自加一层括号。不写括号会先算 & 再算 >,结果全错。 写成 df.数学 > 85 and df.语文 > 85 直接报错:and 只能比两个布尔值,比不了两列。

五、一步取行列:loc 两个参数

df.loc[条件, 列名列表] 一次干两件事:先筛行,再挑列。

姓名 数学
张伟 92
王芳 95

等价写法是 df[df['数学'] > 88][ ['姓名', '数学'] ],但读起来要绕。 loc 的第二个参数可以是单个列名、列名列表,也可以是切片 '语文':'英语'。 筛完再挑列,用一行 loc 比串两个方括号清楚,也少一次中间对象。

六、动手 Demo:抽取列与按条件筛选

同一张成绩表,先演示单列和多列的返回类型差别,再做单条件与双条件筛选。 最后用 loc 一步到位,和串两个方括号的写法对一遍结果。

import pandas as pd

DF = pd.DataFrame({
    "姓名": ["张伟", "王芳", "李娜", "刘洋", "陈静"],
    "语文": [88, 92, 75, 85, 90],
    "数学": [92, 95, 68, 88, 91],
})

print("单列 %s" % type(DF["语文"]).__name__)
print("多列 %s" % type(DF[ ["姓名", "语文"]]).__name__)

hot = DF[DF["数学"] > 88]
print("数学>88:%d 人" % len(hot))

both = DF[(DF["语文"] > 85) & (DF["数学"] > 85)]
print("两科>85:%d 人" % len(both))
print(both.loc[:, ["姓名", "语文", "数学"]].to_string(index=False))

实跑输出:

--------------------------------------------------------------------
抽取列与按条件筛选
--------------------------------------------------------------------

  ① 抽一列和抽多列
  --------------------------------------------------------------------
  抽一列用单方括号:df['语文'],拿到 Series。
  抽多列用双方括号:df[ ['姓名','语文']],拿到 DataFrame。

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

  一个方括号给 Series,两个给 DataFrame。这个区别决定了后面能调什么方法。
  ! Series 没有 columns 属性,DataFrame 有。写 df[ ['语文']].columns 才不会报错。

  ② 按条件筛:造一个布尔列
  --------------------------------------------------------------------
  df['数学'] > 88 得到一串 True/False:[True, True, False, False, False] ……
  把它塞回 df 的方括号里,就只留下 True 那些行。

| 行 | 姓名 | 数学 |
|---|---:|---:|
| 0 | 张伟 | 92 |
| 1 | 王芳 | 95 |
| 6 | 赵敏 | 91 |
| 9 | 吴霞 | 90 |

  数学高于 88 的有 4 人。
  ! 布尔索引的本质:先用条件造一列 True/False,再拿它当行号。顺序不能反。

  ③ 多个条件:要用 & 和 |
  --------------------------------------------------------------------
  两科都高于 85 的有 3 人。

| 行 | 姓名 | 语文 | 数学 |
|---|---:|---:|---:|
| 0 | 张伟 | 88 | 92 |
| 1 | 王芳 | 92 | 95 |
| 6 | 赵敏 | 95 | 91 |

  必须用 & 不能用 and,每个条件要各自加括号。写成 df.数学 > 85 and df.语文 > 85 会报错。
  ! 原因:and 只能比两个布尔值,不能比两个 Series。& 是按元素运算。

  ④ 筛完再挑列
  --------------------------------------------------------------------
  loc 可以一次干两件事:df.loc[条件, 列名列表]。

| 行 | 姓名 | 数学 |
|---|---:|---:|
| 0 | 张伟 | 92 |
| 1 | 王芳 | 95 |
| 6 | 赵敏 | 91 |
| 9 | 吴霞 | 90 |

  这一句等价于 df[df['数学']>88][ ['姓名','数学']],但写法更清楚。
  ! loc 的第二个参数是列,可以是单个名字、列表,也可以是切片。

type(DF['语文']).__name__ 打出来是 Series,多列那个是 DataFrame——差一层方括号就差一个类型。 括号要包住整个条件,不是包住数字。df['数学'] > 88 & 2 是错的,得写 (df['数学'] > 88)。

30 秒实验

  1. 把条件改成 >= 88,看李娜、刘洋会不会进来。
  2. 用 | 筛「语文高于 90 或数学高于 90」,数一数几个人。
  3. 想一想:~ 取反怎么用?把上面那个条件取反,结果是不是正好互补?

常见误区

  1. 用 and 连两个条件:报 ValueError,因为 and 比不了两列。
  2. 条件不加括号:df['数学'] > 88 & 2 被解析成先算 88 & 2,结果莫名其妙。
  3. df['语文'] 后面调 columns:Series 没有这个属性。
  4. 以为 88 会被算进「高于 88」:> 和 >= 差一个人,报告里就差一句话。
自测三问(点开对答案) 1. `df['语文']` 和 `df[ ['语文'] ]` 有什么区别? —— 前者是 Series(一维,有 name 没 columns),后者是 DataFrame(二维,有 columns)。 2. 为什么两个条件必须用 & 而不是 and? —— and 只能对两个布尔值做判断,这里两边都是整列布尔值,要用按元素运算的 &。 3. 条件筛选的本质是什么? —— 先用条件造一列 True/False,再拿这一列当行号去挑行。

行和列都会挑了。接下来反过来:往表里加列、加行,把算出来的结果存进去。

相关笔记