значения обратной засыпки только для выбранных членов группы — панды

#pandas

Вопрос:

Это должно быть просто, но для завершения мне потребовалось слишком много строк:

У меня есть фрейм данных pandas, индексированный по идентификатору и году.

Для групп A и B я хочу заполнить только значения с 2022 по 2021 год (значения 2019 года следует оставить нетронутыми). См.таблицу ниже.

В конце концов это сработало, но было громоздко:

 df = df.swaplevel()
# create a frame with backfilled values
dfbf = df.loc[[2021,2022]].groupby('id')['value'].bfill(limit=1).to_frame()
df = df.join(dfbf, rsuffix = '_fill')
df = df.reset_index(level = "id")
df['value'].loc[2021] = df.loc[2021][['value', 'value_fill']].sum(axis = 1)
df = df.set_index(['id'], append=True).swaplevel()
 

Пример:

ценность ценность
ID год ID год
A 2019 A 2019
A 2020 1 A 2020 1
A 2021 A 2021 3
A 2022 3 A 2022 3
B 2019 B 2019
B 2020 12 B 2020 12
B 2021 B 2021 11
B 2022 11 B 2022 11

Ответ №1:

Выберите строки сначала DataFrame.loc с помощью GroupBy.bfill с DataFrame.update :

 df.update(df.loc[:, [2021,2022], :].groupby('id')['value'].bfill(limit=1))
print (df)
         value
id year       
A  2019    NaN
   2020    1.0
   2021    3.0
   2022    3.0
B  2019    NaN
   2020   12.0
   2021   11.0
   2022   11.0
 

Или используйте маску для ожидаемых строк (фильтруется с обеих сторон для повышения производительности — обрабатываются только выбранные строки, а не все строки):

 #include
m = df.index.get_level_values('year').isin([2021,2022])
#exclude
#m = df.index.get_level_values('year') != 2019
df.loc[m, 'value'] = df[m].groupby('id')['value'].bfill(limit=1)
print (df)
         value
id year       
A  2019    NaN
   2020    1.0
   2021    3.0
   2022    3.0
B  2019    NaN
   2020   12.0
   2021   11.0
   2022   11.0
 

Ответ №2:

Вы можете просто подмножествовать значения для замены, используя loc и условие на уровне «год», чтобы избежать 2019 года:

 df.loc[df.index.get_level_values('year')!=2019] = df.groupby(level=0).bfill()
 

выход:

          value
id year       
A  2019    NaN
   2020    1.0
   2021    3.0
   2022    3.0
B  2019    NaN
   2020   12.0
   2021   11.0
   2022   11.0
 

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

1. да, с точки зрения производительности необходим фильтр с обеих сторон, как в моем ответе.

2. @jezrael Вы имеете в виду, чтобы избежать вычисления заполнения, ненужного для 2019 года?

3. Я думаю в целом.

4. Если это действительно не нужно, я обычно предпочитаю удобочитаемость для первого кадра. Но я всегда ценю ваши проницательные ответы и комментарии, вы, кажется, освоили аспекты производительности панд 🙂

5. хммм, читаемость для add df[m] vs df менее читабельна? Я думаю, что нет. Только если передать 2 маски для 2 сторон, то согласитесь, читается хуже.