Замените значения NaN в столбце фрейма данных Pandas после использования centered .rolling() на первую вычисленную сумму

#python #pandas #dataframe #pandas-groupby

#python #pandas #фрейм данных #pandas-groupby

Вопрос:

Я довольно новичок в Pandas, и это также мой первый актуальный вопрос Stackoverflow, поэтому, пожалуйста, потерпите меня.

Я преобразую фрейм данных с помощью мультииндекса. Я должен вычислить движущуюся сумму из пяти наблюдений в каждом и выполнить ее по центру. Я сделал это при использовании groupby, так что скользящая сумма вычисляется внутри каждой группы, которая сгруппирована по полу, возрасту и типу. Однако это означает, что первая и последние две строки в каждой группе равны NaN . Я хочу, чтобы первые два значения NaN были равны третьему, а последние два были равны третьему последнему.

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

     Gender    Type   Age    Value
1   'f'       A      1       654
2   'f'       A      2       665
3   'f'       A      3       684
4   'f'       A      4       688
5   'f'       A      5       651
6   'f'       A      6       650
7   'f'       A      7       698
8   'f'       A      8       689
9   'f'       A      9       648
10  'f'       A      10      654
11  'f'       B      1       623
12  'f'       B      2       620
13  'f'       B      3       623
14  'f'       B      4       653
15  'f'       B      5       653
16  'f'       B      6       642
17  'f'       B      7       632
18  'f'       B      8       632
19  'f'       B      9       644
20  'f'       B      10      654
21  'm'       A      1       623
22  'm'       A      2       624
23  'm'       A      3       600
24  'm'       A      4       642
25  'm'       A      5       622
26  'm'       A      6       623
27  'm'       A      7       633
28  'm'       A      8       635
29  'm'       A      9       653
30  'm'       A      10      623
31  'm'       B      1       623
32  'm'       B      2       632
33  'm'       B      3       632
34  'm'       B      4       683
35  'm'       B      5       652
36  'm'       B      6       655
37  'm'       B      7       691
38  'm'       B      8       684
39  'm'       B      9       645
40  'm'       B      10      624
  

Это код, который я использую для вычисления скользящей суммы.

 df=df.reset_index().set_index(['Age'])
df=df.groupby(['Gender','Type'])['Value'].rolling(window=5,center=True).sum().reset_index()
  

Это вычисляет это:

 
    Gender    Type   Age    Value
1   'f'       A      1       NaN
2   'f'       A      2       NaN
3   'f'       A      3       3342
4   'f'       A      4       3338
5   'f'       A      5       3371
6   'f'       A      6       3376
7   'f'       A      7       3336
8   'f'       A      8       3339
9   'f'       A      9       NaN
10  'f'       A      10      NaN
11  'f'       B      1       NaN
12  'f'       B      2       NaN
13  'f'       B      3       3172
14  'f'       B      4       3191
15  'f'       B      5       3203
16  'f'       B      6       3212
17  'f'       B      7       3203
18  'f'       B      8       3204
19  'f'       B      9       NaN
20  'f'       B      10      NaN
21  'm'       A      1       NaN
22  'm'       A      2       NaN
23  'm'       A      3       x1
24  'm'       A      4       x2
25  'm'       A      5       x3
26  'm'       A      6       x4
27  'm'       A      7       x5
28  'm'       A      8       x7
29  'm'       A      9       NaN
30  'm'       A      10      NaN
31  'm'       B      1       NaN
32  'm'       B      2       NaN
33  'm'       B      3       x8
34  'm'       B      4       x9
35  'm'       B      5       x10
36  'm'       B      6       x11
37  'm'       B      7       x12
38  'm'       B      8       x13
39  'm'       B      9       NaN
40  'm'       B      10      NaN

  

X — это просто замена скользящих сумм.

Теперь моя проблема. Я хочу заменить значения NaN определенными ячейками в каждой группе. В частности, скользящая сумма за 1 и 2 года в каждой группе должна быть равна сумме за 3 года. Поскольку строка за 3 года также может быть NaN из-за отсутствия возможности вычисления, я не могу использовать код, который просто экстраполирует вперед и назад bfill или hfill . Если 3-летняя строка равна NaN, я хочу, чтобы в течение 1 года и 2 года также входили в группу.

Итак, следующий результат: хочу, я хочу:

     Gender    Type   Age    Value
1   'f'       A      1       3342
2   'f'       A      2       3342
3   'f'       A      3       3342
4   'f'       A      4       3338
5   'f'       A      5       3371
6   'f'       A      6       3376
7   'f'       A      7       3336
8   'f'       A      8       3339
9   'f'       A      9       3339
10  'f'       A      10      3339
11  'f'       B      1       3172
12  'f'       B      2       3172
13  'f'       B      3       3172
14  'f'       B      4       3191
15  'f'       B      5       3203
16  'f'       B      6       3212
17  'f'       B      7       3203
18  'f'       B      8       3204
19  'f'       B      9       3204
20  'f'       B      10      3204
21  'm'       A      1       x1
22  'm'       A      2       x1
23  'm'       A      3       x1
24  'm'       A      4       x2
25  'm'       A      5       x3
26  'm'       A      6       x4
27  'm'       A      7       x5
28  'm'       A      8       x7
29  'm'       A      9       x7
30  'm'       A      10      x7
31  'm'       B      1       x8
32  'm'       B      2       x8
33  'm'       B      3       x8
34  'm'       B      4       x9
35  'm'       B      5       x10
36  'm'       B      6       x11
37  'm'       B      7       x12
38  'm'       B      8       x13
39  'm'       B      9       x13
40  'm'       B      10      x13
  

Я действительно надеюсь, что один из вас мог бы мне помочь. Заранее спасибо.

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

1. не уверен, что я понимаю, какого результата вы ожидаете, если 3-й год равен NaN .. можете ли вы объяснить немного больше?

2. Извините, я понимаю, почему мое письмо вас смутило. Но скажите, что исходные данные NaN до 6 года в группе, зависящей от типа и пола. Следовательно, год 3 после скользящей суммы также будет NaN в этой группе. Но 4-й год этого не сделал. Если я просто использую bfill, то год 1-3 будет равен году 4. Я этого не хочу. Я хочу, чтобы год 1 и 2 были равны году 3. Таким образом, в этом случае год 1 2 также должен быть NaN, как и год 3.

Ответ №1:

После первоначального groupby rolling.sum groupby.transform использования попробуйте с клиентом def :

Настройка

Сделайте год 3 NaN для первой группы для тестирования

 df.loc[2, 'Value'] = np.nan

print(df)

   Gender Type  Age   Value
0     'f'    A    1     NaN
1     'f'    A    2     NaN
2     'f'    A    3     NaN
3     'f'    A    4  3338.0
4     'f'    A    5  3371.0
5     'f'    A    6  3376.0
6     'f'    A    7  3336.0
7     'f'    A    8  3339.0
8     'f'    A    9     NaN
9     'f'    A   10     NaN
10    'f'    B    1     NaN
...
  

Решение

 def custom_rolling_fillna(arr):
    arr.iloc[:2] = arr.iloc[2]
    arr.iloc[-2:] = arr.iloc[-3]
    return arr

df['Value'] = df.groupby(['Gender', 'Type'])['Value'].transform(custom_rolling_fillna)

print(df)

   Gender Type  Age   Value
0     'f'    A    1     NaN
1     'f'    A    2     NaN
2     'f'    A    3     NaN
3     'f'    A    4  3338.0
4     'f'    A    5  3371.0
5     'f'    A    6  3376.0
6     'f'    A    7  3336.0
7     'f'    A    8  3339.0
8     'f'    A    9  3339.0
9     'f'    A   10  3339.0
10    'f'    B    1  3172.0
...
  

Альтернативно, вы могли бы сделать это за один шаг, используя:

 def custom_rolling_fillna(arr):
    rolling = arr.rolling(window=5,center=True).sum()
    rolling.iloc[:2] = arr.iloc[2]
    rolling.iloc[-2:] = arr.iloc[-3]    
    return rolling


df['Value'] = df.groupby(['Gender', 'Type'])['Value'].transform(custom_rolling_fillna)
  

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

1. As 3 year row might also be NaN due to not meing computable, I can't use a code that just extrapolates forward and backwards a bfil

2. Спасибо за вашу помощь. Но это работает не совсем так, как хотелось, поскольку диапазон возраста может варьироваться в пределах каждой группы. В моем точном случае мне нужно 3-летнее значение для 1 и 2 года в каждой группе, и я хочу 108-летнее значение для 109 и 110 лет в каждой группе. Я предполагаю, что вы все еще можете использовать вариант функции custom_rolling_fillna, но используя loc вместо iloc . Но как именно?