Python ile Veri Ön İşlemeye Dalış
Dalış makaleleri/yazıları bizde adettendir deyip, veri bilimi üzerine aşağıdaki makaleleri yazmıştık. Bu yazıya devam etmeden önce, serideki eski yazıları okumanızı tavsiye ediyorum.
Hangi konuların üzerinden geçtiğimizi kısaca hatırlayalım:
- Veri Bilimi Nedir?
- Veri Bilimi İş Akışı Nasıl İşler?
- Python Programlama Dili (Geçmişi ve Yapısı)
- Python Değişken Türleri, Koşul Deyimleri, Döngüleri
- Python Veri Yapıları (Listeler, Sözlükler), Referans Türleri ve Nesne Klonlama
- Python Fonksiyon Tanımları ve Lambda Fonksiyonları
- Veri Bilimi ve Nesne Yönelimli Programlama
- NumPy ve Pandas Kütüphaneleri
- Veri tanıma fonksiyonları (dataframe üzerinden sorgulama)
- Temel istatistik fonksiyonları (mod, medyan, standart sapma, varyans, kovaryans, korelasyon)
Giriş
Veri hazırlama ve ön işleme adımı hem “veriyi sindirmek” hem de makine öğrenmesi modellerinin veriden daha fazla ve daha doğru şekilde yararlanmasını sağlamak için kullanılan yöntemler/teknikler bütünüdür. Gerçek hayatta çalıştığımız veri bilimi projelerinde “topladığımız veri”, “beklediğimiz kadar” temiz olmayacaktır. Karşılaştığımız veri odaklı problemleri aşağıdaki gibi 3 temel kategoriye ayırmak mümkündür:
- Eksik Veri
- Gürültülü Veri
- Tutarsız Veri
Verinin kalitesini kafaya takmalıyız. Gerçekten takmalıyız….
Yapılan araştırmalar ve edinilen geri bildirimlere göre işimizin neredeyse %60-70 zamanlık kısmını “veriyi tanıma ve ön işleme” adımlarına harcıyoruz. Aşağıdaki şekilde de görüldüğü üzere (1) ve (2) numaralı bu adımları “Veri Hazırlama” olarak da adlandırmamız mümkün.
Not: Veri tanıma sürecinde gerçekleştirdiğimiz adımlardan bazılarına Python ile Veri Tanımaya ve Temel İstatistiğe Dalış yazımızda değinmiştik.

Bu yazımızda da “veri ön işleme” konusuna odaklanarak aşağıdaki ön işleme yöntemlerini ve tekniklerini inceleyeceğiz.
- Eksik Veri Doldurma (Data Imputation)
- Yeni Öznitelikler Oluşturma (Feature Extraction)
- Kategorik Değerleri Dönüştürme (Label /One-Hot Encoding)
- Veriyi Ölçeklendirme (Scaling) ve Normalize Etme (Normalization)
- Aykırı/Uç Verileri (Outliers) Tespit Etme
1. Eksik Veri Doldurma (Data Imputation)
Kullanacağımız veri kümesi içerisinde eksik değerler bulunabilir. Bu değerleri doldurmadan ya da veri kümemizden çıkarmadan bir sonraki adıma geçmemiz (Örneğin: herhangi bir makine öğrenmesi algoritması üzerinde çalışmamız) hatalı sonuçlar elde etmemize neden olacaktır. Eksik veri içeren bir veri kümesi üzerinde uygulanabilecek yöntemlerden bazıları aşağıdaki gibidir.
- Eksik öznitelik değerleri olan veri satırlarını (örnekleri) kullanma, onları sil (Oldukça kolay bir çözüm).
- Eksik öznitelik değerlerini elle doldur (Veri büyüdükçe ve eksik olan verinin önemine göre zaman alıcı ve etkin olmayan bir yönteme dönüşebilir).
- Eksik öznitelik değerleri için global bir değişken kullan (Null, bilinmiyor).
- Eksik öznitelik değerlerini ortalama değer ile doldur.
- Aynı sınıfa ait kayıtların öznitelik değerlerinin ortalaması ile doldur.
- Olasılığı en fazla olan öznitelik değeriyle doldur.
- Regresyon yöntemi ile sayısal eksik değerleri tahmin et ve doldur.
Eksik veri doldurma yöntemlerinden bazılarını test etmek için önceki yazımızda kullandığımız veri kümemizi hatırlayalım. 12 satır ve 6 adet öznitelikten oluşan veri kümesi, belirli tarihlerde sınava giren kişilere ait demografik bilgileri, meslek bilgilerini ve sınavdan aldıkları puanları içermekteydi. Veri kümesini tekrar oluşturmak için aşağıdaki kod parçasını kullanabiliriz.
Not: Veri ön işleme adımlarına geçmeden önce elimizdeki veriyi tanımamız gerektiğini tekrar hatırlatalım ve önceki yazıya yönlendirelim.
1.1. Toplam kaç hücrede eksik değer (NaN ya da None) var?
Bu sorunun cevabını aşağıdaki kod parçasını çalıştırarak bulabiliriz.
df.isnull().sum().sum()Kodu çalıştırdığımızda toplam 17 hücrede eksik değer olduğunu görüyoruz.
1.2. Özniteliklerin değer almadığı kaç satır var?
Aşağıdaki kod satırını çalıştıralım ve görelim.
df.isnull().sum()Kod satırını çalıştırdıktan sonra elde ettiğimiz çıktı aşağıdaki gibidir. Buna göre Meslek özniteliğine sahip olmayan 4 satır, Tarih özniteliğine sahip olmaya 4 satır ve ÇocukSayısı özniteliğine sahip olmayan 9 satır bulunmaktadır.

Eksik değerleri sayısal olarak görmek basit olsa da, eksik değerlerin satır bazında yüzdesini görmek, “bundan sonraki adıma karar vermek adına” daha sağlıklıdır. Bunun için aşağıdaki gibi “eksik_deger_tablosu” isimli ve parametre olarak “dataframe” alan bir fonksiyon yazalım.
Fonksiyonu çalıştırdığımızda elde ettiğimiz çıktı tablosu aşağıdaki gibidir.

Burada özellikle %75 eksik değer oranıyla (12 satırın 9'unda bu değer yok) “ÇocukSayısı” özniteliği göze çarpmaktadır.
Karar: Bu kadar eksik değerin olduğu bir öznitelik büyük ihtimalle işe yaramayacaktır. Bu özniteliği veri kümesinden kaldırmak mantıklı olabilir. Aslında şöyle bir strateji de izleyebiliriz: Belirli bir eşik değerin üzerinde, örneğin %70, eksik değer olan öznitelikleri veri kümesinden uçur…
Bu stratejiyi hayata geçirmek için aşağıdaki gibi “df” dataframe’i üzerinde “dropna()” fonksiyonunu threshold (eşik) değeri alacak şekilde kullanabiliriz.
Bu kodu çalıştırdığımızda df’in son halinin aşağıdaki gibi olduğunu ve ÇocukSayısı özniteliğinin dataframe’den kaldırıldığını görürüz.

Meslek ve Tarih öznitelikleri için farklı eksik değer doldurma stratejileri izlenebilir. Örneğin, Meslek özniteliği olmayan kayıtlara “Diğer” değeri atanabilir. Tarih özniteliği eksik olan kayıtlar, Tarih kolonundaki benzersiz değerlerden ilki ile doldurulabilir (Dikkat edelim, sektörde kullanıyoruz ama bazen problem de yaratabilir). Bu stratejileri gerçekleştiren kod satırları aşağıdaki gibidir.
Kodları çalıştırdığımızda elde ettiğimiz dataframe’in son hali ise şu şekilde:

2. Yeni Öznitelikler Oluşturma (Feature Extraction)
Bazı durumlarda mevcut öznitelikleri kullanarak, “daha fazla işe yarayacağı düşünülen” yeni öznitelikler oluşturulabilir. (1) Örneğin, aşağıdaki kod satırları çalıştırılarak, Geçti isimli yeni bir öznitelik oluşturulmuş ve sınavdan 70 (ve) üzerinde not alan kayıtların bu öznitelik değerleri “True” olarak ayarlanmıştır.
Yeni eklenen Geçti özniteliğinin dataframe’e eklenmiş hali aşağıdaki gibidir.

(2) Tarih formatındaki verileri parçalayarak kullanmak sıkça ihtiyaç duyduğumuz bir yöntemdir. Veri kümemizdeki Tarih özniteliğini kullanarak yıl bilgisini almak ve yeni bir öznitelik olarak eklemek istediğimiz durumda aşağıdaki kod satırlarını çalıştırabiliriz.
Son durumda, yeni eklenen iki öznitelikle (Geçti, Yıl) birlikte dataframe’in son hali aşağıdaki gibidir.

3. Kategorik Değerleri Dönüştürme (Label / One-hot Encoding)
Bilgisayar bilimlerinde kategorik verilerle çalışmak, hesaplama ve bilgisayarın bu değerleri anlaması açısından zorluklar içerir. Özellikle makine öğrenmesi modellerinin doğru çalışabilmesi için kategorik verileri, sayısal karşılıklarına (temsillerine) dönüştürmemiz gerekmektedir. Bunu yapmanın en yaygın iki yolu Sklearn kütüphanesi altında yer alan LabelEncoder veya OneHotEncoder(ya da Pandas.get_dummies) sınıflarını kullanmaktır. Aslında bunları farklı iki yaklaşım olarak görmek mümkündür.
3.1. Label Encoder
Elimizdeki verileri direk sayısal temsillerine dönüştürmeye yarar ve kategorik her veriye sayısal bir değer atar. Genelde sadece iki değere sahip özniteliklerde kullanılır. Örneğin, yeni oluşturduğumuz Geçti özniteliğinin sahip olduğu değerlerde (True/False) aşağıdaki dönüşümüzü yapmak istediğimizi varsayalım.
- True değerleri →1
- False değerleri → 0
Bu dönüşümü gerçekleştirmek için preprocessing kütüphanesi altında yer alan LabelEncoder sınıfını kullanan aşağıdaki kod satırlarını çalıştırabiliriz.
Kod satırları çalıştıktan sonra değişen dataframe’e ait ekran çıktısı aşağıdaki gibidir. Değişimi rahat görebilmek için Geçti_Encoded isimli yeni bir öznitelik oluşturulmuştur.

Aynı şekilde Meslek özniteliği için de kategorik değerden sayısal değere dönüşüm işlemi LabelEncoderkullanılarak aşağıdaki gibi gerçekleştirilebilir.
df['Meslek_Encoded']= label_encoder.fit_transform(df['Meslek'])
dfKod satırları çalıştıktan sonra değişen dataframe’e ait ekran çıktısı aşağıdaki gibidir. Burada da Meslek_Encoded isimli yeni bir öznitelik oluşturulmuştur. Bu öznitelik değerleri şu şekildedir:
Diğer → 0, işsiz → 1, işçi → 2, memur → 3, serbest → 4, sigortacı → 5

Problem: Label-Encoding ile kategorik verileri makine tarafından okunabilir biçime (her benzersiz veri değerini benzersiz bir sayıya) dönüştürmek iyi bir yöntem gibi görünebilir. Ancak bu dönüşüm özellikle makine öğrenmesi sürecinde, veri kümelerinin eğitimi aşamasında sorunlara yol açabilir. Sayısal olarak yüksek değerli bir kategori, düşük değerli bir kategoriden daha yüksek önceliğe sahip olarak kabul edilebilir. Örneğin, sigortacı mesleği (5 değerine sahip), memurdan (3 değerine sahip) daha önemli gibi işlem görebilir (hesaplanabilir).
3.2. One-Hot Encoding
Bu yaklaşımda kategorik türde özniteliğe ait tüm değerler yeni birer öznitelik haline getirilir. Her örnek asıl olarak hangi yeni özniteliğe sahipse (kategorik değere) onun değeri “1”, diğer yeni özniteliklerin değerleri ise “0” olur. Aslında bir çeşit “ikili kodlama” (binary coding) olarak da düşünülebilir. Bu dönüşümü gerçekleştirmek için yine preprocessing kütüphanesi altında yer alan OneHotEncoder sınıfı aşağıdaki gibi kullanılabilir. Ancak kullanımı (görece) zor gelebilir.
onehotencoder = preprocessing.OneHotEncoder()Pandas kütüphanesi altında yer alan get_dummies fonksiyonunun kullanılması yazılan kod satırları açısından daha basittir. Aşağıdaki kod satırları kullanılarak Meslek özniteliğinin değerlerinin ikili değerlere dönüştürülme sonucu görülebilir.
df['Meslek'] = pd.Categorical(df['Meslek'])
dfDummies = pd.get_dummies(df['Meslek'], prefix = 'Kat')
dfDummiesKod satırlarına biraz daha dikkatli bakacak olursak;
- Meslek özniteliğinin veri tipinin kategorik olduğu pd.Categorical fonksiyonu kullanılarak, açık (explicit) şekilde belirtilmiştir.
- get_dummies fonksiyonu içerisinde oluşacak her yeni öznitelik için önek (prefix) → Kat olarak belirlenmiştir.
Kodların çalıştırılması sonucu oluşan dfDummies’in ekran çıktısı aşağıdaki gibidir.

Son olarak eski dataframe (df) ile yeni dataframe (dfDummies) aşağıdaki gibi birleştirilerek (pd.concat) son/yeni dataframe (df_new) elde edilir.
df_new = pd.concat([df, dfDummies], axis=1)
df_newdf_new’in ekran çıktısı ise aşağıdaki gibidir.

4. Veriyi Ölçeklendirme (Scaling) ve Normalize Etme (Normalization)
Veri ölçeklendirme ve normalize etme adımları birbirlerine benzer işler gibi görünseler de (hatta birbirleri yerine kullanılsalar da) uygulanma şekilleri farklıdır. Ölçeklendirme işleminde elimizdeki verinin sadece aralığını (range) değiştirirken (örneğin 0–1 arası ya da 1–100 arası gibi), veriyi normalize etme sürecinde verinin dağılımını normal bir dağılım olarak değiştiriyoruz.
Dikkat: 0–1 arasında ölçeklendirme işlemi, günlük hayatta normalizasyon (normalization) olarak adlandırıldığı ve sıkça kullanıldığı için kavram karmaşası buradan gelmektedir.
4.1. Min-Maks Ölçeklendirme
Min-Maks ölçeklendirme için kullanılan formül aşağıdaki gibidir.

Formülde yer alan new_minA ve new_maxA yeni veri aralığımızı temsil etmektedir. Elimizdeki veri kümesinde yer alan Puan özniteliğini bu yaklaşımla ölçeklendirmek için aşağıdaki kod satırlarını kullanabiliriz.
Yukarıdaki kod satırları çalıştırıldığında, Puan özniteliğindeki değerler 0–1 arasında ölçeklendirilerek Puan2 isimli yeni özniteliğe kopyalanmıştır. Minimum ve maksimum puan dönüşümleri aşağıdaki gibidir:
- Puan: 54 değeri → 0
- Puan: 92 değeri → 1

5. Aykırı/Uç Verileri (Outliers) Tespit Etme
Farklı algoritmalar için başa bela olabilen aykırı değerleri her zaman kötülemek ve veriden çıkaracağımızı düşünmek doğru bir yaklaşım değildir. Bazen tek amacımız bu değerleri bulmak hatta bunları gruplayarak, (yapabilirsek) süreç ya da olay bazlı bu durumlarını analiz etmektir.
4.1. Quartile (Kartiller) ve IQR ile Aykırı Değer Tespiti
Aykırı (Uç) değerlerin tespitinde farklı yöntemler kullanılabilir. Bunlardan bir tanesi çeyreklik/kartil (quartile) ya da yüzdelik (percentile) yöntemiyle veriyi parçalara ayırdıktan sonra belirlenen sınır değerleri dışında kalan verileri aykırı değer olarak işaretlemektir. Aşağıdaki grafikte de görüldüğü gibi kartiller veriyi sıralayarak 4 eşit parçaya böler. Q2 aynı zamanda verimizin medyan değeridir.

Verinin Q1, Q2, Q3 değerleri bulunduktan sonra Interquartile range (IQR — Çeyrekler açıklığı) hesaplanır. IQR, birinci çeyrek Q1 ve üçüncü çeyrek Q3 arasındaki uzaklıktır. IQR kullanılarak alt ve üst sınır değerleri aşağıdaki gibi hesaplanır.
- IQR = (Q3 – Q1)
- Alt Sınır Değer (ASD)= Q1–1.5(IQR)
- Üst Sınır Değer (ÜSD)= Q3 + 1.5(IQR)
ASD ve ÜSD dışında kalan tüm değerler outlier (aykırı değer) olarak tespit edilir. Veri keşfinde kullandığımız kuru grafikleri (boxplot) kartil mantığında veriyi gösrselleştirmektedirler. Örneğin veri kümemizde Puan özniteliğine ait değerleri kutu grafiği ile görselleştirmek için seaborn görselleştirme kütüphanesini aşağıdaki gibi kullanabiliriz.
import seaborn as sns
sns.boxplot(x=df['Puan'])Bu kod satırları çalıştırıldığında aşağıdaki grafiğe sahip gibi ekran çıktısı elde ediyoruz.

Grafiği incelediğimizde Q1, Q2 ve Q3 değerlerinin sırasıyla 65–70 arası, 75 ve 80–85 arasında hesaplandığını, ASD’nin 55'in altında olduğunu ÜSD’nin de 90'ın üzerinde olduğunu görüyoruz. Şimdi aşağıdaki kod satırlarını kullanarak bu hesaplamaları biraz daha net anlayalım.
Bu kodlar çalıştırıldığında elde edeceğimiz çıktı değerleri aşağıdaki gibidir.
Q1--> 67.0
Q3--> 81.0
Q2--> 75.0
Q4--> 92.0
IQR--> 14.0
Alt sınır: Q1 - 1.5 * IQR---> 46.0
Üst sınır: Q3 + 1.5 * IQR---> 102.0Grafikte de gördüğümüz üzere veri kümemizde ASD’den küçük veya ÜSD’den büyük aykırı değere sahip örnek bulunmamaktadır. Eğer bu değerlerin olduğunu tespit etseydik aşağıdaki kod satırı ile bu verileri farklı bir dataframe’e kopyalayıp üzerinde çalışma yapabilirdik.
outliers_df = df[(df.Puan < (Q1–1.5*IQR))|(df.Puan > Q3+1.5*IQR)]Çok daha detaylı hale getirmemizin mümkün olduğu ve daha fazla ince ayar verebileceğimiz bu yazımızı da sonlandırsak iyi olacak :) Bu kısma kadar okuduysanız bu işi seviyorsunuz demektir, tebrik ederim…
Bana; www.denizkilinc.com, Linkedin üzerinden veya https://twitter.com/denizkilinc adreslerinden ulaşabilirsiniz.
Bir sonraki yazımızda görüşmek üzere….
Not: Fırsat buldukça eklemeler ve güncellemeler yapacağım.
