Veri bilimi için kullanılan birçok modelin doğru bir şekilde çalışabilmesi için “istatistiksel varsayımları” sağlaması gerekmektedir. İstatistiksel testlerde de sıkça karşılaşılan “istatistiksel varsayımlardan” en önemlisi belki de verinin Normal (Gauss) dağılıma sahip olmasıdır.
Çarpık dağılım nedir?
Bir verinin çarpıklık (skewness) değeri verinin simetrisinin tanımıdır. Verinin sahip olduğu dağılım, veriye dair hangi istatistiksel analizlerin yapılabileceğini, hangi ölçünün veriyi daha iyi tanımlayabileceğine dair kararlara yardımcı olur.
Bir dağılım simetrik, sağa veya sola çarpık olabilir. Verinin:
- Tek bir tepesi, ortadan bölündüğünde sağda ve solda simetrik bir görüntüsü varsa dağılım simetriktir.
- Kuyruğu sağa doğru uzuyorsa, dağılım sağa çarpık veya pozitiftir.
- Kuyruğu sola doğru uzuyorsa, dağılım sola çarpık veya negatiftir.
Kuyruk tanımı ilk başta net olmamış olabilir. Dağılımın kuyruğu ile tam olarak kast edilen bölümü aşağıdaki görselde daha net kavrayabilirsiniz.

Dağılımların şekline göre mod, ortanca ve ortalama değerlerinin birbiriyle ilişkisi farklılık gösterir. Bu ilişkiyi tek bir görselde şöyle özetleyebiliriz:

Ortalama ve ortanca arasındaki ilişkiyi akılda tutmak zor olabilir. Hangi dağılımda ortalama daha büyüktü vs. diye hatırlamak kafa karıştırıcı gelebilir. Sağa çarpık veya sola çarpık dendiğinde, grafikte ortalamanın en sağda veya en solda olduğunu hatırlayarak ortanca ile olan ilişkisini hatırlayabilirsiniz.
Bir değişkende çarpıklık iki sebepten dolayı meydana gelmiş olabilir:
- Değişkenin aykırı değerlere sahip olması.
- Değişkenin dağılımının doğal olarak çarpık olması.
Buraya kadar anlattığımız teoriyi Python üzerinde uygulayıp, gerçekten nasıl göründüğüne bakalım.
Herhangi bir işlem yapmadan önce ilgili kütüphaneleri çalışma dosyamıza çağıracağız.
Veri manipülasyonu için Pandas ve NumPy, görselleştirme için Seaborn ve Matplotlib kütüphanelerini ve istatistiksel işlemler için SciPy kütüphanesinden stats modülünü çağıracağız.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats
SciPy’ın stats modülünü kullanarak örnek veri setleri üretip, daha sonrasında veri setlerini histogram yardımı ile görselleştirip, ortalama ve medyan değerlerinin ilişkisine bakacağız.
Olasılık dağılımlarından bildiğimiz üzere, bazı özel dağılımların belirgin özellikleri vardır. Örneğin Normal dağılıma sahip bir veri simetrik görünüme sahipken, Üstel dağılıma sahip bir veri sağa çarpık görünüme sahiptir. Ayrıca, Beta dağılımı da kullanarak da sola çarpık dağılıma sahip bir veri seti oluşturabiliriz.
norm = stats.norm.rvs(loc = 0, scale = 1, size = 10000)
left = stats.beta.rvs(8, 1, size = 10000)
right = stats.expon.rvs(size = 10000)
- normal dağılım için norm, beta için beta ve üstel dağılım için expon ile stats modülünün bir fonksiyonu olan rvs() metodunu kullandık. Her bir dağılım için size parametresini kullanarak 10,000 adet veri ürettik.
- Normal dağılım parametre olarak ortalama ve standart sapma değerlerini kullanır. Ortalaması 0 ve standart sapması 1 olan, standart normal dağılımı takip eden bir veri seti oluşturduk.
- loc: Ortalama değerini temsil eder.
- scale: Standart sapma değerini temsil eder.
- Beta dağılımı parametre olarak α ve β değerlerini alır.
- α: Başarı sayısı. Alfa değeri arttıkça dağılımın çarpıklığı sola doğru artar.
- β: Başarısızlık sayısı. Beta değeri arttıkça dağılımın çarpıklığı sağa doğru artar.
- Üstel dağılım parametre olarak sadece veri adetini kullanır.
Bir sonraki adımda oluşturduğumuz örnek dağılımları, Seaborn kütüphanesi ile görselleştireceğiz.
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize = (18, 6))
sns.histplot(ax = ax1, data = left, kde = True, alpha = 0.2, element = "step")
ax1.axvline(np.mean(left), color = "red", linestyle = "dashed")
ax1.text(np.mean(left) - 0.19, 400, "Ortalama", fontsize = 13, color = "red")
ax1.axvline(np.median(left), color = "black", linestyle = "dashed")
ax1.text(np.median(left) + 0.01, 300, "Ortanca", fontsize = 13, color = "red")
ax1.set_title("Sola Çarpık (Negatif) Dağılım")
ax1.set_xlabel("Değerler")
ax1.set_ylabel("Frekans")
sns.histplot(ax = ax2, data = norm, kde = True, alpha = 0.2, element = "step")
ax2.axvline(np.mean(norm), color = "red", linestyle = "dashed")
ax2.text(np.mean(norm)+ 0.1, 400, "Ortalama", fontsize = 13, color = "red")
ax2.axvline(np.median(norm), color = "black", linestyle = "dashed")
ax2.text(np.median(norm)+ 0.1, 200, "Ortanca", fontsize = 13, color = "red")
ax2.set_title("Simetrik Dağılım")
ax2.set_xlabel("Değerler")
ax2.set_ylabel("Frekans")
sns.histplot(ax = ax3, data = right, kde = True, alpha = 0.2, element = "step")
ax3.axvline(np.mean(right), color = "red", linestyle = "dashed")
ax3.text(np.mean(right), 600, "Ortalama", fontsize = 13, color = "red")
ax3.axvline(np.median(right), color = "black", linestyle = "dashed")
ax3.text(np.median(right), 200, "Ortanca", fontsize = 13, color = "black")
ax3.set_title("Sağa Çarpık (Pozitif) Dağılım")
ax3.set_xlabel("Değerler")
ax3.set_ylabel("Frekans")
plt.show()
- Öncelikle görselleri yerleştirebilmek için “fig” adında bir çerçeve ve çerçevenin içerisine 1 satır ve 3 sütundan oluşan alt yerler (subplots) tanımladık. 3 subplot için de ax1, ax2, ax3 ismini verdik.
- figsize() parametresi ile de çerçevenin en ve boyutunu “inç” cinsinden belirttik.
- Üç grafiği de çizdirirken aynı yapıyı kullandık ve her grafiğe yönelik özelleştirmeleri de “ax1, ax2, ax3” isimlendirmesi ile sağladık.
- Her bir grafik için kullandığımız yapıyı ve komutları şöyle özetleyebiliriz:
- sns.histplot(): Seaborn kütüphanesinin histogram çizdirebilmek için geliştirdiği komut. Bu komuta parametre olarak grafiğin hangi yerde olacağını (ax) ve hangi veriyi kullanacağını (data) belirttik.
- axvline(): Grafiğe istediğimiz noktada yatay düzleme dik gelecek bir çizgi çekmek için kullanılır.
- set_title(): Grafiğe başlık yazabilmek için kullanılır.
- set_xlabel(): Grafiğin yatay (x) düzlemindeki elemanları tanıtmak için kullanılır.
- set_ylabel(): Grafiğin dikey (y) düzlemindeki elemanları tanıtmak için kullanılır.
- Son olarak da plt.show() komutunu kullanarak çizdirdiğimiz çerçeveyi ekrana yazdırdık.

Grafiklerden de görülebileceği üzere, ortalama ile ortanca değerinin ilişkisi dağılımın çarpıklığına yani dağılımın şekline göre değişiklik göstermektedir.
Peki, çarpıklığı sayısal olarak ifade edebilir miyiz? İfade edebilirsek, bu değer ne anlama gelir?
Her değişkenin bir dağılımı ve her dağılımın da bir çarpıklık (skewness) katsayısı vardır. Çarpıklık katsayısı, bir dağılımın çarpıklığının yönünü ve gücünü gösterir.
- Çarpıklık katsayısı -1’den küçük veya +1’den büyük ise dağılım çok çarpıktır.
- Çarpıklık katsayısı -1 ile -0.5 veya +0.5 ile +1 arasında ise dağılım orta derecede çarpıktır.
- Çarpıklık katsayısı -0.5 ile +0.5 arasında ise dağılım yaklaşık olarak simetriktir.
Çarpıklık katsayısının negatif olması dağılımın sola çarpık, pozitif olması ise dağılımın sağa çarpık olduğu anlamına gelir.
Python’da çarpıklık katsayısını SciPy kütüphanesinin Stats modülünde bulunan skew() fonksiyonu yardımı ile hesaplayabiliriz.
skew_right = stats.skew(right)
skew_left = stats.skew(left)
skew_norm = stats.skew(norm)
print(skew_right, skew_left, skew_norm)
1.8486218263815324 -1.3869320012017545 -0.0031060609696312467
Her üç farklı dağılım için de çarpıklık katsayısını hesapladığımızda simetrik dağılımın en düşük değere sahip olduğunu, sola çarpık olan dağılımın negatif, sağa çarpık olan dağılımın ise pozitif değerler aldığını görüyoruz. Ayrıca çarpık olan verilerin çarpıklığının çok olduğunu anlıyoruz.
Verinin çarpıklığının giderilmesi, simetrik bir dağılıma benzemesi için veri dönüşümü (data transformation) işlemleri uygulanmalıdır.
Veri dönüşümü (data transformation) nedir?
Veri dönüşümü, değişkenin aldığı değerlerin matematiksel bir fonksiyon yardımıyla başka değerlere değişimine denir. Veri dönüşümü, normallik gibi istatistiksel varsayımların sağlanmasına yardımcı olduğu için önemlidir.
Dönüşüm işlemleri uygulanmadan önce:
- dönüşüm ile ulaşılması beklenen hedef,
- dönüşümün veri setinde yaratacağı etkiler ve
- işlenmiş verinin nasıl yorumlanacağı iyice anlaşılmalıdır.
Yazının bundan sonraki sürecinde teknikleri ve etkilerini uygulamalı göstermek adına Spotify API üzerinden elde edilmiş, son bir yüzyılda üretilmiş, her bir ülkeden yaklaşık 100,000’er adet şarkıya dair istatistiklerinin olduğu bir örnek veri seti kullanacağız. Sadelik açısından sadece Türkiye’ye ait istatistikleri kullanacağız.
Örnek veri seti: Spotify 1922-2021 Şarkı İstatistikleri – Türkiye

Spotify – Görsel: Pixabay
Kullanacağımız kütüphaneleri daha önceden çağırmıştık. read_csv() fonksiyonu ile veri setini çağıralım.
spotify = pd.read_csv("spotify-tr.csv")
Veri setini read_csv() fonksiyonu ile çağırdıktan yada dosyayı okuduktan sonra da diyebiliriz, Pandas’ın hazır fonksiyonlarından info()‘yu kullanarak veri setini tanımak için hızlı bir göz atacağız.
spotify.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 94317 entries, 0 to 94316 Data columns (total 20 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id 94317 non-null object 1 name 94317 non-null object 2 popularity 94317 non-null int64 3 duration_ms 94317 non-null int64 4 explicit 94317 non-null int64 5 artists 94317 non-null object 6 id_artists 94317 non-null object 7 release_date 94317 non-null object 8 danceability 94317 non-null float64 9 energy 94317 non-null float64 10 key 94317 non-null int64 11 loudness 94317 non-null float64 12 mode 94317 non-null int64 13 speechiness 94317 non-null float64 14 acousticness 94317 non-null float64 15 instrumentalness 94317 non-null float64 16 liveness 94317 non-null float64 17 valence 94317 non-null float64 18 tempo 94317 non-null float64 19 time_signature 94317 non-null int64 dtypes: float64(9), int64(6), object(5) memory usage: 14.4+ MB
- Veri setinde 19 değişken bulunuyor. Her bir değişken 94,317 adet gözleme sahip.
- Hiç bir değişkende kayıp veri bulunmuyor.
- Şarkılara dair şarkı ismi, yayınlanma tarihi, süresi gibi tanımlayıcı değişkenlerin yanı sıra Spotify tarafından hesaplanmış ve şarkıları içeriğine göre değerlendiren tempo, akustik, enerji gibi farklı değişkenler de bulunuyor.
Keşfe devam edelim ve sayı veri tipine sahip değişkenlerin dağılımlarını inceleyelim.
Veri setindeki değişkenlerin dağılımının belirlenmesi
Veri setinde herhangi bir dönüşüm işlemi uygulamadan önce, nümerik değişkenlerin dağılımı incelenmelidir. Bu analiz sayesinde hangi değişkenlerin müdahaleye ihtiyacı olduğunu belirleyebiliriz.
Bir değişkenin normal dağılıma sahip olup olmadığını anlamak için grafikler ve istatistiksel testler kullanılır. Grafikler hızlı bir yargıda bulunmaya yardımcı olsa da, değişkenin “kesinlikle” normal dağılıma sahip olduğunu söylemekte yetersiz kalır. Değişkenin normal dağılıma sahip olduğunu istatistiksel olarak kanıtlamanın yolu “Kolmogorov Smirnov testi” gibi istatistiksel testler yapmaktır.
Veri seti ile ilgili hızlı bir keşifte bulunmak ve ön bilgi toplamak için grafik yöntemleri kullanacağız. Hızlı bir şekilde ilerlemek için sizlere de tavsiye ederim.
Verinin dağılımını incelemek için Histogram, Kernel Density Plot (kdeplot) ve Kutu grafiği (boxplot) kullanışlıdır. Bazı dezavantajlarına rağmen pratik olması açısından histogramı tercih edeceğiz. Veri setinde bulunan tüm değişkenlerin histogramını tek bir seferde çizdirmek için Pandas’ın hist() fonksiyonunu kullanacağız.
spotify.hist(bins=30, figsize=(15, 10))
plt.show()

Pandas’ın hist() fonksiyonunu kullanarak veri setindeki tüm sayı tipine sahip değişkenlerin histogramını çizdirdik. hist() fonksiyonuna parametre olarak kutu sayısını ve grafiğin boyutlarını kullandık.
Grafikteki sonuca göre, “danceability” değişkeni normal dağılıma en yakın şekle sahip. “danceability” değişkeni dışında neredeyse hiçbir değişken normal dağılıma sahip değil.
“danceability” değişkeni gerçekten normal dağılıma sahip mi?
Bir değişkenin normal dağılıma sahip olduğuna dair biraz daha güçlü kanıtlar sunan bir diğer grafik Q-Q grafiğine bakalım. Q-Q grafiği, teorik bir dağılım ile empirik dağılımı (veri setini) grafiksel olarak karşılaştırır.
stats.probplot(spotify["danceability"], plot=plt)
plt.show()

Q-Q grafiğine göre, bir değişkenin normal dağılıma sahip olması için baştan sona olasılık eğrisini takip etmesi, eğri boyunca hareket etmesi gerekir. Örnek değişkenin grafiğinde ise baştaki ve sondaki değerlerin eğrinden saptığını görüyoruz. Her ne kadar simetriye yakın bir görünümü olsa da “danceability” değişkenin de normal dağılıma sahip olduğunu söylemek zor.
Normal dağılmayan veriler nasıl normale dönüştürülür?
Öncelikle verinin normal dağılmasını etkileyen sebeplerin aykırı değerlerden kaynaklanıp kaynaklanmadığı araştırılmalıdır. Aykırı değer analizinden sonra silme (trimming), sınırlandırma (winsorization) veya atama (imputation) yöntemlerinden uygun bir teknik ile müdahale edilmelidir.
Hem aykırı değerlerin bulunduğu veri setleri hem de doğal olarak çarpıklığa sahip veri setlerini normale dönüştürmek için kullanılabilecek bir diğer teknik ise “veri dönüşümü (data transformation)” işlemleridir. Farklı durumlarda kullanılabilecek 5 dönüşüm işlemini inceleyeceğiz.
1. Log dönüşümü
Veri setindeki değerlerin logaritmasının alınması ile dönüşüm işlemi gerçekleştirilir. Veri setindeki her bir değer (x) işlem sırasında 10 veya 2’lik tabanda log(x) değerine dönüştürülür.

Görsel kaynak: y = log10(x) grafiği
Grafikte bir x değişkeninin 10 tabanında log değerinin değişimini görüyorsunuz. x’in değeri arttıkça, log10(x) değeri yavaş bir artış gösteriyor. Bu nedenle, aşırı çarpık dağılıma sahip değişkenlerde log dönüşümü yüksek etkilidir.
- Değişkenin dağılımı üzerinde “yüksek” etkisi vardır. Bu nedenle çok çarpık değişkenlerde kullanışlıdır.
- Sağa çarpık (pozitif) dağılıma sahip değişkenlerde kullanılır. Değişkende sıfır veya negatif değerler bulunduğu zaman uygulanamaz.
Sola çarpık yani negatif dağılıma sahip değişkenlerde log dönüşümü anca dağılımın çarpıklığının yönü değiştirildiği durumda kullanılabilir.
Python’da log dönüşümü
Python’da log dönüşümünü uygulayabilmek için NumPy kütüphanesinde bulunan log() fonksiyonu kullanışlıdır.
Dönüşümü tanıtırken de bahsettiğimiz üzere, log dönüşümünü uygulayabilmek için veri setinin aşağıdaki şartları sağlaması gerekmektedir:
- Sağa çarpık dağılım
- Pozitif değerler (x > 0)
Tüm nümerik verilerin histogram grafiğini çizdirdiğimiz görsele tekrardan göz attığımızda bu iki şartı sağlayan “duration_ms” değişkeni göze çarpıyor. “duration_ms” değişkeni, parçaların süresinin milisaniye cinsinden uzunluğunu gösteriyor.
İşlem yapmadan önce bu değişkeni biraz daha yakından tanıyalım.
spotify["duration_ms"].describe().apply(lambda x: format(x, 'f'))
count 94317.000000 mean 232909.801743 std 159060.877701 min 3344.000000 25% 167800.000000 50% 203400.000000 75% 263333.000000 max 5042185.000000 Name: duration_ms, dtype: object
- Pandas’da bulunan describe() fonksiyonunu kullanarak, “duration_ms” değişkeninin özet istatistiklerini hesapladık.
- describe() fonksiyonuna ek olarak (chain) apply fonksiyonunu kullandık.
- apply fonksiyonunu kullanmamızın sebebi, describe() fonksiyonunun bu değişken için hesapladığı değerleri bilimsel gösterim (Ör: 9.431700e+04) formatında göstermesidir.
- Bilimsel gösterim formatında sonuçları analiz etmek güçleşeceği için apply() fonksiyonu ile değerleri ondalık olarak gösterecek bir mini fonksiyon tanımladık. Bu fonksiyon describe() fonksiyonunun hesapladığı bir değeri alır (x), format fonksiyonunu kullanarak ondalık formata (“f”) dönüştürür.
Kodu açıkladıktan sonra sonuçlardan ne okuyabileceğimize bakalım.
- “duration_ms” değişkeninde 94,317 adet gözlem bulunuyor.
- Minimum değer 3,344 mili saniye iken maksimum değer 5,042,185 mili saniye olarak kayıt edilmiş. Minimum ve maksimum değerler arasındaki genişlik gerçekten fazla.
- Ortalama parça uzunluğu 232,909 mili saniye, ortanca değeri ise 203,400 olarak hesaplandı. Düz bir sayı doğrusu çizdiğimizde ortalama değerinin, ortanca değerinin sağında olduğunu, yani değişkenin de sağa çarpık bir dağılıma sahip olduğunu söyleyebiliriz.
Bu değişken aykırı değerlere sahip midir?
Histograma baktığımızda bu sorunun cevabını ilk bakışta fark etmek zor. Daha hızlı ve doğru bir yanıt almak için değişkenin kutu grafiğine (boxplot) bakalım.
sns.boxplot(data = spotify["duration_ms"])
plt.show()

Seaborn kütüphanesinden boxplot() fonksiyonunu kullanarak “duration_ms” değişkeninin kutu grafiğini çizdirdik. Kutu grafiğinde “bıyık (whiskers)” olarak tabir edilen alt ve üst yatay çizgilerin dışında kalan noktalar aykırı değer olarak isimlendirilir. Grafiğe göre veri setindeki birçok değer aykırı değer olarak görünüyor.
Log dönüşümünü uygulayalım ve daha sonrasında verinin dönüşüme uğramamış hali ile işlemden sonraki durumlarını karşılaştıralım.
log_duration = np.log(spotify["duration_ms"])
NumPy’ın log() fonksiyonunu kullanarak log dönüşümünü uyguladık ve log_duration isimli değişkene atadık. Yaptığımız işlemin ne derece başarılı olduğunu anlamak için histogram grafiğini kullanarak ham veri ile işleme uğramış veri setinin dağılımlarını karşılaştıracağız.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (15, 6))
sns.histplot(ax = ax1, data = spotify["duration_ms"])
ax1.set_title("Ham Veri")
sns.histplot(ax = ax2, data = log_duration)
ax2.set_title("Log Dönüşümü")
plt.show()

Histogramlara baktığımızda başlangıçta çok çarpık olan dağılımın çarpıklığının baya azaldığını görüyoruz. Bu durumu nümerik olarak da kanıtlayacağız.
before = stats.skew(spotify["duration_ms"])
after = stats.skew(log_duration)
print(before, after)
11.301897080462238 -0.1105131634261521
Scipy’ın skew() fonksiyonunu kullanarak, her iki veri setinin de çarpıklık değerini hesapladık. Ham veri setinin çarpıklık değeri 11.3 iken, log dönüşümü uygulanmış veri setinin çarpıklık değeri -0.11 olarak hesaplandı.
Log dönüşümü ile veri setinin çarpıklık değerini neredeyse simetrik dağılıma uygun olan -0.5 ile 0.5 aralığına dönüştürebildik.
2. Karekök dönüşümü
Veri setindeki değerlerin kare kökünün alınması ile dönüşüm işlemi gerçekleştirilir.
- Değişkenin dağılımı üzerinde “orta” derece etkilidir. Bu nedenle orta derecede çarpık dağılıma sahip değişkenlerde kullanışlıdır.
- Log dönüşümüne göre sağladığı en büyük avantaj sıfır değerini içeren veri setlerine de uygulanabilir olmasıdır.
Python’da karekök dönüşümü
Python’da karekök dönüşümünü uygulayabilmek için NumPy’ın sqrt() fonksiyonu kullanışlıdır.
Karekök dönüşümü log dönüşümüne göre etkinlik olarak daha zayıf bir yöntemdir fakat sıfır (0) değerlere sahip veri setlerine de uygulanabilmesi açısından kullanışlıdır.
Veri setinde bulunan “liveness” değişkenine göz atalım.
spotify["liveness"].describe()
count 94317.000000 mean 0.210137 std 0.174090 min 0.000000 25% 0.101000 50% 0.141000 75% 0.268000 max 0.999000 Name: liveness, dtype: float64
- Sağa çarpık bir dağılıma sahiptir. (Ortalama > Ortanca)
- Sıfır değerine sahip gözlemler mevcuttur.
Değişkenin çarpıklık değerini hesaplayalım.
stats.skew(spotify["liveness"])
2.0817950578401336
skew() fonksiyonunu kullanarak çarpıklık değerini hesapladığımızda verinin çok çarpık dağılıma sahip olduğunu görüyoruz. Fakat, Log dönüşümünü anlatırken kullandığımız değişken duration_ms’in çarpıklık değeri 11.3’tü. “duration_ms” ile karşılaştırdığımız zaman çarpıklık değerinin hala çok çarpık olduğunu ama daha az olduğunu söyleyebiliriz.
Karekök dönüşümünü uygulayıp, işlemin nasıl sonuç vereceğine bakalım.
sqrt_live = np.sqrt(spotify["liveness"])
stats.skew(sqrt_live)
1.2504137500612755
Karekök dönüşümünü uyguladığımız zaman, değişkenin çarpıklığını 1.25’e kadar indirebildik. Karekök dönüşümünün etkili olduğunu fakat verinin dağılımını daha simetrik bir hale getirmek için daha güçlü bir yönteme ihtiyaç duyduğumuzu söyleyebiliriz.
Ham veri ve karekök dönüşümü uygulanmış veri setlerinin dağılımını grafiksel olarak da görelim.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (15, 6))
sns.histplot(ax = ax1, data = spotify["liveness"])
ax1.set_title("Ham Veri")
sns.histplot(ax = ax2, data = sqrt_live)
ax2.set_title("Karekök Dönüşümü")
plt.show()

Verinin dağılımlarına baktığımızda da dönüşüm uygulandıktan sonra dağılımın simetrik dağılıma yaklaştığını görebiliyoruz.
3. Karşıt dönüşüm (Reciprocal)
Veri setindeki değerlerin tersine çevrilmesi ile dönüşüm işlemi gerçekleştirilir. Veri setindeki her bir değer (x), 1/x değerine dönüştürülür.
- Değişkenin dağılımı üzerinde “düşük” seviyede etkilidir.
- Bu teknik değişkende sıfırdan farklı/sıfır olmayan değerler bulunduğu zaman uygulanabilir.
Python’da karşıt dönüşüm
Python’da karşıt (reciprocal) dönüşümü uygulayabilmek için yeni bir değişken oluşturup, ham veri setindeki değerleri “1/değer” şeklinde atamak yeterlidir.
Spotify veri setindeki tüm değişkenler sıfır değeri olan gözlemlere sahiptir. Bu nedenle bu dönüşüm işlemini uygulayabilmek için yapay bir veri seti üretip, onun üzerinde deneyimleyeceğiz.
import random
random.seed(10)
lst = [random.randint(1, 1000) for i in range(100)]
- Rastgele sayılar üretebilmek için “random” kütüphanesini çalışma dosyamıza çağırdık.
- Daha sonrasında bir list comprehension ve for döngüsü yardımı ile 1 ve 1000 arasında 100 rastgele sayı ürettik ve lst adını verdiğimiz bir değişkene atadık.
- Ürettiğimiz rakamların tekrarlanabilir olması için random kütüphanesinde bulunan seed() fonksiyonunu kullandık.
SciPy’ın skew() fonksiyonunu kullanarak, ürettiğimiz yapay değişkenin çarpıklık değerini hesaplayalım.
stats.skew(lst)
0.15653688506105282
Oluşturduğumuz veri seti neredeyse simetrik bir dağılıma sahip. Karşıt dönüşüm işlemini uygulayalım.
lst_rec = 1 / lst_srs
stats.skew(lst_rec)
7.817132741460065
Dönüşüm işlemi uygulayıp, tekrardan çarpıklık değerini hesapladığımızda çarpıklığın arttığını gördük. Bu karşıt dönüşümün “ilgili değişkende” işe yaramadığı anlamına gelir. Karşıt dönüşümün de etkili olabileceği farklı veri setleri olacaktır.
Son olarak aradaki değişimi her zaman olduğu gibi grafiksel olarak da çizdirelim.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (15, 6))
sns.histplot(ax = ax1, data = lst_srs)
ax1.set_title("Ham Veri")
sns.histplot(ax = ax2, data = lst_rec)
ax2.set_title("Karşıt Dönüşüm")
plt.show()

4. Box-Cox dönüşümü
İsmini iki istatistikçi George Box ve Sir David Roxbee Cox’tan alan Box-Cox dönüşümü lambda (λ) parametresini kullanarak veri seti üzerinde birden çok test uygulayıp, normal dağılıma en yakın sonucu üretir. lambda (λ) parametresi -5 ile 5 arasında değer alır.
- Zaman serilerine de uyarlanabilir.
- Box-cox dönüşümünün uygulanabilmesi için veri setindeki değerler pozitif olmalıdır.
Python’da Box-Cox dönüşümü
Python’da Box-Cox dönüşümünü uygulamak için SciPy kütüphanesinden stats modülünde bulunan boxcox() fonksiyonu kullanışlıdır.
Box-Cox dönüşümü uygulanacak veri seti mutlaka pozitif değerlere sahip olmalıdır. Aksi taktirde “ValueError: Data must be positive.” hatası ile karşılaşılır.
Spotify veri setinde Box-Cox dönüşümünü uygulayabileceğimiz tek değişken Log dönüşümünde de kullandığımız duration_ms değişkenidir.
SciPy’ın boxcox() fonksiyonunu kullanarak dönüşümü uygulayalım.
box_duration, lam = stats.boxcox(spotify["duration_ms"])
print(lam)
print(stats.skew(box_duration))
0.02621779602645846 0.021385078895252343
- Boxcox fonksiyonu yanıt olarak 2 elemana sahip bir “tuple” üretir. Bu tuple’ın ilk elemanı dönüşüm işlemi uygulanmış, NumPy array veri tipine sahip bir veri iken, diğer elemanı ise lambda değeridir.
- Dönüşüme uğramış verileri box_duration, lambda değerini ise lam adını verdiğimiz değişkenlere atadık.
- lambda değerini yazdırdığımızda 0.02 değerini bulduk. Boxcox fonksiyonu veri setini inceleyerek en iyi sonucu verecek lambda değerinin 0.02 olacağına karar verip, bu değere göre dönüşüm işlemini uyguladı.
Dönüşüme uğramış veri setinin çarpıklık değerini hesapladığımızda ise 0.021 değerini bulduk. İlk bölümde log dönüşümü uyguladıktan sonra çarpıklık değerini -0.110 olarak hesaplamıştık. Boxcox dönüşümünün log dönüşümünden daha iyi sonuç verdiğini söyleyebiliriz.
Ham veri ve Box-Cox dönüşümü uygulanmış veri setinin histogramlarını karşılaştıralım.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (15, 6))
sns.histplot(ax = ax1, data = spotify["duration_ms"])
ax1.set_title("Ham Veri")
sns.histplot(ax = ax2, data = box_duration)
ax2.set_title("Box-cox Dönüşüm")
plt.show()

Grafikten de görüşebileceği üzere Box-Cox dönüşümü uygulanmış veri seti simetrik bir görünüme sahip oldu.
Box-Cox fonksiyonuna lambda parametresini vererek, daha önce işlediğimiz dönüşümleri de uygulamak mümkündür. Lambda’nın aldığı değerlere göre sağladığı dönüşümleri şöyle özetleyebiliriz:
- λ = -1.0 Karşıt (Reciprocal) Dönüşüm
- λ = -0.5 Karşıt Karekök Dönüşümü
- λ = 0.0 Logaritmik Dönüşüm
- λ = 0.5 Karekök Dönüşümü
- λ = 1.0 Dönüşüm Yok
Hazır lambda parametrelerini kullanarak boxcox() fonksiyonu ile log dönüşümü yapalım.
box_log_duration = stats.boxcox(spotify["duration_ms"], lmbda = 0.0)
print(stats.skew(box_log_duration))
-0.1105131634261521
Yaptığımız işlemin ardından çarpıklık değerini hesapladığımızda NumPy’ın log fonksiyonu ile dönüştürdüğümüz veri seti ile aynı çarpıklık değerine ulaştık.
5. Yeo-Johnson dönüşümü
Box-Cox gibi adını yaratıcılarından alan bir diğer dönüşüm metodu Yeo-Johnson, veri setindeki değerlerin pozitif olma şartı olmadan kullanılabildiği bir dönüşüm metodudur. Bir diğer deyişle, veri setinde negatif ve sıfır değere sahip gözlemler olduğunda kullanışlıdır.
Python’da Yeo-Johnson dönüşümü
Python’da Yeo-Johnson dönüşümünü SciPy’ın stats modülünde bulunan yeojohnson() fonksiyonu ile uygulayabiliriz.
Spotify veri setinde negatif değerlere sahip “loudness” değişkeni bulunmaktadır. Öncelikle değişkenin ne kadar çarpık olduğunu araştıracağız.
print(stats.skew(spotify["loudness"]))
-1.0080337438271187
Değişkenin çarpıklık katsayısı -1 olarak hesaplandı. Bu değer orta çarpık seviyesinin hemen hemen sınırı. yeojohnson() fonksiyonunu kullanarak dönüşüm işlemini gerçekleştireceğiz.
loud_yeo, lamb = stats.yeojohnson(spotify["loudness"])
print(stats.skew(loud_yeo))
-0.009869096212794526
- yeojohnson() fonksiyonu boxcox() fonksiyonu gibi yanıt olarak bir tuple döndürür.
- Bu tuple’ın ilk elemanı değişime uğramış veri seti iken, diğer elemanı ise lambda değeridir.
- lambda değeri verilmediği zaman fonksiyon veri setine uygun en iyi lambda değerini hesaplar ve dönüşüm için kullanır. Aksi taktirde, kullanıcı tarafından verilen lambda değerine göre hesaplama yapar.
Dönüşüme uğramış veri setinin çarpıklık değerini hesapladığımızda başarılı bir dönüşüm işlemi yaptığımızı söyleyebiliriz.
İki veri seti arasındaki farkı daha ne görebilmek için histogramlarını karşılaştıralım.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize = (15, 6))
sns.histplot(ax = ax1, data = spotify["loudness"])
ax1.set_title("Ham Veri")
sns.histplot(ax = ax2, data = loud_yeo)
ax2.set_title("Yeo-Johnson Dönüşümü")
plt.show()

Grafikte de dönüşüme uğramış veri setinin simetrik bir dağılıma yakınsadığı görülmektedir.
Birçok istatistiksel test, makine öğrenmesi modeli analiz için kullanılacak değişkenlerin normal dağılıma uymasını talep eder. Değişkenler, aykırı değerlerin varlığı, örneklem büyüklüğünün yetersizliği veya verinin gerçekten doğal olarak çarpık olmasından dolayı çarpık olabilir. Veri dönüşümleri sayesinde veri setlerini normale yakınsayacak şekilde dönüştürebiliriz.