목록에서 이상 값을 거부하는 numpy 내장 기능이 있습니까?


101

다음과 같은 작업을 수행하는 numpy 내장이 있습니까? 즉, 목록을 가져 와서 에서 점의 일부 가정 분포를 기반으로 제거 된 외부 요소 가있는 목록 d을 반환합니다 .filtered_dd

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여기에서 사용한 것과 같은)을 허용 할 수 있기 때문에 '같은 것'이라고 말합니다 .


관련성 : scipy.stats가 명백한 이상 치를 식별하고 가릴 수 있습니까? , 그 질문은 더 복잡한 상황을 다루는 것 같습니다. 설명한 간단한 작업의 경우 외부 패키지가 과도하게 보입니다.
스벤 Marnach

나는 메인 numpy 라이브러리의 내장 기능을 고려할 때 이것을 할 것이 없다는 것이 이상하다고 생각했습니다. 원시의 시끄러운 데이터와 관련하여 매우 일반적인 일처럼 보입니다.
aaren

답변:


104

이 방법은 당신의 방법과 거의 동일하며 더 numpyst입니다 (또한 numpy 배열에서만 작동합니다).

def reject_outliers(data, m=2):
    return data[abs(data - np.mean(data)) < m * np.std(data)]

3
이 방법 m이 충분히 크면 (예 :) 충분히 잘 작동 m=6하지만 m이 값 이 작은 경우 분산이 강력한 추정자가 아닌 평균으로 인해 어려움을 겪습니다.
Benjamin Bannier

30
그건 정말 생각하는 방법에 대한 불평하지만, '국외자'의 모호한 개념에 대한 불만이 아니다
Eelco Hoogendoorn

m을 어떻게 선택합니까?
john ktejik

1
나는 이것을 작동하지 않았다. 계속 오류가 발생합니다. return data [abs (data-np.mean (data)) <m * np.std (data)] TypeError : 정수 스칼라 배열 만 스칼라 인덱스로 변환 할 수 있거나 내 프로그램을 동결합니다
john ktejik

@johnktejik 데이터 인수는 numpy 배열이어야합니다.
Sander van Leeuwen

181

특이 치를 다룰 때 중요한 것은 추정치를 가능한 한 강력하게 사용해야한다는 것입니다. 분포의 평균은 특이 치에 의해 편향되지만 예를 들어 중앙값은 훨씬 적습니다.

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]작업에 대한 구문, dataNumPy와 배열해야합니다.


5
itl.nist.gov/div898/handbook/eda/section3/eda35h.htm 이것은 기본적으로 여기에서 참조 된 수정 된 Z- 점수이지만 임계 값이 다릅니다. 내 수학이 맞으면 m을 권장합니다 3.5 / .6745 ~= 5.189( s.6745 를 곱하고 m3.5를 지정합니다 abs(s). 아무도 m의 선택을 설명 할 수 있습니까? 아니면 특정 데이터 세트에서 식별 할 수있는 것입니까?
Charlie G

2
@BenjaminBannier : m"순도와 효율성의 상호 작용"과 같은 푹신한 진술보다는 값을 선택하는 데 대한 구체적인 설명을 제공해 주 시겠습니까?
stackoverflowuser2010

1
@ stackoverflowuser2010 : 내가 말했듯이 이것은 특정 요구 사항, 즉 샘플 신호를 얼마나 깨끗하게 (거짓 긍정)해야하는지 또는 신호를 깨끗하게 유지하기 위해 버릴 수있는 신호 측정 수 (거짓 부정)에 따라 다릅니다. . 특정 사용 사례에 대한 구체적인 평가 예는 desy.de/~blist/notes/whyeffpur.ps.gz를 참조 하세요 .
Benjamin Bannier 2017-06-28

2
수레 목록으로 함수를 호출하면 다음 오류가 발생합니다. TypeError: only integer scalar arrays can be converted to a scalar index
Vasilis

2
당신이 그림에서 보면 @Charlie는 itl.nist.gov/div898/handbook/eda/section3/eda356.htm#MAD , 당신은 정규 분포를 처리 할 때 (사실은 당신이 필요 할 경우이하지 않은 것을 볼 수 있습니다 수정 된 z- 점수) SD = 1 인 경우 MAD ~ 0.68로 스케일링 계수를 설명합니다. 따라서 m = 3.5를 선택하면 데이터의 0.05 %를 버릴 것입니다.
Fato39

13

Benjamin Bannier의 답변은 중앙값으로부터의 거리 중앙값이 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)

9

Benjamin에서 빌드 하고을 사용 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되므로 더 많은 특이 치가 포함됩니다.


4

대안은 표준 편차를 강력하게 추정하는 것입니다 (가우스 통계 가정). 온라인 계산기를 보면 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

어떤 접근 방식이 더 효율적이고 강력한 지 모르겠습니다.


3

이 답변에는 "z 점수"에 기반한 솔루션과 "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 기반 방법

이 방법은 값이 SPSS의 플롯 방법과 유사한 q1 - 1.5 * iqr보다 작 거나 큰지 테스트합니다 q3 + 1.5 * iqr.

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선택기를 사용하십시오 .

좋은 하루 되세요.


3

큰 이상 값으로 인해 표준 편차가 매우 커지면 위의 모든 방법이 실패한다는 것을 고려하십시오.

( 평균 계산이 실패하고 오히려 중앙값을 계산해야하는 것과 유사합니다. 그러나 평균은 "stdDv와 같은 오류가 더 발생하기 쉽습니다". )

알고리즘을 반복적으로 적용하거나 사 분위수 범위를 사용하여 필터링 할 수 있습니다. (여기서 "인수"는 * 시그마 범위와 관련이 있지만 데이터가 가우시안 분포를 따르는 경우에만 해당)

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)

죄송합니다. 위의 IQR 제안이 이미 있음을 간과했습니다. 코드가 짧아서이 답변을 남기거나 삭제해야합니까?
K. Foe

1

데이터에서 숫자를 제거하는 대신 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

치수를 유지하기 위해 최소 및 최대 허용 값으로 np.clip 할 수도 있습니다.
Andi R

0

이상치의 인덱스 위치를 얻으려면 idx_list이를 반환합니다.

def reject_outliers(data, m = 2.):
        d = np.abs(data - np.median(data))
        mdev = np.median(d)
        s = d/mdev if mdev else 0.
        data_range = np.arange(len(data))
        idx_list = data_range[s>=m]
        return data[s<m], idx_list

data_points = np.array([8, 10, 35, 17, 73, 77])  
print(reject_outliers(data_points))

after rejection: [ 8 10 35 17], index positions of outliers: [4 5]

0

이미지 세트 (각 이미지에는 3 차원이 있음)의 경우 사용한 각 픽셀에 대한 이상 값을 거부하고 싶었습니다.

mean = np.mean(imgs, axis=0)
std = np.std(imgs, axis=0)
mask = np.greater(0.5 * std + 1, np.abs(imgs - mean))
masked = np.multiply(imgs, mask)

그런 다음 평균을 계산할 수 있습니다.

masked_mean = np.divide(np.sum(masked, axis=0), np.sum(mask, axis=0))

(배경 빼기에 사용합니다)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.