深入理解 Pandas 的 DataFrame(数据框)
·
一、DataFrame 的核心定义
DataFrame 是 Pandas 专为二维结构化数据设计的核心结构,对比 Excel/NumPy 二维数组,核心特征如下:
表格
| 特性 | Pandas DataFrame | Excel 表格 | NumPy 二维数组 |
|---|---|---|---|
| 索引 | 行 / 列均支持自定义标签(字符串 / 数字 / 时间) | 仅单元格位置(A1/B2) | 仅整数位置索引 |
| 数据类型 | 列可不同类型(如一列 int、一列 str) | 支持混合类型 | 仅支持同一种数据类型 |
| 缺失值 | 原生支持 NaN,内置缺失值处理方法 | 空单元格需手动识别 | 需手动处理 NaN |
| 功能 | 内置清洗 / 统计 / 合并 / 透视等全套方法 | 基础计算 / 筛选 | 仅基础数值计算 |
直观理解:把 DataFrame 想象成「Excel 工作表」或「数据库表」,由三部分组成:
- index:行标签(行索引),唯一标识每一行;
- columns:列标签(列名),唯一标识每一列;
- values:数据本身(底层是 NumPy 数组,按列存储)。
二、创建 DataFrame(核心语法)
创建 DataFrame 的核心函数是 pd.DataFrame(),最常用的 5 种方式如下:
1. 从字典创建(最常用)
字典的键 = 列名,值 = 列数据(列表 / NumPy 数组 / Series),行索引默认是 0,1,2...。
python
import pandas as pd
import numpy as np
# 字典:键=列名,值=列数据
data = {
"姓名": ["小明", "小红", "小刚", "小丽"],
"班级": ["1班", "2班", "1班", "2班"],
"分数": [85, 92, 78, 90],
"生日": ["2010-01-15", "2010-03-20", "2010-02-10", "2010-04-05"]
}
# 方式1:基础创建(默认行索引)
df1 = pd.DataFrame(data)
print("基础DataFrame:")
print(df1)
# 方式2:指定行索引
df2 = pd.DataFrame(data, index=["stu01", "stu02", "stu03", "stu04"])
print("\n指定行索引的DataFrame:")
print(df2)
# 方式3:含缺失值的DataFrame
data_nan = {
"姓名": ["小明", "小红", "小刚", "小丽"],
"分数": [85, np.nan, 78, 90],
"班级": ["1班", "2班", np.nan, "2班"]
}
df3 = pd.DataFrame(data_nan)
print("\n含缺失值的DataFrame:")
print(df3)
输出:
plaintext
基础DataFrame:
姓名 班级 分数 生日
0 小明 1班 85 2010-01-15
1 小红 2班 92 2010-03-20
2 小刚 1班 78 2010-02-10
3 小丽 2班 90 2010-04-05
指定行索引的DataFrame:
姓名 班级 分数 生日
stu01 小明 1班 85 2010-01-15
stu02 小红 2班 92 2010-03-20
stu03 小刚 1班 78 2010-02-10
stu04 小丽 2班 90 2010-04-05
含缺失值的DataFrame:
姓名 分数 班级
0 小明 85.0 1班
1 小红 NaN 2班
2 小刚 78.0 NaN
3 小丽 90.0 2班
2. 从列表的列表创建(按行输入)
列表的每个子列表对应一行数据,需手动指定列名。
python
import pandas as pd
# 列表的列表:每一行数据
rows = [
["小明", "1班", 85],
["小红", "2班", 92],
["小刚", "1班", 78]
]
# 指定列名
df = pd.DataFrame(rows, columns=["姓名", "班级", "分数"], index=["001", "002", "003"])
print("按行创建的DataFrame:")
print(df)
3. 从 NumPy 数组创建
适合批量生成模拟数据,需指定列名。
python
import pandas as pd
import numpy as np
# 生成5行3列的随机温度数据
arr = np.random.normal(5, 3, (5, 3)).round(1)
df = pd.DataFrame(
arr,
columns=["凌晨", "中午", "晚上"], # 列名
index=pd.date_range("2026-01-01", periods=5) # 日期行索引
)
print("从NumPy数组创建的温度DataFrame:")
print(df)
4. 从 Series 字典创建
每个 Series 对应一列,行索引会自动对齐(缺失值补 NaN)。
python
import pandas as pd
# 多个Series(列)
s1 = pd.Series(["小明", "小红", "小刚"], name="姓名")
s2 = pd.Series([85, 92, 78, 90], name="分数") # 多一行数据
s3 = pd.Series(["1班", "2班", "1班"], name="班级")
# 组合为DataFrame(行索引自动对齐)
df = pd.DataFrame({"姓名": s1, "分数": s2, "班级": s3})
print("从Series字典创建的DataFrame:")
print(df)
输出(注意第 3 行后分数有值,姓名 / 班级为 NaN):
plaintext
姓名 分数 班级
0 小明 85.0 1班
1 小红 92.0 2班
2 小刚 78.0 1班
3 NaN 90.0 NaN
5. 从外部文件读取(实战最常用)
参考之前讲解的「Pandas 读取外部数据」,核心是 pd.read_csv()/pd.read_excel():
python
import pandas as pd
# 从CSV读取
df = pd.read_csv("student_scores.csv", encoding="utf-8")
# 从Excel读取
df = pd.read_excel("data.xlsx", sheet_name="学生成绩")
三、DataFrame 的核心操作(索引 / 切片 / 筛选)
DataFrame 支持行 / 列的灵活索引,核心是区分「标签索引」和「位置索引」,避免歧义。
1. 列操作(最基础)
(1)选择列
python
import pandas as pd
data = {"姓名": ["小明", "小红", "小刚"], "班级": ["1班", "2班", "1班"], "分数": [85, 92, 78]}
df = pd.DataFrame(data)
# 选择单列(返回Series)
col_score = df["分数"]
print("单列(Series):")
print(col_score)
# 选择多列(返回DataFrame)
cols_name_score = df[["姓名", "分数"]]
print("\n多列(DataFrame):")
print(cols_name_score)
# 重命名列
df.rename(columns={"分数": "数学分数", "班级": "所在班级"}, inplace=True)
print("\n重命名列后:")
print(df.columns)
(2)添加 / 删除列
python
import pandas as pd
import numpy as np
data = {"姓名": ["小明", "小红", "小刚"], "分数": [85, 92, 78]}
df = pd.DataFrame(data)
# 添加新列(直接赋值)
df["班级"] = ["1班", "2班", "1班"]
df["是否及格"] = df["分数"] >= 60 # 布尔列
df["总分"] = df["分数"] + 10 # 计算列
# 添加空列
df["备注"] = np.nan
print("添加列后:")
print(df)
# 删除列(方法1:drop)
df.drop(columns=["备注"], inplace=True)
# 删除列(方法2:del)
del df["总分"]
print("\n删除列后:")
print(df)
2. 行操作(核心:loc/iloc)
DataFrame 不推荐直接用 df[] 选行(易歧义),优先用:
loc:按标签索引(行 / 列标签);iloc:按位置索引(整数位置,0 开始)。
(1)按标签选行(loc)
python
import pandas as pd
data = {"姓名": ["小明", "小红", "小刚"], "分数": [85, 92, 78]}
df = pd.DataFrame(data, index=["stu01", "stu02", "stu03"])
# 选择单行(返回Series)
row_stu02 = df.loc["stu02"]
print("单行(Series):")
print(row_stu02)
# 选择多行(返回DataFrame)
rows_stu01_03 = df.loc[["stu01", "stu03"]]
print("\n多行(DataFrame):")
print(rows_stu01_03)
# 行+列筛选(标签索引):stu02的姓名和分数
val = df.loc["stu02", ["姓名", "分数"]]
print("\nstu02的姓名和分数:")
print(val)
(2)按位置选行(iloc)
python
import pandas as pd
data = {"姓名": ["小明", "小红", "小刚"], "分数": [85, 92, 78]}
df = pd.DataFrame(data)
# 选择第2行(位置1)
row_1 = df.iloc[1]
print("第2行(位置1):")
print(row_1)
# 选择前2行(位置0-1)
rows_0_1 = df.iloc[0:2]
print("\n前2行:")
print(rows_0_1)
# 位置筛选:第1行(0)的第2列(1)
val = df.iloc[0, 1]
print("\n第1行第2列值:", val) # 85
# 多行多列:位置0-2行,0和2列
subset = df.iloc[0:3, [0, 2]]
print("\n多行多列子集:")
print(subset)
3. 布尔筛选(条件筛选行)
最常用的实战技巧,按业务条件筛选数据。
python
import pandas as pd
import numpy as np
data = {
"姓名": ["小明", "小红", "小刚", "小丽", "小强"],
"班级": ["1班", "2班", "1班", "2班", "1班"],
"分数": [85, 92, 78, 90, 88]
}
df = pd.DataFrame(data)
# 条件1:分数≥90的行
high_score = df[df["分数"] >= 90]
print("分数≥90的学生:")
print(high_score)
# 条件2:1班且分数≥80的行(多条件用&,每个条件加括号)
condition = (df["班级"] == "1班") & (df["分数"] >= 80)
class1_high = df[condition]
print("\n1班且分数≥80的学生:")
print(class1_high)
# 条件3:姓名包含"小"(字符串筛选)
name_contains = df[df["姓名"].str.contains("小")]
print("\n姓名含'小'的学生:")
print(name_contains)
4. 行 / 列的修改
python
import pandas as pd
data = {"姓名": ["小明", "小红", "小刚"], "分数": [85, 92, 78]}
df = pd.DataFrame(data, index=["stu01", "stu02", "stu03"])
# 修改单元格值(标签索引)
df.loc["stu03", "分数"] = 80
# 修改整列值
df["分数"] = df["分数"] + 5 # 所有分数+5
# 修改整行值
df.loc["stu01"] = ["小明", 95]
print("修改后DataFrame:")
print(df)
四、DataFrame 的核心属性与方法
1. 核心属性(快速查看数据特征)
表格
| 属性 | 作用 | 示例 |
|---|---|---|
shape | 行数 × 列数(元组) | df.shape → (3,2) |
index | 行索引对象 | df.index → Index(['stu01','stu02'], dtype='object') |
columns | 列名对象 | df.columns → Index ([' 姓名 ',' 分数 '], dtype='object') |
dtypes | 各列数据类型 | df.dtypes → 姓名:object, 分数:int64 |
values | 底层 NumPy 数组 | df.values → array ([[' 小明 ',85], ...]) |
info() | 数据基本信息(缺失值 / 类型) | df.info() |
describe() | 数值列统计特征 | df.describe() |
python
import pandas as pd
import numpy as np
data = {"姓名": ["小明", "小红", "小刚"], "分数": [85, np.nan, 78]}
df = pd.DataFrame(data)
print("=== 核心属性 ===")
print("形状:", df.shape)
print("列名:", df.columns)
print("数据类型:")
print(df.dtypes)
print("\n=== 数据基本信息 ===")
print(df.info())
print("\n=== 数值列统计 ===")
print(df.describe())
2. 常用方法(清洗 / 统计 / 排序)
(1)缺失值处理
python
import pandas as pd
import numpy as np
data = {"姓名": ["小明", "小红", np.nan], "分数": [85, np.nan, 78]}
df = pd.DataFrame(data)
# 统计缺失值
print("缺失值统计:")
print(df.isnull().sum())
# 删除缺失值行
df_drop = df.dropna()
# 填充缺失值
df_fill = df.fillna({"姓名": "未知", "分数": df["分数"].mean()})
print("\n填充缺失值后:")
print(df_fill)
(2)重复值处理
python
import pandas as pd
data = {"姓名": ["小明", "小红", "小明"], "分数": [85, 92, 85]}
df = pd.DataFrame(data)
# 统计重复行
print("重复行数量:", df.duplicated().sum())
# 删除重复行(保留第一次)
df = df.drop_duplicates()
print("\n删除重复行后:")
print(df)
(3)排序
python
import pandas as pd
data = {"姓名": ["小明", "小红", "小刚"], "分数": [85, 92, 78], "班级": ["1班", "2班", "1班"]}
df = pd.DataFrame(data)
# 按分数降序排序
df_sort_score = df.sort_values(by="分数", ascending=False)
print("按分数降序:")
print(df_sort_score)
# 按班级升序、分数降序排序
df_sort_multi = df.sort_values(by=["班级", "分数"], ascending=[True, False])
print("\n按班级+分数排序:")
print(df_sort_multi)
# 按行索引排序
df_sort_idx = df.sort_index(ascending=False)
(4)分组统计(groupby,核心进阶)
python
import pandas as pd
data = {
"姓名": ["小明", "小红", "小刚", "小丽", "小强"],
"班级": ["1班", "2班", "1班", "2班", "1班"],
"分数": [85, 92, 78, 90, 88]
}
df = pd.DataFrame(data)
# 按班级分组,计算分数的均值/最大值
grouped = df.groupby("班级")["分数"].agg(["mean", "max"])
print("按班级分组统计:")
print(grouped)
# 分组后添加新列(每个班级的平均分)
df["班级平均分"] = df.groupby("班级")["分数"].transform("mean")
print("\n添加班级平均分后:")
print(df)
(5)数据合并(merge/concat)
python
import pandas as pd
# 示例1:concat(按行/列拼接)
df1 = pd.DataFrame({"姓名": ["小明", "小红"], "分数": [85, 92]})
df2 = pd.DataFrame({"姓名": ["小刚", "小丽"], "分数": [78, 90]})
df_concat = pd.concat([df1, df2], axis=0) # 按行拼接
print("按行拼接:")
print(df_concat)
# 示例2:merge(按列关联,类似SQL join)
df_info = pd.DataFrame({"姓名": ["小明", "小红"], "性别": ["男", "女"]})
df_merge = pd.merge(df1, df_info, on="姓名", how="left") # 左连接
print("\n按姓名关联:")
print(df_merge)
五、DataFrame 与其他结构的转换
python
import pandas as pd
import numpy as np
data = {"姓名": ["小明", "小红"], "分数": [85, 92]}
df = pd.DataFrame(data)
# 1. DataFrame → NumPy数组
arr = df.values
print("DataFrame转数组:", arr)
# 2. DataFrame → 字典
dict_df = df.to_dict("records") # records:每行一个字典
print("DataFrame转字典:", dict_df)
# 3. DataFrame → CSV/Excel
df.to_csv("student.csv", index=False, encoding="utf-8") # 不保存行索引
df.to_excel("student.xlsx", sheet_name="成绩", index=False)
# 4. NumPy数组 → DataFrame
arr2 = np.array([["小刚", 78], ["小丽", 90]])
df2 = pd.DataFrame(arr2, columns=["姓名", "分数"])
print("\n数组转DataFrame:")
print(df2)
六、实战场景:DataFrame 完整分析流程
python
import pandas as pd
import numpy as np
# 步骤1:创建/读取数据
data = {
"姓名": ["小明", "小红", "小刚", "小丽", "小强", "小芳"],
"班级": ["1班", "2班", "1班", "2班", "1班", "2班"],
"数学": [85, np.nan, 78, 90, 88, 95],
"语文": [92, 88, 80, 85, 90, 82],
"生日": ["2010-01-15", "2010-03-20", "2010-02-10", "2010-04-05", "2010-05-18", "2010-06-22"]
}
df = pd.DataFrame(data)
# 步骤2:数据探索
print("=== 原始数据 ===")
print(df.info())
print("缺失值统计:", df.isnull().sum())
# 步骤3:数据清洗
# 3.1 填充缺失值
df["数学"] = df.groupby("班级")["数学"].fillna(lambda x: x.mean())
# 3.2 转换日期类型
df["生日"] = pd.to_datetime(df["生日"])
# 3.3 衍生特征
df["总分"] = df["数学"] + df["语文"]
df["平均分"] = df["总分"] / 2
df["是否优秀"] = (df["平均分"] >= 90).astype(int)
# 步骤4:数据分析
# 4.1 按班级统计
class_stats = df.groupby("班级").agg({
"总分": ["mean", "max"],
"是否优秀": "sum" # 优秀人数
}).round(1)
print("\n=== 班级统计 ===")
print(class_stats)
# 4.2 筛选优秀学生
excellent_students = df[df["是否优秀"] == 1][["姓名", "班级", "平均分"]]
print("\n=== 优秀学生 ===")
print(excellent_students)
# 步骤5:保存结果
df.to_csv("student_analysis.csv", index=False, encoding="utf-8")
七、避坑点与最佳实践
1. 核心避坑点
- ❌ 混淆
loc/iloc:loc按标签、iloc按位置,索引为整数时df[1]优先按列名,而非行; - ❌ 多条件筛选错误:用
and/or而非&/|,或未加括号(正确:(df["A"]>1) & (df["B"]<5)); - ❌ 原地修改失效:忘记加
inplace=True(如df.drop()需加才会修改原数据); - ❌ 缺失值统计遗漏:用
df.isnull().sum()而非df.isnull().any(); - ❌ 分组后索引混乱:分组后用
reset_index()重置索引(如df.groupby("班级").mean().reset_index())。
2. 最佳实践
- ✅ 索引规范:创建时给行 / 列起有意义的标签,避免默认数字索引;
- ✅ 明确索引方式:优先用
loc/iloc选行,避免df[]歧义; - ✅ 内存优化:大文件读取用
usecols筛选列,分类列用astype("category"); - ✅ 分步操作:复杂清洗分步执行,每步打印 / 检查结果;
- ✅ 链式操作:简单操作可用链式(如
df.fillna(0).sort_values("分数")),复杂操作分步。
总结
DataFrame是 Pandas 二维表格型数据结构,由行索引(index)、列索引(columns)、数据(values)组成,支持混合数据类型和缺失值;- 核心操作:
- 列:直接
df["列名"]选择 / 修改,drop(columns=)删除; - 行:
loc(标签)/iloc(位置)选行,布尔条件筛选行; - 清洗:
dropna()/fillna()处理缺失值,drop_duplicates()去重; - 分析:
groupby()分组统计,merge/concat合并数据;
- 列:直接
- 核心优势:对比 NumPy 二维数组,支持标签索引、缺失值原生处理、丰富的统计 / 清洗方法;
- 避坑关键:明确
loc/iloc区别,正确组合布尔条件,处理缺失值时结合业务规则。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)