第五章 Pandas :序列与数据框
本章定位:Pandas 是 Python 数据分析领域的核心基础库,基于 NumPy 构建,专门用于处理结构化表格数据,提供了高效的数据清洗、筛选、转换、汇总与分析工具,是数据挖掘、数据分析、自动化办公的必备工具。
前置准备:使用前需安装 Pandas 库:
pip install pandas行业惯例导入方式:pythonimport pandas as pd import numpy as np # Pandas 常与 NumPy 搭配使用学习目标:掌握 Series 序列与 DataFrame 数据框的创建、外部数据读写、数据清洗、子集筛选、合并连接与分组汇总,能独立完成基础的表格数据处理任务。
1 序列与数据框基础概念
Pandas 有两大核心数据结构:序列(Series) 和 数据框(DataFrame),分别对应表格的“列”与“整张表”。
1.1 序列 Series
Series 是一维带标签的数组结构,要求内部元素数据类型一致,可理解为表格中的“一列数据”。
- 每个元素都对应一个索引标签(默认从0开始的整数索引,可自定义)
- 支持向量化运算,特性与 NumPy 数组类似
- 同时具备数组的运算能力和字典的索引访问能力
1.2 数据框 DataFrame
DataFrame 是二维表格型结构,由多个 Series 按列组合而成;每一列可以是不同的数据类型(数值、字符串、日期等),对应表格的“整张数据表”。
- 既有行索引(index),也有列索引(columns)
- 每一列都是一个 Series 对象
- 支持丰富的表格操作,是数据分析的核心载体
1.3 核心结构对比
| 结构 | 维度 | 数据类型要求 | 对应表格概念 | 核心作用 |
|---|---|---|---|---|
| Series | 一维 | 元素类型必须一致 | 表格的一列 | 单列数据处理与运算 |
| DataFrame | 二维 | 每列类型可不同 | 整张表格 | 多列结构化数据处理 |
2 序列与数据框的构造
2.1 序列 Series 的构造
语法:pd.Series(data, index=None, dtype=None)
| 数据来源 | 代码示例 | 说明 |
|---|---|---|
| 从列表/元组创建 | ```python | |
| s = pd.Series([10, 20, 30, 40]) | ||
| print(s) |
| 自定义索引 | ```python
s = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
print(s)
``` | index 参数指定行标签 |
| 从字典创建 | ```python
s = pd.Series({"苹果": 5, "香蕉": 3, "橙子": 4})
print(s)
``` | 字典键作为索引,值作为元素 |
| 从 NumPy 数组创建 | ```python
s = pd.Series(np.arange(5))
print(s)
``` | 数组元素转为序列元素 |
### 2.2 数据框 DataFrame 的构造
语法:`pd.DataFrame(data, index=None, columns=None)`
#### 方式1:从“列名-列表”字典创建(最常用)
字典的键为列名,值为对应列的数据列表。
```python
data = {
"姓名": ["张三", "李四", "王五"],
"年龄": [20, 21, 19],
"成绩": [85.5, 92.0, 78.5]
}
df = pd.DataFrame(data)
print(df)方式2:从二维数组创建 + 指定列名
arr = np.array([[1, 2, 3], [4, 5, 6]])
df = pd.DataFrame(arr, columns=["A列", "B列", "C列"], index=["行1", "行2"])
print(df)方式3:从字典列表创建
每个字典代表一行数据。
data = [
{"姓名": "张三", "年龄": 20},
{"姓名": "李四", "年龄": 21}
]
df = pd.DataFrame(data)
print(df)提示
创建 DataFrame 时,若各列数据长度不一致会触发报错;缺失位置会自动填充为 NaN。
3 外部数据读取
Pandas 支持读取多种格式的外部数据,直接加载为 DataFrame,是数据导入的核心方式。
3.1 文本/表格文件读取
| 函数 | 适用格式 | 核心说明 |
|---|---|---|
pd.read_csv() | CSV 逗号分隔文件 | 最常用,专门读取 CSV 格式表格 |
pd.read_table() | TXT/CSV 等文本表格 | 通用文本表格读取,可指定分隔符 |
pd.read_excel() | Excel 文件(.xlsx/.xls) | 读取 Excel 电子表格,需额外安装 openpyxl 依赖库 |
基础示例
# 读取 CSV 文件
df = pd.read_csv("data.csv", encoding="utf-8")
# 读取带分隔符的 TXT 文件,sep 指定分隔符
df = pd.read_table("data.txt", sep="\t", encoding="utf-8")
# 读取 Excel 文件,指定工作表
df = pd.read_excel("data.xlsx", sheet_name="Sheet1")常用参数
sep:指定分隔符,read_csv默认,encoding:指定文件编码,中文文件常用utf-8/gbkheader:指定第几行作为表头,默认第 0 行index_col:指定某列作为行索引nrows:只读取前 N 行数据
3.2 数据库数据读取
可直接从关系型数据库(如 MySQL)中读取 SQL 查询结果为 DataFrame。
前置依赖:需安装数据库驱动库,MySQL 场景需安装
pymysql:pip install pymysql
import pymysql
# 1. 创建数据库连接
con = pymysql.connect(
host="localhost",
user="root",
password="数据库密码",
database="数据库名",
port=3306,
charset="utf8"
)
# 2. 执行 SQL 并读取为 DataFrame
sql = "SELECT * FROM 表名"
df = pd.read_sql(sql, con)
# 3. 关闭连接
con.close()4 数据清洗
数据清洗是数据分析的核心前置步骤,包括类型转换、重复值处理、缺失值处理、异常值处理等,直接决定后续分析结果的准确性。
4.1 数据类型转换
(1)通用类型转换:astype()
用于数值、字符串、布尔型等类型的转换。
# 将年龄列转换为浮点型
df["年龄"] = df["年龄"].astype("float64")
# 转为字符串类型
df["姓名"] = df["姓名"].astype("str")(2)日期时间转换:pd.to_datetime()
专门用于日期格式转换,支持识别多种日期字符串格式。
# 将日期字符串列转换为日期类型,format 指定原数据的格式
df["日期"] = pd.to_datetime(df["日期"], format="%Y-%m-%d")4.2 描述性统计
describe() 可以快速生成数据的统计概览,默认只统计数值型列。
# 默认统计数值型列:计数、均值、标准差、最小值、四分位数、最大值
print(df.describe())
# 包含离散型(object)列的统计:计数、唯一值数、众数、最高频
print(df.describe(include="object"))4.3 重复值处理
| 函数/方法 | 功能 |
|---|---|
df.duplicated() | 识别重复行,返回布尔 Series,重复行为 True |
df.drop_duplicates() | 删除重复行,默认保留第一条 |
# 查看所有重复行
print(df[df.duplicated()])
# 删除重复行,keep='last' 保留最后一条
df = df.drop_duplicates(keep="first")4.4 缺失值处理
(1)缺失值识别
| 方法 | 功能 |
|---|---|
df.isnull() | 识别缺失值,缺失处为 True |
df.notnull() | 识别非缺失值,非缺失处为 True,是 isnull() 的反函数 |
# 统计每列缺失值数量
print(df.isnull().sum())(2)缺失值删除
| 方法 | 功能 |
|---|---|
df.dropna() | 删除包含缺失值的行/列 |
df.drop(labels, axis) | 删除指定的行或列 |
del df["列名"] | 直接删除指定列 |
drop() 核心参数:
labels:待删除的行名或列名axis:删除轴,0删除行,1删除列
# 删除所有包含缺失值的行
df1 = df.dropna(axis=0)
# 删除指定多列
df2 = df.drop(["无用列1", "无用列2"], axis=1)
# 直接删除列(无返回值,直接修改原数据)
del df["废弃列"](3)缺失值填充:fillna()
支持多种填充策略,满足不同业务场景需求。
# 1. 常数填充
df["列名"] = df["列名"].fillna(0)
# 2. 前向填充(用上一行的值填充)
df["列名"] = df["列名"].fillna(method="ffill")
# 3. 后向填充(用下一行的值填充)
df["列名"] = df["列名"].fillna(method="bfill")
# 4. 统计值填充
# 数值型:均值填充
df["年龄"] = df["年龄"].fillna(df["年龄"].mean())
# 数值型:中位数填充(受异常值影响小)
df["收入"] = df["收入"].fillna(df["收入"].median())
# 离散型:众数填充
df["城市"] = df["城市"].fillna(df["城市"].mode()[0])4.5 异常值处理
异常值指偏离整体数据分布的极端值,常用两种识别方法:
方法1:3σ 法(标准差法)
数据落在均值 ± 3倍标准差范围内视为正常,超出则判定为异常值。
col = df["目标列"]
mean_val = col.mean()
std_val = col.std()
# 计算正常范围上下界
upper = mean_val + 3 * std_val
lower = mean_val - 3 * std_val
# 筛选出异常值
outliers = df[(col > upper) | (col < lower)]
print("识别出的异常值:\n", outliers)
# 保留正常数据
df_normal = df[(col <= upper) & (col >= lower)]方法2:箱线图判别法
基于四分位数计算:上限 = Q3 + 1.5×IQR,下限 = Q1 - 1.5×IQR,超出范围为异常值。
col = df["目标列"]
Q1 = col.quantile(0.25) # 下四分位数
Q3 = col.quantile(0.75) # 上四分位数
IQR = Q3 - Q1 # 四分位距
upper = Q3 + 1.5 * IQR
lower = Q1 - 1.5 * IQR
# 筛选正常数据
df_normal = df[(col <= upper) & (col >= lower)]5 数据子集筛选
Pandas 提供了多种索引方式来筛选数据,可根据索引类型选择对应方法。
5.1 iloc:位置索引(整数索引)
通过整数位置筛选数据,和 Python 列表切片规则一致,前闭后开。 语法:df.iloc[行索引, 列索引]
# 取第 0 行整行
print(df.iloc[0])
# 取第 0 到 2 行(不包含 2),第 1 到 3 列(不包含 3)
print(df.iloc[0:2, 1:3])
# 用整数列表指定离散位置
print(df.iloc[[0, 2], [0, 2]])5.2 loc:标签索引
通过行标签/列标签名筛选数据,包含起止两端,即前闭后闭;也支持条件表达式筛选。 语法:df.loc[行条件/行标签, 列标签]
(1)标签筛选
# 行标签为 'row1' 的行,列名为 '姓名'、'成绩' 的列
print(df.loc["row1", ["姓名", "成绩"]])
# 行标签从 'a' 到 'c',所有列
print(df.loc["a":"c", :])(2)条件筛选(最常用)
# 筛选年龄大于20的行
print(df.loc[df["年龄"] > 20, :])
# 多条件筛选:年龄>20 且 成绩>80
print(df.loc[(df["年龄"] > 20) & (df["成绩"] > 80), :])注意
多条件筛选时,每个条件必须用括号包裹,逻辑运算符用 &(且)、|(或),不能使用 and/or。
5.3 ix:混合索引
ix 是 iloc 与 loc 的混合模式,既支持位置索引也支持标签索引。
⚠️ 注意:目前官方已不推荐使用
ix,建议根据场景明确使用iloc或loc,避免索引歧义。
5.4 索引设置与重置
| 方法 | 功能 |
|---|---|
df.set_index("列名") | 将指定列设为行索引 |
df.reset_index(drop=False, inplace=False) | 重置为默认整数索引 |
参数说明:
drop=True:丢弃原索引;False:原索引转为普通列inplace=True:直接修改原数据框;False:返回新对象
# 将姓名列设为行索引
df = df.set_index("姓名")
# 重置索引,丢弃原索引
df = df.reset_index(drop=True)补充
NumPy 的通用函数(如 np.abs()、np.mean()、np.sum()等),大多可直接作用于 Pandas 的 Series 和 DataFrame 对象。
5.5 apply:函数应用
apply() 可以将自定义函数应用到每一行或每一列,实现批量自定义处理。
axis=0(默认):函数作用于每一列axis=1/axis="columns":函数作用于每一行
# 对每一列应用求和函数
print(df.apply(np.sum, axis=0))
# 对每一行应用自定义函数,计算总分
def get_total(row):
return row["语文"] + row["数学"] + row["英语"]
df["总分"] = df.apply(get_total, axis=1)6 数据的合并与连接
6.1 数据拼接:pd.concat()
将多个 DataFrame 按行或按列直接拼接,类似“堆叠”,不做关联匹配。
# 纵向拼接(行堆叠),要求各表列数、列名一致
df_all = pd.concat([df1, df2, df3], axis=0)
# 横向拼接(列合并),要求各表行数一致
df_all = pd.concat([df1, df2], axis=1)6.2 数据连接:pd.merge()
类似 SQL 的表连接,根据共同的键列将两个表关联起来,支持多种连接模式。 语法:pd.merge(left, right, on="关联列", how="inner")
how 连接方式参数:
inner:内连接,只保留两边都匹配的行(默认)left:左连接,保留左表所有行,右表匹配填充right:右连接,保留右表所有行outer:外连接,保留两边所有行
# 内连接,根据 id 列关联
df_merge = pd.merge(df1, df2, on="id", how="inner")
# 左连接,左右表关联列名不同时分别指定
df_merge = pd.merge(df1, df2, left_on="用户id", right_on="id", how="left")7 数据汇总与透视表
7.1 分组统计 groupby
按指定列分组,然后对各组进行统计汇总。
# 按城市分组,计算各组年龄、成绩的均值
df_group = df.groupby("城市")[["年龄", "成绩"]].mean()
# 多维度分组 + 多指标聚合
df_group = df.groupby(["城市", "性别"]).agg(
人数=("姓名", "count"),
平均年龄=("年龄", "mean"),
最高成绩=("成绩", "max")
)7.2 透视表 pivot_table
快速生成交叉汇总透视表,类似 Excel 的数据透视表功能。 语法:pd.pivot_table(data, index=行分组, columns=列分组, values=值列, aggfunc='mean')
# 行:城市;列:性别;值:成绩;聚合方式:均值
pivot = pd.pivot_table(
df,
index="城市",
columns="性别",
values="成绩",
aggfunc="mean"
)
print(pivot)本章练习
基础题
- 创建一个包含姓名、年龄、城市、成绩 4 列的学生 DataFrame,包含 5 条测试数据。
- 将上述数据保存为 CSV 文件,再重新读取出来,查看各列的数据类型。
- 为上述数据新增 2 条重复数据,识别并删除重复值。
- 将成绩列的缺失值用均值填充,然后筛选出成绩大于 80 分的学生。
- 按城市分组,统计每个城市的平均成绩和学生人数。
进阶题
- 读取一个 Excel 销售表格,完成完整数据清洗:去除空列、转换日期格式、填充缺失值、识别并处理异常值。
- 有订单表和用户表,根据用户 ID 将两表左连接,计算每个用户的订单总金额。
- 生成销售数据透视表:行维度为月份,列维度为产品类别,值为销售额,聚合方式为求和。
本章常见易错点
- iloc 与 loc 混淆:iloc 是整数位置、前闭后开;loc 是标签、前闭后闭,注意区分使用场景。
- 多条件筛选括号缺失:多条件筛选时每个条件必须加括号,否则运算符优先级会导致结果错误。
- drop 的 axis 搞反:
axis=0删除行,axis=1删除列,记忆口诀:0行1列。 - fillna 不生效:
fillna默认返回新对象,需要赋值接收,或使用inplace=True参数。 - 分组后索引变化:groupby 后分组列会变成索引,需要保留列可使用
reset_index()或as_index=False。 - merge 列名不匹配:左右表关联列名不同时,需用
left_on和right_on分别指定。