Ответ 1
Этот метод почти идентичен вашему, просто больше numpyst (также работает только с массивами numpy):
def reject_outliers(data, m=2):
return data[abs(data - np.mean(data)) < m * np.std(data)]
Есть ли встроенный numpy, чтобы сделать что-то вроде следующего? То есть, возьмите список d
и верните список filtered_d
с любыми удаленными элементами, удаленными на основе некоторого предполагаемого распределения точек в d
.
import numpy as np
def reject_outliers(data):
m = 2
u = np.mean(data)
s = np.std(data)
filtered = [e for e in data if (u - 2 * s < e < u + 2 * s)]
return filtered
>>> d = [2,4,5,1,6,5,40]
>>> filtered_d = reject_outliers(d)
>>> print filtered_d
[2,4,5,1,6,5]
Я говорю "что-то вроде", потому что функция может допускать различные распределения (пуассоны, гауссовы и т.д.) и различные пороги выбросов в этих дистрибутивах (например, m
, которые я использовал здесь).
Этот метод почти идентичен вашему, просто больше numpyst (также работает только с массивами numpy):
def reject_outliers(data, m=2):
return data[abs(data - np.mean(data)) < m * np.std(data)]
Что важно при работе с выбросами, так это то, что нужно стараться использовать как можно более надежные оценки. Среднее распределение будет смещено выбросами, но, например, Медиана будет намного меньше.
Опираясь на ответ eumiro:
def reject_outliers(data, m = 2.):
d = np.abs(data - np.median(data))
mdev = np.median(d)
s = d/mdev if mdev else 0.
return data[s<m]
Здесь я должен заменить среднее значение более устойчивой медианой, а стандартное отклонение - абсолютным расстоянием до медианы. Затем я масштабировал расстояния по их (опять же) срединному значению, чтобы m
находился в разумном относительном масштабе.
Обратите внимание, что для работы синтаксиса data[s<m]
, data
должен быть пустым массивом.
Ответ бенджамина Банье дает возможность прохода, когда медиана расстояний от медианы равна 0, поэтому я нашел эту модифицированную версию несколько более полезной для случаев, приведенных в примере ниже.
def reject_outliers_2(data, m=2.):
d = np.abs(data - np.median(data))
mdev = np.median(d)
s = d / (mdev if mdev else 1.)
return data[s < m]
Пример:
data_points = np.array([10, 10, 10, 17, 10, 10])
print(reject_outliers(data_points))
print(reject_outliers_2(data_points))
дает:
[[10, 10, 10, 17, 10, 10]] # 17 is not filtered
[10, 10, 10, 10, 10] # 17 is filtered (it distance, 7, is greater than m)
На основе Benjamin's, используя pandas.Series
, и заменив MAD с IQR:
def reject_outliers(sr, iq_range=0.5):
pcnt = (1 - iq_range) / 2
qlow, median, qhigh = sr.dropna().quantile([pcnt, 0.50, 1-pcnt])
iqr = qhigh - qlow
return sr[ (sr - median).abs() <= iqr]
Например, если вы установите iq_range=0.6
, процентили межквартильного диапазона станут: 0.20 <--> 0.80
, поэтому будет добавлено больше выбросов.
Альтернативой является создание надежной оценки стандартного отклонения (предполагая гауссову статистику). Если посмотреть онлайн-калькуляторы, я вижу, что 90% процентиль соответствует 1.2815 & сигма; и 95% составляет 1,645 & sigma; (http://vassarstats.net/tabs.html?#z)
В качестве простого примера:
import numpy as np
# Create some random numbers
x = np.random.normal(5, 2, 1000)
# Calculate the statistics
print("Mean= ", np.mean(x))
print("Median= ", np.median(x))
print("Max/Min=", x.max(), " ", x.min())
print("StdDev=", np.std(x))
print("90th Percentile", np.percentile(x, 90))
# Add a few large points
x[10] += 1000
x[20] += 2000
x[30] += 1500
# Recalculate the statistics
print()
print("Mean= ", np.mean(x))
print("Median= ", np.median(x))
print("Max/Min=", x.max(), " ", x.min())
print("StdDev=", np.std(x))
print("90th Percentile", np.percentile(x, 90))
# Measure the percentile intervals and then estimate Standard Deviation of the distribution, both from median to the 90th percentile and from the 10th to 90th percentile
p90 = np.percentile(x, 90)
p10 = np.percentile(x, 10)
p50 = np.median(x)
# p50 to p90 is 1.2815 sigma
rSig = (p90-p50)/1.2815
print("Robust Sigma=", rSig)
rSig = (p90-p10)/(2*1.2815)
print("Robust Sigma=", rSig)
Выход, который я получаю:
Mean= 4.99760520022
Median= 4.95395274981
Max/Min= 11.1226494654 -2.15388472011
Sigma= 1.976629928
90th Percentile 7.52065379649
Mean= 9.64760520022
Median= 4.95667658782
Max/Min= 2205.43861943 -2.15388472011
Sigma= 88.6263902244
90th Percentile 7.60646688694
Robust Sigma= 2.06772555531
Robust Sigma= 1.99878292462
Что близко к ожидаемому значению 2.
Если мы хотим удалить точки выше/ниже 5 стандартных отклонений (с 1000 точками мы ожидаем 1 значение > 3 стандартных отклонения):
y = x[abs(x - p50) < rSig*5]
# Print the statistics again
print("Mean= ", np.mean(y))
print("Median= ", np.median(y))
print("Max/Min=", y.max(), " ", y.min())
print("StdDev=", np.std(y))
Что дает:
Mean= 4.99755359935
Median= 4.95213030447
Max/Min= 11.1226494654 -2.15388472011
StdDev= 1.97692712883
Я понятия не имею, какой подход является более эффективным/надежным
Я хотел сделать что-то подобное, за исключением того, что установил число на NaN, а не удалял его из данных, так как, если вы удалите его, вы измените длину, которая может испортить график (то есть, если вы удаляете выбросы только из одного столбца в таблице, но вам нужно, чтобы он оставался таким же, как и другие столбцы, чтобы вы могли наносить их друг на друга).
Для этого я использовал функции маскировки пустышки:
def reject_outliers(data, m=2):
stdev = np.std(data)
mean = np.mean(data)
maskMin = mean - stdev * m
maskMax = mean + stdev * m
mask = np.ma.masked_outside(data, maskMin, maskMax)
print('Masking values outside of {} and {}'.format(maskMin, maskMax))
return mask
В этом ответе я хотел бы предоставить два метода: решение, основанное на "z Score", и решение, основанное на "IQR".
Код, приведенный в этом ответе, работает как с одним массивом dim numpy
, так и с несколькими массивами numpy
.
Давайте сначала импортируем некоторые модули.
import collections
import numpy as np
import scipy.stats as stat
from scipy.stats import iqr
Этот метод проверяет, выходит ли число за пределы трех стандартных отклонений. Исходя из этого правила, если значение является выбросом, метод вернет true, если нет - false.
def sd_outlier(x, axis = None, bar = 3, side = 'both'):
assert side in ['gt', 'lt', 'both'], 'Side should be 'gt', 'lt' or 'both'.'
d_z = stat.zscore(x, axis = axis)
if side == 'gt':
return d_z > bar
elif side == 'lt':
return d_z < -bar
elif side == 'both':
return np.abs(d_z) > bar
Этот метод проверяет, является ли значение меньше, чем q1 - 1.5 * iqr
или больше, чем q3 + 1.5 * iqr
, что аналогично методу построения графика SPSS.
def q1(x, axis = None):
return np.percentile(x, 25, axis = axis)
def q3(x, axis = None):
return np.percentile(x, 75, axis = axis)
def iqr_outlier(x, axis = None, bar = 1.5, side = 'both'):
assert side in ['gt', 'lt', 'both'], 'Side should be 'gt', 'lt' or 'both'.'
d_iqr = iqr(x, axis = axis)
d_q1 = q1(x, axis = axis)
d_q3 = q3(x, axis = axis)
iqr_distance = np.multiply(d_iqr, bar)
stat_shape = list(x.shape)
if isinstance(axis, collections.Iterable):
for single_axis in axis:
stat_shape[single_axis] = 1
else:
stat_shape[axis] = 1
if side in ['gt', 'both']:
upper_range = d_q3 + iqr_distance
upper_outlier = np.greater(x - upper_range.reshape(stat_shape), 0)
if side in ['lt', 'both']:
lower_range = d_q1 - iqr_distance
lower_outlier = np.less(x - lower_range.reshape(stat_shape), 0)
if side == 'gt':
return upper_outlier
if side == 'lt':
return lower_outlier
if side == 'both':
return np.logical_or(upper_outlier, lower_outlier)
Наконец, если вы хотите отфильтровать выбросы, используйте селектор numpy
.
Хорошего дня.
Учтите, что все вышеперечисленные методы не работают, когда ваше стандартное отклонение становится очень большим из-за огромных выбросов.
(Simalar, так как среднее вычисление терпит неудачу и должно скорее вычислять среднее значение. Хотя среднее значение "более подвержено такой ошибке, как stdDv".)
Вы можете попытаться итеративно применить свой алгоритм или выполнить фильтрацию, используя межквартильный диапазон: (здесь "фактор" относится к диапазону n * sigma, но только тогда, когда ваши данные соответствуют распределению Гаусса)
import numpy as np
def sortoutOutliers(dataIn,factor):
quant3, quant1 = np.percentile(dataIn, [75 ,25])
iqr = quant3 - quant1
iqrSigma = iqr/1.34896
medData = np.median(dataIn)
dataOut = [ x for x in dataIn if ( (x > medData - factor* iqrSigma) and (x < medData + factor* iqrSigma) ) ]
return(dataOut)