Есть ли встроенный numpy для отклонения выбросов из списка
есть ли 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
я использовал здесь).
7 ответов:
этот метод почти идентичен вашему, просто больше numpyst (также работает только с массивами numpy):
def reject_outliers(data, m=2): return data[abs(data - np.mean(data)) < m * np.std(data)]
что-то важное при работе с выбросами заключается в том, что следует попытаться использовать оценки как можно более надежные. Среднее значение распределения будет смещено выбросами, но, например, медиана будет намного меньше.
основываясь на ответе эумиро:
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
на разумный относительный масштаб.
опираясь на Бенджамина, используя
pandas.Series
и заменить сумасшедший с 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
, Так что больше выбросы будут включены.
ответ Бенджамина баннера дает проход, когда медиана расстояний от медианы равна 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's distance, 7, is greater than m)
альтернативой является робастная оценка стандартного отклонения (при условии гауссовой статистики). Глядя на онлайн калькуляторы, я вижу, что 90% процентиль соответствует 1.2815 σ, А 95% - 1.645 σ (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
Я понятия не имею, какой подход более эффективный/надежный
Я хотел бы предоставить два метода в этом ответе, решение на основе "Z score" и решение на основе "IQR".
код, приведенный в этом ответе, работает как на одном dim
numpy
массив и несколькоnumpy
массив.давайте сначала импортируем некоторые модули.
import collections import numpy as np import scipy.stats as stat from scipy.stats import iqr
Z оценка на основе метода
этот метод будет проверять, если число выходит за рамки трех стандартных отклонений. На основе этого правила, если значение является выбросом, метод будет верните 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
метод на основе IQR
этот метод будет проверять, если значение меньше
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
селектор.хорошего дня.
Я хотел сделать что-то подобное, за исключением установки числа в NaN, а не удаления его из данных, так как если вы удалите его, вы измените длину, которая может испортить построение (т. е. если вы только удаляете выбросы из одного столбца в таблице, но вам нужно, чтобы он оставался таким же, как и другие столбцы, чтобы вы могли построить их друг против друга).
для этого я использовал маскирующие функции numpy:
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