Вычесть значение в поле в одной строке из всех других строк того же поля в pandas dataframe

#python #pandas #dataframe

#python #pandas #dataframe

Вопрос:

У меня есть фрейм данных, как показано ниже:

 data = {'sid':[1,1,1,2,2,2],
        'field1':['start', None, None, 'start', None, None], 
        'field2':['a', 'b', 'z', 'd', 'z','s'],
        'val':[20, 22, 23, 40, 45, 47]}
df = pd.DataFrame(data)
print(df)

   sid field1  val
0    1  start   20
1    1   None   22
2    1   None   23
3    2  start   40
4    2   None   45
5    2   None   47
 

Я хотел бы создать новое поле newval, в котором хранится разница между значением val этой строки и значением val в строке с тем же sid и field1 = ‘start’ .

    sid field1  val  newval
0    1  start   20     NaN
1    1   None   22     2.0
2    1   None   23     3.0
3    2  start   40     NaN
4    2   None   45     5.0
5    2   None   47     7.0
 

Я пробовал diff() с помощью groupby(), но это дает мне большую разницу.

  df['newval'] = df.groupby('sid')['val'].diff()

   sid field1  val  newval
0    1  start   20     NaN
1    1   None   22     2.0
2    1   None   23     1.0
3    2  start   40     NaN
4    2   None   45     5.0
5    2   None   47     2.0
 

Как я могу получить разницу из конкретной строки?

Ответ №1:

Вы можете использовать groupby со вспомогательным столбцом, sid а затем получить первое значение группы, а затем вычесть из val поля.

 df['new_val'] = (df['val']-
     df.groupby(['sid',df['field1'].eq("start").cumsum()])['val'].transform("first"))
 

 print(df)

   sid field1 field2  val  new_val
0    1  start      a   20        0
1    1   None      b   22        2
2    1   None      z   23        3
3    2  start      d   40        0
4    2   None      z   45        5
5    2   None      s   47        7
 

Вспомогательный столбец, как показано ниже, что помогает в группировке:

 print(df['field1'].eq("start").cumsum())
0    1
1    1
2    1
3    2
4    2
5    2
Name: field1, dtype: int32
 

Комментарии:

1. В самом общем случае, когда в каждом идентификаторе есть несколько запусков и данные не отсортированы по идентификатору, это может привести к сбою.

Ответ №2:

Вероятно, не самое элегантное решение, но вы могли бы объединить только строки с field1 = 'start'

Использование df , как определено выше:

 tmp = pd.merge(df, df.loc[df['field1'] == 'start', ['sid', 'val']],
        how = 'left', left_on = 'sid', right_on = 'sid',
        suffixes = ['', 'start_val'])

tmp['newval'] = np.where(tmp['field1'] == 'start', np.nan, tmp['val'] - tmp['valstart_val'])
tmp.drop('valstart_val', axis = 1, inplace = True)
 

Вывод:

 sid field1  field2  val newval
1   start   a   20  NaN
1   None    b   22  2.0
1   None    z   23  3.0
2   start   d   40  NaN
2   None    z   45  5.0
2   None    s   47  7.0
 

Комментарии:

1. Мне нравится решение anky

2. К вашему сведению, существует pandas.DataFrame.where и pd.NA , который устраняет необходимость импорта numpy .

Ответ №3:

Похоже, ваши данные уже отсортированы, sid и start в первой строке каждого идентификатора есть только один. В этом случае вы можете сделать:

 ids = df.duplicated(['sid'])
# also
# ids = df['field1'].astype(bool)

df['newval'] = df['val'].sub(df['val'].mask(ids).ffill()).where(ids)
 

Вывод:

    sid field1 field2  val  newval
0    1  start      a   20     NaN
1    1   None      b   22     2.0
2    1   None      z   23     3.0
3    2  start      d   40     NaN
4    2   None      z   45     5.0
5    2   None      s   47     7.0