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)