Veri Biliminde Normal Dağılımın R ve Python ile Testi ve Yorumlanması (Skewness ve Kurtosis, Shapiro-Wilk, Kolmogorov-Smirnov)

Yiğit Şener
Data Runner
Published in
6 min readSep 20, 2020

Normal dağılıma dair serinin üçüncü yazısında öne çıkan normallik varsayımı testlerini R ve Python üzerinden kodlayarak bahsedeceğiz. Serinin ilk yazısında normal dağılımın teorik yapısından bahsedilmektedir. İkincisinde ise örnek veri seti üzerinden grafikler yardımı ile normal dağılım incelemesi yapılmıştır. Bu yazıda ise ikinci yazıdaki veri seti kullanılarak testler uygulanmaktadır.

Normal Dağılım Serisinin İçeriği:

  1. Veri Biliminde İstatistik Alanının Kalbi Olan Normal Dağılım (Gaussian Distriburion) Konusuna Genel Bir Bakış
  2. Veri Biliminde Normal Dağılımın Python Üzerinden Görselleştirilmesi ve Yorumlanması (Histogram, Box Plot, KDE Plot, QQ Plot ve Violin Plot)
  3. Veri Biliminde Normal Dağılımın R ve Python ile Testi ve Yorumlanması (Skewness ve kurtosis, Shapiro - Wilk, Kolmogorov - Smirnov)
  4. Veri Biliminde Normal Dağılmayan Verilerin Dönüştürülme (Transformation) Yöntemleri Logaritmik, Box-Cox, Karekök, Reciprocal

Testlerimize geçmeden önce serinin ikinci yazında kullandığımız veriyi Pyrhon ve R ortamına GITHUB üzerinden çekelim. Aşağıdaki kodları kullanarak veriyi alamıyorsanız bu adres üzerinden indirebilirsiniz.

Python

import pandas as pd
import numpy as np
from scipy import stats

# Veriye github üzerinden ulaşalım
url = "https://github.com/yigitsener/machine-learning/blob/master/data/lessons.csv"
df = pd.read_csv(url + "?raw=true", error_bad_lines=False)

# Kolon isimlerini değiştirme
df = df.rename(columns = {"Maths" : "Matematik",
"English" : "İngilizce",
"History" : "Tarih"})

# ilk iki satıra bakalım
df.head(2)
# Matematik İngilizce Tarih
# 67.970442 60.990250 72.200954
# 58.438226 64.157607 62.687182

R

# Eğer kütüphaneler yok ise yüklenir
# install.packages("nortest")
# install.packages("psych")
# Kütüphanelerin çağırılması
library("nortest")
library("psych")
# Veriyi GITHUB'dan çekme
url = "
https://github.com/yigitsener/machine-learning/blob/master/data/lessons.csv?raw=true"
df = read.csv(url)
# Kolon isimlerini değiştirelim
names(df) = c("Matematik","ingilizce","Tarih")
attach(df)
# İlk iki satırı getirelim
head(df,2)
# Matematik ingilizce Tarih
# 67.97044 60.99025 72.20095
# 58.43823 64.15761 62.68718

Basıklık (Kurtosis) ve Çarpıklık (Skewness)

Skewness kavramından serinin ilk yazısında bahsetmiştim. Kısacası normal dağılımı bir çan eğrisi olarak düşündüğünüzde çanın tepe noktasının sağa mı yoksa sola doğru mu çarpıklığı olduğunu göstermektedir. Kurtosis ise çanın basık mı yoksa yüksek mi olduğunu veren değeri bize göstermektedir.

Yukarıdaki örnek grafiklerde de görüldüğü üzere skewness yatay düzlemde , kurtosis ise dikey anlamda normal dağılım hakkında bize ipuçları vermektedir. Bu anlamda sadece dağılımı değil olası aykırı değerlerin oluşabileceğine yönelik işaretleri de yansıtabilmektedir. Çarpıklık ve basıklık değerleri kimi akademik makalelerde -1,5 ile 1,5 arasını, kimisinde ise -2 ile 2 arasını normal olarak kabul etmektedir. Bu çalışılan disipline/projeye göre değişkenlik göstermektedir.

Python ve R üzerinden veri setimizdeki değişkenlerin skewness ve kurtosis değerlerine bakalım.

Python

# Skewness ve kurtosis değerleri
m = df.Matematik
i = df.İngilizce
t = df.Tarih
print("Matematik")
print(f"Skewness: {stats.skew(m)} Kurtosis: {stats.kurtosis(m)}")
print("İngilizce")
print(f"Skewness: {stats.skew(i)} Kurtosis: {stats.kurtosis(i)}")
print("Tarih")
print(f"Skewness: {stats.skew(t)} Kurtosis: {stats.kurtosis(t)}")
# ÇIKTI:
# Matematik
# Skewness: -0.182 Kurtosis: -0.446
# İngilizce
# Skewness: 0.838 Kurtosis: -0.036
# Tarih
# Skewness: -0.970 Kurtosis: 0.810

R

# skewness ve kurtosis değerleri
ms = skew(Matematik)
mk = kurtosi(Matematik)
is = skew(ingilizce)
ik = kurtosi(ingilizce)
ts = skew(Tarih)
tk = kurtosi(Tarih)
print("Matematik")
paste0("Skewness: ",ms," Kurtosis: ", mk)
print("ingilizce")
paste0("Skewness: ",is," Kurtosis: ", ik)
print("Tarih")
paste0("Skewness: ",ts," Kurtosis: ", tk)
# ÇIKTI
# Matematik
# Skewness: -0.179 Kurtosis: -0.497
# ingilizce
# Skewness: 0.826 Kurtosis: -0.095
# Tarih
# Skewness: -0.956 Kurtosis: 0.734

Skewness ve kurtosis sonuçları için R ve Python’da yer alan sonuçlar birbirinden ufacıkta olsa farklı görünüyor. Bunun başlıca sebebi kütüphanenin kendi akademik kabulüne göre ilerlemiş olmasından kaynaklanabilir. Açıkçası akademide ya da bir makine öğrenmesi projesinde hangi sonuç sizin işinize yarıyorsa onu kullanabilirsiniz.

Sonuçları incelediğimizde en çarpık olmayan değişken Matematik iken basıklık düzeyi diğerlerine nispeten daha duran ders ise Tarihtir. Skewness değerlerinin pozitif ya da negatif yönelimli olması onun hangi tarafa doğru bir çarpıklığının olduğu göstermektedir. Aynı şekilde kurtosis için 0'ın üzeri çan eğrisinin yukarı doğru yükselmesi 0'ın altı ise aşağıya doğru basıklığın olduğunu göstermektedir.

Normal dağılım sınaması için Shapiro-Wilk ve Kolmogorov-Smirnov en yaygın olarak kullanılan testler olup temelde hipotezleri aynıdır

  • H0: Değişken normal dağılıma sahiptir. p-value > 0.05
  • H1: Değişken normal dağılıma sahip değildir. p-value < 0.05

Shapiro-Wilk Testi

Uygulamada, Shapiro-Wilk testinin güvenilir bir normallik testi olduğuna inanılır, ancak testin daha küçük veri örnekleri için (x < 1000) uygun olabileceğine dair bazı araştırmalar mevcuttur. Python ve R üzerinden değişkenlerimizin Shapiro-Wilk testi sonucunu görüp yorumlayalım.

Python

# Shapiro - Wilk testi
m = df.Matematik
i = df.İngilizce
t = df.Tarih
print("Matematik")
print(f"T: {stats.shapiro(m)[0]} P-Value: {stats.shapiro(m)[1]}")
print("İngilizce")
print(f"T: {stats.shapiro(i)[0]} P-Value: {stats.shapiro(i)[1]}")
print("Tarih")
print(f"T: {stats.shapiro(t)[0]} P-Value: {stats.shapiro(t)[1]}")

# ÇIKTI:
# Matematik
# T: 0.988 P-Value: 0.550
# İngilizce
# T: 0.925 P-Value: 0.00020
# Tarih
# T: 0.932 P-Value: 0.00070

R

> shapiro.test(Matematik)Shapiro-Wilk normality testdata:  Matematik
W = 0.98856, p-value = 0.5503
> shapiro.test(ingilizce)Shapiro-Wilk normality testdata: ingilizce
W = 0.92535, p-value = 0.00020
> shapiro.test(Tarih)Shapiro-Wilk normality testdata: Tarih
W = 0.93257, p-value = 0.00070

Her bir değişken için sonuçları incelerken hipotez reddi/kabülu için p-value’ya bakmamız yeterlidir. Buna göre;

  • Matematik değişkenine ait p-value değeri > 0.5503 olduğuna göre H0 hipotezini kabul etmiş oluyoruz. Dolayısıyla değişken normal bir dağılım göstermektedir.
  • İngilizce ve Tarih değişkenleri için p-value < 0,05 olduğu için H0 hipotezini reddediyoruz. Dolayısıyla bu değişkenler normal bir dağılım göstermemektedir.

Kolmogorov-Smirnov

Shapiro-Wilk testi, normalliği belirlemek için popülerdir ve genellikle çok iyi performans gösterir, ancak evrensel olarak en iyisi değildir. Bir değişkeni incelerken tek bir teste bağlı kalmadan sonuçlarınızı doğrulamak için ek yöntemler kullanmanız gerekir. Bunun için Kolmagorov-Smirnov testi uygulanabilir.

Python

# Kolmogorov–Smirnov test
m = df.Matematik
i = df.İngilizce
t = df.Tarih

m_kstest = stats.kstest(m, 'norm',
args=(m.mean(),
m.std()))
i_kstest = stats.kstest(i, 'norm',
args=(i.mean(),
i.std()))
t_kstest = stats.kstest(t, 'norm',
args=(t.mean(),
t.std()))
print("Matematik")
print(f"T: {m_kstest[0]} P-Value: {m_kstest[1]}")
print("İngilizce")
print(f"T: {i_kstest[0]} P-Value: {i_kstest[1]}")
print("Tarih")
print(f"T: {t_kstest[0]} P-Value: {t_kstest[1]}")

# ÇIKTI:
# Matematik
# T: 0.053 P-Value: 0.923
# İngilizce
# T: 0.135 P-Value: 0.045
# Tarih
# T: 0.123 P-Value: 0.088

R

> ks.test(Matematik, "pnorm", mean(Matematik), sd(Matematik))One-sample Kolmogorov-Smirnov testdata:  Matematik
D = 0.053379, p-value = 0.9382
alternative hypothesis: two-sided
> ks.test(ingilizce, "pnorm", mean(ingilizce), sd(ingilizce))One-sample Kolmogorov-Smirnov testdata: ingilizce
D = 0.13578, p-value = 0.05009
alternative hypothesis: two-sided
> ks.test(Tarih, "pnorm", mean(Tarih), sd(Tarih))One-sample Kolmogorov-Smirnov testdata: Tarih
D = 0.12309, p-value = 0.09658
alternative hypothesis: two-sided

Kolmogorov-Smirnov, bir değişkenin dağılımını, kümülatif dağılım fonksiyonu (cumulative distribution function - CDF) yardımı ile normal dağılıma uyup uymadığını test eder. Veya, iki farklı değişken arasındaki ilişki CDF üzerinden test edilir. Bizim örneğimizde değişkenleri kendi içinde değerlendirdiğimiz için karşısına koyacağımız parametre olarak o değişkene ait ortalama ve standart sapma değerlerini formüle ekledik. Yukarıdaki stats.kstest ve ks.test içerisinde yer alan ortalama (mean) ve standart (std/sd) sapma değerleri buradan gelmektedir.

Sonuçları incelediğimiz zaman R ve Python arasındaki çıktılarda ufak farklılar bulunduğunu görmekteyiz. Yukarıda belirttiğimiz hipotez testlerine dayanarak aşağıdaki sonuçlara varabiliriz.

  • Matematik değişkeni için H0 hipotezi kabul edilmiştir (p-value > 0.05). Dolayısı ile değişkenin normal bir dağılım gösterdiği söylenebilir.
  • İngilizce değişkeni için R’da çıkan sonuç (0.05009) p-value ölçüm sınırında görülmekle beraber Python’da ise sınırın altında (0.045) kaldığı için H0 hipotezini reddedebiliriz (p-value < 0.05). Dolayısıyla değişkenin normal bir dağılım göstermediği söylenebilir.
  • Tarih değişkeni Kolmogorov–Smirno testinde Shapiro-Wilk testine göre farklı bir sonuç çıkarmıştır. R ve Python’da yapılan testlerin sonucu ortak olarak p-value > 0.05 olup H0 hipotezi kabul edilmiştir. Dolayısıyla bu değişken için normal dağılım gösteriyor diyebiliriz.

Sonuç

Testler birbirine yaklaşık sonuçlar verse dahi yukarıda görüldüğü üzere Tarih değişkeni için farklı sonuçlar çıkarabilmektedir. Burada test seçiminden ziyade değişkenin kimyasını iyi anlamak için bu testler uygulanmalı ve model için uygunluğu bu şekilde değerlendirilmelidir.

--

--