Search code examples
pandasrowpandas-groupbysubtraction

How to subtract one row to other rows in a grouped by dataframe?


I've got this data frame with some 'init' values ('value', 'value2') that I want to subtract to the mid term value 'mid' and final value 'final' once I've grouped by ID.

import pandas as pd
df = pd.DataFrame({ 
    'value': [100, 120, 130, 200, 190,210],
    'value2': [2100, 2120, 2130, 2200, 2190,2210],  
     'ID': [1, 1, 1, 2, 2, 2], 
     'state': ['init','mid', 'final', 'init', 'mid', 'final'], 
 })

My attempt was tho extract the index where I found 'init', 'mid' and 'final' and subtract from 'mid' and 'final' the value of 'init' once I've grouped the value by 'ID':

group = df.groupby('ID')
group['diff_1_f'] = group['value'].iloc[group.index[group['state'] == 'final'] - group['value'].iloc[group.index[dfs['state'] == 'init']]]]
group['diff_2_f'] =  group['value2'].iloc[group.index[group['state'] == 'final'] - group['value'].iloc[group.index[dfs['state'] == 'init']]]
group['diff_1_m'] = group['value'].iloc[group.index[group['state'] == 'mid'] - group['value'].iloc[group.index[dfs['state'] == 'init']]]
group['diff_2_m'] =  group['value2'].iloc[group.index[group['state'] == 'mid'] - group['value'].iloc[group.index[dfs['state'] == 'init']]]  

But of course it doesn't work. How can I obtain the following result:

df = pd.DataFrame({ 
    'diff_value': [20, 30, -10,10],
    'diff_value2': [20, 30, -10,10],  
     'ID': [ 1, 1, 2, 2], 
     'state': ['mid', 'final', 'mid', 'final'], 
 }) 

Also in it's grouped form.


Solution

  • Use:

    #columns names in list for subtract
    cols = ['value', 'value2']
    #new columns names created by join
    new = [c + '_diff' for c in cols]
    #filter rows with init
    m = df['state'].ne('init')
    
    #add init rows to new columns by  join and filter no init rows
    df1 = df.join(df[~m].set_index('ID')[cols], lsuffix='_diff', on='ID')[m]
    #subtract with numpy array by .values for prevent index alignment
    df1[new] = df1[new].sub(df1[cols].values)
    #remove helper columns
    df1 = df1.drop(cols, axis=1)
    print (df1)
       value_diff  value2_diff  ID  state
    1          20           20   1    mid
    2          30           30   1  final
    4         -10          -10   2    mid
    5          10           10   2  final