DataFrame принимает объединение столбцов и сохраняет поиск первого значения, отличного от NaN

#python #pandas #dataframe

#python #панды #dataframe

Вопрос:

Dataframe df содержит много тысяч столбцов и строк. Для подмножества столбцов, которые заданы в определенной последовательности, скажем, столбцов B, C, E , я хочу заполнить NaN значения B первым значением, отличным от NaN, найденным в оставшихся столбцах ( C, E ), последовательно выполняя поиск. Наконец C, E , отбрасываются

Образец df может быть построен следующим образом:

 import numpy as np
import pandas as pd
df = pd.DataFrame(10*(2 np.random.randn(6, 5)), columns=list('ABCDE'))
df.loc[1, 'B'] = np.nan
df.loc[2, 'B'] = np.nan
df.loc[5, 'B'] = np.nan
df.loc[2, 'C'] = np.nan
df.loc[5, 'C'] = np.nan
df.loc[2, 'D'] = np.nan
df.loc[2, 'E'] = np.nan
df.loc[4, 'E'] = np.nan
df
           A         B          C          D          E
0  18.161033  6.453597  25.253036  18.542586  20.667311
1  27.629402       NaN  40.654821  22.804547  23.633502
2  15.459256       NaN        NaN        NaN        NaN
3  19.115203  4.002131  14.167508  23.796780  29.557706
4  27.180622       NaN  20.763618  15.923794        NaN
5  17.917170       NaN        NaN  21.865184   9.867743
  

Ожидаемый результат заключается в следующем:

            A         B         D
0  18.161033  6.453597 18.542586
1  27.629402 40.654821 22.804547
2  15.459256       NaN       NaN
3  19.115203  4.002131 23.796780
4  27.180622 20.763618 15.923794
5  17.917170  9.867743 21.865184
  

Ответ №1:

Вот один из способов

 drop = ['C', 'E']
fill= 'B'
d=dict(zip(df.columns,[fill if x in drop else x for x in df.columns.tolist() ]))
df.groupby(d,axis=1).first()
Out[172]: 
           A          B          D
0  14.472915  30.598602  24.528571
1  22.010242  22.215140  15.412039
2   5.383674        NaN        NaN
3  38.265940  24.746673  35.367622
4  22.730089  20.244289  27.570413
5  31.216037  15.496690   9.746814
  

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

1. интересное использование groupby здесь!

2. @coldspeed ага, я чувствую, что это даст больше гибкости в функции 🙂

Ответ №2:

IIUC, используйте bfill для обратной засыпки, а затем drop для удаления ненужных столбцов.

 df.assign(B=df[['B', 'C', 'E']].bfill(axis=1)['B']).drop(['C', 'E'], axis=1)

           A          B          D
0  18.161033   6.453597  18.542586
1  27.629402  40.654821  22.804547
2  15.459256        NaN        NaN
3  19.115203   4.002131  23.796780
4  27.180622  20.763618  15.923794
5  17.917170   9.867743  21.865184
  

Вот немного более обобщенная версия приведенной выше,

 to_drop = ['C', 'E']
upd = 'B'

df.update(df[[upd, *to_drop]].bfill(axis=1)[upd])  # in-place
df.drop(to_drop, axis=1)                           # not in-place, need to assign

           A          B          D
0  18.161033   6.453597  18.542586
1  27.629402  40.654821  22.804547
2  15.459256        NaN        NaN
3  19.115203   4.002131  23.796780
4  27.180622  20.763618  15.923794
5  17.917170   9.867743  21.865184
  

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

1. Это работает как шарм. Никогда не знал об bfill этом раньше.