2016-11-15 42 views
3

我正试图计算一个对象和它的基准之间的差异。我有一个包含每天记录所有看起来像这样的对象及其对应的值的数据集:基于熊猫中的外键减去多列

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'作为基准。

+0

这个链接看起来很接近我想要做的: http://stackoverflow.com/questions/20100717/subtract-a-column-from-one-pandas-dataframe-from-another – Charles

回答

1

步骤:

执行合并:

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') 

Sub从objectDFbenchmarkDF道值:

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) 

enter image description here

注:更新DF用于在结果到达。

+0

您提供的解决方案需要进行一些修改才能运行。它也有一些问题。 1)列索引应该是[2:6]和[6:]以适应基准ID,并且由于索引范围不重叠。 2)新的数据帧列名称不反映新列是差异而不是原始值 3)这假设列顺序是有保证的,虽然没有在问题中明确指定,但在我的用例中不一定是真实的。 – Charles

+0

*请参阅编辑*。我已更新我的帖子,但没有考虑列顺序。如果你知道每个“DF”的开始和结束列名,你可以得到这个工作是我盲目的猜测。 –

+0

我对自己的用例做了一些细微的修改,但我认为这足以回答发布的问题。 – Charles

4

我认为你可以使用sub,然后通过concat和最后reindex列相同的顺序添加列idbenchmark_idobj_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 
obj_df.reset_index(level=1, inplace=True) 
bm_df.reset_index(level=1, inplace=True) 
cols = ['value_a','value_b','value_c', 'value_d'] 
df = obj_df[cols].sub(bm_df[cols]) 
df = pd.concat([df, obj_df[['id','benchmark_id']]], axis=1) 
     .reindex(columns=obj_df.columns) 
     .reset_index() 

print (df) 
     date id value_a value_b value_c value_d benchmark_id 
0 01/21/2015 abc  -2  1  7  2   efg 
1 01/22/2015 abc  0  2  -3  0   efg 
2 01/21/2015 xyz  2  3  2  7   tuv 
3 01/22/2015 xyz  0  5  -7  5   tuv 
+0

我想你以前需要'obj_df = obj_df.reset_index(level = 1) bm_df = bm_df.reset_index(level = 1)'。 – jezrael

0

使用合并:

#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] 
3
odf = obj_df.set_index(['date', 'benchmark_id']) 
bdf = bm_df.set_index(['date', 'id']) 

odf.update(odf.drop('id', 1).sub(bdf)) 
odf.reset_index().reindex_axis(obj_df.columns, 1) 

enter image description here

+0

This spits this error: NotImplementedError:与两个多索引合并未实现 – Charles

+0

@jezrael。你发布的解决方案也给了我这个错误。这个问题可能是我的一个问题。有任何想法吗? – Charles

+0

@Charles如果更新熊猫是一个选项,那就这样做。否则,您必须告诉我们您正在运行的是哪个版本,以便我们知道我们可以做什么,不可以做什么。 – piRSquared