Chciałbym Ci przedstawić 3 skuteczne metody analizy normalności rozkładu w Python. Każda z nich ma inne zalety i możesz używać ich w zależności od potrzeb i kontekstu wykonywanej analizy.
Metod możesz używać rozłącznie lub razem, gdyż się wzajemnie uzupełniają:
- Pierwsza z nich bazuje na statystykach opisowych i parametrach rozkładu normalnego wynikających z jego definicji. Daje dosyć ogólny pogląd na to, czego możemy się po zbiorze spodziewać.
- Druga również jest metodą subiektywną, oparta o wizualizację.
- Trzecia jest w 100% obiektywna i bazuje na teście statystycznym.
Być może zadajesz sobie pytanie: w zasadzie to, po co badać zmienne pod kątem normalności? Odpowiedź na to pytanie jest relatywnie prosta: w większości przypadków nie musisz tego robić. 🙂
W statystyce i uczeniu maszynowym jest masa metod, które nie mają założeń dotyczących rozkładu zmiennych - tzw. metody nieparametryczne. Są jednak też takie, które mają pewne założenia i których to powinniśmy przestrzegać, tzw. metody parametryczne. To dla części metod z drugiej grupy analiza normalności rozkładu ma sens (ps. przynajmniej w części przypadków, ale nie chcę zbyt głęboko wchodzić w szczegóły ;)).
Zanim zaczniemy, poniżej przedstawiam informacje o zbiorze danych z którego będę korzystać:
- Wine quality
- Opis zbioru
- Opublikowany przez: Paulo Cortez.
1. Wczytanie niezbędnych bibliotek.¶
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import scipy.stats
2. Wczytanie zbioru.¶
white_wines = pd.read_csv('data/winequality-white.csv', sep = ';')
red_wines = pd.read_csv('data/winequality-red.csv', sep = ';')
Na potrzeby dalszej analizy łącze oba zbiory w jeden.
wines = pd.concat([white_wines, red_wines], axis = 0)
3. Podgląd scalonego zbioru.¶
wines.head()
print('Zbiór zawiera {} obserwacji i {} zmiennych.'.format(wines.shape[0], wines.shape[1]))
print('Lista zmiennych dostępnych w zbiorze: {}'.format(list(wines.columns)))
Chcesz pozbyć się skośności i wartości odstających ze zbioru? Sprawdź wpisy o kategoryzacji zmiennych ciągłych i transformacji z użyciem metody WoE.
4. Metody analizy normalności rozkładu.¶
4.1. Weryfikacja z użycie podstawowych statystyk.¶
Metoda pierwsza. Nie jest ona bardzo dokładna, lecz prosta, szybka, intuicyjna i daje pewne przesłanki na temat zmiennych.
Zgodnie z definicją, parametry rozkładu normalnego powinny wynosić:
- kurtoza = 0
- skośność = 0.
Wyznaczenie skośności rozkładu
Za pomocą metody agg
dostępnej w bibliotece Pandas wyznaczamy dwie statystyki: średnią i medianę. Zgodnie z założeniami rozkładu normalnego wartość średnia i mediana powinny być do siebie bardzo zbliżone (najlepiej równe). Wtedy mamy szanse podejrzewać rozkład zmiennej o brak skośności.
Różnice w wartościach świadczą o skośności rozkładu. Kolejno:
- średnia mniejsza od mediany - rozkład lewostronnie skośny (wydłużone lewe ramię rozkładu),
- średniej większa od mediany - rozkład prawostronnie skośny (wydłużone prawe ramię rozkładu).
Dla przypomnienia, poniżej przedstawiam przykład rozkładu lewostronnie (ujemny współczynnik skośności) i prawostronnie skośnego (dodatni współczynnik skośności).
wines.agg(['mean', 'median'])
W zbiorze jest jedna zmienna, której średnia jest nieco wyższa niż mediana ("residual sugar"). Może to wskazywać na skośność rozkładu. Należy się jednak dodatkowo upewnić. Można to zrobić w jednej linii kodu, poprzez wyznaczenie współczynnika skośności rozkładu, który bierze pod uwagę również odchylenie standardowe danej zmiennej.
wines.skew()
Uzyskane wyniki interpretujemy w sposób następujący:
- Współczynnik o wartości 0 to rozkład symetryczny.
- Współczynnik o wartości ujemnej to rozkład lewostronnie skośny (wydłużone lewe ramię rozkładu; średnia mniejsza od mediany).
- Współczynnik o wartości dodatniej to rozkład prawostronnie skośny (wydłużone prawe ramię rozkładu; średniej większa od mediany).
Wyznaczenie kurtozy
Kurtoza jest miarą spłaszczenia rozkładu. Mówi o tym, jak bardzo względem rozkładu normalnego jest spłaszczony (ujemne wartości kurtozy) lub wydęty (dodatnie wartości kurtozy) rozkład badanej zmiennej.
Dla porządku wyznaczę najpierw wartość bezwzględną, a później posortuję wartości.
wines.kurtosis().abs().sort_values()
Pandas daje nam możliwość połączenia podsumowania obu statystyk w jedną ramke danych. 🙂
wines.agg(['kurtosis', 'skew']).T
Na podstawie powyższych informacji o skośności i kurtozie można stwierdzić, że najbliżej normalności są zmienne: "total sulfur dioxide", "quality", "pH".
4.2. Wizualizacja rozkładów z użyciem wykresu gęstości.¶
Metoda numer dwa. W 100% subiektywna. Dla każdej zmiennej wykonam wykres gęstości i metodą "na oko" postaram się określić, które ze zmiennych cechują się rozkładem normalnym. 😉 Przy okazji zweryfikuje moje typy z poprzedniego punktu: "total sulfur dioxide", "quality" i "pH".
Dla przypomnienia rozkład normalny zgodny z definicją powinien wyglądać tak (ze zilustrowaną regułą trzech sigm):
print('Liczba zmiennych w zbiorze:', wines.shape[1])
Cały zbiór ma 12 zmiennych, więc wykonam wizualizację czwórkami.
Grupa 1.
f, axes = plt.subplots(2, 2, figsize=(15, 10))
sns.distplot(wines['fixed acidity'], color='skyblue', ax=axes[0, 0])
sns.distplot(wines['volatile acidity'], color='olive', ax=axes[0, 1])
sns.distplot(wines['citric acid'], color='gold', ax=axes[1, 0])
sns.distplot(wines['residual sugar'], color='teal', ax=axes[1, 1])
plt.show()
Z tej grupy najbliżej rozkładu normalnego jest zdecydowanie "fixed acidity".
Grupa 2.
f, axes = plt.subplots(2, 2, figsize=(15, 10))
sns.distplot(wines['chlorides'], color='skyblue', ax=axes[0, 0])
sns.distplot(wines['free sulfur dioxide'], color='olive', ax=axes[0, 1])
sns.distplot(wines['total sulfur dioxide'], color='gold', ax=axes[1, 0])
sns.distplot(wines['density'], color='teal', ax=axes[1, 1])
plt.show()
Z grupy numer 2 wszystkie zmienne wyglądają porównywalnie kiepsko. Widoczny tu jest jeden z moich typów: "total sulfur dioxide".
Grupa 3.
f, axes = plt.subplots(2, 2, figsize=(15, 10))
sns.distplot(wines['pH'], color='skyblue', ax=axes[0, 0])
sns.distplot(wines['sulphates'], color='olive', ax=axes[0, 1])
sns.distplot(wines['alcohol'], color='gold', ax=axes[1, 0])
sns.distplot(wines['quality'], color='teal', ax=axes[1, 1])
plt.show()
Spośród tej pozostałych zmiennych najlepiej wyglądają "quality" i "pH". Ta ostatnia w mojej ocenie jest "czarnym koniem" konkursu. 😀
4.3. Test na normalność rozkładu.¶
Metoda numer trzy. W 100% obiektywna, co nie znaczy, że w 100% skuteczna. Bazuje na teście statystycznym obarczonym pewnymi założenia dotyczącymi sposobu badania normalności. W przypadku tego testu użyte są statystyki skośności i kurtozy.
help(scipy.stats.normaltest)
Zgodnie z opisem testu: "This function tests the null hypothesis that a sample comes from a normal distribution.". A więc:
- H0 = brak podstaw na odrzucenie hipotezy zerowej - przyjmuję, że zmienna pochodzi z rozkładu normalnego,
- H1 = odrzucam hipotezę zerową - przyjmuję hipotezę alternatywn - zmienna nie pochodzi z rozkładu normalnego
results = []
for feature in wines.columns:
alpha = 0.05
p_value = scipy.stats.normaltest(wines[feature])[1]
results.append([feature, p_value])
if(p_value < alpha):
print('Dla zmiennej \'' + feature +'\' odrzucam hipotezę zerową. Zmienna NIE POCHODZI z rozkładu normalnego. P-value:', p_value)
else:
print('Dla zmiennej \'' + feature +'\' nie wykryto podstaw do odrzucenia hipitezy zerowej. Zmienna POCHODZI z rozkładu normalnego. P-value:', p_value)
Jak się okazuje, w zbiorze żadna ze zmiennych nie cechuje się rozkładem normalnym. Spójrzmy jeszcze na szczegółowe statystyki testu.
podsumowanie = pd.DataFrame(results)
podsumowanie.columns = ['nazwa_zmiennej', 'p_value']
podsumowanie.set_index('nazwa_zmiennej', inplace = True)
podsumowanie.sort_values('p_value', ascending = False, inplace = True)
podsumowanie
Założony przeze mnie poziom istotności wynosi alpha = 0.05
. Dla zmiennych o p_value > alpha
możemy mówić o normalności. Wszystkie zmienne są bardzo daleko od granicy 0.05. Najbliżej jest zmienna quality
, później total sulfur dioxide
i pH
, którą namaściłem cichym faworytem.
Przeczytaj również dwuczęściowy wpis, o tym jakich narzędzi używam w swojej pracy: część pierwsza, część druga.
5. Podsumowanie.¶
Pierwsze dwie metody pokazują, jak trudnym zadaniem jest określenie typu rozkładu bez dostępu do tetsu statystycznego. Pomimo pewnych przesłanek, żadna ze zmiennych nie okazała się cechować rozkładem normalnym.
W praktyce niestety sytuacja zazwyczaj wygląda podobnie. Niezbędne są testy, a następnie przekształcenia zmiennych mające na celu usuwanie skośności rozkładu i eliminację wartości odstających. ps. Ten ostatni temat przedstawię w kolejnym wpisie. 🙂
PODOBAŁ CI SIĘ TEN ARTYKUŁ?
Jeśli tak, to zarejestruj się, by otrzymywać informacje o nowych wpisach.
Dodatkowo w prezencie wyślę Ci bezpłatny poradnik :-)
super 🙂
Bardzo jasno opisana metodyka 🙂 na pewno będę tu zaglądać