本节摘要:Series 是带标签的一维数组,DataFrame 是由多个 Series 组成的二维表格——Pandas 的一切操作都建立在这两个结构上。本节讲清它们的组织方式、索引(index)的核心作用,以及最常用的创建与属性操作。
阅读完本节,你应当能够:
上一章的 NumPy 数组很强大,但它有个短板:没有名字。数组里的第 3 个元素到底是"北京店的销售额"还是"上海店的销售额",NumPy 不知道。真实数据几乎都带着语义——列名、行名、日期标签。Pandas 就是给 NumPy 数组"贴上标签"的库:一维带标签的叫 Series,二维带标签的叫 DataFrame。
这就好比 NumPy 是仓库里码好的货物,Pandas 是给每件货贴上名称和编号的货架系统。有了标签,你才能说"取北京店的数据"而不是"取第 3 行的数据"。
先看两个结构的关系,再逐个展开:
索引是 Series 的灵魂——有了它,s2["上海"] 就能直接取出 15,而不是靠位置猜。
DataFrame 可以看作"多个 Series 并排拼成的表":每一列是一个 Series,所有列共享同一个行索引;每一行是一条记录。它同时有行索引(index)和列名(columns)两套标签。
# 从字典创建,键是列名 df = pd.DataFrame({ "门店": ["北京", "上海", "广州"], "销售额": [12, 15, 9], "客单价": [299, 355, 410], })
df["销售额"] 取出一列,返回的正是 Series。反过来,把多个 Series 用字典装起来就能拼成 DataFrame。这个"列即 Series"的关系是理解 Pandas 一切操作的钥匙。
df.shape # 行数、列数,如 (3, 3) df.index # 行索引 df.columns # 列名 df.dtypes # 每列的类型 df.values # 转成 NumPy 数组 df.head() # 前 5 行 df.info() # 类型 + 非空计数 df.describe() # 数值列统计摘要
# 方式一:字典(键为列名,值为列表) df1 = pd.DataFrame({"a": [1, 2], "b": [3, 4]}) # 方式二:列表套字典(每行一个字典) df2 = pd.DataFrame([{"a": 1, "b": 3}, {"a": 2, "b": 4}]) # 方式三:从 NumPy 数组 + 指定列名 import numpy as np df3 = pd.DataFrame(np.random.rand(3, 2), columns=["x", "y"]) # 方式四:指定行索引 df4 = pd.DataFrame(data, index=["一月", "二月", "三月"])
| 场景 | 用哪个 | 原因 |
|---|---|---|
| 单独一列数据 | Series | 轻量、够用 |
| 多列关联数据 | DataFrame | 结构完整 |
| 中间计算结果(如均值序列) | Series | groupby 聚合结果 |
| 建模输入 | DataFrame | 特征矩阵形式 |
💡 关键直觉:看到"一维数据 + 标签"就想到 Series,看到"表格"就想到 DataFrame。转换也很简单:
df["列名"]得 Series,s.to_frame()回 DataFrame。
import pandas as pd df_orders = pd.DataFrame({ "订单号": ["A001", "A002", "A003", "A004"], "门店": ["北京", "上海", "北京", "广州"], "金额": [199, 355, 89, 1299], "日期": ["2026-01-01", "2026-01-01", "2026-01-02", "2026-01-02"], }) print(df_orders) print("表格形状:", df_orders.shape) print("金额列均值:", df_orders["金额"].mean())
这张表就是贯穿全书案例的雏形,下一节我们学怎么把它加载得更真实、筛选得更灵活。
索引不只是"标签",还承担着定位与对齐的职责:
df = pd.DataFrame({"金额": [199, 355, 89]}, index=["A001", "A002", "A003"]) df.index # 查看索引 df.reset_index() # 索引变回普通列 df.set_index("订单号") # 指定列变索引 df.sort_index() # 按索引排序 df.loc["A001"] # 按索引取行
💡 关键直觉:
set_index与reset_index是一对互逆操作。分组聚合后索引通常是分组键,用reset_index()把它还原成普通列,是"分组结果继续参与运算"的必备动作,第 3.4 节还会见到。
Series 之间运算时,按索引对齐而不是按位置:
s1 = pd.Series({"北京": 12, "上海": 15}) s2 = pd.Series({"上海": 10, "北京": 8}) print(s1 + s2) # 北京 20,上海 25——按标签对齐相加
这个特性让"两个来源的数据按名称合并"变得自然:不需要关心顺序,标签相同就自动对上。对齐不到的标签结果是 NaN,这也是合并后常见缺失的来源。
| 需求 | 代码 |
|---|---|
| 行列数 | df.shape |
| 列名列表 | df.columns.tolist() |
| 行索引 | df.index |
| 每列类型 | df.dtypes |
| 转 NumPy | df.values 或 df.to_numpy() |
| 转字典 | df.to_dict() |
| 取单列成 Series | df["列名"] |
| Series 转回表 | s.to_frame() |
to_numpy() 是 Pandas 与 NumPy 互通的桥——把 DataFrame 交给算法库时,通常要先转成数组。
⚠️ 常见坑:
df["列名"]取单列返回的是 Series 而不是 DataFrame——它没有 columns 属性,df["列名"].columns会报错。需要保持表结构时用双括号df[["列名"]]。这个差别在链式操作里最容易踩。
df[列名] 返回 Series,s.to_frame() 变回 DataFrame,两者随时互转df.values 转 NumPy 数组,是 Pandas 与 NumPy 的桥结构认识了,接下来让数据真正"进来"——下一节讲数据加载与索引筛选,掌握 loc、iloc 和布尔索引三种取数姿势。