我有一个代码,看起来像
for index, row in data.iterrows():
data.loc[index, "myCol"] = someFunc(data.loc[index-1, "myCol")
该代码正在使用递归,因此我无法对其进行矢量化,因为在许多与 iterrows 性能相关的问题中都建议使用递归。
我在性能方面优化它的最佳方法是什么?
虽然 Pandas 的
iterrows
方法为循环遍历 DataFrame 提供了一种直观的方式,但当涉及到性能时,它并不是最有效的方法,尤其是在大型数据集上。你的代码似乎递归地更新“myCol”列,其中每个值都取决于其前一个值。这种模式确实限制了矢量化(Pandas 中基于数组的操作,速度要快得多)的机会。
让我们探讨一下在保持递归行为的同时优化代码的一些策略:
-
使用
shift()
进行高效滞后计算:
Pandas 的
shift()
方法非常适合访问 DataFrame 中先前行的值,这使其成为递归模式的理想选择。你可以通过以下方式重构代码以利用它:
python
data['myCol_shifted'] = data['myCol'].shift(1)
data['myCol'] = data.apply(lambda row: someFunc(row['myCol_shifted']) if not pd.isnull(row['myCol_shifted']) else row['myCol'], axis=1)
data = data.drop('myCol_shifted', axis=1)
在这个解决方案中,我们首先创建
myCol
的移位副本。然后,我们使用
apply
和 lambda 函数,如果存在先前值(非空),则有条件地应用
someFunc
。这确保了递归计算的正确性。最后,我们删除辅助移位列。
-
使用
itertuples()
进行更快的行迭代(如果shift()
不适用):
如果由于
someFunc
的复杂性或你的数据的特定特征而导致
shift()
方法不可行,那么与
iterrows
相比,
itertuples()
提供了一个更快的 DataFrame 迭代选项。
itertuples
以元组的形式迭代 DataFrame 的行,提供对列值的更快访问。
python
for i in range(1, len(data)):
data.loc[i, "myCol"] = someFunc(data.loc[i-1, "myCol"])
- 使用 Numba JIT 编译(用于潜在的显著加速):
如果你的函数
someFunc
可以使用 Numba 进行编译,你就可以获得显著的性能提升,尤其是对于大型数据集。Numba 可以对 Python 代码进行 JIT 编译,包括循环和递归函数,使它们能够以接近本机的速度运行。
```python from numba import jit
@jit(nopython=True) def someFunc(x): # someFunc 的实现 return x
for i in range(1, len(data)): data.loc[i, "myCol"] = someFunc(data.loc[i-1, "myCol"]) ```
使用
@jit(nopython=True)
装饰器,Numba 尝试在不依赖 Python 解释器的情况下编译该函数。这对于数值运算密集型任务非常有效。
- 探索 Cython 实现(用于高级优化):
对于性能至关重要的场景,考虑使用 Cython 实现你的递归逻辑。Cython 允许你将 Python 代码与 C 数据类型混合,从而实现更接近本机代码的性能。
请记住,最佳方法取决于你的数据的具体特征、
someFunc
的复杂性以及你的性能需求。对这些选项进行基准测试以确定哪种方法最适合你的用例非常重要。