在这个例子中避免使用 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'))
改进方法包括:
-
使用
groupby
和groups
索引来避免重复的布尔计算。
使用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 案例?