附加具有多列索引和重叠列名的 DataFrame
Posted
技术标签:
【中文标题】附加具有多列索引和重叠列名的 DataFrame【英文标题】:Append DataFrames with multi-column index and overlapping column names 【发布时间】:2019-07-27 09:43:40 【问题描述】:我有多个数据帧在循环的不同迭代中生成,如下所示: d1 在迭代 1 中创建,d2 在迭代 2 中创建,依此类推..
d1=pd.DataFrame('PARTICIPANT_ID':['idA'],'AGE':[32],'GENDER':['male'],'colA':[20],'colB':[100])
d2=pd.DataFrame('PARTICIPANT_ID':['idA'],'AGE':[32],'GENDER':['male'],'colC':[1],'colD':[6])
d3=pd.DataFrame('PARTICIPANT_ID':['idA'],'AGE':[32],'GENDER':['male'],'colE':[60],'colF':[11])
d4=pd.DataFrame('PARTICIPANT_ID':['idB'],'AGE':[43],'GENDER':['female'],'colA':[30],'colB':[200])
d5=pd.DataFrame('PARTICIPANT_ID':['idB'],'AGE':[43],'GENDER':['female'],'colC':[2],'colD':[7])
d6=pd.DataFrame('PARTICIPANT_ID':['idB'],'AGE':[43],'GENDER':['female'],'colE':[70],'colF':[12])
d7=pd.DataFrame('PARTICIPANT_ID':['idC'],'AGE':[28],'GENDER':['female'],'colE':[56],'colF':[48])
我想在每次迭代后继续将这些数据帧合并到一个更大的最终数据帧,或者将它们存储为字典或其他数据类型,并在循环结束时将它们合并在一起。
这是输出需要的样子(PARTICIPANT_ID 单独可以作为这些数据帧的索引):
PARTICIPANT_ID AGE GENDER colA colB colC colD colE colF
idA 32 male 20.0 100.0 1.0 6.0 60 11
idB 43 female 30.0 200.0 2.0 7.0 70 12
idC 28 female NaN NaN NaN NaN 56 48
我目前正在做这样的事情:
df_final = df_final.set_index(['PARTICIPANT_ID','AGE','GENDER'],inplace=True).combine_first(d1.set_index(['PARTICIPANT_ID','AGE','GENDER'],inplace=True))
其中 df_final 是最终的输出数据帧,我正在为每次迭代中生成的每个新数据帧循环重复此过程。
这种合并的问题是它非常缓慢。有人可以建议一种更好的方法,以更快、更高效的方式实现相同的输出。
请注意,循环迭代了数十万条记录,并且列数比上面示例中显示的要多。
【问题讨论】:
对@ALollz 感到抱歉...感谢您的关注。我更正了列名 【参考方案1】:你可以通过concat
+ groupby
+ first
得到同样的逻辑,也许你的真实数据更快:
df_res = (pd.concat([d1, d2, d3, d4, d5, d6, d7], sort=False)
.groupby(['PARTICIPANT_ID', 'AGE', 'GENDER']).first())
# colA colB colC colD colE colF
#PARTICIPANT_ID AGE GENDER
#idA 32 male 20.0 100.0 1.0 6.0 60.0 11.0
#idB 43 female 30.0 200.0 2.0 7.0 70.0 12.0
#idC 28 female NaN NaN NaN NaN 56.0 48.0
否则,我会说reduce
,但你似乎已经这样做了:
from functools import reduce
reduce(lambda l,r: l.combine_first(r),
[x.set_index(['PARTICIPANT_ID', 'AGE', 'GENDER']) for x in [d1, d2, d3, d4, d5, d6, d7]])
myl = [d1, d2, d3, d4, d5, d6, d7]
%timeit pd.concat(myl, sort=False).groupby(['PARTICIPANT_ID', 'AGE', 'GENDER']).first()
#9.11 ms ± 310 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit reduce(lambda l,r: l.combine_first(r), [x.set_index(['PARTICIPANT_ID', 'AGE', 'GENDER']) for x in myl])
#61.3 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
【讨论】:
我不是一次获得所有数据帧,而是在大循环的每次迭代中获得一个。我如何存储这些数据帧,以便以后像您建议的那样连接。如果我一次只使用 2 个数据帧进行连接,那么随着最终数据帧变大,整个过程会变得很慢,这是连接的结果。 酷,让我试试你建议的列表选项。如果有帮助,我会告诉你的。谢谢! 是的,你不应该在循环中附加或连接DataFrames
,因为它会导致不必要的复制;见***.com/a/37009561/4333359。只需初始化一个空列表mylist=[]
,然后在循环中执行mylist.append(df)
(其中df
是您获得的小子数据帧),然后在循环之后(外部)进行一次调用pd.concat(mylist)
@anon 我认为这里的主要放缓是您最终需要组合数千个小框架。我怀疑这会导致不必要的复制,随着基帧变大,复制速度会减慢(您可以通过计时每次迭代来检查时间是否在增长)。我怀疑你会看到一个巨大的改进,将小帧存储在一个列表中,并且当你拥有它们时只连接一次。
你是 100% 正确的@ALollz..我可以看到速度至少提高了 6 到 8 倍。你真棒。我现在越来越贪心了。您认为甚至将数据存储在数据框以外的其他东西中会更好吗?进一步提高速度。以上是关于附加具有多列索引和重叠列名的 DataFrame的主要内容,如果未能解决你的问题,请参考以下文章