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

从 DataFrame 里取一列,拿到的是 Series:一组值加一组标签,还有自己的名字。 它的 values 是 ndarray,index 是 RangeIndex,name 记着它来自哪一列。 手工造 Series 只要三个参数:值、标签、名字。标签可以不是数字。

📝 本章和前面几章的关系

上一篇认识了 DataFrame 这张表,这一篇认识它的一个零件。 Series 是 Pandas 的另一个核心对象。 看懂它,DataFrame 就没有神秘的地方了——它就是一列一列的 Series 拼起来的。

一、取一列,拿到什么

df['数学'] 拿到的 type 是 Series,长度 10,和原表行数一样。 前三个值是 92、95、68。index 是 0 到 9 的 RangeIndex。 取值用 s.iloc[0],不要用 s[0]。pandas 3 里 s[0] 是按标签找,标签里没有 0 就报 KeyError。

这是从 pandas 2 到 3 最容易踩的一个变化:老代码里到处是 s[0],升级后全炸。

二、两个核心属性

属性 放什么 本列的类型
values 纯数据 ndarray
index 标签 RangeIndex(0 到 9)
name 名字 数学

name 这个属性不起眼,但很有用:打印出来时它显示在底部,一看就知道是哪一列。 index 一旦从数字换成姓名,取值的写法就从 s.iloc[0] 变成 s['张伟']。下一篇专讲。

三、手工造一个

pd.Series([88, 92, 75], index=['张伟','王芳','李娜'], name='数学')。 三个参数:值、标签、名字。名字可选,但有了它打印好看得多。

姓名 数学
张伟 88
王芳 92
李娜 75

标签不写的话自动给 0、1、2。值和标签的长度必须一样,否则报错。

四、为什么要有标签

因为位置会变。排一次序、删掉一行,原来的「第 3 个」就不是同一个人了。 标签不会变。s['张伟'] 永远是张伟那个人的数学分,不管他排第几。 这是 Pandas 和普通列表最根本的区别:列表认位置,Series 认标签。

代价是内存多一点、速度慢一点。换来的是代码不容易错,这笔交易划算。

五、DataFrame 与 Series 的关系

df['数学'] 取一列 → Series。df.loc[0] 取一行 → 也是 Series,但标签变成了列名。 所以「一行」和「一列」在 Pandas 里是同一种东西,只是标签轴不同。 反过来说,DataFrame 可以看成「一列一列的 Series,共用一套行标签」。

记住这句话,后面 groupby 的返回结构就很好懂——它返回的常常是 Series。

六、动手 Demo:Series:带标签的一列数

从成绩表里取数学一列,打它的类型、长度、前几个值、索引。 再手工造一个带姓名标签的 Series,对比两者。

import pandas as pd

DF = pd.DataFrame({
    "姓名": ["张伟", "王芳", "李娜"],
    "数学": [92, 95, 68],
})

s = DF["数学"]
print("type %s,长度 %d" % (type(s).__name__, len(s)))
print("前三个值 %s" % s.head(3).tolist())
print("index 是 %s" % list(s.index))
print("name 是 %s" % s.name)
print("values 类型 %s" % type(s.values).__name__)

s2 = pd.Series([88, 92, 75], index=["张伟", "王芳", "李娜"], name="数学")
print(s2.to_string())
print("按标签取:%d" % s2["王芳"])

实跑输出:

--------------------------------------------------------------------
Series:带标签的一列数
--------------------------------------------------------------------

  ① 从表里取一列,拿到的是什么
  --------------------------------------------------------------------
  type 是 Series。
  它的长度 10,和原表行数一样。
  前三个值:92、95、68。
  它的 index 是 [0, 1, 2, 3, 4]。
  ! 取值用 s.iloc[0],不是 s[0]——pandas 3 里 s[0] 按标签找,找不到 0 就报错。

  ② 一列数 = 一组值 + 一组标签
  --------------------------------------------------------------------
  Series 的两个核心属性:values 放值,index 放标签。
  values 的类型是 ndarray。
  index 的类型是 RangeIndex,这里是 RangeIndex,从 0 到 9。
  name 属性是 数学——它记得自己是从哪一列来的。
  ! 标签可以不是数字。下一节把姓名设成标签,取值就变成 s['张伟']。

  ③ 手工造一个 Series
  --------------------------------------------------------------------

| 行 | 数学 |
|---|---:|
| 张伟 | 88 |
| 王芳 | 92 |
| 李娜 | 75 |

  三个参数:值、标签、名字。名字可选,但有了它打印出来好看得多。
  ! DataFrame 的每一列抽出来就是一个 Series,每一行抽出来也是(类型叫 Series,标签是列名)。

  ④ 为什么要有标签
  --------------------------------------------------------------------
  因为位置会变。排序一次、删掉一行,原来的「第 3 个」就不是同一个人了。
  标签不会变。s['张伟'] 永远是张伟那个人的数学分,不管他排第几。
  ! 这是 Pandas 和普通列表最根本的区别:列表认位置,Series 认标签。

s.values 是 ndarray,不是 list。要 list 得再 .tolist() 一次。 s2['王芳'] 能直接按标签取,因为标签就是姓名。如果标签是 0、1、2,那就得用 iloc 才稳。

30 秒实验

  1. 把 s2 的 index 去掉,重新按 0、1、2 取,看看 s2[0] 和 s2['张伟'] 分别会发生什么。
  2. 给 Series 加一个 name,再打印,看看区别在哪里。
  3. 想一想:为什么行取出来的 Series,标签是列名而不是行号?

常见误区

  1. 用 s[0] 取值:pandas 3 里按标签找,标签不是 0 就报 KeyError,要用 iloc。
  2. 以为 Series 就是列表:它有标签,能对齐,能做向量化运算。
  3. 值和标签长度不一致:直接报 ValueError,这个错很好认。
  4. 忽略 name 属性:调试时少了一个定位线索。
自测三问(点开对答案) 1. Series 的两个核心属性是什么? —— values 放数据,index 放标签。另有一个 name 记自己的名字。 2. 为什么 pandas 3 里 s[0] 可能报错? —— 因为方括号按标签查找。标签被换成姓名之后,0 不是一个存在的标签。 3. DataFrame 和 Series 是什么关系? —— DataFrame 是多列 Series 共用一套行标签;反过来,取一列或一行都是一条 Series。

既然标签这么重要,那按标签取和按位置取到底差在哪?下一篇把 loc 和 iloc 摆在一起比。

相关笔记