Python 基于熊猫中的外键减去多列
我试图计算一个对象和它的基准之间的差异。我有一个数据集,其中包含所有对象的每日记录及其相应的值,如下所示:Python 基于熊猫中的外键减去多列,python,pandas,Python,Pandas,我试图计算一个对象和它的基准之间的差异。我有一个数据集,其中包含所有对象的每日记录及其相应的值,如下所示: obj_df date id value_a value_b value_c value_d benchmark_id 01/21/2015 abc 10 41 19 22 efg 01/22/2015 abc 15 43 11 21 e
obj_df
date id value_a value_b value_c value_d benchmark_id
01/21/2015 abc 10 41 19 22 efg
01/22/2015 abc 15 43 11 21 efg
01/21/2015 xyz 16 45 13 26 tuv
01/22/2015 xyz 13 48 12 22 tuv
01/21/2015 tru 10 39 15 21 efg
01/21/2015 tru 11 37 13 20 efg
我也有关于基准的数据。值列在数据帧之间共享。基准集中的id对应于原始对象数据帧中的基准id
bm_df
date id value_a value_b value_c value_d
01/21/2015 efg 12 40 12 20
01/22/2015 efg 15 41 14 21
01/21/2015 tuv 14 42 11 19
01/22/2015 tuv 13 43 19 17
我试图找到一种简单的方法来返回一个数据帧,它给出了对象值和相应的基准值之间的差异,从而得到一个类似这样的数据帧
diff_df
date id diff_a diff_b diff_c diff_d benchmark_id
01/21/2015 abc -2 1 7 2 efg
01/22/2015 abc 0 2 -3 0 efg
01/21/2015 xyz 2 3 2 7 tuv
01/22/2015 xyz 0 5 -7 5 tuv
01/21/2015 tru -4 -3 4 2 efg
01/21/2015 tru -2 -6 -6 3 efg
需要注意的几点:-对象比基准多,因此索引的大小将不同。
-每个对象都有一个基准。
-我并不特别关心原始值。只是不同而已。
-一些基准对应于多个对象。例如,“abc”和“tru”都使用“efg”作为基准。我认为您可以使用,然后添加列
id
和benchmark\u id
,最后一列的顺序与obj\u df
的列相同:
print (obj_df)
value_a value_b value_c value_d benchmark_id
date id
01/21/2015 abc 10 41 19 22 efg
01/22/2015 abc 15 43 11 21 efg
01/21/2015 xyz 16 45 13 26 tuv
01/22/2015 xyz 13 48 12 22 tuv
print (bm_df)
value_a value_b value_c value_d
date id
01/21/2015 efg 12 40 12 20
01/22/2015 efg 15 41 14 21
01/21/2015 tuv 14 42 11 19
01/22/2015 tuv 13 43 19 17
使用合并:
#inner join on FK
merge = obj_df.merge(bm_df, left_on = 'benchmark_id', right_on = 'id', suffixes = ['_obj', '_bm'])
#create new columns
for value in ['a', 'b', 'c']:
merge.loc[:, 'diff_%s'%value] = merge['value_%s_obj'%value] - merge['value_%s_bm'%value]
步骤:
执行合并:
df = obj_df.merge(bm_df, left_on=['benchmark_id', 'date'], right_on=['id', 'date']) \
.drop(['id_y'], 1).set_index(['date'])
通过输入开始和结束列名查找列索引位置的帮助函数:
def col_locate(df, start, end):
start_loc = df.columns.get_loc(start)
end_loc = df.columns.get_loc(end)
return list(range(start_loc, end_loc+1))
fir, sec = col_locate(df,'value_a_x','value_d_x'), col_locate(df,'value_a_y','value_d_y')
从objectDF
和benchmarkDF
中减去值:
df_diff = pd.DataFrame(df.iloc[:, fir].values - df.iloc[:, sec].values,
columns=list('abcd'), index=df.index).add_prefix('diff_')
最后,按列连接它们:
pd.concat([df[['id_x', 'benchmark_id']], df_diff], axis=1)
注意:更新了用于得出结果的
DF
。此链接似乎与我尝试的操作非常接近:这会导致此错误:未实现错误:与两个多索引合并不正确implemented@jezrael. 你发布的解决方案也给了我这个错误。这个问题可能是我这边的问题。有什么想法吗?@Charles如果可以更新熊猫,那就这么做。否则,您必须告诉我们您正在运行的版本,以便我们知道我们可以做什么和不能做什么。这与我在问题中给出的虚拟数据有关,因此我将其标记为正确。也就是说,当我尝试将它应用到我正在处理的真实数据(具有相同的结构)时,我得到了NotImplementedError。你知道为什么会这样吗?具体来说,它在odf.sub(bdf)上中断,我看不出是什么导致了错误。您可以找到产生中断的数据子集,并将其作为示例数据发布。我认为您需要obj_df=obj_df.reset_index(level=1)bm_df=bm_df.reset_index(level=1)
。它也有一些问题。1) 列索引应为[2:6]和[6:],以适应基准id,并且由于索引范围不重叠。2) 新的dataframe列名并不反映新列是不同的,而不是原始值3)这假设列顺序是有保证的,这在我的用例中不一定是正确的,尽管问题中没有明确指定。请参阅编辑。我已经更新了我的帖子,没有考虑专栏顺序。如果你知道每个DF
的开始和结束列名,你可以让这个工作是我瞎猜的。我对我自己的用例做了一些轻微的修改,但我认为这足以回答发布的问题。
df_diff = pd.DataFrame(df.iloc[:, fir].values - df.iloc[:, sec].values,
columns=list('abcd'), index=df.index).add_prefix('diff_')
pd.concat([df[['id_x', 'benchmark_id']], df_diff], axis=1)