一、DataFrame 的核心定义

DataFrame 是 Pandas 专为二维结构化数据设计的核心结构,对比 Excel/NumPy 二维数组,核心特征如下:

表格

特性Pandas DataFrameExcel 表格NumPy 二维数组
索引行 / 列均支持自定义标签(字符串 / 数字 / 时间)仅单元格位置(A1/B2)仅整数位置索引
数据类型列可不同类型(如一列 int、一列 str)支持混合类型仅支持同一种数据类型
缺失值原生支持 NaN,内置缺失值处理方法空单元格需手动识别需手动处理 NaN
功能内置清洗 / 统计 / 合并 / 透视等全套方法基础计算 / 筛选仅基础数值计算

直观理解:把 DataFrame 想象成「Excel 工作表」或「数据库表」,由三部分组成:

  1. index:行标签(行索引),唯一标识每一行;
  2. columns:列标签(列名),唯一标识每一列;
  3. 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/ilocloc 按标签、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("分数")),复杂操作分步。

总结

  1. DataFrame 是 Pandas 二维表格型数据结构,由行索引(index)、列索引(columns)、数据(values)组成,支持混合数据类型和缺失值;
  2. 核心操作:
    • 列:直接 df["列名"] 选择 / 修改,drop(columns=) 删除;
    • 行:loc(标签)/iloc(位置)选行,布尔条件筛选行;
    • 清洗:dropna()/fillna() 处理缺失值,drop_duplicates() 去重;
    • 分析:groupby() 分组统计,merge/concat 合并数据;
  3. 核心优势:对比 NumPy 二维数组,支持标签索引、缺失值原生处理、丰富的统计 / 清洗方法;
  4. 避坑关键:明确 loc/iloc 区别,正确组合布尔条件,处理缺失值时结合业务规则。
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐