迭代熊猫系列元素的最佳方式
以下所有内容似乎都适用于迭代熊猫系列的元素。我相信有更多的方法可以做到这一点。有什么区别,哪种方法最好?
import pandas
arr = pandas.Series([1, 1, 1, 2, 2, 2, 3, 3])
# 1
for el in arr:
print(el)
# 2
for _, el in arr.iteritems():
print(el)
# 3
for el in arr.array:
print(el)
# 4
for el in arr.values:
print(el)
# 5
for i in range(len(arr)):
print(arr.iloc[i])
回答
TL; 博士
在Pandas 中迭代是一种反模式,通常可以通过向量化、应用、聚合、转换或cythonizing来避免。
但是,如果绝对需要系列迭代,则性能将取决于 dtype 和索引:
| 指数 | 如果numpy dtype最快 | 如果pandas dtype最快 | 惯用语 |
|---|---|---|---|
| 不需要 | in s.to_numpy() |
in s.array |
in s |
| 默认 | in enumerate(s.to_numpy()) |
in enumerate(s.array) |
in s.items() |
| 风俗 | in zip(s.index, s.to_numpy()) |
in s.items() |
in s.items() |
对于基于 numpy 的系列,请使用 s.to_numpy()
-
如果 Series 是python 或numpy dtype ,迭代底层 numpy ndarray 通常是最快的:
for el in s.to_numpy(): # if dtype is datetime, int, float, str, string约会时间 整数 漂浮 浮动 + 南 字符串 细绳 -
要访问索引,实际上最快的是
enumerate()或zip()numpy ndarray:for i, el in enumerate(s.to_numpy()): # if default range indexfor i, el in zip(s.index, s.to_numpy()): # if custom index两者都比惯用的
s.items()/快s.iteritems():日期时间 + 索引 -
微优化,切换到
s.tolist()更短int/float/str系列:for el in s.to_numpy(): # if >100K elementsfor el in s.tolist(): # to micro-optimize if <100K elements警告:不要使用,
list(s)因为它不使用编译代码,这会使它变慢。
对于基于熊猫的系列,请使用s.array或s.items()
Pandas 扩展 dtypes包含额外的(元)数据,例如:
| 大熊猫数据类型 | 内容 |
|---|---|
Categorical |
2个阵列 |
DatetimeTZ |
数组 + 时区元数据 |
Interval |
2个阵列 |
Period |
数组 + 频率元数据 |
| ... | ... |
将这些扩展数组转换为 numpy “可能很昂贵”,因为它可能涉及复制/强制数据,因此:
-
如果 Series 是Pandas 扩展 dtype,则迭代底层 Pandas 数组通常是最快的:
for el in s.array: # if dtype is pandas-only extension例如,有 ~100 个唯一
Categorical值:分类的 日期时间TZ 时期 间隔 -
要访问索引,
s.items()pandas dtypes的惯用语非常快:for i, el in s.items(): # if need index for pandas-only dtype日期时间TZ + 索引 间隔 + 索引 期间+索引 -
要进行微优化,请切换到
enumerate()默认索引Categorical数组的稍微快一点:for i, el in enumerate(s.array): # to micro-optimize Categorical dtype if need default range index分类+索引
注意事项
-
避免使用
s.values:- 使用
s.to_numpy()得到根本numpy的ndarray - 用
s.array获得的潜在大熊猫阵列
- 使用
-
避免修改迭代的 Series:
你永远不应该修改你正在迭代的东西。这不能保证在所有情况下都有效。根据数据类型,迭代器返回一个副本而不是一个视图,写入它没有任何效果!
-
尽可能避免手动迭代:
-
向量化、(布尔)索引等。
-
应用函数,例如:
s.apply(some_function)s.agg(['min', 'max', 'mean'])s.transform([np.sqrt, np.exp])
注意:这些是不是尽管常见的误解vectorizations。
-
卸载到 cython/numba
-
规格:的ThinkPad X1极端创3(核心i7-10850H 2.70GHz,32GB DDR4 2933MHz)
版本:python==3.9.2,pandas==1.3.1,numpy==1.20.2
测试数据:系列生成代码段中的代码
'''
Note: This is python code in a js snippet, so "run code snippet" will not work.
The snippet is just to avoid cluttering the main post with supplemental code.
'''
import pandas as pd
import numpy as np
int_series = pd.Series(np.random.randint(1000000000, size=n))
float_series = pd.Series(np.random.randn(size=n))
floatnan_series = pd.Series(np.random.choice([np.nan, np.inf]*n + np.random.randn(n).tolist(), size=n))
str_series = pd.Series(np.random.randint(10000000000000000, size=n)).astype(str)
string_series = pd.Series(np.random.randint(10000000000000000, size=n)).astype('string')
datetime_series = pd.Series(np.random.choice(pd.date_range('2000-01-01', '2021-01-01'), size=n))
datetimetz_series = pd.Series(np.random.choice(pd.date_range('2000-01-01', '2021-01-01', tz='CET'), size=n))
categorical_series = pd.Series(np.random.randint(100, size=n)).astype('category')
interval_series = pd.Series(pd.arrays.IntervalArray.from_arrays(-np.random.random(size=n), np.random.random(size=n)))
period_series = pd.Series(pd.period_range(end='2021-01-01', periods=n, freq='s'))