我一直在阅读许多有关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 = x
和NaN + y = y
。
我看到的一种解决方案是合并,然后对重叠的列求和,而忽略NaN:
NaN + NaN = NaN
鉴于此数据集的大小,拥有一种最有效的方式来汇总所有数据的方法将是很棒的。是否有更好的方法,也许一步一步完成?
谢谢, 克里斯
答案 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