Pandas:合并数据框但汇总重叠的列

Chr*_*wne 7 python pandas

我一直在阅读很多关于merge()join()方法的帖子pandas.DataFrames,并针对我自己的问题尝试了这些,但还没有完全找到解决方案。

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

由于内存限制,我需要分read_csv块处理每小时消耗文件(使用chunk_size选项),并最终加载了几个月的 ID 消耗数据帧,例如:

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
Run Code Online (Sandbox Code Playgroud)

为这篇文章生成:

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')
Run Code Online (Sandbox Code Playgroud)

请注意,消耗0.0和之间存在差异NaN0.0表示0.0当月至少有一个消费读数,但NaN表示根本没有记录消费值,在这种情况下,不能假设为 0。为了我的目的,这种差异必须保持明显。

因为数据文件是分块处理的,所以有一些 ID 出现在多个 DataFrame 中,例如YYY,对于这些 ID,有时月份也会重叠,例如NovID YYY。在这种情况下,上半月的消费是 in df1,下半月是 in df2

因此,为了聚合消耗,我需要按“ID”合并这些数据帧,并在重叠的“月”中汇总值。

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

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
Run Code Online (Sandbox Code Playgroud)

我认为这是因为当它的 IDs/Monthsdf1没有出现时,df2它返回一个 NaN。

外部合并为重叠月份生成后缀列:

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
Run Code Online (Sandbox Code Playgroud)

我也不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
Run Code Online (Sandbox Code Playgroud)

重叠月份的总和使得x + NaN = x,NaN + y = yNaN + NaN = NaN


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

df3 = df1.merge(df2,how='outer',on='ID',suffixes=['','_x'])
overlapping_months_sufx = df3.columns.values[df3.columns.str.endswith('_x')]
for mnth_sufx in overlapping_months_sufx:
    mnth = mnth_sufx[:-2]
    df3[mnth][df3[mnth_sufx].notnull()] = df3[mnth].fillna(0) + df3[mnth_sufx]
    df3=df3.drop(columns=mnth_sufx)
df3 = 
 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
Run Code Online (Sandbox Code Playgroud)

鉴于此数据集的大小,最好能以最有效的方式汇总所有这些数据。有没有更好的方法来做到这一点,也许只需一步?

谢谢,克里斯

tim*_*geb 9

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

鉴于:

>>> 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
Run Code Online (Sandbox Code Playgroud)

解决方案:

>>> 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
Run Code Online (Sandbox Code Playgroud)

解释:

级联只是把df2df1

>>> 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我这样做是为了有一个带有名称的列'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
Run Code Online (Sandbox Code Playgroud)

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

sum(min_count=1)像这样工作:每个组中的值相对于它们的列相加。该参数min_count=1确保NaNNaN汇总时产生一系列所有值。

>>> 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
Run Code Online (Sandbox Code Playgroud)

演示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
Run Code Online (Sandbox Code Playgroud)

  • 我也是这么理解的,直接删了我的评论草稿。干净的做事方式! (2认同)