Преобразование выбросов в Pandas DataFrame с использованием .apply, .applymap, .groupby

Я пытаюсь преобразовать объект pandas DataFrame в новый объект, который содержит классификацию точек на основе некоторых простых порогов:

  • Значение преобразуется в 0, если точка NaN
  • Значение преобразуется в 1, если точка отрицательная или равна 0
  • Значение преобразуется в 2, если оно не соответствует определенным критериям на основе всего столбца.
  • Значение 3 в противном случае

Вот очень простой автономный пример:

import pandas as pd
import numpy as np

df=pd.DataFrame({'a':[np.nan,1000000,3,4,5,0,-7,9,10],'b':[2,3,-4,5,6,1000000,7,9,np.nan]})

print(df)

введите здесь описание изображения

Процесс преобразования, созданный до сих пор:

#Loop through and find points greater than the mean -- in this simple example, these are the 'outliers'
outliers = pd.DataFrame()
for datapoint in df.columns:
    tempser = pd.DataFrame(df[datapoint][np.abs(df[datapoint]) > (df[datapoint].mean())])
    outliers = pd.merge(outliers, tempser, right_index=True, left_index=True, how='outer')

outliers[outliers.isnull() == False] = 2


#Classify everything else as "3"
df[df > 0] = 3

#Classify negative and zero points as a "1"
df[df <= 0] = 1

#Update with the outliers
df.update(outliers)

#Everything else is a "0"
df.fillna(value=0, inplace=True)

В результате чего:

введите здесь описание изображения

Я пытался использовать .applymap() и/ или .groupby(), чтобы ускорить процесс, если не повезло. Я нашел некоторые рекомендации в этом ответе, однако я все еще не уверен, насколько полезен .groupby(), когда вы не группируете внутри колонка панд.


person Clayton    schedule 23.06.2015    source источник


Ответы (1)


Вот замена части выбросов. Это примерно в 5 раз быстрее для ваших образцов данных на моем компьютере.

>>> pd.DataFrame( np.where( np.abs(df) > df.mean(), 2, df ), columns=df.columns )

    a   b
0 NaN   2
1   2   3
2   3  -4
3   4   5
4   5   6
5   0   2
6  -7   7
7   9   9
8  10 NaN

Вы также можете сделать это с помощью apply, но это будет медленнее, чем подход np.where (но примерно с той же скоростью, что вы сейчас делаете), хотя и намного проще. Вероятно, это хороший пример того, почему вы всегда должны избегать apply, если это возможно, когда вам важна скорость.

>>> df[ df.apply( lambda x: abs(x) > x.mean() ) ] = 2

Вы также можете сделать это быстрее, чем apply, но медленнее, чем np.where:

>>> mask = np.abs(df) > df.mean()
>>> df[mask] = 2

Конечно, эти вещи не всегда масштабируются линейно, поэтому протестируйте их на реальных данных и посмотрите, как они сравниваются.

person JohnE    schedule 23.06.2015
comment
Что касается выброса, я хочу, чтобы значения заменялись на 2 только тогда, когда они соответствуют условному оператору только для своего столбца, не для всего фрейма данных — я думаю, что ваши решения используют весь фрейм данных? - person Clayton; 23.06.2015
comment
@ cmiller8 Нет, это на столбец. Введите df.mean(), и вы увидите, что это дает вам среднее значение для каждого столбца. Вы также можете попробовать другие образцы данных, чтобы проверить их. - person JohnE; 24.06.2015
comment
ты прав! И ваш метод в 300 раз быстрее, чем 10 000 столбцов, 25 000 строк данных. - person Clayton; 24.06.2015