сравнение последних значений в pandas groupby

#python #pandas #pandas-groupby

#python #pandas #pandas-groupby

Вопрос:

Это мой фрейм данных:

 df = pd.DataFrame({'a': list('xxxxxzzz'), 'b':[0,0,1,0,1,0,1,1], 'c': [100, 101, 105, 110, 120, 125, 100, 150], 'd':[0,0,0,1,1,0,0,0]})
  

Я группирую их:

 groups = df.groupby(['a', 'd'])
  

Я хочу добавить еще один столбец, в df который в каждой группе показывает разницу (в процентах) между последним значением, c для которого его b значение равно 0, и последним значением, для которого его b значение равно 1.

Например, в первой группе я хочу сравнить c строки 2 и строки 1.

Мое желаемое groups выглядит следующим образом:

 ('x', 0)
   a  b    c  d   result
0  x  0  100  0     3.96
1  x  0  101  0     3.96
2  x  1  105  0     3.96
('x', 1)
   a  b    c  d   result
3  x  0  110  1     9.09
4  x  1  120  1     9.09
('z', 0)
   a  b    c  d   result
5  z  0  125  0     20.0
6  z  1  100  0     20.0
7  z  1  150  0     20.0
  

Ответ №1:

Определите пользовательскую функцию и используйте GroupBy.apply

 def func(s):
    l0 = s[s.b==0].tail(1).c.item()
    l1 = s[s.b==1].tail(1).c.item()
    s['result'] = (l1 - l0)/l0 * 100
    return s

df.groupby(['a','d']).apply(func)
  

Выводит

     a   b   c   d   result
0   x   0   100 0   3.960396
1   x   0   101 0   3.960396
2   x   1   105 0   3.960396
3   x   0   110 1   9.090909
4   x   1   120 1   9.090909
5   z   0   125 0   20.000000
6   z   1   100 0   20.000000
7   z   1   150 0   20.000000
  

Если вам нужна каждая группа отдельно, просто используйте представление списка [func(g) for n, g in df.groupby(['a','d'])]

Ответ №2:

Здесь мы можем сделать следующее:

  1. Примените .pct_change метод для вычисления процентного изменения каждой строки
  2. Условно заполните значения result столбца NaN
  3. После этого примените fillna с bfill помощью или ffill
 # first we apply .pct_change to all rows
df['result'] = abs(round(df.groupby(['a', 'd', 'b']).c.pct_change() * 100, 2))

# after that we check if the value if b = 1 and the value of the row before = 0 and we fill in NaN if condition not true
df['result'] = np.where((df.b == 1) amp; (df.b.shift(1) == 0), df.result, np.NaN)
  

Итак, мы получаем:

    a  b    c  d  result
0  x  0  100  0     NaN
1  x  0  101  0     NaN
2  x  1  105  0    3.96
3  x  0  110  1     NaN
4  x  1  120  1    9.09
5  z  0  125  0     NaN
6  z  1  100  0   20.00
7  z  1  150  0     NaN
  
 # then backfill and forwardfill NaN
df.result.fillna(method='bfill', inplace=True)
df.result.fillna(method='ffill', inplace=True)

print(df)
   a  b    c  d  result
0  x  0  100  0    3.96
1  x  0  101  0    3.96
2  x  1  105  0    3.96
3  x  0  110  1    9.09
4  x  1  120  1    9.09
5  z  0  125  0   20.00
6  z  1  100  0   20.00
7  z  1  150  0   20.00