Daniel Szukitsch

things i do

Ausreißer in Einer Liste Von Zahlen Erkennen

Vor kurzem fragte mich einer meiner Freunde ob ich eine Idee hätte wie man mehrere Ausreißer in einer kleinen Liste von Zahlen (maximal 8 Zahlen) erkennen könnte. Ich hatte zwar eine ungefähre Idee wie man das Problem angehen könnte, aber das Problem war doch schwieriger als ich dachte und begann im Internet zu recherchieren.

Die für mich am besten für mich passende Möglichkeit Ausreißer zu finden ist die modified Z-scores Methode von Boris Iglewicz und David Hoaglin. Mit dieser Methode ist es möglich mehrere Ausreißer in einer kleinen( ≤ 8 Elemente) Liste zu identifizieren. Größere Listen funktionieren ebenfalls ;)

Die modified Z-scores Methode berechnet für jedes Element in der Liste einen modified Z-score Mi und prüft diesen Wert gegen einen threshold (typischerweise 3.5). Wenn der modified Z-score eines Elements den threshold übersteigt wird das Element als Ausreißer markiert.

Der modified Z-score Mi wird wie folgt berechnet: M i = 0.6745 ( x i x ~ ) MAD

Der in der Formel angegebene MAD ist die median absolute deviation.

Mit Hilfe dieser Formel habe ich erst mal einen kleinen Prototypen in python gebaut um das ganze testen zu können.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import numpy as np

def calc_mad(data, median):
  medians = []

  for x in data:
      medians.append(np.abs(x - median))

  return np.median(medians)

def iglewicz_hoaglin(threshold, data):
    median = np.median(data)
    mad = calc_mad(data, median)
    outliers = []

    for x in data:
      score = 0.6745 * (x - median) / mad
      print(str(x) + ':\t' + str(score))

      if(score > threshold):
          outliers.append(x)

    return outliers

threshold = 3.5
data = [10, 22, 30, 100, 15, 80, 8, 9]

modified_z_scores = iglewicz_hoaglin(threshold,data)
print('\nresult: '+ str(modified_z_scores))

Bei meinen ersten Tests hat diese Implementation ohne Probleme funktioniert, jedoch bin ich bald auf ein Problem gestoßen, welches ich nicht alleine lösen konnte. Beim testen der Liste [8,8,8,8,8,8,100] wurde der MAD=0 was einen division by 0 error beim berechnen des Mi auslöste. Das Problem liegt daran, dass der MAD immer 0 wird wenn mehr als die Hälfte der Element in der Liste den gleichen Wert haben.

Mir gelang es zwar einen Workaround zu basteln der funktionierte, jedoch sich falsch anfühlte. Also beschloss ich um Hilfe im Statistikableger von StackExchange Cross Validated zu fragen. Mit der dortigen Hilfe konnte ich das Problem lösen. Immer wenn der MAD 0 wird muss der MAD auf die kleinste mögliche Zahl gesetzt werden.

Zum Schluss habe ich, ein gist geschrieben, dass vollkommen ohne imports funktioniert:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def calc_median(data):
    result = 0.0
    length = len(data)

    if length % 2 == 0:
        index01 = (length/2) - 1
        index02 = index01 + 1
        result = (data[index01] + data[index02])/2.0

    else:
        result = data[length//2]

    return result

def calc_mad(data, median):
    medians = []

    for x in data:
        medians.append(abs(x - median))

    medians.sort()
    mad =  calc_median(medians)

    if mad == 0:
        mad = 2.2250738585072014e-308 # sys.float_info.min

    return mad

def iglewicz_hoaglin(threshold, data):
    data.sort()
    median = calc_median(data)
    mad = calc_mad(data, median)
    result = []

    print('threshold:' + str(threshold))
    print('median:' + str(median))
    print('MAD:' + str(mad))
    print('data:'+ str(data))
    print('')

    for x in data:
      score = abs(0.6745 * (x - median) / mad)
      print(str(x) + ':\t' + str(score))

      if(score > threshold):
          result.append(x)

    return result

threshold = 3.5
data = [10, 22, 30, 100, 15, 80, 8, 9]

modified_z_scores = iglewicz_hoaglin(threshold,data)
print('\nresult: '+ str(modified_z_scores))

Links und Quellen:

« verschwundene Windows 10 taskbar/desktop icons