在这个例子中避免使用 iterrows 的好方法是啥?

Posted

技术标签:

【中文标题】在这个例子中避免使用 iterrows 的好方法是啥?【英文标题】:What is a good way to avoid using iterrows in this example?在这个例子中避免使用 iterrows 的好方法是什么? 【发布时间】:2014-09-12 13:09:57 【问题描述】:

我讨论了 iterrows previously 的性能问题,并得到了很好的普遍回应。这个问题是一个特殊情况,我希望您能帮助我更好地应用一些东西,因为 iterrows 很慢。

我相信这个问题对于任何觉得被行迭代思维困住的新 python/pandas 程序员都有用。

我看到的使用“map”或“apply”的示例通常显示一个看起来足够直观的数据表。但是,我正在处理两个表,它们很大(T1 是 250 万行,T2 是 96000 行)。

这是一个简单的例子(它适用于我的会话):

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]

print table3

输出是:

  letter number2
0      a     0.5
1      b     0.1

[2 rows x 2 columns]

总体思路:

    生产表 3 是目标 - 它的尺寸与表 1 相同 根据表 1 中的相应输入,使用表 2 中的“最佳”行填充表 3。 要使用的表 2 中的数据是基于表 1 中“字母”的子集

(显然这种情况并不慢,因为它很小,但在处理数百万行时却很慢。请记住,在实际示例中,我在两个表中都有更多列。)

【问题讨论】:

合并 T1 和 T2 开始会产生 50 亿行,所以我想我需要避免这种情况(除非我低估了我的计算机的内存) 我认为你误解了合并——它需要你合并的列的交集。合并您的示例数据框会给出一个有四行的数据框——而不是 8 行。 (取决于您传递给how 参数的内容。inner 是默认值。) 这对你有用吗? 不幸的是没有。我收到此消息:“ValueError:数组太大。”我很确定我将获得 50 亿行查看数据(我同意它不是在创建笛卡尔积)。我计划尝试使用 groupby 功能的 itertools。我可能会制作两个分组的对象,每个表一个,开始。然后迭代以找到“匹配”组。然后,我将像您所做的那样合并并应用于每个,聚合到一个新表中。如果你知道怎么做,我会很感激在这个(小)例子中看到它。如果我成功了,我会自己发布:) 【参考方案1】:

正如我在 the other answer 中发布的那样,有时问题不在于循环,而是不必要地将数据装箱到 DataFrame 或 Series 中。

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:])
    return pd.DataFrame(ret, columns=('letter', 'number2'))

改进方法包括:

    使用groupbygroups 索引来避免重复的布尔计算。 使用to_records 避免将记录与Series 相互转换。 在编译完所有数据之前不要创建 DataFrame。

【讨论】:

【参考方案2】:

对我来说,看起来最简单的事情是合并letter,然后合并groupby

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)

table3 = table1.merge(table2,on='letter')

grouped = table3.groupby('letter')

def get_optimization(df):
    product_column = df.number1 * df.number2
    idx_of_prod_col_max = product_columns.idxmax()
    return_val = df.ix[idx_of_prod_col_max]['number2']
    return return_val

table3 = grouped.apply(get_optimization)

【讨论】:

以上是关于在这个例子中避免使用 iterrows 的好方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

VBA中的StringFromIID - 避免手动管理内存的好方法是啥?

如何在 pandas 中使用 apply 函数来实现这个 iterrow 案例?

避免“分享”的好方法?

Pandas.DataFrame 的 iterrows()方法详解

iterrows 的更快替代方案

有没有使用 pywin32 createprocessasuser 并获取输出的好例子?