熊猫:合并数据框,但对重叠的列求和

时间:2018-12-11 12:01:02

标签: python pandas

我一直在阅读许多有关merge()的{​​{1}}和join()方法的帖子,并尝试解决我自己的问题,但仍未找到解决方法。

我有一个非常大的数据文件(.csv),其中包含每小时消耗的各种ID的文件。我想汇总每个ID在每个月的消费。

由于内存的限制,我需要使用pandas.DataFrames来分块处理小时消耗文件(使用read_csv选项),最后要花费几个月的时间来消耗ID的DataFrames负载,例如:

chunk_size

为此帖子生成的来源:

df1 = 
 Month  Dec  Nov
ID             
XXX    4.0  1.0
YYY    8.0  3.0
ZZZ    4.0  1.0 

df2 = 
 Month  Dec  Nov  Oct
ID                  
AAA    1.0  7.0  9.0
BBB    0.0  NaN  2.0
YYY    5.0  5.0  0.0

请注意,df1 = pd.DataFrame({'ID': ['XXX','XXX','YYY','YYY','ZZZ','ZZZ'], 'Month': ['Nov','Dec']*3, 'Consumption': [1.0,4.0,3.0,8.0,1.0,4.0]}) df1 = df1.pivot(index='ID', columns='Month', values='Consumption') df2 = pd.DataFrame({'ID': ['AAA','AAA','AAA','YYY','YYY','YYY','BBB','BBB','BBB'], 'Month': ['Oct','Nov','Dec']*3, 'Consumption': [9,7,1,0,5,5,2,np.nan,0]}) df2 = df2.pivot(index='ID', columns='Month', values='Consumption') 0.0的消耗量有所不同。 NaN表示一个月中至少有一个0.0的消耗量读数,而0.0意味着根本没有记录任何消耗量值,在这种情况下,不能假定为0。为了我的目的,必须保持区别。

由于数据文件是按块处理的,因此多个ID中会出现一些ID,例如NaN,并且对于那些ID,有时月份也会重叠,例如YYY为ID Nov。在这种情况下,本月上半月的消费量为YYY,下半月的消费量为df1

因此,要汇总消耗量,我需要按“ ID”合并这些DataFrame,然后将值叠加在“ Months”中。

直接求和:DataFrame产生许多NaN:

df2

我认为这是因为当汇总df1 + df2 = Month Dec Nov Oct ID AAA NaN NaN NaN BBB NaN NaN NaN XXX NaN NaN NaN YYY 13.0 8.0 NaN ZZZ NaN NaN NaN 中没有显示的df1的ID /月份时,会返回NaN。

外部合并产生重叠月份的后缀列:

df2

我也无法df1.merge(df2,how='outer',on='ID') = Month Dec_x Nov_x Dec_y Nov_y Oct ID XXX 4.0 1.0 NaN NaN NaN YYY 8.0 3.0 5.0 5.0 0.0 ZZZ 4.0 1.0 NaN NaN NaN AAA NaN NaN 1.0 7.0 9.0 BBB NaN NaN 0.0 NaN 2.0 做我想做的事。

我想要的是中间的东西,看起来像这样:

combine_first

其中重叠的月份总计为 Month Dec Nov Oct ID XXX 4.0 1.0 NaN YYY 13.0 8.0 0.0 ZZZ 4.0 1.0 NaN AAA 1.0 7.0 9.0 BBB 0.0 NaN 2.0 x + NaN = xNaN + y = y


我看到的一种解决方案是合并,然后对重叠的列求和,而忽略NaN:

NaN + NaN = NaN

鉴于此数据集的大小,拥有一种最有效的方式来汇总所有数据的方法将是很棒的。是否有更好的方法,也许一步一步完成?

谢谢, 克里斯

2 个答案:

答案 0 :(得分:2)

这是尝试。如果我理解正确,请发表评论。

给出:

>>> df1                                                                                                                
Month  Dec  Nov
ID             
XXX    4.0  1.0
YYY    8.0  3.0
ZZZ    4.0  1.0
>>> df2                                                                                                                
Month  Dec  Nov  Oct
ID                  
AAA    1.0  7.0  9.0
BBB    0.0  NaN  2.0
YYY    5.0  5.0  0.0

解决方案:

>>> pd.concat([df1, df2]).reset_index().groupby('ID', sort=False).sum(min_count=1)
      Dec  Nov  Oct
ID                 
XXX   4.0  1.0  NaN
YYY  13.0  8.0  0.0
ZZZ   4.0  1.0  NaN
AAA   1.0  7.0  9.0
BBB   0.0  NaN  2.0

说明:

串联只会将df2放在df1下。

>>> cat = pd.concat([df1, df2])                                                                                        
>>> cat                                                                                                                
     Dec  Nov  Oct
ID                
XXX  4.0  1.0  NaN
YYY  8.0  3.0  NaN
ZZZ  4.0  1.0  NaN
AAA  1.0  7.0  9.0
BBB  0.0  NaN  2.0
YYY  5.0  5.0  0.0

reset_index将索引移到列中。

>>> cat = cat.reset_index()                                                                                            
>>> cat                                                                                                                
    ID  Dec  Nov  Oct
0  XXX  4.0  1.0  NaN
1  YYY  8.0  3.0  NaN
2  ZZZ  4.0  1.0  NaN
3  AAA  1.0  7.0  9.0
4  BBB  0.0  NaN  2.0
5  YYY  5.0  5.0  0.0

我这样做是为了使有一个名称为'ID'的列,通过该列可以对其他值进行分组。 groupby('ID', sort=False)'ID'列中创建共享相同值的行组(并且sort=False确保最终结果中的行不进行排序以匹配您的输出)。

我们可以像这样检查组的大小:

>>> cat.groupby('ID', sort=False).size()                                                                               
ID
XXX    1
YYY    2
ZZZ    1
AAA    1
BBB    1
dtype: int64

如您所见,我们只有一组大小为2的组,因为'YYY' ID是唯一重复的一组。

sum(min_count=1)的工作原理是:将每个组中的值相对于其列进行汇总。参数min_count=1确保所有NaN值的序列在累加后得出NaN

>>> cat.groupby('ID', sort=False).sum(min_count=1)                                                      
      Dec  Nov  Oct
ID                 
XXX   4.0  1.0  NaN
YYY  13.0  8.0  0.0
ZZZ   4.0  1.0  NaN
AAA   1.0  7.0  9.0
BBB   0.0  NaN  2.0

min_count的演示:

>>> s = pd.Series([np.nan, np.nan])                                                                                    
>>> s                                                                                                                  
0   NaN
1   NaN
dtype: float64
>>>                                                                                                                    
>>> s.sum()                                                                                                            
0.0
>>> s.sum(min_count=1)                                                                                                 
nan
>>> s[0] = 1                                                                                                           
>>> s                                                                                                                  
0    1.0
1    NaN
dtype: float64
>>> s.sum()                                                                                                            
1.0
>>> s.sum(min_count=1)                                                                                                 
1.0
>>> s.sum(min_count=2)                                                                                                 
nan

答案 1 :(得分:1)

我的方法是将那些仅在一个数据帧中的值插入到总和结果为NaN的那些索引处:

result = df1 + df2

Month   Dec  Nov  Oct
ID                   
AAA     NaN  NaN  NaN
BBB     NaN  NaN  NaN
XXX     NaN  NaN  NaN
YYY    13.0  8.0  NaN
ZZZ     NaN  NaN  NaN

result = result.where(~result.isna(), df1)

Month   Dec  Nov  Oct
ID                   
AAA     NaN  NaN  NaN
BBB     NaN  NaN  NaN
XXX     4.0  1.0  NaN
YYY    13.0  8.0  NaN
ZZZ     4.0  1.0  NaN

result = result.where(~result.isna(), df2)

Month   Dec  Nov  Oct
ID                   
AAA     1.0  7.0  9.0
BBB     0.0  NaN  2.0
XXX     4.0  1.0  NaN
YYY    13.0  8.0  0.0
ZZZ     4.0  1.0  NaN