DataFrame:二维数据结构
本教程共 54 篇 · 第 6 篇 · 更新于 2026-08-11 · 约 8 分钟阅读
本节目标:掌握 DataFrame 的创建、列操作、行操作、常用属性和转置,能独立组装出一张自己的数据表。
DataFrame 是什么
DataFrame 是二维带标签表格:行有索引,列有列名,每列可以类型不同。官方定义:通用二维带标签、可变大小的表格结构。
三个视角理解它:
- 像一张带表头的 Excel 表格
- 像一张 SQL 数据表
- 像一组共享索引的 Series 组成的字典
为什么标签重要
NumPy 数组只有位置(第 0 行、第 3 列),pandas 给行和列都贴了标签:按名字取列、按标签取行、运算时自动对齐、筛选时保留上下文。
标签是 pandas 的灵魂。习惯了按名取数,你会觉得「第 5 列」这种说法又拗口又容易错。
举个例子:一张 100 列的报表,想取「销售额」这一列,df[“销售额”] 一行搞定,不用数它在第几列;插入一列、删掉一列,其他列的位置变了,代码却不用改。这就是标签的价值。
行也一样:按行标签取数据,插入、删除、排序后标签依然准确,位置却会漂移。位置索引 iloc 只用于「就要第几行」的场景。
「按标签」还是「按位置」,是 pandas 里最高频的区分,§19 会彻底讲透。
创建 DataFrame
最常见的方式是字典,键是列名,值是列数据:
import pandas as pd
# 键 -> 列名,值 -> 列数据(列表)
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五"],
"年龄": [25, 30, 35],
"在职": [True, False, True],
})
print(df)
也可以由 Series 拼成:
import pandas as pd
apples = pd.Series([1, 3, 7, 4])
bananas = pd.Series([2, 6, 3, 5])
# 两个 Series 按列拼成一张表
df2 = pd.DataFrame({"苹果": apples, "香蕉": bananas})
print(df2)
多个 Series 索引不一致时,结果行索引取并集,缺的位置填 NaN:
import pandas as pd
s1 = pd.Series([1, 2], index=["a", "b"])
s2 = pd.Series([10, 20], index=["b", "c"])
# a 行没有 s2 的数据,自动填 NaN
df = pd.DataFrame({"一": s1, "二": s2})
print(df)
从二维 ndarray 创建:
import numpy as np
import pandas as pd
arr = np.array([[1, 2], [3, 4], [5, 6]])
df3 = pd.DataFrame(arr, columns=["列A", "列B"], index=["r1", "r2", "r3"])
print(df3)
从「列表套字典」创建,每个字典是一行:
import pandas as pd
rows = [{"a": 1, "b": 2}, {"a": 5, "b": 10}]
df4 = pd.DataFrame(rows)
print(df4)
想只保留部分列,或调整列顺序,用 columns 参数过滤:pd.DataFrame(rows, columns=[“b”])。
从字典嵌套创建时,如果内层键不一致,缺失位置同样填 NaN,规则和 Series 拼接一致。
嵌套字典也支持:外层键是列名,内层键是行标签:
import pandas as pd
data = {"a": {"r1": 1, "r2": 2}, "b": {"r1": 3, "r2": 4}}
df5 = pd.DataFrame(data)
print(df5)
查看表的基本信息
import pandas as pd
# 沿用上一节的员工表
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五"],
"年龄": [25, 30, 35],
"在职": [True, False, True],
})
print(df.shape) # (3, 3):3 行 3 列
print(df.index) # 行索引
print(df.columns) # 列名
print(df.dtypes) # 每列类型
print(df.head()) # 前 5 行
print(df.info()) # 概况:列、非空数、内存
不传 index 时,行索引默认是 0 到行数减一的整数(RangeIndex),这和电子表格的行号差不多。
info() 输出末尾的 memory usage 是内存占用,数据量大时值得盯。想逐列看内存,用 df.memory_usage()。
列操作
DataFrame 的列操作和字典很像:
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
# 取一列 -> Series
print(df["a"])
# 取多列并调整顺序
print(df[["b", "a"]])
# 新增一列:基于已有列计算
df["c"] = df["a"] * 2
print(df)
# 标量自动广播到整列
df["全为1"] = 1
# 删除列
del df["全为1"]
# 指定位置插入列
df.insert(1, "新列", [7, 8, 9])
print(df)
列名是合法 Python 标识符时还能用属性访问 df.a,效果同 df["a"]。不过正式代码里推荐统一用中括号,避免和 pandas 内置属性撞名。
改名用 rename,可以一次改多列:
import pandas as pd
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})
df = df.rename(columns={"a": "甲", "b": "乙"})
print(df)
rename 不修改原表,返回新表,记得接住返回值。
行操作
取行分两种:按标签用 loc,按位置用 iloc。
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}, index=["r1", "r2", "r3"])
# 按标签取一行 -> Series
print(df.loc["r2"])
# 按位置取一行 -> Series
print(df.iloc[1])
# 取多行
print(df.iloc[1:3])
切片取行得到的是 DataFrame(保留列结构),取单行得到的是 Series,注意区分。
按条件选行也走布尔筛选,比如:
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
print(df[df["a"] > 1]) # 留下 a 大于 1 的行
布尔筛选是「条件选行」的简写形式,loc/iloc 的完整用法后面章节会系统讲。
加新行用 pd.concat 拼接(老版本的 df.append() 已移除,别再用了)。具体写法和参数在 §33 详讲。
常见坑
- KeyError:列名或行标签不存在,检查拼写和大小写
- ValueError: Length of values does not match length of index:新增列长度不等于行数
- 列名重复:df[“a”] 会返回 DataFrame 而不是 Series,建表时尽量避免重名
改完数据记得看一眼
和
df.head(),确认行数列数没跑偏,这是低成本的自检习惯。
转置
转置就是行列互换,用 .T:
import pandas as pd
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})
print(df.T)
输出:
0 1
a 1 2
b 3 4
转置后原来的列名变成了行索引,原来的行索引变成了列名。行列内容不对称的表,转置后常常更好看。
常用属性汇总
df.shape:形状 (行, 列)df.index:行索引df.columns:列名df.dtypes:每列类型df.to_numpy():转成 NumPy 数组(丢掉标签)df.T:转置
还有一个常用属性 df.empty:判断表是否为空,写循环或分支时用得上。
copy() 与修改安全
想独立修改一份数据而不影响原表,用 copy():
import pandas as pd
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})
df_copy = df.copy() # 独立副本
df_copy.iloc[0, 0] = 99
print(df) # 原表不变
CoW 下直接 df[["a"]] 取出来的子集,修改也不影响原表,但显式 copy() 语义最清楚,适合正式代码。
什么时候需要 copy?要保留原始数据做对照,或者把数据传给函数、而函数内部会修改数据时。
运算的方向
df.sum() 默认按列求和:每一列算一个数,结果是一个 Series,索引是列名。想按行求和,传 axis=1:
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
print(df.sum()) # 按列:a=6, b=15
print(df.sum(axis=1)) # 按行:每行两个数相加
axis=0 是默认方向(按列操作),axis=1 是跨列方向(按行操作)。这个概念后面会反复出现,先混个脸熟。
简单的整表操作
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
# 每列求和
print(df.sum())
# 每列均值
print(df.mean())
# 按条件筛选行
print(df[df["a"] > 1])
# 数值列的统计摘要
print(df.describe())
两张 DataFrame 做运算时也会按标签对齐:行和列都取并集,缺的位置填 NaN。这个特性在 §05 讲过雏形,后面章节会展开。
DataFrame 和 Series 混合运算也常见,比如每列都减去第一行的值:df - df.iloc[0]。pandas 会把 Series 沿列方向广播,省去手动复制。
sum、mean 这类统计默认跳过缺失值;想保留缺失就传 skipna=False。
这些只算开胃菜:列选择、筛选、统计会在后面的章节系统展开。先把「造表、取列、加行、转置」练熟,后面所有章节都建立在这套基本功上,练熟之后会轻松很多。
动手练一下:用字典造一张 5 行 3 列的「成绩表」(姓名、科目、分数),试一遍取列、加列、删列、转置,再试试用 pd.concat 加两行数据。
DataFrame 取一列得到 Series,多个 Series 按列拼回去又得到 DataFrame。理解「表 = 一堆列(Series)」这个视角,后面学 groupby、merge 会顺畅很多。