Обнаружение и исключение выбросов в кадре данных Pandas

У меня есть пандас dataframe с несколькими столбцами.

Теперь я знаю, что определенные строки являются выбросами на основе определенного значения столбца.

Например, столбцы - "Vol" имеет все значения около 12xx, а одно значение равно 4000 (Outlier).

Теперь я хотел бы исключить те строки, которые имеют столбец "Vol", как это. Итак, по сути, мне нужно поместить фильтр на фрейм данных, чтобы мы выбирали все строки, где значения определенного столбца находятся в пределах, скажем, 3 стандартных отклонения от среднего значения.

Какой элегантный способ добиться этого.

+139
21 апр. '14 в 14:51
источник поделиться
17 ответов

Если в вашем фрейме данных есть несколько столбцов и вы хотите удалить все строки, у которых есть выбросы хотя бы в одном столбце, следующее выражение сделает это за один кадр.

df = pd.DataFrame(np.random.randn(100, 3))

from scipy import stats
df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]

описание:

  • Для каждого столбца сначала вычисляется Z-оценка каждого значения в столбце относительно среднего значения столбца и стандартного отклонения.
  • Тогда это берет абсолют Z-счета, потому что направление не имеет значения, только если оно ниже порога.
  • all (axis = 1) гарантирует, что для каждой строки все столбцы удовлетворяют ограничению.
  • Наконец, результат этого условия используется для индексации кадра данных.
+149
21 апр. '14 в 17:20
источник

Связанные вопросы


Похожие вопросы

Используйте boolean индексирование, как в numpy.array

df = pd.DataFrame({'Data':np.random.normal(size=200)})
# example dataset of normally distributed data. 

df[np.abs(df.Data-df.Data.mean()) <= (3*df.Data.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'Data'.

df[~(np.abs(df.Data-df.Data.mean()) > (3*df.Data.std()))]
# or if you prefer the other way around

Для серии это похоже:

S = pd.Series(np.random.normal(size=200))
S[~((S-S.mean()).abs() > 3*S.std())]
+121
21 апр. '14 в 15:44
источник

Для каждого столбца dataframe вы можете получить квантиль с помощью:

q = df["col"].quantile(0.99)

а затем с помощью:

df[df["col"] < q]
+65
29 мар. '17 в 12:22
источник

Этот ответ аналогичен тому, который предоставляется @tanemaki, но использует выражение lambda вместо scipy stats.

df = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))

df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < 3).all(axis=1)]

Чтобы фильтровать DataFrame, где только один столбец (например, "B" ) находится в пределах трех стандартных отклонений:

df[((df.B - df.B.mean()) / df.B.std()).abs() < 3]
+26
19 июл. '15 в 15:44
источник
#------------------------------------------------------------------------------
# accept a dataframe, remove outliers, return cleaned data in a new dataframe
# see http://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm
#------------------------------------------------------------------------------
def remove_outlier(df_in, col_name):
    q1 = df_in[col_name].quantile(0.25)
    q3 = df_in[col_name].quantile(0.75)
    iqr = q3-q1 #Interquartile range
    fence_low  = q1-1.5*iqr
    fence_high = q3+1.5*iqr
    df_out = df_in.loc[(df_in[col_name] > fence_low) & (df_in[col_name] < fence_high)]
    return df_out
+14
14 окт. '17 в 3:09
источник

Для каждой серии в кадре данных вы можете использовать between и quantile чтобы удалить выбросы.

x = pd.Series(np.random.normal(size=200)) # with outliers
x = x[x.between(x.quantile(.25), x.quantile(.75))] # without outliers
+8
22 янв. '19 в 4:16
источник

scipy.stats имеет методы trim1() и trimboth(), чтобы вырезать выбросы в одной строке в соответствии с ранжированием и введенным процентом удаленных значений.

+7
20 сент. '16 в 10:40
источник

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

import pandas as pd
from scipy.stats import mstats
%matplotlib inline

test_data = pd.Series(range(30))
test_data.plot()

Исходные данные

# Truncate values to the 5th and 95th percentiles
transformed_test_data = pd.Series(mstats.winsorize(test_data, limits=[0.05, 0.05])) 
transformed_test_data.plot()

Winsorized data

+3
13 июл. '17 в 14:14
источник

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

#Outlier Treatment

def outlier_detect(df):
    for i in df.describe().columns:
        Q1=df.describe().at['25%',i]
        Q3=df.describe().at['75%',i]
        IQR=Q3 - Q1
        LTV=Q1 - 1.5 * IQR
        UTV=Q3 + 1.5 * IQR
        x=np.array(df[i])
        p=[]
        for j in x:
            if j < LTV or j>UTV:
                p.append(df[i].median())
            else:
                p.append(j)
        df[i]=p
    return df
+1
21 нояб. '18 в 5:31
источник

Если вам нравится цепочка методов, вы можете получить свое логическое условие для всех числовых столбцов, например:

df.sub(df.mean()).div(df.std()).abs().lt(3)

Каждое значение каждого столбца будет преобразовано в True/False в зависимости от того, будет ли его меньше трех стандартных отклонений от среднего или нет.

+1
04 нояб. '17 в 2:22
источник

Ниже приведен полный пример с данными и двумя группами:

Импорт:

from StringIO import StringIO
import pandas as pd
#pandas config
pd.set_option('display.max_rows', 20)

Пример данных с двумя группами: G1: группа 1. G2: группа 2:

TESTDATA = StringIO("""G1;G2;Value
1;A;1.6
1;A;5.1
1;A;7.1
1;A;8.1

1;B;21.1
1;B;22.1
1;B;24.1
1;B;30.6

2;A;40.6
2;A;51.1
2;A;52.1
2;A;60.6

2;B;80.1
2;B;70.6
2;B;90.6
2;B;85.1
""")

Прочитать текстовые данные в pandas dataframe:

df = pd.read_csv(TESTDATA, sep=";")

Определите выбросы, используя стандартные отклонения

stds = 1.0
outliers = df[['G1', 'G2', 'Value']].groupby(['G1','G2']).transform(
           lambda group: (group - group.mean()).abs().div(group.std())) > stds

Определите значения отфильтрованных данных и выбросы:

dfv = df[outliers.Value == False]
dfo = df[outliers.Value == True]

Распечатать результат:

print '\n'*5, 'All values with decimal 1 are non-outliers. In the other hand, all values with 6 in the decimal are.'
print '\nDef DATA:\n%s\n\nFiltred Values with %s stds:\n%s\n\nOutliers:\n%s' %(df, stds, dfv, dfo)
0
20 мар. '18 в 1:06
источник

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

Возможно, вы захотите сбросить выбросы только для числовых атрибутов (категориальные переменные вряд ли могут быть выбросами).

Определение функции

Я расширил предложение @tanemaki для обработки данных, когда присутствуют также нечисловые атрибуты:

from scipy import stats

def drop_numerical_outliers(df, z_thresh=3):
    # Constrains will contain 'True' or 'False' depending on if it is a value below the threshold.
    constrains = df.select_dtypes(include=[np.number]) \
        .apply(lambda x: np.abs(stats.zscore(x)) < z_thresh, reduce=False) \
        .all(axis=1)
    # Drop (inplace) values set to be rejected
    df.drop(df.index[~constrains], inplace=True)

использование

drop_numerical_outliers(df)

пример

Представьте набор данных df с некоторыми значениями о домах: переулок, контур земли, цена продажи, например: документация данных

Во-первых, вы хотите визуализировать данные на диаграмме рассеяния (с z-счетом Thresh = 3):

# Plot data before dropping those greater than z-score 3. 
# The scatterAreaVsPrice function definition has been removed for readability sake.
scatterAreaVsPrice(df)

Before - Gr Liv Area Versus SalePrice

# Drop the outliers on every attributes
drop_numerical_outliers(train_df)

# Plot the result. All outliers were dropped. Note that the red points are not
# the same outliers from the first plot, but the new computed outliers based on the new data-frame.
scatterAreaVsPrice(train_df)

After - Gr Liv Area Versus SalePrice

0
23 июн. '19 в 15:33
источник

Моя функция отбрасывать выбросы

def drop_outliers(df, field_name):
    distance = 1.5 * (np.percentile(df[field_name], 75) - np.percentile(df[field_name], 25))
    df.drop(df[df[field_name] > distance + np.percentile(df[field_name], 75)].index, inplace=True)
    df.drop(df[df[field_name] < np.percentile(df[field_name], 25) - distance].index, inplace=True)
0
15 июн. '18 в 8:15
источник

Получите 98-й и 2-й процентили как пределы наших выбросов

upper_limit = np.percentile(X_train.logerror.values, 98) 
lower_limit = np.percentile(X_train.logerror.values, 2) # Filter the outliers from the dataframe
data[‘target].loc[X_train[‘target]>upper_limit] = upper_limit data[‘target].loc[X_train[‘target]<lower_limit] = lower_limit
0
25 апр. '19 в 0:47
источник

Я предпочитаю обрезать, а не уронить. следующее будет закреплено на 2-м и 98-м пестиле.

df_list = list(df)
minPercentile = 0.02
maxPercentile = 0.98

for _ in range(numCols):
    df[df_list[_]] = df[df_list[_]].clip((df[df_list[_]].quantile(minPercentile)),(df[df_list[_]].quantile(maxPercentile)))
0
30 окт. '18 в 19:40
источник

Вы можете использовать логическую маску:

import pandas as pd

def remove_outliers(df, q=0.05):
    upper = df.quantile(1-q)
    lower = df.quantile(q)
    mask = (df < upper) & (df > lower)
    return mask

t = pd.DataFrame({'train': [1,1,2,3,4,5,6,7,8,9,9],
                  'y': [1,0,0,1,1,0,0,1,1,1,0]})

mask = remove_outliers(t['train'], 0.1)

print(t[mask])

выход:

   train  y
2      2  0
3      3  1
4      4  1
5      5  0
6      6  0
7      7  1
8      8  1
0
21 мая '19 в 10:02
источник

Удаление и удаление выбросов, которые я считаю статистически неправильными. Это отличает данные от исходных данных. Также делает данные неравномерной формы и, следовательно, лучший способ состоит в том, чтобы уменьшить или избежать влияния выбросов путем лог-преобразования данных. Это сработало для меня:

np.log(data.iloc[:, :])
-3
21 окт. '18 в 11:35
источник

Посмотрите другие вопросы по меткам или Задайте вопрос