【问题标题】:Subtract multiple columns based on foreign key in pandas根据熊猫中的外键减去多列
【发布时间】:2016-11-16 22:57:19
【问题描述】:

我正在尝试计算对象与其基准之间的差异。我有一个数据集,其中包含所有对象的每日记录及其对应的值,如下所示:

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

【问题讨论】:

标签: python pandas


【解决方案1】:

我认为您可以使用sub,然后将idbenchmark_id 列添加到concat 和最后一个reindex 列,其顺序与obj_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

【讨论】:

  • 我觉得你之前需要obj_df = obj_df.reset_index(level=1) bm_df = bm_df.reset_index(level=1)
【解决方案2】:
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)

【讨论】:

  • 这会吐出这个错误:NotImplementedError: 没有实现与两个多索引合并
  • @jezrael。您发布的解决方案也给了我这个错误。这个问题可能是我的问题。有什么想法吗?
  • @Charles 如果可以选择更新熊猫,那么就这样做。否则,您必须告诉我们您正在运行什么版本,以便我们知道我们能做什么和不能做什么。
  • 这适用于我在问题中提供的虚拟数据,因此我将其标记为正确。话虽如此,当我尝试将其应用于我正在处理的真实数据(具有相同的结构)时,我得到了 NotImplementedError。你知道为什么会这样吗?具体来说,它在 odf.sub(bdf) 上中断
  • 我看不出是什么引发了错误。您可以找到产生中断的数据子集并将其作为示例数据发布。
【解决方案3】:

步骤:

执行合并:

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

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)

注意:更新了 DF 用于得出结果。

【讨论】:

  • 您提供的解决方案需要一些修改才能运行。它也有一些问题。 1) 列索引应该是 [2:6] 和 [6:] 以适应基准 id 并且由于索引范围不重叠。 2)新的数据框列名不反映新列是差异而不是原始值 3)这假设列顺序是有保证的,这在我的用例中不一定正确,尽管问题中没有明确指定。
  • 查看编辑。我已经更新了我的帖子,它没有考虑列顺序。如果你知道每个DF 的开始和结束列名,你可以让它工作是我的盲目猜测。
  • 我对自己的用例做了一些细微的修改,但我认为这足以回答发布的问题。
【解决方案4】:

使用合并:

#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]

【讨论】:

    猜你喜欢
    • 2018-10-13
    • 2020-10-08
    • 1970-01-01
    • 2013-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多