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