首页 / Pandas 入门教程 / DataFrame:二维数据结构

Pandas 入门教程

DataFrame:二维数据结构

本教程共 54 篇 · 第 6 篇 · 更新于 2026-08-11 · 约 8 分钟阅读

PandasPandas 入门教程DataFrame二维表格列操作转置

本节目标:掌握 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 沿列方向广播,省去手动复制。

summean 这类统计默认跳过缺失值;想保留缺失就传 skipna=False

这些只算开胃菜:列选择、筛选、统计会在后面的章节系统展开。先把「造表、取列、加行、转置」练熟,后面所有章节都建立在这套基本功上,练熟之后会轻松很多。

动手练一下:用字典造一张 5 行 3 列的「成绩表」(姓名、科目、分数),试一遍取列、加列、删列、转置,再试试用 pd.concat 加两行数据。

DataFrame 取一列得到 Series,多个 Series 按列拼回去又得到 DataFrame。理解「表 = 一堆列(Series)」这个视角,后面学 groupby、merge 会顺畅很多。