迭代熊猫系列元素的最佳方法
Posted
技术标签:
【中文标题】迭代熊猫系列元素的最佳方法【英文标题】:best way to iterate through elements of pandas Series 【发布时间】:2021-10-10 18:34:13 【问题描述】:以下所有内容似乎都适用于遍历熊猫系列的元素。我相信还有更多的方法可以做到这一点。有什么区别,哪种方法最好?
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])
【问题讨论】:
为什么需要迭代? why you shouldn't useiterrows
的许多论点可能也适用于系列。话虽如此,“最好的方式”是什么?表现?简明?惯用语?
@fsimonjetz,比方说惯用语
如果迭代的目的只是为了打印,那么很难看出如何做真的很重要。您在上面显示的任何方法都可以。如果您正在做一些数值操作,那么我同意@tdy 的回答,即您应该转换为 numpy 数组并对其进行迭代。 FWIW,我也有另一个使用 numpy 循环的问题的答案,这同样适用于您的问题(如果它是数字的):***.com/questions/7837722/…
为什么需要迭代?这几乎总是不必要的,有.apply()
、系列加法和乘法等。你没有展示为什么print()
不是用例的例子。向我们展示一些用例。
【参考方案1】:
TL;DR
Iterating in pandas is an antipattern,通常可以通过矢量化、applying, aggregating, transforming 或cythonizing 来避免。
但是,如果 Series 迭代是绝对必要的,那么性能将取决于 dtype 和 index:
Index | Fastest if numpy dtype | Fastest if pandas dtype | Idiomatic |
---|---|---|---|
Unneeded | in s.to_numpy() |
in s.array |
in s |
Default | in enumerate(s.to_numpy()) |
in enumerate(s.array) |
in s.items() |
Custom | in zip(s.index, s.to_numpy()) |
in s.items() |
in s.items() |
对于基于 numpy 的系列,使用 s.to_numpy()
如果 Series 是 python or numpy dtype,通常迭代底层 numpy ndarray 是最快的:
for el in s.to_numpy(): # if dtype is datetime, int, float, str, string
datetime |
---|
int | float | float + nan | str | string |
---|---|---|---|---|
要访问索引,实际上最快的是enumerate()
或zip()
numpy ndarray:
for i, el in enumerate(s.to_numpy()): # if default range index
for i, el in zip(s.index, s.to_numpy()): # if custom index
两者都比惯用的 s.items()
/ s.iteritems()
快:
datetime + index |
---|
要进行微优化,切换到s.tolist()
以缩短int
/float
/str
系列:
for el in s.to_numpy(): # if >100K elements
for el in s.tolist(): # to micro-optimize if <100K elements
警告:不要使用list(s)
,因为doesn't use compiled code 会变慢。
对于基于 pandas 的系列,请使用 s.array
或 s.items()
Pandas extension dtypes 包含额外的(元)数据,例如:
pandas dtype | contents |
---|---|
Categorical |
2 arrays |
DatetimeTZ |
array + timezone metadata |
Interval |
2 arrays |
Period |
array + frequency metadata |
... | ... |
将这些扩展数组转换为 numpy "may be expensive",因为它可能涉及复制/强制数据,所以:
如果 Series 是 pandas extension dtype,通常迭代底层 pandas 数组是最快的:
for el in s.array: # if dtype is pandas-only extension
例如,具有约 100 个唯一 Categorical
值:
Categorical |
---|
DatetimeTZ | Period | Interval |
---|---|---|
要访问索引,惯用的 s.items()
对于 pandas dtypes 来说非常快:
for i, el in s.items(): # if need index for pandas-only dtype
DatetimeTZ + index | Interval + index | Period + index |
---|---|---|
要进行微优化,为默认索引的Categorical
数组切换到稍快的enumerate()
:
for i, el in enumerate(s.array): # to micro-optimize Categorical dtype if need default range index
Categorical + index |
---|
注意事项
Avoid using s.values
:
s.to_numpy()
获取底层的numpy ndarray
使用s.array
获取底层pandas数组
Avoid modifying the iterated Series:
你应该永远不要修改你正在迭代的东西。这不能保证在所有情况下都有效。根据数据类型,迭代器返回一个副本而不是一个视图,写入它不会有任何效果!
Avoid iterating manually 尽可能改为:
矢量化、(布尔)索引等
Applying functions,例如:
s.apply(some_function)
s.agg(['min', 'max', 'mean'])
s.transform([np.sqrt, np.exp])
注意:尽管存在常见的误解,但它们不是矢量化。
Offloading to cython/numba
规格:ThinkPad X1 Extreme Gen 3(Core i7-10850H 2.70GHz,32GB DDR4 2933MHz)版本:python==3.9.2
, pandas==1.3.1
, numpy==1.20.2
测试数据:sn-p 中的系列生成代码
'''
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'))
【讨论】:
【参考方案2】:使用items
:
for i, v in arr.items():
print(f'index: i and value: v')
输出:
index: 0 and value: 1
index: 1 and value: 1
index: 2 and value: 1
index: 3 and value: 2
index: 4 and value: 2
index: 5 and value: 2
index: 6 and value: 3
index: 7 and value: 3
【讨论】:
【参考方案3】:测试结果如下:循环的执行速度最慢。 iterrows()针对pandas的dataframe进行了优化,相比直接循环有明显提升。 apply() 方法也在行之间循环,但它比 iterrows 高效得多,因为使用了 python 等迭代器进行了一系列全局优化。 numpy 数组的向量化运行最快,其次是 pandas 系列的向量化。由于矢量化同时作用于整个序列,因此可以节省更多时间。 Numpy在底层使用预编译的C代码进行优化,避免了pandas系列操作中的大量开销。所以numpy数组的运算速度比pandas系列快很多。
loop: 1.80301690102
iterrows: 0.724927186966
apply: 0.645957946777
pandas series: 0.333024024963
numpy array: 0.260366916656
列表循环 > numpy 数组 > pandas 系列 > 应用 > iterrows
【讨论】:
您说的是迭代数据帧(系列甚至没有.iterrows()
方法)。数据框迭代已在许多其他 SO 帖子中介绍。【参考方案4】:
遍历 pandas/python 的方法
arr = pandas.Series([1, 1, 1, 2, 2, 2, 3, 3])
#Using Python range() method
for i in range(len(arr)):
print(arr[i])
范围不包括序列中的结束值
#List Comprehension
print([arr[i] for i in range(len(arr))])
列表推导可以使用并且可以识别输入是列表、字符串还是元组
#Using Python enumerate() method
for el,j in enumerate(arr):
print(j)
#Using Python NumPy module
import numpy as np
print(np.arange(len(arr)))
for i,j in np.ndenumerate(arr):
print(j)
enumerate 使用非常广泛,因为 enumerate 将计数器添加到列表或任何其他可迭代对象,并由函数将其作为枚举对象返回。它减少了在迭代操作时保持元素计数的开销。这里不需要柜台。您可以使用 np.ndenumerate() 来模拟 numpy 数组的枚举行为。对于非常大的 n 维列表,建议使用 numpy。
您还使用传统的 for 循环和 while 循环
x=0
while x<len(arr):
print(arr[x])
x +=1
#Using lambda function
list(map(lambda x:x, arr))
lambda 减少了代码行数,可与侧过滤器、reduce 或 map 一起使用。
如果您想遍历数据框的行而不是系列,我们可以使用 iterrows、itertuple 和 iteritems。就内存和计算而言,最好的方法是将列用作向量并使用 numpy 数组执行向量计算。当涉及到大数据时,循环非常昂贵。当您将它们制作成 numpy 数组并对其进行处理时,它会更容易和更快。
【讨论】:
【参考方案5】:我认为,更重要的是了解对化妆品的要求,同时寻找针对个人要求的解决方案。
在我看来,除非我们正在处理的数据量很大,否则成本不会太高,在这种情况下,我们必须在我们的方法中有所选择,对于小数据集,无论哪种方法都可以,如下所述..
PEP 469、PEP 3106和Views And Iterators Instead Of Lists中有很好的解释
在 Python 3 中,只有一个名为 items() 的方法。它使用迭代器,因此速度很快,并且允许在编辑时遍历字典。请注意,方法 iteritems() 已从 Python 3 中删除。
可以查看 Python3 Wiki Built-In_Changes 以获取更多详细信息。
arr = pandas.Series([1, 1, 1, 2, 2, 2, 3, 3])
$ for index, value in arr.items():
print(f"Index : index, Value : value")
Index : 0, Value : 1
Index : 1, Value : 1
Index : 2, Value : 1
Index : 3, Value : 2
Index : 4, Value : 2
Index : 5, Value : 2
Index : 6, Value : 3
Index : 7, Value : 3
$ for index, value in arr.iteritems():
print(f"Index : index, Value : value")
Index : 0, Value : 1
Index : 1, Value : 1
Index : 2, Value : 1
Index : 3, Value : 2
Index : 4, Value : 2
Index : 5, Value : 2
Index : 6, Value : 3
Index : 7, Value : 3
$ for _, value in arr.iteritems():
print(f"Index : index, Value : value")
Index : 7, Value : 1
Index : 7, Value : 1
Index : 7, Value : 1
Index : 7, Value : 2
Index : 7, Value : 2
Index : 7, Value : 2
Index : 7, Value : 3
Index : 7, Value : 3
$ for i, v in enumerate(arr):
print(f"Index : i, Value : v")
Index : 0, Value : 1
Index : 1, Value : 1
Index : 2, Value : 1
Index : 3, Value : 2
Index : 4, Value : 2
Index : 5, Value : 2
Index : 6, Value : 3
Index : 7, Value : 3
$ for value in arr:
print(value)
1
1
1
2
2
2
3
3
$ for value in arr.tolist():
print(value)
1
1
1
2
2
2
3
3
有一篇关于How to iterate over rows in a DataFrame in Pandas 的好帖子虽然说的是df,但它解释了所有关于item()
、iteritems()
等的内容。
关于 SO items & iteritems 的另一个很好的讨论。
【讨论】:
【参考方案6】:对于向量编程(pandas、R、octave、..),建议不要对向量进行迭代。相反,请使用库提供的映射函数来应用系列或数据集。
在您将打印功能应用于每个元素的情况下,代码将是:
import pandas
arr = pandas.Series([1, 1, 1, 2, 2, 2, 3, 3])
arr.apply(print)
【讨论】:
以上是关于迭代熊猫系列元素的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章