本篇文章全面讲解 pandas 最核心的二维数据结构 DataFrame:创建方式、结构组成、常用属性、行列操作、增删改查。学完本章,你将能够熟练地在内存中"搭建和摆弄"一张表格。
目录
1. 什么是 DataFrame
2. 创建 DataFrame 的 6 种方式
3. DataFrame 的组成结构
4. 常用属性
5. 查看数据概览
6. 行列的增删改
7. DataFrame 与 Series 的相互转换
8. DataFrame 的运算
9. 转置与轴向操作
10. 常见坑与注意事项
11. 本章小结与练习
1. 什么是 DataFrame
DataFrame 是 pandas 的二维表格型数据结构,可以理解成"一张带行索引和列名的 Excel 工作表"。
特点:
- 有行索引(index)和列名(columns);
- 每一列本质上是一个 Series(共享同一行索引);
- 各列类型可以不同(一列是整数、一列是字符串、一列是日期);
- 支持丰富的行/列操作、分组聚合、合并、透视等高级功能。
姓名 年龄 城市
id=1 张三 25 北京
id=2 李四 30 上海
id=3 王五 28 广州
2. 创建 DataFrame 的 6 种方式
2.1 从字典创建(最常用)
import pandas as pd
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五"],
"年龄": [25, 30, 28],
"城市": ["北京", "上海", "广州"],
})
print(df)
姓名 年龄 城市
0 张三 25 北京
1 李四 30 上海
2 王五 28 广州
- 字典的键成为列名;
- 每个值(列表)成为一列数据;
- 默认行索引为 0,1,2...
2.2 从列表的列表 / 元组创建
df2 = pd.DataFrame(
[["张三", 25, "北京"],
["李四", 30, "上海"],
["王五", 28, "广州"]],
columns=["姓名", "年龄", "城市"],
)
print(df2)
2.3 从 NumPy 数组创建
import numpy as np
arr = np.random.randint(0, 100, size=(4, 3))
df3 = pd.DataFrame(arr, columns=["语文", "数学", "英语"])
print(df3)
2.4 从 Series 字典创建
df4 = pd.DataFrame({
"a": pd.Series([1, 2, 3]),
"b": pd.Series([10, 20]),
})
print(df4)
# a b
# 0 1 10.0
# 1 2 20.0
# 2 3 NaN
两个 Series 长度不一致时,缺失位置自动补 NaN。
2.5 从列表的字典(每条记录一个字典)
records = [
{"姓名": "张三", "年龄": 25},
{"姓名": "李四", "年龄": 30, "城市": "上海"},
]
df5 = pd.DataFrame(records)
print(df5)
# 姓名 年龄 城市
# 0 张三 25.0 NaN
# 1 李四 30.0 上海
2.6 从 CSV / Excel 等文件读取
df6 = pd.read_csv("data.csv")
df7 = pd.read_excel("data.xlsx", sheet_name="Sheet1")
(详细读写知识见第 4 章)
创建方式速查表
| 数据源 | 写法 | 特点 |
|--------|------|------|
| 字典 | pd.DataFrame({列: 列表}) | 最直观,键为列名 |
| 列表的列表 | pd.DataFrame([...], columns=[...]) | 需手动指定列名 |
| ndarray | pd.DataFrame(arr, columns=[...]) | 适合程序化生成 |
| Series 字典 | pd.DataFrame({"a": s1, "b": s2}) | 索引自动对齐 |
| 记录列表 | pd.DataFrame([{...}, {...}]) | 适合 API 返回数据 |
| 文件 | pd.read_csv/excel/json | 最常用 |
3. DataFrame 的组成结构
一个 DataFrame 由三部分组成:
df = pd.DataFrame(
{"姓名": ["张三", "李四"], "年龄": [25, 30]},
index=["r1", "r2"],
)
print(df)
# 姓名 年龄
# r1 张三 25
# r2 李四 30
# 三部分
print(df.index) # Index(['r1', 'r2'], dtype='object') 行索引
print(df.columns) # Index(['姓名', '年龄'], dtype='object') 列名
print(df.values) # [['张三' 25] ['李四' 30]] 数据(ndarray)
values是所有列拼成的二维 NumPy 数组,混合类型时 dtype 会变成object。
4. 常用属性
import pandas as pd
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五"],
"年龄": [25, 30, 28],
"城市": ["北京", "上海", "广州"],
})
print(df.shape) # (3, 3) 行数、列数
print(df.size) # 9 元素总数
print(df.ndim) # 2 维度
print(df.dtypes) # 每列类型(注意是 dtypes 复数)
print(df.index) # 行索引
print(df.columns) # 列名
print(df.values) # 数据数组
print(df.empty) # False
print(df.axes) # [行索引, 列名] 列表
print(df.T) # 转置
print(df.memory_usage()) # 每列内存占用
df.dtypes 输出:
姓名 object
年龄 int64
城市 object
dtype: object
单列与多列访问
# 取一列 -> Series
age = df["年龄"]
print(type(age)) # <class 'pandas.core.series.Series'>
# 取多列 -> DataFrame
sub = df[["姓名", "城市"]]
print(type(sub)) # <class 'pandas.core.frame.DataFrame'>
5. 查看数据概览
# 前几行 / 后几行
print(df.head(2)) # 前 2 行
print(df.tail(2)) # 后 2 行
# 随机抽样
print(df.sample(2))
# 基本信息(列名、非空计数、类型)
print(df.info())
# 数值列统计摘要
print(df.describe())
# 每列唯一值个数
print(df.nunique())
# 每列缺失值个数
print(df.isna().sum())
describe() 输出:
年龄
count 3.000000
mean 27.666667
std 2.516611
min 25.000000
25% 26.500000
50% 28.000000
75% 29.000000
max 30.000000
describe(include="object") 可以查看字符串列的统计(count、unique、top、freq)。6. 行列的增删改
6.1 新增列
# 直接赋值新列
df["部门"] = ["技术", "销售", "市场"]
print(df.columns) # Index(['姓名', '年龄', '城市', '部门'], ...)
# 用已有列计算新列
df["明年年龄"] = df["年龄"] + 1
# 用条件生成列
df["是否成年"] = df["年龄"] >= 18
# 用 assign 生成(不修改原表,返回新表)
df2 = df.assign(年龄平方=df["年龄"] ** 2)
6.2 删除列
# 删除列(axis=1),inplace 表示原地修改
df = df.drop("明年年龄", axis=1)
# 等价写法
df = df.drop(columns=["是否成年"])
# 删除行
df = df.drop(0) # 删除索引为 0 的行
df = df.drop([1, 2]) # 删除多行
重要:drop默认返回新对象,不修改原表。想原地修改需inplace=True,但官方推荐的做法是直接重新赋值(如df = df.drop(...))。
6.3 修改数据
# 修改单个值(推荐 .at / .loc)
df.at[0, "年龄"] = 26
df.loc[1, "城市"] = "深圳"
# 修改整列
df["年龄"] = [26, 31, 29]
# 按条件批量修改
df.loc[df["年龄"] > 28, "备注"] = "资深"
6.4 重命名
# 重命名列
df = df.rename(columns={"姓名": "name", "年龄": "age"})
# 重命名索引
df = df.rename(index={0: "第一行"})
7. DataFrame 与 Series 的相互转换
# DataFrame -> 取列得 Series
s = df["年龄"]
# DataFrame -> 取一行得 Series(索引为列名)
row = df.loc[0]
# DataFrame -> 字典 {列名: Series}
d = df.to_dict()
# DataFrame -> 列表的列表
lst = df.values.tolist()
# Series -> DataFrame
s_df = s.to_frame(name="年龄")
# Series -> 字典 / 列表
print(s.to_dict())
print(s.to_list())
8. DataFrame 的运算
8.1 列与列的运算
df["总价"] = df["单价"] * df["数量"] # 向量化逐行运算
df["折扣价"] = df["总价"] * 0.9
8.2 行/列方向的统计(axis 参数)
import numpy as np
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
# 默认 axis=0:按列统计(对每列求)
print(df.sum()) # a=6 b=15 c=24
print(df.mean(axis=0)) # 每列均值
# axis=1:按行统计(对每行求)
print(df.sum(axis=1)) # 12 15 18
print(df.mean(axis=1)) # 4.0 5.0 6.0
记忆技巧:axis=0是"沿着行方向移动"→ 对每一列计算;axis=1是"沿着列方向移动"→ 对每一行计算。很多新手在这里混淆,请务必亲手跑一遍。
8.3 DataFrame 与 Series 运算(广播)
# 每列都减去该列的均值(自动按列对齐)
df2 = df - df.mean()
# 每行都除以该行的和(需要配合 axis 参数)
df3 = df.div(df.sum(axis=1), axis=0)
9. 转置与轴向操作
# 转置:行列互换
print(df.T)
# 沿着轴堆叠
df_a = pd.DataFrame({"x": [1, 2]})
df_b = pd.DataFrame({"x": [3, 4]})
# 纵向拼接(axis=0,默认):行数增加
pd.concat([df_a, df_b], axis=0)
# 横向拼接(axis=1):列数增加
pd.concat([df_a, df_b], axis=1)
(合并的完整知识见第 11 章)
10. 常见坑与注意事项
| 坑 | 现象 | 解决办法 |
|----|------|----------|
| 链式赋值警告 | df[df.x>1]["y"]=5 报 SettingWithCopyWarning | 改用 .loc 一次性操作 |
| axis 记反 | 统计结果与预期相反 | 记住 axis=0 按列、axis=1 按行 |
| drop 忘赋值 | 删除后原表没变 | df = df.drop(...) 或 inplace=True |
| 混用 df.列名 与 df["列名"] | 列名含空格/中文/特殊字符时 df.列名 报错 | 统一用 df["列名"] |
| 误以为 df["a","b"] 可取两列 | 报 KeyError | 必须写成 df[["a","b"]] |
11. 本章小结与练习
小结
- DataFrame 是二维表格,由 index + columns + values 组成;
- 常用创建方式:字典、列表的列表、ndarray、记录列表、文件读取;
- 核心属性:
shape、dtypes、index、columns、values; - 增删改:新增列直接赋值、
drop删行列、.at/.loc改值、rename改名; axis=0按列、axis=1按行,这是最容易出错也最重要的概念之一。
练习题
1. 用字典创建包含 4 名学生(姓名、语文、数学、英语)的 DataFrame。
2. 新增一列"总分"= 三门成绩之和,再新增一列"平均分"。
3. 打印出所有学生的平均分均值、总分最大值。
4. 删除"平均分"列,重命名"语文"为"chinese"。
5. 使用 .loc 将分数低于 60 的单元格改为 60。
下一篇预告:第 4 章 数据读取与写入 —— 从 CSV、Excel、JSON、SQL 等常见格式读写数据。
文章回复
0 条公开回复