Pandas Dataframe: замена NaN на средний уровень

Я пытаюсь изучить pandas, но я был озадачен следующим, пожалуйста. Я хочу заменить NaNs, это dataframe со средним значением строки. Следовательно, что-то вроде 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

Ответ 1

Как прокомментировал аргумент оси 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

Ответ 2

В качестве альтернативы вы также можете использовать 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

Ответ 3

Как уже fillna(<value>, >axis=1) ранее: fillna(<value>, >axis=1) еще не реализована в Pandas, поэтому я использую операцию Transpose для ее выполнения:

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

Это может не иметь проблем с производительностью, так как Transopose отмечает копирование данных, и работал для меня.

Ответ 4

Я предложу альтернативу, которая включает в себя приведение к массивам. С точки зрения производительности, я думаю, что это более эффективно и, вероятно, масштабируется лучше, чем другие предложенные решения.

Идея состоит в том, чтобы использовать матрицу индикатора (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