pandas iterrows 有性能问题吗?

2024-11-25 08:50:00
admin
原创
197
摘要:问题描述:我注意到使用 pandas 的 iterrows 时性能非常差。它是特定于 iterrows 的吗?对于特定大小的数据(我正在处理 200 万到 300 万行)是否应该避免使用该函数?GitHub 上的这个讨论让我相信这是由于在数据框中混合使用 dtypes 导致的,但是下面的简单示例显示即使使用一...

问题描述:

我注意到使用 pandas 的 iterrows 时性能非常差。

它是特定于 iterrows 的吗?对于特定大小的数据(我正在处理 200 万到 300 万行)是否应该避免使用该函数?

GitHub 上的这个讨论让我相信这是由于在数据框中混合使用 dtypes 导致的,但是下面的简单示例显示即使使用一种 dtype (float64) 也会出现这种情况。这在我的计算机上需要 36 秒:

import pandas as pd
import numpy as np
import time

s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})

start = time.time()
i=0
for rowindex, row in dfa.iterrows():
    i+=1
end = time.time()
print end - start

为什么像 apply 这样的矢量化操作会快得多?我想那里肯定也有一些逐行迭代。

我无法弄清楚如何在我的情况下不使用 iterrows(我将留到以后再问)。因此,如果您一直能够避免这种迭代,我将不胜感激。我正在根据单独数据框中的数据进行计算。

我想要运行的简化版本:

import pandas as pd
import numpy as np

#%% Create the original tables
t1 = {'letter':['a','b'],
      'number1':[50,-10]}

t2 = {'letter':['a','a','b','b'],
      'number2':[0.2,0.5,0.1,0.4]}

table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)

#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=[0])

#%% Iterate through filtering relevant data, optimizing, returning info
for row_index, row in table1.iterrows():
    t2info = table2[table2.letter == row['letter']].reset_index()
    table3.ix[row_index,] = optimize(t2info,row['number1'])

#%% Define optimization
def optimize(t2info, t1info):
    calculation = []
    for index, r in t2info.iterrows():
        calculation.append(r['number2']*t1info)
    maxrow = calculation.index(max(calculation))
    return t2info.ix[maxrow]

解决方案 1:

一般来说,iterrows只应在非常特殊的情况下使用。这是执行各种操作的一般优先顺序:

  1. 矢量化

  2. 使用自定义 Cython 例程

  3. 申请

    • 可以在 Cython 中执行的减少

    • Python 空间中的迭代

  4. 迭代元组

  5. 迭代

  6. 更新空框架(例如,一次使用 loc 一行)

使用自定义 Cython 例程通常过于复杂,所以我们暂时跳过它。

  1. 向量化始终是首选,也是最佳选择。但是,有一小部分情况(通常涉及递归)无法以明显的方式进行向量化。此外,在较小的情况下DataFrame,使用其他方法可能会更快。

  2. apply 通常可以由 Cython 空间中的迭代器处理。这由 pandas 内部处理,但这取决于表达式内部的情况apply。例如,df.apply(lambda x: np.sum(x))将非常迅速地执行,当然,df.sum(1)甚至更好。然而,像df.apply(lambda x: x['b'] + 1)将在 Python 空间中执行,因此速度要慢得多。

  3. itertuples不会将数据装入框中Series。它只是以元组的形式返回数据。

  4. iterrows 数据装入框中Series。除非您确实需要这样做,否则请使用其他方法。

  5. 一次一行地更新空框架。我见过太多人使用这种方法。这是迄今为止最慢的方法。这可能是常见的方法(对于某些 Python 结构来说速度相当快),但对DataFrame索引进行了相当多的检查,因此每次更新一行总是非常慢。创建新结构和 会更好concat

解决方案 2:

Numpy 和 pandas 中的矢量运算比原始 Python 中的标量运算快得多,原因如下:

  • 摊销类型查找:Python 是一种动态类型语言,因此数组中的每个元素都有运行时开销。但是,Numpy(以及 pandas)使用 C(通常通过 Cython)执行计算。数组的类型仅在迭代开始时确定;仅此一项节省就是最大的好处之一。

  • 更好的缓存:对 C 数组进行迭代有利于缓存,因此速度非常快。pandas DataFrame 是一个“面向列的表”,这意味着每一列实际上只是一个数组。因此,您可以在 DataFrame 上执行的本机操作(例如对列中的所有元素求和)将很少发生缓存未命中。

  • 更多并行机会:可以通过 SIMD 指令操作简单的 C 数组。Numpy 的某些部分启用 SIMD,具体取决于您的 CPU 和安装过程。并行性的好处不会像静态类型和更好的缓存那样引人注目,但它们仍然是一个坚实的优势。

这个故事的寓意是:使用 Numpy 和 pandas 中的向量运算。它们比 Python 中的标量运算更快,原因很简单,这些运算正是 C 程序员手写的内容。(除了数组概念比嵌入 SIMD 指令的显式循环更容易阅读。)

解决方案 3:

这是解决你的问题的方法。这都是矢量化的。

In [58]: df = table1.merge(table2,on='letter')

In [59]: df['calc'] = df['number1']*df['number2']

In [60]: df
Out[60]: 
  letter  number1  number2  calc
0      a       50      0.2    10
1      a       50      0.5    25
2      b      -10      0.1    -1
3      b      -10      0.4    -4

In [61]: df.groupby('letter')['calc'].max()
Out[61]: 
letter
a         25
b         -1
Name: calc, dtype: float64

In [62]: df.groupby('letter')['calc'].idxmax()
Out[62]: 
letter
a         1
b         2
Name: calc, dtype: int64

In [63]: df.loc[df.groupby('letter')['calc'].idxmax()]
Out[63]: 
  letter  number1  number2  calc
1      a       50      0.5    25
2      b      -10      0.1    -1

解决方案 4:

不要使用 iterrows!

...或者iteritems,或者itertuples。说真的,不要。尽可能地将代码矢量化。如果你不相信我,可以问 Jeff。

我承认,对DataFrame 进行迭代iter*确实有合法的用例,但有比系列函数更好的迭代替代方案,即

  • cython / numba

  • 列表推导,以及

  • (极少数情况)apply

很多 Pandas 初学者经常会问一些与 相关的代码问题iterrows。由于这些新用户可能不熟悉矢量化的概念,他们会将解决问题的代码想象成涉及循环或其他迭代例程的东西。由于也不知道如何迭代,他们通常会陷入这个问题并学到所有错误的东西。


支持论点

迭代的文档页面上有一个巨大的红色警告框,上面写着:

遍历 pandas 对象通常比较慢。在许多情况下,不需要手动遍历行 [...]。

如果这还不能说服你,请看一下我在 这里的帖子中对添加两列“A + B”的矢量化和非矢量化技术的性能比较。

IT科技
基准测试代码,供您参考。iterrows是迄今为止最糟糕的,并且还值得指出的是,其他迭代方法也好不到哪里去。

底部的一行测量了一个用 numpandas 编写的函数,numpandas 是 Pandas 的一种风格,它与 NumPy 大量混合,以发挥最大的性能。除非您知道自己在做什么,否则应避免编写 numpandas 代码。尽可能坚持使用 API(即,优先vecvec_numpy)。


综上所述

始终寻求矢量化。有时,根据问题或数据的性质,这并不总是可行的,因此请寻求比 更好的迭代例程iterrows。除了处理极少量行时的方便之外,几乎没有合法的用例,否则请准备好等待很长时间,因为您的代码可能会运行数小时。

查看下面的链接来确定解决代码的最佳方法/矢量化例程。

  • 10 分钟了解 Pandas及其基本功能- 有用的链接,向您介绍 Pandas 及其矢量化*/cythonized 函数库。

  • 增强性能- 增强标准 Pandas 操作的文档入门指南

解决方案 5:

另一种选择是使用to_records(),它比itertuples和都快iterrows

但就您的情况而言,其他类型的改进空间还很大。

这是我最终的优化版本

def iterthrough():
    ret = []
    grouped = table2.groupby('letter', sort=False)
    t2info = table2.to_records()
    for index, letter, n1 in table1.to_records():
        t2 = t2info[grouped.groups[letter].values]
        # np.multiply is in general faster than "x * y"
        maxrow = np.multiply(t2.number2, n1).argmax()
        # `[1:]`  removes the index column
        ret.append(t2[maxrow].tolist()[1:])
    global table3
    table3 = pd.DataFrame(ret, columns=('letter', 'number2'))

基准测试:

-- iterrows() --
100 loops, best of 3: 12.7 ms per loop
  letter  number2
0      a      0.5
1      b      0.1
2      c      5.0
3      d      4.0

-- itertuple() --
100 loops, best of 3: 12.3 ms per loop

-- to_records() --
100 loops, best of 3: 7.29 ms per loop

-- Use group by --
100 loops, best of 3: 4.07 ms per loop
  letter  number2
1      a      0.5
2      b      0.1
4      c      5.0
5      d      4.0

-- Avoid multiplication --
1000 loops, best of 3: 1.39 ms per loop
  letter  number2
0      a      0.5
1      b      0.1
2      c      5.0
3      d      4.0

完整代码:

import pandas as pd
import numpy as np

#%% Create the original tables
t1 = {'letter':['a','b','c','d'],
      'number1':[50,-10,.5,3]}

t2 = {'letter':['a','a','b','b','c','d','c'],
      'number2':[0.2,0.5,0.1,0.4,5,4,1]}

table1 = pd.DataFrame(t1)
table2 = pd.DataFrame(t2)

#%% Create the body of the new table
table3 = pd.DataFrame(np.nan, columns=['letter','number2'], index=table1.index)


print('
-- iterrows() --')

def optimize(t2info, t1info):
    calculation = []
    for index, r in t2info.iterrows():
        calculation.append(r['number2'] * t1info)
    maxrow_in_t2 = calculation.index(max(calculation))
    return t2info.loc[maxrow_in_t2]

#%% Iterate through filtering relevant data, optimizing, returning info
def iterthrough():
    for row_index, row in table1.iterrows():   
        t2info = table2[table2.letter == row['letter']].reset_index()
        table3.iloc[row_index,:] = optimize(t2info, row['number1'])

%timeit iterthrough()
print(table3)

print('
-- itertuple() --')
def optimize(t2info, n1):
    calculation = []
    for index, letter, n2 in t2info.itertuples():
        calculation.append(n2 * n1)
    maxrow = calculation.index(max(calculation))
    return t2info.iloc[maxrow]

def iterthrough():
    for row_index, letter, n1 in table1.itertuples():   
        t2info = table2[table2.letter == letter]
        table3.iloc[row_index,:] = optimize(t2info, n1)

%timeit iterthrough()


print('
-- to_records() --')
def optimize(t2info, n1):
    calculation = []
    for index, letter, n2 in t2info.to_records():
        calculation.append(n2 * n1)
    maxrow = calculation.index(max(calculation))
    return t2info.iloc[maxrow]

def iterthrough():
    for row_index, letter, n1 in table1.to_records():   
        t2info = table2[table2.letter == letter]
        table3.iloc[row_index,:] = optimize(t2info, n1)

%timeit iterthrough()

print('
-- Use group by --')

def iterthrough():
    ret = []
    grouped = table2.groupby('letter', sort=False)
    for index, letter, n1 in table1.to_records():
        t2 = table2.iloc[grouped.groups[letter]]
        calculation = t2.number2 * n1
        maxrow = calculation.argsort().iloc[-1]
        ret.append(t2.iloc[maxrow])
    global table3
    table3 = pd.DataFrame(ret)

%timeit iterthrough()
print(table3)

print('
-- Even Faster --')
def iterthrough():
    ret = []
    grouped = table2.groupby('letter', sort=False)
    t2info = table2.to_records()
    for index, letter, n1 in table1.to_records():
        t2 = t2info[grouped.groups[letter].values]
        maxrow = np.multiply(t2.number2, n1).argmax()
        # `[1:]`  removes the index column
        ret.append(t2[maxrow].tolist()[1:])
    global table3
    table3 = pd.DataFrame(ret, columns=('letter', 'number2'))

%timeit iterthrough()
print(table3)

最终版本比原始代码快近 10 倍。策略是:

  1. 用于groupby避免重复比较值。

  2. 用于to_records访问原始 numpy.records 对象。

  3. 在编译完所有数据之前,请勿对 DataFrame 进行操作。

解决方案 6:

如果您确实需要对其进行迭代并按名称访问行字段,只需将列名保存到列表中并将数据框转换为NumPy数组:

import pandas as pd
import numpy as np
import time

s1 = np.random.randn(2000000)
s2 = np.random.randn(2000000)
dfa = pd.DataFrame({'s1': s1, 's2': s2})
columns = list(dfa.columns)
dfa = dfa.values
start = time.time()
i=0
for row in dfa:
    blablabla = row[columns.index('s1')]
    i+=1
end = time.time()
print (end - start)

0.9485495090484619

解决方案 7:

是的,Pandas itertuples()比 iterrows() 更快。您可以参考文档:pandas.DataFrame.iterrows

为了在迭代行时保留 dtypes,最好使用 itertuples(),它返回值的命名元组,并且通常比 iterrows 更快。

解决方案 8:

在建议不要使用的答案中iterrows,请尝试以下操作:将 DataFrame 转储到(临时)CSV 文件,然后重新加载以执行iterrows。在我的实验中,对于 600-700 万行数据,它将速度提高了 100 倍以上。

    # The three lines below are the reason why the `iterrows` becomes slow...
    all_df = ...
    for path in glob.glob("..."):
        all_df = pd.concat(all_df, pd.read_csv(path))

    # This is my solution, just dump it to a file
    all_df.to_csv("tmp.csv")
    all_df = pd.read_csv("tmp.csv")

    # because I like tqdm, so I have to use iterrows..
    for index, row in tqdm(df.iterrows(), total=all_df.shape[0]):
        # do something
        ... 

        # set something
        all_df.at[index, ..] = ...

p/s:不确定是否有更好的解决方案..

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用