Pandas Dataframe: Замяна на NaN със средна стойност на ред

Опитвам се да науча панди, но бях озадачен от следното, моля. Искам да заменя NaNs е рамка от данни със средната стойност на реда. Следователно нещо като df.fillna(df.mean(axis=1)) трябва да работи, но по някаква причина не успява за мен. Пропускам ли нещо, моля, нещо, което правя грешно? Това е така, защото не е внедрено; вижте връзка тук

import pandas as pd
import numpy as np
​
pd.__version__
Out[44]:
'0.15.2'

In [45]:
df = pd.DataFrame()
df['c1'] = [1, 2, 3]
df['c2'] = [4, 5, 6]
df['c3'] = [7, np.nan, 9]
df

Out[45]:
    c1  c2  c3
0   1   4   7
1   2   5   NaN
2   3   6   9

In [46]:  
df.fillna(df.mean(axis=1)) 

Out[46]:
    c1  c2  c3
0   1   4   7
1   2   5   NaN
2   3   6   9

Въпреки това нещо подобно изглежда работи добре

df.fillna(df.mean(axis=0)) 

Out[47]:
    c1  c2  c3
0   1   4   7
1   2   5   8
2   3   6   9

person Aenaon    schedule 10.10.2015    source източник
comment
Тази функционалност все още не е внедрена - проблемът остава отворен.   -  person Alex Riley    schedule 10.10.2015


Отговори (5)


Както беше коментирано, аргументът axis на fillna е NotImplemented.

df.fillna(df.mean(axis=1), axis=1)

Забележка: това би било критично тук, тъй като не искате да попълвате своите n-ти колони със средната стойност на n-тия ред.

Засега ще трябва да преминете през:

In [11]: m = df.mean(axis=1)
         for i, col in enumerate(df):
             # using i allows for duplicate columns
             # inplace *may* not always work here, so IMO the next line is preferred
             # df.iloc[:, i].fillna(m, inplace=True)
             df.iloc[:, i] = df.iloc[:, i].fillna(m)

In [12]: df
Out[12]:
   c1  c2   c3
0   1   4  7.0
1   2   5  3.5
2   3   6  9.0

Алтернатива е да запълните транспонирането и след това да транспонирате, което може да е по-ефективно...

df.T.fillna(df.mean(axis=1)).T
person Andy Hayden    schedule 10.10.2015

Като алтернатива можете също да използвате apply с израз lambda като този:

df.apply(lambda row: row.fillna(row.mean()), axis=1)

отстъпчив също

    c1   c2   c3
0  1.0  4.0  7.0
1  2.0  5.0  3.5
2  3.0  6.0  9.0
person Cleb    schedule 12.12.2017

Ще предложа алтернатива, която включва кастинг в масиви numpy. Що се отнася до производителността, мисля, че това е по-ефективно и вероятно се мащабира по-добре от другите предложени решения досега.

Идеята е да се използва индикаторна матрица (df.isna().values, която е 1, ако елементът е N/A, 0 в противен случай) и да се умножи това по средните стойности на реда. Така завършваме с матрица (точно същата форма като оригиналния df), която съдържа средната стойност на реда, ако оригиналният елемент е N/A, и 0 в противен случай.

Добавяме тази матрица към оригиналния df, като се уверяваме, че запълваме с 0, така че всъщност да сме попълнили N/A със съответните средни стойности на реда.

# setup code
df = pd.DataFrame()
df['c1'] = [1, 2, 3]
df['c2'] = [4, 5, 6]
df['c3'] = [7, np.nan, 9]

# fillna row-wise
row_avgs = df.mean(axis=1).values.reshape(-1,1)
df = df.fillna(0) + df.isna().values * row_avgs
df

даване

    c1   c2   c3
0   1.0  4.0  7.0
1   2.0  5.0  3.5
2   3.0  6.0  9.0
person Troy    schedule 25.09.2019

Просто имах същия проблем. Открих, че това решение работи:

df.transpose().fillna(df.mean(axis=1)).transpose()

Не съм сигурен обаче за ефективността на това решение.

person LKho    schedule 20.02.2020
comment
Това не е ли същото като приетото решение, само че използвате .transpose вместо .T? - person Cleb; 28.11.2020

Можете да излъчите средната стойност към DataFrame със същия индекс като оригинала и след това да използвате update с overwrite=False, за да получите поведението на .fillna. За разлика от .fillna, update позволява попълване, когато индексите имат дублирани етикети. Трябва да е по-бързо от цикъла .fillna за по-малки от 50 000 реда или така.

fill = pd.DataFrame(np.broadcast_to(df.mean(1).to_numpy()[:, None], df.shape), 
                    columns=df.columns,
                    index=df.index)

df.update(fill, overwrite=False)
print(df)

     1    1    1
0  1.0  4.0  7.0
0  2.0  5.0  3.5
0  3.0  6.0  9.0
person ALollz    schedule 26.03.2020