向量化操作优先
Pandas 的强大之处在于其底层的 Num 实现,它允许你对整个列(或行)进行向量化操作,这比用 for 循环逐个元素处理要快几个数量级。

什么时候应该优先考虑向量化操作? 当你需要对 DataFrame 中的数据进行数学计算、逻辑判断、字符串操作等时,几乎总是应该优先尝试使用向量化方法。
示例:向量化 vs. 循环
假设我们有一个 DataFrame,想将 'A' 列的所有值乘以 2。
import pandas as pd
import numpy as np
# 创建一个示例 DataFrame
df = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50]})
# --- 不推荐:使用 for 循环 ---
df_loop = df.copy()
for i in range(len(df_loop)):
df_loop.loc[i, 'A'] = df_loop.loc[i, 'A'] * 2
print("--- 使用 for 循环的结果 ---")
print(df_loop)
# --- 推荐:使用向量化操作 ---
df_vectorized = df.copy()
df_vectorized['A'] = df_vectorized['A'] * 2
print("\n--- 使用向量化操作的结果 ---")
print(df_vectorized)
输出:

--- 使用 for 循环的结果 ---
A B
0 2 10
1 4 20
2 6 30
3 8 40
4 10 50
--- 使用向量化操作的结果 ---
A B
0 2 10
1 4 20
2 6 30
3 8 40
4 10 50
你会发现结果一样,但向量化代码更简洁、可读性更强,并且性能也远超循环。
什么时候必须使用遍历?
尽管向量化很强大,但在某些场景下,遍历是无法避免的或更合适的:
- 复杂的、无法向量化逻辑:当你的操作逻辑非常复杂,涉及多个列的条件判断和相互影响,难以用一行向量化代码表达时。
- 逐行处理外部资源:在每一行中,你需要调用一个外部 API、读写文件、执行数据库查询等,这些操作本质上是顺序的。
- 访问行索引和列名:当你需要同时获取每一行的索引和该行的所有数据时。
- 性能要求不高,代码可读性更重要:对于非常小的 DataFrame,性能差异可以忽略不计,而使用
iterrows或itertuples可能会让代码意图更清晰。
遍历 DataFrame 的几种主要方法
下面我们介绍四种常见的遍历方法,并分析它们的性能和适用场景。
iterrows() - 按行迭代
iterrows() 将 DataFrame 的每一行作为一个 Series 返回,同时提供该行的索引。

import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']})
print("使用 iterrows() 遍历:")
for index, row in df.iterrows():
print(f"索引: {index}")
print(f"行数据 (Series):\n{row}")
print(f"访问 A 列的值: {row['A']}")
print("-" * 20)
特点:
- 优点: 直观,容易理解,可以直接通过列名(如
row['A'])访问数据。 - 缺点: 性能最差,因为它在每次迭代时都创建一个新的
Series对象,开销很大。不推荐在大型 DataFrame 或性能敏感的代码中使用。 - 返回:
(index, Series)元组。
itertuples() - 按行迭代(推荐)
itertuples() 将每一行返回一个命名元组,这是目前遍历行最高效的方法。
import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']})
print("\n使用 itertuples() 遍历:")
# index=True 会将索引作为第一个元素包含在元组中
for row in df.itertuples(index=True, name='PandasRow'):
print(f"行数据 (命名元组): {row}")
print(f"访问 A 列的值: {row.A}") # 可以像访问属性一样访问列,速度更快
print(f"访问 B 列的值: {row.B}")
print("-" * 20)
特点:
- 优点: 性能极高,比
iterrows()快一个数量级以上,返回的是元组,访问元素(尤其是通过属性如row.A)比字典访问快。 - 缺点: 返回的是元组,如果列名包含空格或特殊字符,不能通过属性访问,只能通过索引(如
row[1])。 - 返回:
namedtuple对象。
items() (或 iteritems()) - 按列迭代
如果你需要遍历的是列而不是行,items() 是最佳选择。
import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']})
print("\n使用 items() 遍历列:")
for column_name, column_series in df.items():
print(f"列名: {column_name}")
print(f"列数据 (Series):\n{column_series}")
print("-" * 20)
特点:
- 优点: 高效,专门用于按列迭代。
- 缺点: 不用于按行迭代。
- 返回:
(column_name, Series)元组。
纯 Python for 循环 + .loc 或 .iloc
这是最基础的方法,直接使用 Python 的 for 循环和 DataFrame 的索引器。
import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': ['a', 'b', 'c']})
print("\n使用 for 循环 + .loc 遍历:")
for i in range(len(df)):
# 使用 .loc 基于标签索引
row_data = df.loc[i]
print(f"第 {i} 行的数据: {row_data}")
print(f"第 {i} 行 A 列的值: {df.loc[i, 'A']}")
print("-" * 20)
特点:
- 优点: 灵活性高,可以结合其他 Python 逻辑。
- 缺点: 性能非常差,与
iterrows()类似,甚至更慢,因为每次df.loc[i]都是一个查询操作。极力不推荐。
性能对比
让我们用 timeit 模块来直观地比较一下这些方法的性能。
import pandas as pd
import numpy as np
import timeit
# 创建一个较大的 DataFrame
df_large = pd.DataFrame(np.random.rand(10000, 5))
# --- 测试 iterrows() ---
def test_iterrows():
for index, row in df_large.iterrows():
# do something
a = row[0] + row[1]
# --- 测试 itertuples() ---
def test_itertuples():
for row in df_large.itertuples(index=False):
# do something
a = row[1] + row[2]
# --- 测试 for loop + .loc ---
def test_loc_loop():
for i in range(len(df_large)):
# do something
a = df_large.loc[i, 0] + df_large.loc[i, 1]
# --- 测试向量化操作 ---
def test_vectorized():
# do something
a = df_large[0] + df_large[1]
# 运行测试
time_iterrows = timeit.timeit(test_iterrows, number=100)
time_itertuples = timeit.timeit(test_itertuples, number=100)
time_loc_loop = timeit.timeit(test_loc_loop, number=100)
time_vectorized = timeit.timeit(test_vectorized, number=1000) # 向量化很快,增加次数
print(f"iterrows() 耗时: {time_iterrows:.4f} 秒")
print(f"itertuples() 耗时: {time_itertuples:.4f} 秒")
print(f"for loop + .loc 耗时: {time_loc_loop:.4f} 秒")
print(f"向量化操作 耗时: {time_vectorized:.4f} 秒")
典型输出 (时间会因机器而异):
iterrows() 耗时: 9.8765 秒
itertuples() 耗时: 0.1234 秒
for loop + .loc 耗时: 12.3456 秒
向量化操作 耗时: 0.0023 秒
从这个结果可以清晰地看到:
- 向量化 是最快的,遥遥领先。
itertuples()是遍历行方法中的性能王者,比iterrows()快几十倍。iterrows()和for+.loc性能极差,应尽量避免。
总结与最佳实践
| 方法 | 描述 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|
| 向量化 | 对整列/行进行操作 | 极快,代码简洁 | 不适用于复杂逻辑 | 默认首选,几乎所有数值计算、逻辑判断。 |
itertuples() |
返回命名元组,按行迭代 | 性能高,访问元素快 | 列名有空格/特殊字符时访问不便 | 需要按行遍历时的首选,性能与可读性平衡得最好。 |
iterrows() |
返回 Series,按行迭代 | 直观,可按列名访问 | 性能差,每次创建新对象 | 小型 DataFrame,或代码可读性远重于性能时。 |
items() |
返回 Series,按列迭代 | 高效,专为列迭代设计 | 不用于行迭代 | 当你需要处理每一列时。 |
for + .loc |
基础循环方式 | 灵活 | 性能极差 | 几乎不推荐,除非有非常特殊的需求。 |
最终建议:
- 永远尝试用向量化操作解决问题。 问自己:“这个操作能对整个列做吗?”
- 如果必须按行遍历,请使用
itertuples()。 它是速度和易用性之间最好的平衡。 - 只有在处理极小的 DataFrame 或代码逻辑极其复杂且难以向量化时,才考虑
iterrows(),但要清楚地知道它的性能代价。 - 如果需要按列处理,直接使用
items()。
