我试图基于某个键在两个数据帧上进行成对比较,但是我在pandas groupby的double for循环中遇到了困难,因为它非常慢。有什么我可以优化的方法,这样我每次运行外循环时都不必重新计算组?
我尝试使用相同的groupby变量,但似乎无法解决重新计算问题。
mygroups = mydf.groupby('mykey')
for key1,subdf1 in mygroups:
for key2,subdf2 in mygroups:
if(key2 <= key1):
continue
do_some_work(subdf1,subdf2)
subdf2
似乎从第一个键而不是从key1之后的下一个键开始重新计算。在我的用例场景中,我期望key2将是迭代中key1之后的下一个,依此类推。无需重新计算如何发生这种行为?
答案 0 :(得分:1)
您的观察是正确的,内部循环遍历整个数据帧,而不仅仅是key1之后的记录。
我先创建一个包含组的列表,然后遍历该列表。 这就是我要做的:
mygroups_list= [(key, subdf) for (key, subdf) mydf.groupby('mykey')]
for len(mygroups_list) > 0:
key1,subdf1= mygroups_list.pop(0)
for key2,subdf2 in mygroups_list:
do_some_work(subdf1,subdf2)
您只需要确保对组进行了真正的排序,但是AFAIK仍然可以通过.groupby
方法来完成。如果不确定,可以在循环外添加一个mygroups_list.sort(key=lambda tup: tup[0])
。
对于较大的数据框,您可以避免立即实例化这些数据框,而只需将其推迟到您实际需要这样的数据时即可:
# create the groupby object as usual
group_by= mydf.groupby('mykey')
# now fetch the row indices from the groupby object
# and because this is actually a dictionary
# extract the keys from it and sort them
mygroups_dict= group_by.indices
mygroups_labels= list(mygroups_dict)
mygroups_labels.sort()
# now use a similar approach as above
while len(mygroups_labels) > 0:
key1= mygroups_labels.pop(0)
# but instead of creating the sub dataframes
# before you enter the loop, just do it
# within the loop and use the row indices
# the groupby object evaluated
subdf1= mydf.iloc[mygroups_dict[key1]]
for key2 in mygroups_labels:
subdf2= mydf.iloc[mygroups_dict[key2]]
do_some_work(subdf1, subdf2)
这应该减少大量的内存,因为您只需要存储行索引,而不是在整个孔处理时间内存储整个行。
对于以下示例设置:
import numpy as np
def do_some_work(subdf1, subdf2):
print('{} --> {} (len={}/{})'.format(subdf1['mykey'].iat[0], subdf2['mykey'].iat[0], len(subdf1), len(subdf2)))
mydf= pd.DataFrame(dict(mykey=np.random.randint(5, size=100), col=range(1, 101)))
这将输出类似(当然,由于randint,len信息在每次运行时看起来都将有所不同)。但是请注意组标签(箭头的左右)。在右侧,您会一直看到key2,它始终是> key1:
0 --> 1 (len=21/16)
0 --> 2 (len=21/21)
0 --> 3 (len=21/20)
0 --> 4 (len=21/22)
1 --> 2 (len=16/21)
1 --> 3 (len=16/20)
1 --> 4 (len=16/22)
2 --> 3 (len=21/20)
2 --> 4 (len=21/22)
3 --> 4 (len=20/22)