Функция np.mean() заполняет массив NaN

data = [[None,4,None,3,4],[2,3,4,5,4]]
data1 = [[None,4,None,4,4],[2,3,4,5,4]]
df = pd.DataFrame(data)
df1 = pd.DataFrame(data1)
def x(x):
  return (x-np.mean(x))/(np.max(x)-np.min(x))
norm = df.apply(x, axis=1)
norm2 = df1.apply(x, axis=1)
print(norm)
print(norm2)

Результат:

# data
          0         1         2         3         4
0       NaN  0.333333       NaN -0.666667  0.333333
1 -0.533333 -0.200000  0.133333  0.466667  0.133333

# data1
          0    1         2         3         4
0       NaN  NaN       NaN       NaN       NaN
1 -0.533333 -0.2  0.133333  0.466667  0.133333

Почему все данные во втором примере NaN? У них же одинаковое количество данных, только во втором примере все известные данные 4.


Ответы (1 шт):

Автор решения: Алексей Р

В случае, когда max()==min(), знаменатель (np.max(x)-np.min(x)) == 0. Возникает ошибка деления на 0 и вместо числа получается NaN.
Что с этим делать?

Вариант 1 - немного изменить функцию x(), добавив обработку при max()==min(). А именно, например, сделав знаменатель 1. Тогда числитель равен 0 (при max()==min()==x==mean()). Ноль делится на 1, получаем ноль (а там, где были NaN, они останутся). К слову, библиотеку np нет необходимости подключать явно, т.к. метод mean() есть в pandas:

def x(x):
    mx, mn = x.agg(('max', 'min'))
    if mx == mn:
        base = 1
    else:
        base = mx - mn
    return x.sub(x.mean()).div(base)


norm = df.apply(x, axis=1)
norm2 = df1.apply(x, axis=1)
print(norm)
print(norm2)

Получаем на исходных данных:

          0         1         2         3         4
0       NaN  0.333333       NaN -0.666667  0.333333
1 -0.533333 -0.200000  0.133333  0.466667  0.133333
          0    1         2         3         4
0       NaN  0.0       NaN  0.000000  0.000000
1 -0.533333 -0.2  0.133333  0.466667  0.133333

Вариант2 - я бы предложил уйти от apply() и переписать функцию нормализации так, чтобы подавать на вход сразу весь исходный фрейм, а на выходе получать нормализованный:

def normalize(df):
    base = df1.max(axis=1).sub(df1.min(axis=1)).replace(0, 1)
    return df.sub(df.mean(axis=1), axis=0).div(base, axis=0)


print(normalize(df))
print(normalize(df1))

На выходе тот же результат

          0         1         2         3         4
0       NaN  0.333333       NaN -0.666667  0.333333
1 -0.533333 -0.200000  0.133333  0.466667  0.133333
          0    1         2         3         4
0       NaN  0.0       NaN  0.000000  0.000000
1 -0.533333 -0.2  0.133333  0.466667  0.133333

→ Ссылка