Обнаружение и исключение выбросов в кадре данных Pandas
У меня есть фрейм данных панд с несколькими столбцами.
Теперь я знаю, что определенные строки являются выбросами на основе определенного значения столбца.
Например,
столбец 'Vol' имеет все значения около 12xx
, а одно значение - 4000
(выброс).
Теперь я хотел бы исключить те строки, в которых есть столбец Vol
, как этот.
Итак, по сути, мне нужно поместить фильтр на фрейм данных так, чтобы мы выбирали все строки, в которых значения определенного столбца находятся в пределах, скажем, 3 стандартных отклонения от среднего значения.
Какой элегантный способ добиться этого?
Ответы
Ответ 1
Если в вашем фрейме данных есть несколько столбцов и вы хотите удалить все строки, у которых есть выбросы хотя бы в одном столбце, следующее выражение сделает это за один кадр.
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) гарантирует, что для каждой строки все столбцы удовлетворяют ограничению.
- Наконец, результат этого условия используется для индексации кадра данных.
Ответ 2
Используйте 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())]
Ответ 3
Для каждого столбца dataframe вы можете получить квантиль с помощью:
q = df["col"].quantile(0.99)
а затем с помощью:
df[df["col"] < q]
Ответ 4
Этот ответ аналогичен тому, который предоставляется @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]
Ответ 5
#------------------------------------------------------------------------------
# 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
Ответ 6
Для каждой серии в кадре данных вы можете использовать between
и quantile
чтобы удалить выбросы.
x = pd.Series(np.random.normal(size=200)) # with outliers
x = x[x.between(x.quantile(.25), x.quantile(.75))] # without outliers
Ответ 7
Поскольку я не видел ответа, касающегося числовых и нечисловых атрибутов, здесь приведен дополнительный ответ.
Возможно, вы захотите сбросить выбросы только для числовых атрибутов (категориальные переменные вряд ли могут быть выбросами).
Определение функции
Я добавил предложение @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)
Usage
Usage
drop_numerical_outliers(df)
ПримерExample
Представьте набор данных 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]()
Ответ 8
scipy.stats
имеет методы trim1()
и trimboth()
, чтобы вырезать выбросы в одной строке в соответствии с ранжированием и введенным процентом удаленных значений.
Ответ 9
Другой вариант - преобразовать ваши данные, чтобы смягчить эффект выбросов. Вы можете сделать это, используя ваши данные.
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]()
Ответ 10
Если вам нравится цепочка методов, вы можете получить свое логическое условие для всех числовых столбцов, например:
df.sub(df.mean()).div(df.std()).abs().lt(3)
Каждое значение каждого столбца будет преобразовано в True/False
в зависимости от того, будет ли его меньше трех стандартных отклонений от среднего или нет.
Ответ 11
Вы можете использовать логическую маску:
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
Ответ 12
Поскольку я нахожусь на очень ранней стадии моего пути в науке о данных, я лечу выбросы с помощью приведенного ниже кода.
#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
Ответ 13
Ниже приведен полный пример с данными и двумя группами:
Импорт:
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)
Ответ 14
Моя функция отбрасывать выбросы
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)
Ответ 15
Я предпочитаю обрезать, а не уронить. следующее будет закреплено на 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)))
Ответ 16
Получите 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
Ответ 17
Удаление и удаление выбросов, которые я считаю статистически неправильными. Это отличает данные от исходных данных. Также делает данные неравномерной формы и, следовательно, лучший способ состоит в том, чтобы уменьшить или избежать влияния выбросов путем лог-преобразования данных. Это сработало для меня:
np.log(data.iloc[:, :])