我正在寻找一种优雅的方法将所有行从一个DataFrame附加到另一个DataFrame(两个DataFrame具有相同的索引和列结构),但是如果两个DataFrame中都出现相同的索引值,请使用第二个数据中的行帧.
所以,例如,如果我开始:
df1:
A B
date
'2015-10-01' 'A1' 'B1'
'2015-10-02' 'A2' 'B2'
'2015-10-03' 'A3' 'B3'
df2:
date A B
'2015-10-02' 'a1' 'b1'
'2015-10-03' 'a2' 'b2'
'2015-10-04' 'a3' 'b3'
Run Code Online (Sandbox Code Playgroud)
我希望结果如下:
A B
date
'2015-10-01' 'A1' 'B1'
'2015-10-02' 'a1' 'b1'
'2015-10-03' 'a2' 'b2'
'2015-10-04' 'a3' 'b3'
Run Code Online (Sandbox Code Playgroud)
这类似于我认为在某些SQL系统中称为"upsert"的内容---更新和插入的组合,在某种意义上,每行来自df2(a)用于更新现有行,df1如果行键已经存在于df1,或(b)df1如果行密钥尚不存在则插入到末尾.
我想出了以下内容
pd.concat([df1, df2]) # concat the two DataFrames
.reset_index() # turn 'date' into a regular column
.groupby('date') # group rows by values in the 'date' column
.tail(1) # take the last row in each group
.set_index('date') # restore 'date' as the index
Run Code Online (Sandbox Code Playgroud)
这似乎工作,但这取决于每个groupby组中的行的顺序始终与原始DataFrames相同,我没有检查过,并且看起来令人不快复杂.
有没有人对更简单的解决方案有任何想法?
Ale*_*der 27
一种解决方案是df1与新行连接df2(即索引不匹配).然后用来自的那些更新值df2.
df = pd.concat([df1, df2[~df2.index.isin(df1.index)]])
df.update(df2)
>>> df
A B
2015-10-01 A1 B1
2015-10-02 a1 b1
2015-10-03 a2 b2
2015-10-04 a3 b3
Run Code Online (Sandbox Code Playgroud)
编辑: 根据@chrisb的建议,这可以进一步简化如下:
pd.concat([df1[~df1.index.isin(df2.index)], df2])
Run Code Online (Sandbox Code Playgroud)
谢谢克里斯!
bil*_*oie 12
从pandas 1.0.3开始,所需的功能直接由以下方式给出combine_first:
combined = df2.combine_first(df1)
print(combined)
# A B
# 2015-10-01 A1 B1
# 2015-10-02 a1 b1
# 2015-10-03 a2 b2
# 2015-10-04 a3 b3
Run Code Online (Sandbox Code Playgroud)
为了获得这种行为,其数据具有优先级的数据帧(在本例中为更新数据帧df2)必须是调用该函数的数据帧。
它基本上:(1)协调行和列,(2)优先考虑非 NaN 数据,(3)如果两个数据帧中定义的数据点,则优先考虑 中的数据df2,这本质上就是您想要的。
编辑:我的理解是,它combine_first 确实满足了所请求的“如果存在则更新,如果不存在则插入”行为。然而,根据 Vijchti 在评论中的说法(谢谢),这并不严格对应于 SQL UPSERT 操作,因为逻辑是逐个值应用的,而不是整个行。我从我的答案中删除了对 UPSERT 的任何引用。