在数据处理的真实场景中,原始数据往往不会一开始就以整齐的表格形态出现。它们可能来自各种源头:如列表(list)、字典(dict)、CSV/Excel文件,或是程序运行时临时生成的数据集合。如果这些数据没有被整理成一个结构清晰的表格对象,后续的数据选取、清洗、统计和可视化等操作就失去了统一的基础。
Pandas 的核心任务之一,正是将这些原始数据组织为可计算、可检查、可分析的标准表格结构。其最重要的数据结构就是 DataFrame。
本文将以一张学生成绩表作为贯穿始终的示例,详细讲解如何将原始数据一步步整理成 DataFrame,并在此基础上,带你认识它的基本结构、内容查看方式以及常见的完整性检查方法。
例如,我们希望最终得到的学生成绩表是这样的:
| 学号 |
姓名 |
班级 |
语文 |
数学 |
英语 |
| S001 |
张三 |
C01 |
88 |
92 |
85 |
| S002 |
李四 |
C01 |
76 |
81 |
79 |
| S003 |
王五 |
C02 |
90 |
87 |
93 |
| S004 |
赵六 |
C02 |
85 |
89 |
88 |
| S005 |
孙七 |
C03 |
91 |
95 |
90 |
| S006 |
周八 |
C03 |
78 |
84 |
80 |
下面,我们就围绕构建这样一张表,来学习如何在 Pandas 中创建 DataFrame。
为什么需要表格数据?
在 Python 中,列表、字典、元组这些原生数据结构都可以用来保存数据。但当数据本身具有明确的行列结构时(比如学生成绩),继续用这些通用容器来组织,对于后续的分析工作就很不方便。
以学生成绩数据为例,这类数据通常具有几个鲜明特点:
- 每一行对应一个对象(一名学生)
- 每一列对应一个字段(如姓名、数学成绩)
- 不同对象在相同字段上具有可比较性(比如所有人的数学成绩可以一起比较)
当数据还停留在原始列表或字典的层面时,行列关系、字段含义和结构边界往往不够清晰。这时数据虽然被保存了,但还不能称之为“已整理”的表格形式。
Pandas 中几乎所有高阶操作——数据选取、筛选、清洗、统计与合并——都以结构清晰的表格为基础。因此,学习 Pandas 的正确起点,不是立刻去做复杂的分析,而是先把你的原始数据整理成 DataFrame。
认识 DataFrame 和 Series
Pandas 有两个核心的数据结构:
DataFrame 用于表示二维表格数据,而 Series 用于表示一维的带标签数据。
1. DataFrame
DataFrame 是 Pandas 提供的二维带标签数据结构。它既包含二维数据本身,也包含了用于标识每一行和每一列的标签信息。
我们先尝试将学生成绩的原始数据整理成一个 DataFrame,并命名为 scores:
import pandas as pd
data = [
{"学号": "S001", "姓名": "张三", "班级": "C01", "语文": 88, "数学": 92, "英语": 85},
{"学号": "S002", "姓名": "李四", "班级": "C01", "语文": 76, "数学": 81, "英语": 79},
{"学号": "S003", "姓名": "王五", "班级": "C02", "语文": 90, "数学": 87, "英语": 93},
{"学号": "S004", "姓名": "赵六", "班级": "C02", "语文": 85, "数学": 89, "英语": 88},
{"学号": "S005", "姓名": "孙七", "班级": "C03", "语文": 91, "数学": 95, "英语": 90},
{"学号": "S006", "姓名": "周八", "班级": "C03", "语文": 78, “数学”: 84, “英语”: 80}
]
scores = pd.DataFrame(data)
print(scores)
输出:
学号 姓名 班级 语文 数学 英语
0 S001 张三 C01 88 92 85
1 S002 李四 C01 76 81 79
2 S003 王五 C02 90 87 93
3 S004 赵六 C02 85 89 88
4 S005 孙七 C03 91 95 90
5 S006 周八 C03 78 84 80
从结构上看,我们可以这样理解它:
| 行索引 |
学号 |
姓名 |
班级 |
语文 |
数学 |
英语 |
| 0 |
S001 |
张三 |
C01 |
88 |
92 |
85 |
| 1 |
S002 |
李四 |
C01 |
76 |
81 |
79 |
| 2 |
S003 |
王五 |
C02 |
90 |
87 |
93 |
| 3 |
S004 |
赵六 |
C02 |
85 |
89 |
88 |
| 4 |
S005 |
孙七 |
C03 |
91 |
95 |
90 |
| 5 |
S006 |
周八 |
C03 |
78 |
84 |
80 |
在这张结构表中:
- 最左侧一列是行索引
- 第一行中的“学号、姓名、班级、语文、数学、英语”是列标签
- 中间区域是数据值
所以,DataFrame 可以概括为:由数据值、行索引和列标签共同构成的二维表格对象。
2. Series
Series 是 Pandas 提供的一维带标签数据结构。
如果把 DataFrame 看作一整张表,那么表中的任何一列数据,单独拿出来通常就是一个 Series。例如,我们取出 scores 表中的“数学”列:
math_scores = scores["数学"]
print(math_scores)
输出:
0 92
1 81
2 87
3 89
4 95
5 84
Name: 数学, dtype: int64
可以看到,Series 并非单纯的一列数值,而是“一列值 + 对应的行索引标签”的组合。它同样拥有自己的名称(Name)和数据类型(dtype)。
因此,两者的关系可以概括为:
Series 是一维的。
DataFrame 是二维的。
DataFrame 中的单列(或多列)数据可以视为一个(或多个)Series。
如何构建一个 DataFrame?
在 Pandas 中,最常用的构建方法是 pd.DataFrame()。构建一张表时,我们通常需要明确三件事:
- 数据值:表格里具体是什么数字或文字。
- 行的组织方式:数据是按行组织还是按列组织。
- 列标签的名称:每一列叫什么名字。
根据原始数据的形态,最常见的构建方式有三种。
方式一:由“值为列表的字典”构建
如果你的数据已经自然地按列组织好了,那么字典是最直观的输入形式。字典的键(key)会自动成为列标签,各个键对应的值(必须是长度一致的列表)会成为各列的数据。
示例:
import pandas as pd
data = {
"学号": ["S001", "S002", "S003", "S004", "S005", "S006"],
"姓名": ["张三", "李四", "王五", "赵六", "孙七", "周八"],
"班级": ["C01", "C01", "C02", "C02", "C03", "C03"],
"语文": [88, 76, 90, 85, 91, 78],
"数学": [92, 81, 87, 89, 95, 84],
"英语": [85, 79, 93, 88, 90, 80]
}
scores = pd.DataFrame(data)
print(scores)
如果你想显式控制列的顺序,可以指定 columns 参数:
scores = pd.DataFrame(
data,
columns=["学号", "姓名", "班级", "语文", "数学", "英语"]
)
print(scores)
方式二:由“列表嵌套列表”构建
如果你的数据是按行组织的,即每一个内部列表代表一行数据,那么可以使用这种形式。
data = [
["S001", "张三", "C01", 88, 92, 85],
["S002", "李四", "C01", 76, 81, 79],
["S003", "王五", "C02", 90, 87, 93],
["S004", "赵六", "C02", 85, 89, 88],
["S005", "孙七", "C03", 91, 95, 90],
["S006", "周八", "C03", 78, 84, 80]
]
scores = pd.DataFrame(
data,
columns=["学号", "姓名", "班级", "语文", "数学", "英语"]
)
print(scores)
注意:当输入是二维列表时,Pandas 无法自动知道每一列的含义,因此必须通过 columns 参数主动提供列名,否则列标签会显示为默认的整数索引(0, 1, 2...)。
方式三:由“字典列表”构建
如果你的原始数据中,每一条记录(一个学生的信息)本身就是一个字典,那么直接使用字典列表来构建 DataFrame 最为方便。这种形式在 JSON 数据或从 API 接口获取的数据中非常常见。
本文开头的第一个示例采用的就是这种方式。
如何选择构建方式?
没有绝对最好的方式,选择应该顺应你原始数据的组织逻辑:
- 数据天然按列组织 → 用“值为列表的字典”。
- 数据天然按行组织 → 用“列表嵌套列表”。
- 数据是一条条记录/对象 → 用“字典列表”。
理解表格的行、列与标签
把数据变成 DataFrame 之后,第一件事就是准确理解它的结构。从表格的语义来看,一张 DataFrame 包含三个核心层面:行、列和标签。
1. 行
在学生成绩表中,每一行对应一名学生的所有数据。例如,第 0 行是学生“张三”的全部信息。
在表格数据中,一行通常代表一个对象、一条记录或一次观察结果。
2. 列
列对应的是对象的属性或指标。在我们的表里,就是“学号”、“姓名”、“班级”、“语文”、“数学”、“英语”。
后续的分析工作几乎都是围绕列展开的,比如计算数学平均分、按班级筛选学生等。
3. 行索引
行索引就是表格最左边的那一列,用于定位和标识每一行。
print(scores.index)
输出(示意):
RangeIndex(start=0, stop=6, step=1)
上面的例子中,Pandas 自动生成了从 0 开始的默认整数索引。
这里要分清:“行”是数据本身,而“行索引”是用来标记这些数据的标签。
4. 列标签
列标签就是表格顶部的列名。
print(scores.columns)
输出(示意):
Index(['学号', '姓名', '班级', '语文', '数学', '英语'], dtype='object')
清晰、明确的列名是后续所有数据操作的基础。
5. 注意:业务ID与索引的区别
在我们的表里,“学号”是具有明确业务含义、能唯一标识学生的字段。但它目前只是表中的一列普通数据,并不等同于行索引。
索引是 DataFrame 内部的结构标签,而“学号”是业务数据字段。两者可以相同,也可以不同。很多时候,我们会把“学号”这样的字段设置为新的索引,以便快速查询,但那属于更进阶的操作。
初步检查:怎么看懂你刚创建的表?
DataFrame 创建好后,不要急着做复杂操作。先按“看内容 → 看结构 → 看类型”的顺序做一遍初步检查,确保数据加载正确。
1. 查看表格内容
如果数据量很小,可以直接打印整个 DataFrame:
print(scores)
但对于可能很大的数据集,更明智的做法是使用 head() 或 tail() 方法预览一部分。
2. 查看表格整体结构
了解内容后,再从整体上把握这张表。
3. 查看数据类型与基本信息
这是确保后续计算能顺利进行的关键一步。我们需要确认每一列的数据类型是否符合预期(例如,成绩应该是数字,而不是文本)。
-
快速概览:info() 方法能提供一份非常全面的报告,包括行数、每列的非空值数量、数据类型以及内存占用。
scores.info()
这个方法特别适合用来快速发现缺失值和错误的数据类型。
-
仅看数据类型:如果只关心类型,可以使用 dtypes 属性。
print(scores.dtypes)
输出类似:
学号 object
姓名 object
班级 object
语文 int64
数学 int64
英语 int64
dtype: object
这表明“学号”、“姓名”、“班级”三列是对象类型(在 Pandas 中通常指字符串),而三门成绩都是整数类型,这完全符合我们的业务逻辑。
完整实战示例
让我们把上面的步骤串起来,形成一个从数据到初步检查的完整流程:
import pandas as pd
# 1. 原始数据(字典列表格式)
students = [
{"学号": "S001", "姓名": "张三", "班级": "C01", "语文": 88, "数学": 92, "英语": 85},
{"学号": "S002", "姓名": "李四", "班级": "C01", "语文": 76, "数学": 81, "英语": 79},
{"学号": "S003", "姓名": "王五", "班级": "C02", "语文": 90, "数学": 87, "英语": 93},
{"学号": "S004", "姓名": "赵六", "班级": "C02", "语文": 85, “数学”: 89, “英语”: 88},
{"学号": "S005", "姓名": "孙七", "班级": "C03", "语文": 91, “数学”: 95, “英语”: 90},
{"学号": "S006", "姓名": "周八", "班级": "C03", "语文": 78, “数学”: 84, “英语”: 80}
]
# 2. 构建 DataFrame
scores = pd.DataFrame(students)
# 3. 初步检查流程
print("===== 预览前几行 =====")
print(scores.head())
print("\n===== 预览后几行 =====")
print(scores.tail())
print("\n===== 表格形状(行,列) =====")
print(scores.shape)
print("\n===== 所有列名 =====")
print(scores.columns)
print("\n===== 行索引 =====")
print(scores.index)
print("\n===== 各列数据类型 =====")
print(scores.dtypes)
print("\n===== 详细信息(含缺失值检查) =====")
scores.info()
这个示例清晰地展示了一个标准的起始工作流:
- 整理:将原始数据转化为
DataFrame。
- 查看:快速浏览数据内容,确认无误。
- 检查:核实表格结构和数据类型,为下一步分析扫清障碍。
完成这些步骤后,你的数据就正式进入了规整的、可供 Pandas 进一步处理的“表格阶段”。
总结
DataFrame 是 Pandas 进行数据分析的基石。将杂乱无章的原始数据成功组织成一个结构清晰的 DataFrame,是所有后续数据操作(筛选、清洗、统计、可视化)不可逾越的第一步。本文通过一个具体的成绩表示例,详细介绍了 DataFrame 与 Series 的概念、三种最常用的构建方法,以及创建完成后必须进行的初步检查步骤。
掌握这些基础内容,就像学会了如何把一堆散乱的积木整理到正确的盒子里,接下来无论你想搭建什么,都有了可靠的材料基础。希望这篇入门指南能帮助你在 Python 和 Pandas 的数据处理之路上迈出坚实的第一步。如果你想了解更多关于 Python 数据处理的知识,欢迎在云栈社区与更多开发者交流探讨。