Bir veri bilimi/analizi projesinin başarısını belirleyen en kilit faktör analiz için kullanılacak olan verinin kalitesidir. Analiz için kullanacağınız ham veri eksik veya aykırı değerlere sahip, değişken isimleri belirsiz, birden fazla değerin tek bir sütunda tutulduğu yani kısacası kirli bir veri seti ise analiz sonuçlarınızın gerçeği yansıtması mümkün değildir. Bu nedenle kolları sıvayıp herhangi bir analize girişilmeden önce, veri seti mutlaka temizlenmelidir.
Veri bilimciler arasında yapılan bir ankete göre, bir veri bilimci zamanının %80’ini veri temizliği/veri ön işlemesi gibi veri hazırlığı aşamalarına harcamaktadır. 2016 yılında IBM’in yaptığı tahminlere göre ise kalitesiz, kötü verinin Amerikan ekonomisine maliyeti yıllık 3.1 trilyon dolar olarak göze çarpıyor.
Veri temizliğinin bir veri bilimi/analizi projesi için kritik öneme sahip olduğunu artık biliyoruz. Peki, temiz veri nedir? Veri nasıl temizlenir? Verinin temiz olduğunu nasıl anlarız?
Bir veri setinin temiz olabilmesi için:
- Analize uygun belirli bir yapısal formda olması,
- Verinin kaliteli olması gerekmektedir.
Düzenli veri (tidy data) nedir?
Verinin nasıl bir yapısal formda olması gerektiği ile ilgili metodoloji 2014 yılında yayımlanan “Tidy Data” isimli bir makale ile, istatistikçi ve aynı zamanda R yazılım dilindeki “tidyverse” paketinin yaratıcısı olan Hadley Wickham tarafından oluşturuldu.
Wickham’ın tanımına göre, yapısı (satır ve sütunlar) ve anlamı (değişken ve gözlemler) arasında standartlaşmış bir bağlantı olan veri setlerine temiz veri denir. Bu tanıma göre, temiz veri setlerinin 3 özelliği vardır:
- Her sütunda bir değişken bulunur.
- Her satırda bir gözlem bulunur.
- Her bir gözlem birimi bir hücre oluşturur.

Temiz veri setlerinin hepsi birbirine benzer fakat her kirli veri setinin kendine has bir dağınıklığı vardır.
Hadley Wickham
Bir veri setinin temiz olarak kabul edilmesi için yukarıda belirtilen 3 şartı sağlaması gerekiyor. Aşağıdaki örnek veri seti her ne kadar temiz gibi görünse de ilk kuralı ihlal ettiği için temiz değildir. Her değişken kendi sütuna sahip olmalıdır. Bu veri setinde üç adet (Tarih, Yaş, Reklam Gösterim Sayısı) değişken olmasına rağmen, dört farklı sütun bulunmaktadır.
Tarih | 18-24 | 25-34 | 35-44 |
01-01-2021 | 123,000 | 112,000 | 95,000 |
02-01-2021 | 12,500 | 65,000 | 83,000 |
03-01-2021 | 24,600 | 86,500 | 42,000 |
Örnek veri setinin temizlenmesi için formatı değiştirilmelidir. Her değişken tek bir sütunda yer almalıdır. Yaş değişkeni için yeni bir sütun oluşturup, tablo formatı düzenlendiği durumda temiz veri standartları sağlanmaktadır.
Tarih | Yaş | Reklam Gösterim Sayısı |
01-01-2021 | 18-24 | 123,000 |
01-01-2021 | 25-34 | 112,000 |
01-01-2021 | 35-44 | 95,000 |
02-01-2021 | 18-24 | 12,500 |
02-01-2021 | 25-34 | 65,000 |
02-01-2021 | 35-44 | 83,000 |
03-01-2021 | 18-24 | 24,600 |
03-01-2021 | 25-34 | 86,500 |
03-01-2021 | 35-44 | 42,000 |
Kaliteli veri nedir?
Verinin analize hazır hale gelmesi için sadece temiz veri prensiplerine uygun şekilde formatlanması yeterli değildir. Verinin kalitesinin de kontrol edilmesi ve gerekli durumlarda artırılması için de bazı işlemlerin yapılması gerekmektedir.
Kaliteli veri 5 karakteristik özelliğe sahiptir:
- Uygunluk: Veriler uygun tip ve formatta olmalıdır. Farklı tipteki veriler uygun veri tipleri ile saklanmalıdır.
- Doğruluk: Veriler gerçeği yansıtmalıdır. Bir insanın göz renginin mavi girilmesi uygun olabilir fakat doğru olmayabilir.
- Doluluk: İhtiyaç duyulan veriler eksiksiz ve tekil olmalıdır.
- Tutarlılık: Veri setindeki iki farklı değer birbiriyle tutarlı olmalıdır. Evli olarak kayıt edilen bir müşterinin yaşı 10 olmamalıdır.
- Monotonluk: Veri setindeki değerler aynı birim cinsinden olmalıdır. Örnek olarak iki farklı şehir için de hava sıcaklığı santigrat veya fahrenhayt olmalıdır.
Veri temizliği nasıl yapılır?
Herhangi bir veri setinin temizliği için öncelikle verinin yapısal düzeni, daha sonrasında da verinin kalitesi kontrol edilmeli ve belirli kurallara göre veri temizlenmelidir.
Yazının devamında süreci daha iyi kavrayabilmek için örnek bir veri seti ile uygulamalı olarak ilerleyeceğiz.
Örnek veri seti: New York’ta bulunan The Metropolitan Museum of Art müzesindeki eserler hakkındaki bilgiler.
Ön Keşif
Bir veri temizliği projesinde atılacak ilk adım problemlerin tespiti olmalıdır. Veri setindeki problemleri tespit edip, listelemeden ilerlediğiniz zaman yaptığınız işlemler içinde kaybolmanız, bazı detayları gözden kaçırmanız olasıdır. Bu nedenle keşif aşaması kesinlikle atlanmamalıdır.
Keşif sürecine, ilgili kütüphaneleri yükledikten sonra veri seti okunarak başlanır.
Veri manipülasyonu için Pandas, NumPy ve Re, veri görselleştirmesi için Matplotlib, istatistiksel hesaplamalar için SciPy VE ve son olarak da eksik veri analizi için Missingno kütüphanelerini kullanacağız.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import missingno as msno
import re
moma = pd.read_csv("MetObjects.csv")
Veri setiyle ilgili pek bir bilgiye sahip değiliz. Derinlemesine keşfe dalmadan önce veri setini tanımak için biraz bilgi toplamak faydalı olacaktır. Veri setinin kaç gözlemden oluştuğuna, kaç değişkeni olduğuna, değişkenlerin veri tiplerine genel bir göz atalım.
moma.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 474833 entries, 0 to 474832 Data columns (total 54 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Object Number 474833 non-null object 1 Is Highlight 474833 non-null bool 2 Is Timeline Work 474833 non-null bool 3 Is Public Domain 474833 non-null bool 4 Object ID 474833 non-null int64 5 Gallery Number 54984 non-null object 6 Department 474833 non-null object 7 AccessionYear 469957 non-null object 8 Object Name 473241 non-null object 9 Title 445217 non-null object 10 Culture 206751 non-null object 11 Period 90512 non-null object 12 Dynasty 23145 non-null object 13 Reign 11180 non-null object 14 Portfolio 23522 non-null object 15 Constiuent ID 272977 non-null object 16 Artist Role 270881 non-null object 17 Artist Prefix 89647 non-null object 18 Artist Display Name 272977 non-null object 19 Artist Display Bio 234207 non-null object 20 Artist Suffix 11946 non-null object 21 Artist Alpha Sort 272935 non-null object 22 Artist Nationality 182379 non-null object 23 Artist Begin Date 236668 non-null object 24 Artist End Date 236685 non-null object 25 Artist Gender 101588 non-null object 26 Artist ULAN URL 213047 non-null object 27 Artist Wikidata URL 208346 non-null object 28 Object Date 460055 non-null object 29 Object Begin Date 474833 non-null int64 30 Object End Date 474833 non-null int64 31 Medium 467722 non-null object 32 Dimensions 399400 non-null object 33 Credit Line 474430 non-null object 34 Geography Type 59372 non-null object 35 City 31999 non-null object 36 State 2530 non-null object 37 County 8358 non-null object 38 Country 75299 non-null object 39 Region 31346 non-null object 40 Subregion 22059 non-null object 41 Locale 15605 non-null object 42 Locus 7401 non-null object 43 Excavation 16010 non-null object 44 River 2098 non-null object 45 Classification 418743 non-null object 46 Rights and Reproduction 23585 non-null object 47 Link Resource 474833 non-null object 48 Object Wikidata URL 22118 non-null object 49 Metadata Date 0 non-null float64 50 Repository 474833 non-null object 51 Tags 216366 non-null object 52 Tags AAT URL 215894 non-null object 53 Tags Wikidata URL 214588 non-null object dtypes: bool(3), float64(1), int64(3), object(47) memory usage: 186.1+ MB
- Veri setinde 474,833 adet gözlem, 54 adet farklı değişken bulunmaktadır.
- 4-5 değişken hariç neredeyse geri kalan hepsinde kayıp değerler bulunmaktadır. Metadata Date değişkeninde hiç kayıt bulunmamaktadır. Kayıp verilerle ilgili özel bir analize ihtiyaç olacak gibi görünüyor.
- Değişkenlerin 47’si string, 3 tanesi integer, 1 tanesi float ve 3 tanesi boolean veri tipine sahiptir. ObjectDate, Object Begin Date gibi tarih içeren değişkenlerin string veya integer veri tipine sahip. Bu da veri tipleriyle ilgili hatalar olabileceğini ve değişkenlerin kontrol edilmesi gerektiğine yönelik ipuçları veriyor.
Veri setindeki gözlemlere dair fikir edinmek için veri setine farklı bir yönden bakmamız gerekiyor. Fikir elde etmek için ilk ve son 5 kayıta göz atacağız.
moma.head()

moma.tail()

- Sütun yani değişken isimleri açıklayıcı ve net bir şekilde yazılmış.
- Kayıp veriler NaN olarak işaretlenmiş görünüyor. Fakat yine de tüm değerler kontrol edilmeli.
- Metinler düzgün, typo hatası olmadan yazılmış olarak görünüyor.
- ObjectID değişkeni, objelere dair tekil bir kimlik numarası olarak göze çarpıyor. Fakat bu değişkenin, veri setinin indeksi olmadığını görüyoruz.
- ObjectNumber değişkeninde bulunan rakamlar, birkaç sütundaki değerin birleşimiyle olmuş gibi görünüyor. Biraz dikkatli bakıldığında, ilk 4 karakterin yılı temsil ettiği belli oluyor. Fakat bu değerin bazı gözlemlerde 4 haneli, bazı gözlemlerde 2 haneli olduğu yani bir tutarsızlık olduğu göze çarpıyor.
Veri setiyle ilgili artık daha fazla bilgi sahibiyiz. Ön analiz sonucunda yapacağımız en net çıkarım, veri setinin dağınık olduğu ve bu haliyle analiz için uygun olmadığı olacaktır.
Veri setini temizlemek için sistematik olarak öncelikle yapısına, daha sonrasında da kalitesine bakacağız.
Yapısal düzen
Veri setinin yapısal düzeninin, düzenli veri (tidy data) prensiplerine göre 3 kuralı yerine getirmesi gerekmektedir. Bu kuralları tekrar hatırlayalım:
- Her sütunda bir değişken bulunur.
- Her satırda bir gözlem bulunur.
- Her bir gözlem birimi bir hücre oluşturur.
Veri setinin yapısını incelerken, önce sütunlara, sonra satırlara ve daha sonrasında da değerlere bakmak iyi bir alışkanlık olacaktır. Böylelikle düzenli veri kurallarından hangilerinin ihlal edildiğini kolaylıkla tespit edebilirsiniz.
Örnek veri setimiz 54 farklı değişkenden oluşuyor. Pandas DataFrame yapısında belirli sayıda değişkeni tek bir seferde gösterir. Değişken sayısı arttıkça, tablonun ilk ve son değişkenlerini gösterebilmek adına aradaki değişkenleri üç nokta ile gösterir. Bu nedenle, kolaylık olması açısından veri setindeki değişkenleri iloc fonksiyonu ile seçip, belirli gruplar halinde inceleyip, sadece sorunlu olan değişkenleri yeni bir tablo haline getireceğiz.
moma.iloc[:15, 0:10]
- iloc fonksiyonunda virgülden önceki değerler satırları, virgülden sonraki değerler sütunların indeksini temsil eder.
- Yeterince satır görebilmek için ilk 15 satırı, hiçbir detayı kaçırmamak adına da değişkenleri 10’arlı olarak filtreliyoruz.
- Bu işlemi 54 adet değişkeni de kapsayacak şekilde yineleyeceğiz.
Tüm bu işlemler sonucunda, 6 adet değişkenin düzenli veri kurallarını ihlal ettiğini tespit ettik. Problemli değişkenler şunlardır:
moma[["Artist Role", "Artist Display Bio", "Object Date", "Dimensions", \
"Artist Display Bio", "Credit Line", "Tags"]][0:15]

- Veri setinde yaptığımız keşif sayesinde, her satırda da tek bir gözlemin bulunduğunu söyleyebiliriz.
- Yukarıdaki tabloda bulunan değişkenler, her değişkenin tek bir sütunda bulunması ve her hücrede tek bir gözlem olması kuralını ihlal etmektedir.
- Örnek olarak “Credit Line” değişkenini ele aldığımızda, ilgili objenin kim tarafından hediye edildiğini ve ayrıca hangi yılda hediye edildiğini görebiliriz. Yani, tek bir hücrede iki farklı değer bulunmaktadır.
- Aynı durumu diğer değişkenlerde de görebiliriz. “Dimensions” değişkeninde, objenin çevresinin hem inç hem de santimetre cinsinden 2 farklı ölçüm cinsinde aynı hücrede saklandığını söyleyebiliriz.
Örnek olarak veri setindeki “Credit Line” değişkenine göz atalım. Bu değişken, envanterin kim tarafından hediye ve aynı zamanda hangi yılda hediye edildiğine dair bilgileri bulundurmaktadır. Değişkenin değerleri iki farklı değer bulundurduğu için düzenli veri prensiplerinden üçüncü kuralı ihlal etmektedir.
Yeni bir değişken oluşturarak yıl değerini “Credit Line” değişkeninden, ayrı bir değişkene atayacağız.
moma["Credit_Year"] = moma["Credit Line"].str.split(",").str[1]
print(moma["Credit_Year"])
Object ID 1 1979 2 1980 3 1967 4 1967 5 1967 ... 850647 1952 850648 1952 850649 1952 850651 1952 850652 1943 Name: Credit Line, Length: 474833, dtype: object
- “Credit Line” değişkeninde bulunan gözlemlerde, yıl verisi hediye eden kişinin ardından virgül ile ayrılarak saklanmıştır.
- String metotlarından split() metodunu kullanarak “Credit Line” değişkenini virgül ifadesinden itibaren ayırdık ve bir listeye dönüştürdük. Bu liste hediye eden kişi ve yıl olarak iki elemente sahip oldu.
- Yıl değişkenin listedeki indeksi ile çağırdık ve “Credit_Year” adındaki yeni oluşturduğumuz değişkene sakladık.
Veri kalitesi
Veri setinin yapısına bakıldıktan sonra, bir sonraki adımda veri kalitesi değerlendirilmelidir. Veri kalitesi ile ilgili 5 farklı karakteristik özelliğe dikkat edilmesi gerekiyor.
1.Uygunluk
Veriler uygun tip ve formatta saklanmalıdır. Farklı tipteki veriler uygun veri tipleri ile saklanmalıdır. Python’un Pandas kütüphanesinde 6 farklı veri tipi vardır.
- String (str): Metin değerler için.
- Integer (int): Tam sayılar için.
- Float: Ondalık sayılar için.
- Boolean (bool): Mantıksal ifadeler (True/False) için.
- Datetime (datetime): Tarih ve saat gibi zamansal değerler için.
- Category (category): Kategorik değişkenler için. Örnek: Renk – Kırmızı, Mavi, Beyaz vs.
Veri tipleri, değişkenlerle ilgili yapılabilecek işlemleri ve limitleri belirler. Farklı veri tiplerinin farklı fonksiyonları ve işlevleri vardır. Bu nedenle, değişkenlerin uygun veri tipleri ile saklanması gerekmektedir.
Veri setindeki değişkenlerin veri tiplerini info() fonksiyonu veya dtypes nesnesi ile sorgulayabiliriz.
moma.info()
moma.dtypes
info() ve dtypes arasındaki fark, info fonksiyonunun veri seti ile ilgili her değişkende bulunan gözlem sayısı ve eksik verilerle ilgili bilgiler de vermesi. dtypes ile değişkenlerin sadece veri tipini öğrenebilirsiniz.
Veri setindeki 54 değişkeni tek tek incelediğimiz zaman, tam sayı (integer) veri tipine sahip olması gereken bazı değişkenlerin string veri tipine sahip olduğunu söyleyebiliriz:
- Integer olması gereken değişkenler – Gallery Number, Constiuent ID
Veri tipi dönüşümlerinde dikkat edilmesi gereken en önemli durum, gözlemlerin dönüşeceği yeni veri tipi formatına uygun olmasıdır. Gözlemleri tutarlı ve uygun bir hale getirmeden, veri tipi değişikliği yapıldığı zaman hatalarla karşılaşılacaktır.
String – integer dönüşümü
String veri tipinden integer veri tipine dönüşüm yaparken tüm gözlemlerin sayı formatında olması ve boş (Null) değerlerin olmaması gerekmektedir.
Örnek veri setindeki string veri tipine sahip değişkenlerden biri olan “Gallery Number” değişkenini inceleyelim.
Değişkende bulunan gözlemlerin içerisinde boş değerler (Null, NaN) olması durumunda dönüşüm işlemi uygulandığı zaman ValueError hatası oluşur.
moma["Gallery Number"].astype("int")
ValueError: cannot convert float NaN to integer
Değişkende boş değerlerden kaynaklı dönüşüm işleminin tamamlanamadığına dair bir hata oluştu. Peki, değişkende ne kadar boş değer bulunuyor?
moma["Gallery Number"].isnull().sum()
419849
Bu durumu gidermek için değişkende bulunan tüm boş değerleri kolayca ayırt edebileceğimiz ve değişkende bulunmayan bir değer ile değiştirip, dönüşüm işlemini uygulayıp, daha sonrasında da tekrardan boş değer olarak güncelleyebiliriz.
moma["Gallery Number"] = moma["Gallery Number"].replace(np.nan, 9999999)
replace() fonksiyonunu kullanarak tüm boş değerleri “9999999” gibi absürt bir rakamla değiştirdik. Dönüşüm işlemini tekrar uygulamayı deneyelim:
moma["Gallery Number"].astype("int")
ValueError: invalid literal for int() with base 10: 'in Great Hall'
Bu kez de gözlemlerin içerisinde sayı formatına uymayan değerler olduğunu belirten bir ValueError hatası oluştu. Sayı formatına uymayan değerleri boş değer olarak değiştirip, daha sonrasında tekrardan “9999999” değerini atayarak bu sorunu çözeceğiz.
Öncelikle, değişkende bulunan ve sayı formatına uymayan yani harf karakterleri içeren kayıtları listeleyelim.
moma[moma["Gallery Number"].str.contains("\D") == True]["Gallery Number"]
10166 in Great Hall 102694 Petrie Ct. Café 103680 Petrie Ct. Café 107173 Petrie Ct. Café 111965 Petrie Ct. Café 112215 Petrie Ct. Café 112219 Petrie Ct. Café 114763 Petrie Ct. Café 116093 Petrie Ct. Café 117222 Petrie Ct. Café 323577 on Fifth Avenue 469805 in Great Hall 469806 in Great Hall
contains() fonksiyonu ve regex’i kullanarak rakam içermeyen tüm kayıtları filtreledik. Çıkan sonuçlara göre “in Great Hall”, “Petrie Ct. Café” ve “on Fifth Avenue” değerlerinin değişmesi gerekmektedir.
gallery_strings = {
"in Great Hall": np.nan,
"Petrie Ct. Café": np.nan,
"on Fifth Avenue": np.nan
}
moma["Gallery Number"] = moma["Gallery Number"].map(gallery_strings)
moma["Gallery Number"] = moma["Gallery Number"].replace(np.nan, 9999999)
- Python’daki gelişmiş veri saklama formatlarından olan dictionary kullanarak değişmesi gereken stringleri boş değerler ile eşleştirdik.
- map() fonksiyonu yardımı ile eşleşme işlemini uygulamış olduk.
- Daha sonrasında da replace() fonksiyonu ile boş değerlere “9999999” değerini atadık.
Değişkendeki veri temizliği işlemleri tamamlandı. Artık dönüşüm işlemi için hazır. Dönüşüm işlemini tekrardan uygulayalım:
moma["Gallery Number"] = moma["Gallery Number"].astype("int")
moma["Gallery Number"].dtypes
dtype('int64')
Dönüşüm işlemini uyguladıktan sonra ilgili değişkenin veri tipi incelediğinde “int” yani sayı olarak güncellendiği görülmektedir.
Son olarak, dönüşüm işlemini tamamlanabilmesi için “9999999” olarak değiştirilen boş değerleri tekrardan “np.nan” olarak güncellenmesi gerekmektedir.
moma["Gallery Number"] = moma["Gallery Number"].replace(9999999, np.nan)
moma["Gallery Number"].isnull().sum()
419862
replace() fonksiyonunu kullanarak gerekli değişikliği sağladık. Değişkendeki boş gözlem sayısı 419,849’du. Daha sonrasında sayı formatına uymayan 13 gözlemi daha boş değere çevirmiştik. Şu an toplam boş gözlem sayısı ilk rakamdan 13 fazla olarak 419,862’dir.
String – date dönüşümü
Python’da tarih değerleri “datetime” veri tipine sahip olmalıdır. Python’da tarihsel değerler üzerindeki işlemler “datetime” kütüphanesi yardımı ile yapılır.
Örnek veri setinde bu dönüşümü uygulayacak bir değişken bulunmadığı için başka bir örnek üzerinden ilerleyeceğiz.
import datetime
date = "2016-23-12"
date = datetime.strptime(date, "%Y-%d-%m")
type(date)
datetime.datetime
- datetime kütüphanesi ile çalışabilmek için öncelikle kütüphanenin çağrılması gerekiyor.
- Örnek olarak tanımlanan “Yıl-Gün-Ay” formatındaki string veri tipine sahip değişken datetime kütüphanesine ait strptime metodu ile datetime veri tipine dönüştürülüyor.
- striptime metodu dönüştürülmesi gereken stringi ve bu stringin bulunduğu formatı parametre olarak kullanır.
String değerleri tarihe çevirirken Python’da kullanılan formatların tam listesine buradan erişebilirsiniz.
2. Doğruluk
Veriler gerçeği yansıtmalıdır. Bir insanın göz renginin mavi girilmesi uygun olabilir fakat doğru olmayabilir.
Özellikle string veri tipine sahip değişkenlerde sık görülen problemlerden bir tanesi çeşitli yazım hatalarından kaynaklı gözlemlerdeki tutarsızlıklarıdır. Örneğin, medeni durum kategorik bir değişkendir ve “Evli, Boşanmış, Bekar, Dul” gibi belirli değerlerden birini alması gerekir.
Veri girişi sırasında yapılan büyük/küçük harf tutarsızlıkları, isimlerin yanlış telaffuzları, metinlerin başındaki veya sonundaki boşluklar gibi yazım hataları aynı olan değerlerin farklı gibi görünmesine neden olur. Buradaki temel sorun, aynı değerlerin farklı yazılan birçok versiyonundan dolayı tekil gözlem olarak görünmesidir.
Bu kategorideki hatalar verinin birleştirilmesi/taşınması veya veri girişi sırasında yaşanan sorunlardan oluşur.
Yazım Hataları
Veri setindeki gözlemler veri girişi sırasında farklı farklı şekilde girilebilir. Bu tutarsızlıklar, tekil gözlemlerin birden fazla görünmesine neden olur. Örnek üzerinden gidersek, ülke değişkeninde “Türkiye” değeri şu şekillerde girilmiş olabilir:
- Turkey
- Türkiye
- TR
- Türkie
- Türkiye?
Yukarıda yazılan tüm farklı değerlere rağmen, aslında tek bir gözlem vardır: “Türkiye”.
Örnek olarak veri setimizdeki “Country” değişkenini inceleyelim. Bu değişken, eserlerin bulundukları ülke değerini barındırır. Öncelikle bu değişkende kaç adet tekil değer bulunduğuna bakalım.
moma["Country"].nunique()
930
930 tekil gözlem gayet yüksek bir rakam. Dünya üzerinde 2021 yılında güncel olarak bilinen 195 ülke vardır. “Country” değişkenindeki rakam bunun 4.5 katından da fazla. Bu rakam biraz işçilik gerektireceğini gösteriyor.
Peki, bu değişkende nasıl yazım problemleri var?
İlk önce en çok yazılan, ilk 30 değere bakacağız ve yavaş yavaş ilerleyeceğiz. Tek tek tüm değerleri incelemek zaman olarak problem yaratacaktır. Amacımız, belirli desenler yakaladıktan sonra ortak problemleri elemek için müdahale etmek.
- Eğer değişkenin tekil gözlem değeri az ise hatalı kayıtları doğru değerlerle bir dictionary kullanarak eşleştirmek mantıklı bir yöntemdir.
- Eğer değişkeninin tekil gözlem sayısı çok ise tek tek tüm değerler kontrol edilemeyeceği için düzenli ifadeler ve iki metnin birbirine benzerliğini hesaplamaya olanak sağlayan FuzzyWuzzy paketinden faydalanılabilir.
moma["Country"].value_counts()[0:30]
Egypt 31114 United States 9047 Iran 6152 Peru 3396 France 1702 Byzantine Egypt 1673 Mexico 1535 Indonesia 1390 India 1376 England 1117 China 917 Turkey 900 Papua New Guinea 879 Germany 852 Nigeria 636 Italy 510 Democratic Republic of the Congo 494 Syria 482 Spain 411 Iraq 391 Canada 381 Mali 378 Colombia 333 Côte d'Ivoire 299 America 257 United States|United States 249 Japan 239 Cameroon 232 United Kingdom 204 Costa Rica 199
İlk 30 kayıt içerisinde dikkat çeken problemlerden bir tanesi Amerika’nın adının iki farklı şekilde yazılmış olması. İkinci kırmızı ile işaretlenmiş değerde United States’in “|” işareti ile bir daha yazıldığını görüyoruz. İkinci en yaygın 30 değere bakalım:
moma["Country"].value_counts()[31:60]
Iran|Iran 192 Egypt or Syria 190 present-day France 178 Bolivia 164 Ghana 151 Northern France 138 Ecuador 129 United States|England 129 Saudi Arabia 127 Austria 123 present-day Uzbekistan 122 Republic of Benin 119 Panama 109 Guatemala 100 Netherlands 96 Palestine 96 Nubia (Sudan) 91 Burkina Faso 91 Russia 90 England|France 86 Algeria 83 Philippines 83 Australia 78 Gabon 72 Czech Republic 71 Solomon Islands 70 present-day Afghanistan 69 Vanuatu 66 Thailand 65 Name: Country, dtype: int64
Göze yeni problemler çarpıyor. Sadece aynı ülkenin farklı değerlerle yazılmadığını, aynı zamanda iki farklı ülkenin de “veya (|)” ile birlikte yazıldığını görüyoruz. Tahmin yürütecek olursak, büyük ihtimalle eserin bulunduğu ülke konusunda bir emin olamama durumu söz konusu.
Bir diğer problem ise “present-day” ifadesi. Muhtemelen, eserin bulunduğu coğrafyada daha önceden farklı bir devlet vardı fakat veri sahipleri günümüzün geçerli devletini belirtmek istemişler.
Değişkenin farklı farklı gözlemlerine baktığımızda problem yaratan ortak desenleri şöyle kategorize edebiliriz:
Emin olunmayan kayıtlar:
- France?
- United States|England
- Iran|present-day Uzbekistan
- Bolivia, Peru, or Chile
- Philippines (?)
- Tibet/Nepal
Yarı-emin olunan kayıtlar:
- probably Egypt
- possibly Dem. Rep. of Congo
Tekrarlı yazılan kayıtlar:
- United States|United States
- Iran|Iran
Yeni ülkeler:
- present-day Afghanistan
- Myanmar (formerly Burma)
- Bosnia (former Yugoslavia)
- Lake Van region, Vaspurakan (now Turkey) 1
- Austria (now Italy, Trentino-Alto Adige)
Ülke içinde yön belirten coğrafik kayıtlar:
- Eastern Iran
- Probably West Central Turkey
- probably Western Turkey
- Central Turkey
Çift ülkeler:
- Spain and Italy
- Laos and Thailand
- Liberia-Côte d’Ivoire (Ivory Coast) border
- Uganda-Sudan
- Republic of Congo & Angola
Veri bilimcisinin/analistinin burada bazı kararlar vermesi gerekiyor. Veri temizliği sırasında sektör tecrübesi önemli hale geliyor. Burada verilecek kararlar ile veri üzerinde teknik olarak doğru fakat değerli bilgilerin kaybolmasına neden olan işlemler yapılabilir.
Şöyle bir strateji ile ilerleyeceğiz:
- Emin olunmayan kayıtlara “Unknown” adında yeni bir değer atamak.
- Yarı-emin kayıtları tam kayıt olarak kategorize etmek için eklerini temizlemek.
- Tekrarlı yazılan kayıtlarda, fazladan yazılan parçayı temizlemek.
- Yeni veya eski ülkeleri belirten ön ekleri temizlemek.
- Ülke içerisindeki yön belirten ekleri temizlemek.
- Çift ülkelere sahip kayıtları, ilk değere eşitlemek.
Tüm bu işlemleri yaptıktan sonra FuzzyWuzzy paketini kullanarak, “Country” değişkenindeki gözlemlerin ne kadar doğru yazıldığını tespit etmek için güncel ülke isimleri ile karşılaştıracağız.
“Emin olunmayan kayıtlar” ile başlayalım. Buradaki sorunu çözmek için özel bir fonksiyon üretip, bu fonksiyonu tüm değişkene uygulayacağız.
def not_sure_cleaner(val):
if val == val:
if " or " in val:
val = "Unknown"
elif "|" in val:
new_lst = val.split("|")
if new_lst[0] != new_lst[1]:
val = "Unknown"
else:
val = new_lst[0]
elif "?" in val:
val = re.sub("\W*\?\W*", "", val)
elif "/" in val:
val = "Unknown"
else:
val == val
else:
val = np.nan
return val
- String manipülasyonu yapacağımız için string metotlarını kullanmak istiyoruz. String metotları da sadece string veri tipinde çalışmaktadır. Null değerler pandas’da “float” veri tipine sahiptir. Bu hücrelere string metotları uygulanamadığı için hata alınacaktır. Bu hatayı engellemek için fonksiyonda ilk olarak bir değerin boş mu (null) olup olmadığını kontrol ediyoruz. Bir hücre kendine eşitse (val == val) boş değildir. Eğer hücre boş ise, boş olarak bırakmaya devam ediyoruz. Değil ise de bir dizi kural belirleyip, manipülasyon yapacağız.
- Yazdığımız fonksiyon, boş olmayan değerler için eğer string’de;
- ” or ” değeri varsa “Unknown” ile;
- “|” değeri varsa ve “|”tan önce ve sonra gelen değerler birbirine eşitse aynı değeri, değilse de “Unknown” ile;
- “?” değeri varsa bunu bir regex sorgusu ile yakalayıp boşluk ile;
- “/” değeri varsa yine “Unknown” ile değiştiriyor.
- Yukarıda bahsedilen şartlardan herhangi biri sağlanmıyorsa, fonksiyon gözlem değerini olduğu gibi bırakıyor.
Fonksiyonu oluşturduktan sonra veri setimizde “cleaned_country” adında yeni bir sütun oluşturup, “Country” değişkeninde yaptığımız değişiklikleri bu yeni değişkende saklayacağız. “Country” değişkeni ham veri olduğu için, yeni oluşturduğumuz değişken ile bir nevi yedek almış oluyoruz. Yeni oluşturduğumuz değişkene uygulayacağımız işlemlerin sonucundan memnun olmadığımız durumda, “Country” değişkenini tekrardan kullanabiliriz.
Pandas’da bulunan apply() fonksiyonunu kullanarak oluşturduğumuz fonksiyonu “Country” değişkenine uygulayıp, sonrasında da ne kadar tekil kayıt olduğuna bakacağız.
moma["cleaned_country"] = moma["Country"].apply(not_sure_cleaner)
moma["cleaned_country"].nunique()
455
Tekil kayıtları neredeyse yarı yarıya indirmeyi başardık. İşlemlere devam edelim. Yarı emin olunan kayıtlar ve yeni ülkelerin yarattığı sorunları çözecek bir fonksiyon üreteceğiz.
def half_sure(val):
if val == val:
if "former" in val:
val = re.sub("\(former\w*\s*\w*\s*\w*\)", "", val)
elif ("probably" in val) or ("Probably" in val):
if "(" in val:
val = re.sub("\([Pp]robably\)", "", val)
else:
val = re.sub("[Pp]robably", "", val)
elif ("present" in val) or ("Present" in val):
if "(" in val:
val = re.sub("\([Pp]resent\W*day\s\w+\)", "", val)
else:
val = re.sub("[Pp]resent\W*day", "", val)
elif ("possibly" in val) or ("Possibly" in val):
if "(" in val:
val = re.sub("\(*[Pp]ossibly\)*\s*.*", "", val)
else:
val = re.sub("[Pp]ossibly", "", val)
elif "(now" in val:
val = str(re.findall("now\s*\w*", val)).replace("now", "")
else:
val == val
else:
val = np.nan
return val
- İlk yazdığımız fonksiyondaki gibi önce hücrenin boş olup olmadığını kontrol ediyoruz.
- Yazdığımız fonksiyon, boş olmayan değerler için eğer string’de “former”, “probably”, “present”, “possibly”, “now” gibi değerler ile eşleşme sağladığı zaman “boşluk” ile değiştiriyor. Ülke değerlerinin önüne veya sonuna gelen bu ekler çift kayıt yarattığı için değerlerin önünden ve arkasından çıkardık.
Değişkendeki gözlemlerde çok farklı desenler olduğu için düzenli ifadeler (regex) kullandık. Regex, kodun daha basit olmasını ve farklı farklı desenleri kolaylıkla yakalamamızı sağladı.
Bundan sonra yapacağımız tüm işlemleri “Country” değişkenine değil, yeni oluşturduğumuz “cleaned_country” değişkenine uygulayacağız.
moma["cleaned_country"] = moma["cleaned_country"].apply(half_sure)
moma["cleaned_country"].nunique()
431
Son olarak, ülkelerin önünde bulunan “North”, “East”, “West” gibi yön eklerini temizlemek için özel bir fonksiyon üreteceğiz.
def directions(val):
if val == val:
if "outh" in val:
if "South Africa" == val:
val = "South Africa"
else:
val = re.sub("([Ss]outh)\s*(ern|west|western|central)*", "", val)
if "est" in val:
val = re.sub("([Ww]est|[Nn]orth)\s*(ern|-central)*", "", val)
if "orth" in val:
if "(" in val:
val = re.sub("\([Nn]orth\)", "", val)
else:
val = re.sub("([Nn]orth)\s*(ern|east)*", "", val)
if "ast" in val:
val = re.sub("([Ee]ast)(ern|-central)", "", val)
if ("cent" in val) or ("Cent" in val):
val = re.sub("[Cc]entral", "", val)
else:
val == val
else:
val = np.nan
return val
- Bu son fonksiyonun çalışma prensibi üsttekilerle bire bir aynıdır.
- Öncelikle bir hücrenin boş olup olmadığı kontrol edilir.
- Boş olmayan değerler için eğer string’de “West”, “East”, “North”, “Central” gibi değerler ile eşleşme sağladığı zaman “boşluk” ile değiştiriyor. Bu ekler değişik birçok formatta yazıldığı için regex ile esneklik sağlayabiliyoruz.
moma["cleaned_country"] = moma["cleaned_country"].apply(directions)
moma["cleaned_country"].nunique()
375
İlk başta 930 tekil değere sahip olan değişkendeki tekil kayıt sayısını yaptığımız işlemlerle 375’e kadar indirdik. Yapılacak işlemler henüz bitmedi.
Üstteki fonksiyonları uyguladıktan sonra, ön ekleri vs. boşluk ile temizlediğimiz içim, bazı değerlerin başında boşluklar oluştu. Ayrıca, işlemler sırasında bazı gözlemlerde büyük/küçük harf kullanımı konusunda tutarsızlıklar fark ettik.
Bu tarz problemleri çözmeden, değerlerin doğru yazılıp yazılmadığını dışarıdan bir kaynak ile eşleştirmeye çalışmak riskli olabilir. Bu nedenle, burada uyguladığımız işlemlere bir ara verip, diğer potansiyel problemlere bakıp, onları da çözdükten sonra “FuzzyWuzzy” ile eşleşme işlemini tamamlayacağız.
Yanlış büyük/küçük harf kullanımı
Bir değişkendeki gözlemlerin bazılarının büyük harf, bazılarının küçük harf ile başladığı veya bazı gözlemlerin tamamen büyük harfle yazıldığı gibi durumlarda yaşanan problemlerdir. String veri tipi büyük/küçük harfe duyarlıdır. Bu da demek oluyor ki, “Mehmet” ile “mehmet” Python tarafından 2 farklı değer olarak algılanacaktır. Bu sorunun giderilmesi için lower(), upper(), title() gibi string metotlarından faydalanılacaktır.
Ülke isimleri özel isim olduğundan ilk harfi, birden fazla kelimeden oluşuyorsa da tüm kelimelerin ilk büyük olmalıdır. Öncelikle ilgili değişkende hangi değerlerin küçük yazıldığına bakalım.
lower_country = moma[moma["cleaned_country"].str.islower() == True]
lower_country["cleaned_country"].value_counts()
iran 4 india 1 iran 1 islamic lands 1 italy 1
- İlk olarak tüm karakterleri küçük olan gözlemleri “lower_country” yeni bir değişkene filtreledik.
- Bu filtreleme sırasında, tüm karakterleri küçük olan gözlemlerin “moma” veri setindeki 54 değişkenini de listelemiş olduk. Yani, minik bir dataframe oluşturmuş olduk.
- Odaklanmak istediğimiz alan sadece “cleaned_country” olduğu için, ikinci satırda yeni DataFrame’in sadece “cleaned_country” sütununu filtreledik ve burada da en çok görülen değerleri value_counts() metodu ile listeledik.
Tüm değerleri title() fonksiyonu ile ilk harfleri büyük olacak şekilde değiştirmek istiyoruz. Daha sonrasında da yaptığımız değişikliğin uygulanıp uygulanmadığını kontrol edeceğiz.
moma["cleaned_country"] = moma["cleaned_country"].str.title()
lower_country["cleaned_country"].value_counts()
Series([], Name: cleaned_country, dtype: int64)
- “cleaned_country” değişkenine title() fonksiyonunu uyguladık.
- Daha sonrasında value_counts() metodu ile daha önceden listelediğimiz küçük harfle başlayan değişkenleri bir daha çağırdık.
- Boş liste yani hiç değer bulunamadı. Bu da değişikliğin işe yaradığını gösteriyor.
Önemli Not: Bu metodun dikkat edilmesi gereken ve zayıf kaldığı yanlarından bir tanesi İngilizce’de bulunan “the, of, and” gibi ön ekleri de uygulanan gözlemde bulunması durumunda ilk harfi büyük hale getirmesidir. Mesela “Democratic Republic of the Congo” değeri, title() fonksiyonu uygulandıktan sonra “Democratic Republic Of The Congo” olarak dönüşecektir.
Bölümün sonunda, değişkende bulunan gözlemleri FuzzyWuzzy paketini kullanarak hali hazırda doğru yazılmış bir ülke listesi ile karşılaştırıp, değiştireceğimiz için bu konuda bir önlem almayacağız.
Boşluklar
Sıklıkla karşılaşılan bir diğer yazım hatası problemi ise değerlerin başında ve sonunda bulunan boşluklardır. Python büyük/küçük harfe olduğu gibi boşluklara karşı da duyarlıdır. “Türkiye”, ” Türkiye” ve “Türkiye ” temelde aynı değer olsa da Python tarafından 3 farklı şekilde yazıldığı için 3 farklı değer olarak algılanacaktır.
Küçük harfle başlayan değerleri filtrelediğimiz şekilde, boşluk ile başlayan değerleri filtreleyeceğiz.
spaces = moma[moma["cleaned_country"].str.startswith(" ", 0) == True]
spaces["cleaned_country"].value_counts()
France 330 Uzbekistan 123 Iran 96 Afghanistan 72 Egypt 53 ... Democratic Republic Of The Congo 1 Dem. Rep. Congo 1 Guatemala 1 Italy 1 Mexico 1 Name: cleaned_country, Length: 65, dtype: int64
- Startswith() foksiyonunu kullanarak ilk karakteri “boşluk” ile başlayan kayıtları filtreledik ve bu DataFrame’i “spaces” adlı bir değişkene atadık.
- Daha sonrasında da sadece “cleaned_country” sütununda en çok görülen kayıtları value_counts() ile listeledik.
- Görünüşe göre 65 farklı tekil değerde boşluklarla ilgili problemler var.
moma["cleaned_country"] = moma["cleaned_country"].str.strip()
spaces["cleaned_country"].value_counts()
Series([], Name: cleaned_country, dtype: int64)
Strip() fonksiyonu değişkende bulunan boşlukları temizledi.
moma["cleaned_country"].nunique()
300
Tüm bu değişikliklerden sonra değişkendeki tekil kayıt sayısına baktığımızda 375’ten 300’e düştüğünü görüyoruz.
“Country” değişkenindeki işlemleri tamamlamak için son olarak FuzzyWuzzy paketi ile güncel ülke listelerini karşılaştırdıktan sonra hala çözülemeyen değerleri de manuel olarak eşleştirip bu değişkendeki işlemleri tamamlayacağız.
FuzzyWuzzy varsayılan kütüphanelerden biri olmadığı için kullanılabilmesi öncelikle yüklenmelidir. pip ile yüklemek için:
pip install fuzzywuzzy
Collecting fuzzywuzzy Using cached fuzzywuzzy-0.18.0-py2.py3-none-any.whl (18 kB) Installing collected packages: fuzzywuzzy Successfully installed fuzzywuzzy-0.18.0 Note: you may need to restart the kernel to use updated packages.
Paket sisteme yüklendi. Daha sonrasında kullanabilmek için Fuzzywuzzy paketini “fuzz” takma adıyla çağırıyoruz.
import fuzzywuzzy as fuzz
Yapmak istediğimiz şey, “cleaned_country” değişkenindeki değerleri, güncel ülke listesi ile karşılaştırıp, daha sonrasında da bu liste içerisinden gözlem ile eşleşen en yakın 2 değeri bulmak.
country_list = pd.read_csv("https://pkgstore.datahub.io/core/country-list/data_csv/data/d7c9d7cfb42cb69f4422dec222dbbaa8/data_csv.csv")
c_lst = list(country_list["Name"])
- Güncel ülke listesini buradaki kaynaktan bulduk. Daha sonra dosyayı country_list adında bir DataFrame’e kaydettik ve c_lst adında bir listeye dönüştürdük.
Temizlediğimiz kayıtlarla, güncel ülke listesini karşılaştırıp, en yakın 2 adet kayıtı getirecek bir fonksiyona ihtiyacımız var.
def matcher(val):
if val == val:
if re.search("[A-Za-z]", val) != None:
first_char = (re.search("\w", val).group(0)).upper()
new_lst = [c_lst[i] for i in range(len(c_lst)) \
if c_lst[i][0] == first_char]
val = process.extract(query = val, choices = new_lst, limit = 2)
else:
val = np.nan
else:
val = np.nan
return val
- matcher() adını verdiğimiz fonksiyon, değişkende bulunan gözlemlerle güncel ülke listesini karşılaştırıp en yakın 2 adet kayıtı tuple formatında geri getiriyor.
- Fonksiyon ilk önce bir gözlemin boş değere (Null veya NaN) sahip olup olmadığını kontrol ediyor.
- Sonrasında bir regex fonksiyonu olan search fonksiyonunu kullanarak değerin ilk karakterini buluyor. İlk karakter bir harf ise ülke listesine gidip, bu harf ile başlayan ülkeleri “new_lst” adı verilen yeni bir listeye kayıt ediyor.
- Bunu tercih etmemizin sebebi ise tamamen verimlilik. Bir gözlemi yaklaşık 200 değer içerisinde aramakla, 10 değer içerisinde aramak arasında ciddi bir verimlilik farkı var.
- Daha sonraki adımda ise değişkende bulunan değer, aynı harf ile başlayan ülkeler listesinde FuzzyWuzzy’de bulunan extract() metodu ile aranıyor. extract() metodu 3 adet parametre kullanıyor:
- query: Aranılacak değer
- choices: Aramanın yapılacağı liste
- limit: Bulunması gereken en yakın eşleşme adeti
- Extract metodu sonuç olarak en yakın eşleşen değerleri ve eşleşme/benzerlik skorlarını getiriyor.
- Eğer aranılacak değer bir harf ile başlamıyorsa veya boş değer ise sonuç boş değere eşitleniyor.
Oluşturduğumuz fonksiyonu apply() metodu ile uyguladık ve yeni değerleri görmek için filtreledik.
moma["fuzzy_cleaned_country"] = moma["cleaned_country"].apply(matcher)
moma[moma["fuzzy_cleaned_country"].isnull() == False][["cleaned_country", "fuzzy_cleaned_country"]].head()
cleaned_country fuzzy_cleaned_country Object ID 15 Mexico [(Mexico, 100), (Micronesia, Federated States ... 16 Mexico [(Mexico, 100), (Micronesia, Federated States ... 17 Mexico [(Mexico, 100), (Micronesia, Federated States ... 18 Mexico [(Mexico, 100), (Micronesia, Federated States ... 19 Mexico [(Mexico, 100), (Micronesia, Federated States ...
En yakın eşleşen kayıtları ve benzerlik skorlarını “fuzzy_cleaned_country” adında yeni bir sütunda listeledik. Şimdi final eşleşmelerin yapılması gerekiyor. Bunun için de bir fonksiyona ihtiyacımız var.
def checker(val):
if val == val:
if val[0][1] >= 90:
val = val[0][0]
else:
val = "No clear match"
else:
val = np.nan
return val
- checker() fonksiyonu boş olmayan kayıtları inceleyip, 90 ve üzeri eşleşme skoru olan değeri doğru eşleşme olarak kabul ediyor. 90’ın altında kalan skorlarda ise kesin bir eşleşme olmadığı ve inceleme yapabilmemiz için “No clear match” ifadesini atıyor.
moma["final_cleaned_country"] = moma["fuzzy_cleaned_country"].apply(checker)
moma["final_cleaned_country"].nunique()
152
Başlarda 900 olan tekil gözlem sayısını 152’ye kadar düşürebildik. Son olarak manuel kontrollerle “No clear match” yani kesin eşleşme sağlanamayan kayıtlara göz atıp, problemleri tek tek gidereceğiz.
no_clear_match_filter = moma["final_cleaned_country"] == "No clear match"
moma[no_clear_match_filter]["cleaned_country"].value_counts()
Byzantine Egypt 1673 Unknown 1561 England 1130 Democratic Republic Of The Congo 498 Republic Of Benin 120 ... New Spain (Mexico) 1 Republic Of Congo & Angola 1 Caucasus 1 Republic Of Madagascar 1 La Tène Ii 1 Name: cleaned_country, Length: 113, dtype: int64
“No clear match” değeri atadığımız gözlemlerin, “FuzzyWuzzy” eşleşmesi yaptırmadan önceki değerlerine baktığımız zaman algoritmanın bazı değerlerle eşleştirme yapamadığını görüyoruz. Bunları bir dictionary yardımı ile doğru değerlerle eşleştirip, map() fonksiyonunu kullanarak final sütuna uygulayacağız.
problem_names = {
"Byzantine Egypt": "Egypt",
"New Spain (Mexico)": "Mexico",
"Republic Of The Philippines": "Philippines",
"Ivory Coast": "Côte d'Ivoire",
"Bohemia (Hungary)": "Hungary",
"Sarawak":"Malaysia",
"Indiaal": "India",
"Gotland": "Sweden"
}
moma["final_cleaned_country"] = moma["final_cleaned_country"].map(problem_names)
Aralık sınırları
Genel olarak sayısal ve tarihsel değerler belirli bir aralık içerisinde bulunur. Bu aralığın dışında kalan değerler dikkatle incelenip, müdahale edilmelidir.
Örnek olarak:
- Afrika’da tarih boyunca en yüksek sıcaklık 55 derece, en düşük sıcaklık ise -23.9 derece olarak kayıt edilmiştir. Bu değerlerin dışındaki değerler,
- Aylık satış verinize baktığınızda gelecek yıla ait gördüğünüz değerler
- Film değerlendirme puanlarında minimum 1, maksimum 5 yıldız değerleme yapılırken bu değerlerden farklı gördüğünüz değerler,
- Negatif yaş ve boy değerleri.
Burada rastlanılan problemleri aykırı değerlerin analizini yaparken detaylıca inceleyip giderebilirsiniz.
Format sınırları ve kısıt ihlalleri
Bazı özel metin alanları belirli bir formatı takip etmelidir. Örnek olarak Türkiye’deki posta kodu 5 hanelidir ve posta kodu değişkeninizde 5 haneden farklı değerler görüyorsanız bu problem giderilmelidir.
Bazı yazılım hataları, veri girişi sırasındaki problemler veya veri toplanan form tasarımında yapılan hatalar nedeniyle değişkeniniz istediğiniz formatın dışında kalan değerler içerebilir.
Örnek veri setinde bulunan “Object Number” değişkeni, belirli bir formata göre envantere ait üç farklı bilginin birleşmesiyle oluşur.
Ör: 1979.486.1
İlk 4 rakam, envanterin müzeye kazandırılma yılını temsil eder. Değişkendeki rakamları incelendiğinde bu formata uymayan gözlemler fark ediliyor.
moma["Object Number"].value_counts()
62.635 4 25.2.13 3 32.64.11 3 83.2.3 2 41.100.147 2 .. 1982.1164.1 1 2011.604.1.2526 1 2004.216 1 63.350.214.172.135 1 89.4.2227 1 Name: Object Number, Length: 472036, dtype: int64
Bazı gözlemlerin yılın son iki rakamını, bazılarının ise tüm rakamlarını kullandığını görüyoruz. Verilerin hangi yılda müzeye kazandırıldığını belirten “AccessionYear” değişkenini kullanarak, “Object Number” değişkeninde düzenlemeler yapacağız.
Yapacağımız art arda işlemler ile önce “Object Number” değişkeninin başındaki düzensiz yıl değerlerini çıkarıp, onun yerine “AccessionYear” değişkenindeki yıl değerini atayacağız.
moma["Object_Number_Cleaned"] = moma["Object Number"].str.split(".").str[1:]
moma["Object_Number_Cleaned"] = moma["Object_Number_Cleaned"].apply(lambda x: ".".join(x))
moma["AccessionYear_str"] = moma["AccessionYear"].astype("str")
moma["Object_Number_Cleaned"] = moma["AccessionYear_str"].str[0:4] + "." + moma["Object_Number_Cleaned"]
print(moma["Object_Number_Cleaned"])
Object ID 1 1979.486.1 2 1980.264.5 3 1967.265.9 4 1967.265.10 5 1967.265.11 ... 850647 1952.632.215 850648 1952.632.216 850649 1952.632.217 850651 1952.632.220 850652 1943.79.2(58) Name: Object_Number_Cleaned, Length: 474833, dtype: object
- İlk adımda değerleri nokta işaretinden split() fonksiyonu ile ayırarak bir liste elde ettik ve yıl değerinden sonra gelen değerleri seçerek yıl değerini dışarıda bıraktık.
- Sonraki adımda elde ettiğimiz listeyi tekrardan eski formata getirmek için join() fonksiyonu ile nokta kullanarak birleştirdik.
- “AccessionYear” değişkeni float veri tipine sahip olduğu için bu haliyle diğer string ile birleştirilemez. String-string veri tipine sahip değerleri birleştirebiliriz. Bu nedenle, “AccessionYear” değişkeninin veri tipini stringe çevirdik ve yeni bir değişkene sakladık.
- Yıl değerleri float’tan string’e dönüştüğü için ondalık değerlere de sahip. Mesela 1916 yılı float’tan döndüğü için string’de “1916.0” olarak görünüyor.
- Birleştirme işlemini yaparken ondalık kısmı dışarıda bırakmak için ilk 4 karakteri seçtik ve nokta işareti ile birlikte yıl değerlerini dışarıda bıraktığımız “Object Number” değerleri ile birleştirdik.
Aykırı değerler
Veri temizliği sırasında dikkat edilmesi gereken durumlardan bir tanesi de aykırı değerlerdir. Verinin geri kalanından belirgin derecede farklı olan değerlere aykırı değerler denir.
Aykırı değerler veri girişi problemlerinden olabileceği gibi aşırı performanslardan da kaynaklanabilir.
Veri seti için aykırı değer analizi yapılmalı ve analiz sonucunda elde edilen bulgulara göre müdahale edilmeli veya veri analizi bu farkındalık ışığında yapılmalıdır.
Örnek veri setinde, bir envanterin yapımına başlangıç tarihini temsil eden “Object Begin Date” değişkenine göz atalım.
Aykırı değer analizinde değişken ile ilgili fikir edinmek adına öncelikle temel istatistikleri ve kutu grafiğini kullanabiliriz.
moma["Object Begin Date"].describe()
count 474833.000000 mean 1294.368574 std 1718.349258 min -400000.000000 25% 1526.000000 50% 1800.000000 75% 1890.000000 max 5000.000000 Name: Object Begin Date, dtype: float64
describe() fonksiyonu ile bir Pandas serisinin temel istatistiklerine hızlıca göz atabiliriz. İlgili değişkene baktığımız zaman minimum ve maksimum değerleri çok uçuk görünüyor.
not_null_object_begin_date = moma[moma["Object Begin Date"].isnull() == False]["Object Begin Date"]
plt.boxplot(not_null_object_begin_date)

Kutu grafiği (boxplot) çizdirirken değişkenin boş değerleri olmaması gerekir. Bu nedenle öncelikle boş verileri filtreledik ve daha sonrasında da Matplotlib’i kullanarak boxplot() fonksiyonu ile grafiği çizdirdik.
Grafikteki daireler aykırı değerleri temsil ediyor. Grafikten de anlaşılacağı üzere değişkende aykırı değerler bulunuyor.
Aykırı değerlerin istatistiksel yöntemlerle de tespit edilmesi ve daha sonrasında da yönetilmesi gerekiyor. Veride ciddi aykırı değerler bulunduğu için çift yönlü ortanca mutlak sapma metodunu kullanabiliriz.
Çift yönlü ortanca mutlak sapma metodu ile alt ve üst aykırı değer limitlerini bulabilmek için bir fonksiyona ihtiyacımız var.
def double_mad(val):
c = 1.4826
q2 = np.median(val)
bot_val = [i for i in val if i <= q2]
bot_mad = (np.median(abs(bot_val - q2))) * c
up_val = [i for i in val if i >= q2]
up_mad = (np.median(abs(up_val - q2))) * c
lower_t = q2 - (3 * bot_mad)
upper_t = q2 + (3 * up_mad)
return lower_t, upper_t
lower_t = double_mad2(moma["Object Begin Date"])[0]
upper_t = double_mad2(moma["Object Begin Date"])[1]
print(lower_t, upper_t)
777.0060000000001 2200.302
- Çift yönlü ortanca sapma metodunda, öncelikle değişkenin ortanca değeri bulunur.
- Daha sonrasında veri, ortanca değerinden üst ve alt olarak iki gruba ayrılır. Ortanca değeri her iki gruba da dahil edilir.
- Hem alt hem de üst grup için ortanca mutlak sapma değeri hesaplanır ve katsayı (C) ile çarpılır. Katsayı, ortanca mutlak sapmanın popülasyonun tutarlı bir tahmincisi olmasını sağlar.
- Her iki grup için de ortanca mutlak sapma değerleri hesaplandıktan sonra alt ve üst medyandan 3 kat uzak olan değerler aykırı olarak kabul edilir.
Hesaplamalara göre 777 ve 2200 yıllarının arasında kalan değerler aykırı olarak işaretlenmelidir. 2020’den sonra gelen tüm değerlerin zaten aykırı değer olarak kabul edilmesini gerektiğini düşünebilirsiniz ve doğru. Veri setine baktığınızda 2020’den sonra 2 adet 5000 değerinin girildiğini ve bunların aykırı değer olarak kabul edilmesi gerektiğini görebilirsiniz.
c1 = (moma["Object Begin Date"] < lower_t)
c2 = (moma["Object Begin Date"] > upper_t)
moma[(c1 | c2)]["Object Begin Date"]
Object ID 2586 0 12942 0 14546 0 14683 0 14720 0 .. 848378 20 848616 0 848618 0 849427 0 849433 0 Name: Object Begin Date, Length: 83940, dtype: int64
777’den küçük ve 2200’den büyük rakamları filtrelemek için 2 adet koşul oluşturduk ve “pipe (|)” yani veya operatörü ile bu koşulları birlikte kullandık. 83,940 adet gözlem aykırı olarak görünüyor.
Aykırı değerleri tespit edildikten sonra aşağıdaki yöntemlerden uygun olanı ile işlenmelidir:
- Silme (Trimming)
- Değer atama (Imputation)
- Sınırlandırma (Winsorization)
- Dönüştürme (Transformation)
Bu değişken için aykırı değerleri daha uygun değerler ile değiştirmeyi sağlayan sınırlandırma (winsorization) işlemini uygulayacağız.
Daha önceden alt sınır olarak 777, üst sınır olarak da 2200’den büyük değerlerin aykırı olduğunu bulmuştuk. Alt ve üst sınırın verinin hangi dilimine yani kaçıncı kartile denk geldiğini bulmamız gerekiyor.
Gözlemlerin hangi kartilden itibaren sınırlandırılacağını bulmak için SciPy kütüphanesinin “stats” modülüne ait percentileofscore() fonksiyonundan yararlanacağız.
from scipy import stats
l_percentile = stats.percentileofscore(moma["Object Begin Date"], 777)
u_percentile = stats.percentileofscore(moma["Object Begin Date"], 2200)
print(l_percentile, u_percentile)
(17.677372886888655, 99.99957879928311)
Hesaplamalara göre en düşük %17 değer ve en yüksek %0.0001 değer aykırı değer olarak işaretlenmeli ve değiştirilmelidir. Değiştirme için yine SciPy’da bulunan “winsorize” modülünü kullanacağız.
from scipy.stats.mstats import winsorize
moma["Object_Begin_Date_Winsorized"] = winsorize(moma["Object Begin Date"], limits=[l_quart/100, 0.00001])
moma["Object_Begin_Date_Winsorized"].sort_values()
Object ID 322812 779 308493 779 308494 779 308495 779 308496 779 ... 843477 2020 839575 2020 839561 2020 502410 2020 839559 2020 Name: Object_Begin_Date_Winsorized, Length: 474833, dtype: int64
- winsorize() fonksiyonu, manipüle edilecek değişkeni, alt ve üst kartil değerlerini parametre olarak kullanır. alt ve üst kartil değerleri 0-1 arasında değer olarak girilmelidir.
moma[["Object_Begin_Date_Winsorized", "Object Begin Date"]].describe()
Object_Begin_Date_Winsorized Object Begin Date count 474833.000000 474833.000000 mean 1598.687951 1294.368574 std 421.423678 1718.349258 min 779.000000 -400000.000000 25% 1526.000000 1526.000000 50% 1800.000000. 1800.000000 75% 1890.000000 1890.000000 max 2020.000000 5000.000000
Sınırlandırılma işlemi uygulanmış değişken ile başlangıçta hiçbir işlem uygulanmamış değişkenlerin temel istatistikleri karşılaştırıldığında, işlem uygulanmış standart sapmasının azaldığını ve aykırı değerlerinin giderildiğini söyleyebiliriz.
3. Doluluk
İhtiyaç duyulan veriler eksiksiz ve tekil olmalıdır.
Çift kayıtlar
Veri setindeki gözlemler tekil olmalıdır. Farklı kaynaklardan verilerin toplanması veya veri girişi sırasında yaşanan hatalardan kaynaklı olarak çift kayıtlar görülebilir. Çift kayıtlar ile 2 farklı şekilde karşılaşmak mümkündür:
1- Tam çift kayıtlar: Birden fazla gözlem tüm değişkenlerde aynı kayıtlara sahiptir.
2- Bölgesel çift kayıtlar: Birden fazla gözlem bazı değişkenlerde çift kayıtlara sahiptir.
Bir veri setindeki çift kayıtları analiz edebilmek için Pandas’ın .duplicated() fonksiyonu kullanılır.
moma.duplicated()
duplicated() fonksiyonu herhangi bir parametre verilmeden kullanıldığında tam çift kayıtları bulmaya odaklanır. Bu noktada dikkat edilmesi gereken nokta, eğer bir değişkende “Türkiye”, “türkiye” gibi yazım hatalarından kaynaklı farklı değerler varsa, duplicated() fonksiyonu bunu farklı değerler olarak algılayacaktır.
Yani, çift kayıtları analiz etmeden önce veri setindeki yazım hataları ve yapısal problemler mutlaka giderilmelidir.
Bölgesel çift kayıtları aramak için duplicated() fonksiyonuna subset parametresi ile hangi değişkenlerde arama yapılacağı belirtilmelidir. Birden fazla değişkende çift kayıtları aramak için de subset parametresine ilgili değişkenler liste olarak belirtilmelidir.
moma.duplicated(subset = "Country")
moma.duplicated(subset = ["Country", "Title"])
İlgili çift kayıtları bulduktan sonra filtreleyebilir, düzeltebilir veya silebilirsiniz. Silme işlemi için yine Pandas’tan drop_duplicates() fonksiyonunu kullanabilirsiniz.
moma.drop_duplicates()
drop_duplicates() fonksiyonu herhangi bir parametre almadığı zaman, tam çift kayıtları siler. drop_duplicates()’i daha kullanışlı hale getiren 3 farklı parametre vardır:
- subset: Bölgesel çift kayıtları silmek için hangi değişken/değişkenlerin kontrol edileceğini belirtir.
- keep: Hangi kopyaların saklanacağını belirtir.
- first – İlk rastlanan çift kayıtı tekil olarak kabul eder ve saklar. Geri kalan kopyaları siler.
- last – Son rastlanan çift kayıtı tekil olarak kabul eder ve saklar. Geri kalan kopyaları siler.
- False – Tüm çift kayıtları kopya olarak kabul eder ve siler.
- inplace: Yapılan değişikliklerin DataFrame’e kalıcı olarak uygulanacağını belirtir.
- True: Değişiklikler kalıcı olarak uygulanır.
- False: Değişiklikler kalıcı olarak uygulanmaz.
moma.drop_duplicates(subset = "Country", keep = "first", inplace = True)
Eksik/Kayıp veriler
Veri setindeki eksik değerler veri kalitesini düşürdüğü gibi analiz sonuçlarını da olumsuz etkiler. Çift kayıtlarda olduğu gibi eksik değerler de analiz edilip, mutlaka müdahale edilmelidir.
Kayıp değerler farklı nedenlerden oluşabilir ve mekanizmalarına göre farklı şekillerde işlemlere ihtiyaç duyarlar. Kayıp veri mekanizmalarına göre 3’e ayrılır:
1- Tamamen Rastgele Kayıp Veri (MCAR): Kayıplar tamamen rastgele oluşmuştur. Herhangi bir kasıtlı neden yoktur.
2- Rastgele Kayıp Veri (MAR): Kayıp veriler ile “ölçülen” değerler arasında sistematik bir ilişki vardır. Kayıplar rastgele oluşmasına rağmen, ölçülen değerler ile tahmin edilebilir.
3- Rastgele Olmayan Kayıp Veri (MNAR): Kayıp verilerin kayıp olmasının nedeni tamamen değişkenin kendisiyle veya veri setinde ölçülmemiş farklı bir değişkenle ilgilidir.
Bir veri setinde bulunan değişkenlerdeki kayıp veri miktarı isnull() fonksiyonu yardımı ile sorgulanılabilir. isnull() fonksiyonu her bir değeri kontrol eder ve doğru veya yanlış (True & False) değeri üretir. True değeri 1, False değeri ise 0’a eşittir. True değerlerini topladığımız zaman ise her değişkendeki eksik sayısını bulabiliriz.
moma.isnull().sum()
Object Number 0 Is Highlight 0 Is Timeline Work 0 Is Public Domain 0 Gallery Number 419849 Department 0 AccessionYear 4876 Object Name 1592 Title 29616 Culture 268082 Period 384321 Dynasty 451688
Veri setinde 54 adet değişken olduğu için çıktının nasıl göründüğünü göstermek amacıyla küçük bir bölümünü çıktı olarak ekledik. Tüm değişkenlere baktığımız zaman 9 adet değişken dışındaki tüm kalan değişkenlerde değişen miktarlarda eksik veri olduğunu görebiliriz.
Kayıp verileri görselleştirmek hem daha kolay analiz yapmak için hem de kayıp veri mekanizmasını belirlemek için yardımcı olacaktır. Kayıp verileri görselleştirmek için missingno kütüphanesini kullanacağız. Kullanmadan önce kütüphaneyi çağıracağız:
import missingno as msno
Kayıp verilerin miktarını görselleştirmek için sütun grafiğinden faydalanabiliriz.
msno.bar(moma)
Not: Sadelik ve okunabilirlik açısından en yüksek kayıp değere sahip ilk 12 değişkene ait grafik kullanılmıştır.

- Kayıp verilere dair sütun grafiğinde gri alanlar dolu, beyaz alanlar ise boşlukları temsil eder.
- Her sütunun en üstünde bulunan değerler, o değişkende bulunan gözlem sayısını ifade eder.
Sütun grafiği, hangi değişkende ne kadar eksik veri olduğuna dair hızlıca bir fikir edinmek için idealdir. Kayıp veri mekanizmalarını bulabilmek için değişkenler arası ilişkilere ihtiyaç vardır. Bu nedenle yine missingno kütüphanesinde bulunan kayıp veri matriksi, korelasyon ısı haritası ve dendrogram gibi ilişkisel grafikler kullanılmalıdır.
İlk önce kayıp veri matriksi ile başlayalım. Kayıp veri matriksi, kayıp verilerin değişken içerisindeki yerini görselleştirir. Bu sayede farklı değişkenlerin kayıp verileri arasında bir ilişki olup olmadığına dair fikir edinilebilir.
msno.matrix(moma)

- Matriks grafiğinde gri alanlar dolu değerleri, beyazlar ise kayıp verileri ifade eder.
- Sarı ile işaretlenen değişkenlerde, kayıp veri miktarı gayet az. Ayrıca, birbirleri arasında bir desen var gibi görünmüyor. Kayıpların tamamen rastgele oluşma (MCAR) ihtimali yüksek.
- Kırmızı ve mavi ile ayrı ayrı işaretlenen değişkenlerdeki kayıp verilerin kendi içerisinde bir ilişkisi olduğu görülüyor. Yani, iki farklı değişkenin kayıp değerlerinin birbirleriyle arasında bir ilişki (pozitif veya negatif) olması kayıp verilerin rastgele oluşmadığına (MNAR) dair ipuçları veriyor.
Kayıp verilerin giderilmesi ile ilgili bir aksiyon almadan önce ilgili değişkenin analiz için kullanıp kullanılmayacağına karar verilmeli, sonrasında da kayıp veri mekanizmasına göre silme veya atama işlemlerinden birini uygulanmalıdır.
4. Tutarlılık
Veri setindeki iki farklı değer birbiriyle tutarlı olmalıdır. Evli olarak kayıt edilen bir müşterinin yaşı 10 veya bir müşterinin ev adresi farklı tablolarda farklı değerlere sahip olmamalıdır.
Örnek veri setinde bu durumu müzede bulunan envanterin, (tahmini olarak), yapımına başlanıp bitirildiği zamanı belli eden “Object Begin Date”, “Object End Date” değişkenleri ile inceleyebiliriz.
Mantıken, bitiş tarihinin başlangıç tarihinden büyük olması gerekiyor. Bu durumu ihlal eden kayıtların “Tutarlılık” prensibine uymadığını söyleyebiliriz.
obj_dates = moma[["Object Date", "Object Begin Date", "Object End Date"]]
obj_dates["obj_dates_diff"] = obj_dates["Object End Date"] - obj_dates["Object Begin Date"]
obj_dates[obj_dates["obj_dates_diff"] < 0]
- Öncelikle daha rahat analiz yapabilmek için “Object Date”, “Object Begin Date” ve “Object End Date” değişkenleri ile “obj_dates” adını verdiğimiz yeni bir DataFrame oluşturduk.
- Sonrasında, yukarıda bahsedildiği gibi bitiş tarihini, başlangıç tarihinden çıkardık ve 0’dan küçük olan yani başlangıç tarihi, bitiş tarihinden büyük olan kayıtları filtreledik.
- 188 kayıtta bir sorun olduğunu görüyoruz.
Filtrelediğimiz sonuçlara göre ilk 5 gözlem şu şekilde görünüyor:
Object Date | Object Begin Date | Object End Date | obj_dates_diff |
5th–1st century B.C. | 5 | 0 | -5 |
ca. 50 B.C. | -45 | -55 | -10 |
1861, 9th month | 1861 | 9 | -1852 |
1800–1875 | 1875 | 1800 | -75 |
late 18th century | 1785 | 1779 | -6 |
Sonuç tablosunda dikkat çeken bazı problemler var:
- Veri setinde tarih öncesi yani milattan önce (BC) kayıtlar bulunduğu ve milattan öncesi yıllarda büyükten 0’a doğru bir sayım olduğu için bazı değerler filtremize takıldı.
- Bazı gözlemlerde tarihler yanlış kayıt edilmiş. Üçüncü satırda bitiş yılının 9 olduğunu görüyoruz. Dördüncü satırda ise başlangıç tarihi ile bitiş tarihinin tam ters şekilde yazılması gerektiğini anlıyoruz.
Milattan önceki sonuçların doğru olduğunu kabul edip, diğer problemlere odaklanalım.
cond1 = (obj_dates["obj_dates_diff"] < 0)
cond2 = (obj_dates["Object Date"].str.contains("B.C.") == False)
obj_dates[cond1 & cond2]
- Bitiş tarihi ile başlangıç tarihi arasındaki farkın negatif ve milattan sonraki kayıtları filtrelemek istiyoruz.
- 2 adet filtre koşulu belirledik.
- Bitiş ve başlangıç arasındaki farkı negatif olan kayıtlar
- Object Date sütununda milattan önce anlamına gelen “B.C.” ifadesine sahip olmayan kayıtlar
- Bu iki koşulu “&” operatorü ile uyguladık.
Filtrelenen sonuçlardan bazılarını listeledik:
Object Date | Object Begin Date | Object End Date | obj_dates_diff |
after 364 | 364 | 0 | -364 |
1815 (2nd ed.) | 1815 | 0 | -1815 |
1893, begun in 1891 | 1893 | 1891 | -2 |
June 10, 1935, July 27, 1949 | 1949 | 1935 | -14 |
Bitiş tarihi sıfır olan kayıtları hatalı olarak kabul edip, sıfır değerini “NULL” olarak değiştirebiliriz.
obj_dates["Object End Date"] = obj_dates["Object End Date"].apply(lambda x: np.nan if x == 0 else x)
- apply() fonksiyonu ile “0” olan bitiş tarihi kayıtlarını boş değere (NaN veya NULL) çevirdik.
Bitiş tarihi sıfır olmayan ve başlangıç tarihi ile arasında 100 yıldan az bir fark bulunan kayıtları ise başlangıç tarihi ile değiştirebiliriz. 100 yıldan az olan kayıtlara baktığımızda başlangıç ve bitiş tarihlerinin ters olduğuna dair neredeyse tamamen eminiz.
cond1 = (obj_dates["obj_dates_diff"] > -100) & (obj_dates["obj_dates_diff"] < 0)
cond2 = (obj_dates["Object Date"].str.contains("B.C.") == False)
obj_dates[cond1 & cond2][["Object Begin Date", "Object End Date"]]
Object Begin Date Object End Date 79162 1875 1800.0 118870 1785 1779.0 136493 1785 1773.0 138547 1785 1779.0 205751 1893 1891.0 249613 1785 1780.0 332136 1949 1935.0
- Verileri filtrelerken 2 adet koşul yarattık.
- Bitiş ve başlangıç tarihi arasındaki fark negatif ve 100 yıldan fazla olmamak,
- B.C yani milattan önce ifadesine sahip olmamak
- Bu iki koşulu sağlayan kayıtları filtreledik.
- Sonuç tablosunda da görüldüğü üzere başlangıç ve bitiş tarihinin yer değiştirmesi gerekiyor.
filter_condition = (cond1) & (cond2)
obj_dates["Object Begin Date"], obj_dates["Object End Date"] = np.where(filter_condition, \
[obj_dates["Object End Date"], obj_dates["Object Begin Date"]], \
[obj_dates["Object Begin Date"], obj_dates["Object End Date"]])
- Değerlerin değişmesi için NumPy kütüphanesinde bulunan where() fonksiyonunu kullandık. where() fonksiyonu 3 adet parametre kullanır:
- Kontrol edilmesi gereken koşul,
- Koşul doğruysa gerçekleşecek işlem,
- Koşul yanlışsa gerçekleşecek işlem.
- İlk parametrede fonksiyona daha önceden yukarıda kullandığımız koşulu belirttik. Tek bir koşul girişi yapabileceğimiz için yukarıda kullandığımız iki farklı koşulu “&” ve operatörü ile “filter_condition” adında bir değişkende birleştirdik.
- Yapacağımız işlemin “Object Begin Date” ve “Object End Date” değişkenlerine uygulanmasını istiyoruz. Bu nedenle sonucu bu değişkenlere atayacağımız için denklemin sol tarafında bu iki değişkeni belirttik.
- np.where() fonksiyonuna önce kontrol edilmesini istediğimiz koşulu verdik. Daha sonrasında bu koşul sağlandığında End Date değerinin Begin Date değeri yerine yazılmasını, koşul sağlanmadığında ise aynı şekilde kalması gerektiğini belirttik.
obj_dates[filter_condition][["Object Begin Date", "Object End Date"]]
Object Begin Date Object End Date 79162 1800.0 1875.0 118870 1779.0 1785.0 136493 1773.0 1785.0 138547 1779.0 1785.0 205751 1891.0 1893.0 249613 1780.0 1785.0 332136 1935.0 1949.0
İşlemin başarılı olup olmadığını kontrol etmek için tekrardan filtrelediğimizde sonuç tablosunda değişikliklerin doğru olarak uygulandığını görüyoruz.
Veri temizliği sırasında kararları verirken sektörde uzman birinden fikir/yönlendirme almak ve ek veri kaynakları ile eksik/hatalı verileri tamamlamaya çalışmak faydalı olabilir.
5. Monotonluk
Veri setindeki değerler aynı birim cinsinden olmalıdır. Örnek olarak iki farklı şehir için de hava sıcaklığı santigrat veya fahrenhayt olmalıdır.
Örnek veri setinde “Dimensions” değişkeninde, müzede bulunan envanterlere dair en, boy, çap vs. gibi ölçülere dair değerler bulunmaktadır. Değişkene göz atalım.
moma["Dimensions"].value_counts()[0:10]
sheet: 2 11/16 x 1 3/8 in. (6.9 x 3.5 cm) 2297 Sheet: 2 3/4 x 1 1/2 in. (7 x 3.8 cm) 2278 Sheet: 2 1/2 × 1 7/16 in. (6.4 × 3.7 cm) 2117 Sheet: 2 3/4 × 1 1/2 in. (7 × 3.8 cm) 1997 35mm 1458 Sheet: 2 11/16 × 1 3/8 in. (6.8 × 3.5 cm) 1317 4 x 5 in. 1307 Sheet: 2 3/4 x 1 3/8 in. (7 x 3.5 cm) 1151 Approx. 3 1/2 × 7 in. (8.9 × 17.8 cm) 989 Sheet: 2 5/8 × 1 1/2 in. (6.7 × 3.8 cm) 913 Name: Dimensions, dtype: int64
Değişkende en sık rastlanan 10 değere baktığımızda, değerlerin hem inç hem de santimetre olarak tutulduğunu görüyoruz. Bu durum bazı gözlemlerde inç, bazılarında ise santimetre cinsinden ölçüm değerleri ile de kendini gösterebilirdi. Her iki durumda da monotonluk ilkesi ihlal ediliyor.
“Dimensions” değişkeninde birçok farklı ölçüm değeri bulunuyor. Örneğin sadeliği açısından sadece levha (sheets) boyutlarını düzenleyip, santimetre değerler ile ilerleyeceğiz.
sheets = moma[moma["Dimensions"].str.contains("Sheet|sheet") == True][["Dimensions"]]
sheets["cm_dimensions"] = sheets["Dimensions"].str.extract("(\(\d*\.?\d*\s*.\s*\d*\d*\.?\d*\s*cm\))")
sheets["cm_dimensions"] = sheets["cm_dimensions"].str.replace("\(", "").str.replace("\)", "")
sheets["cm_dimensions"].value_counts()[0:10]
7 x 3.8 cm 2328 6.9 x 3.5 cm 2297 6.4 × 3.7 cm 2117 7 × 3.8 cm 1998 6.8 × 3.5 cm 1317 7 x 3.5 cm 1151 6.7 × 3.8 cm 913 8.9 × 6.4 cm 887 8.9 × 6.3 cm 872 13.6 × 8.5 cm 782
- Öncelikle “Dimensions” değişkeninde “Sheet veya sheet” içeren gözlemleri filtreleyerek, yeni bir değişkene atadık.
- Sonrasında extract() fonksiyonunu kullanarak yazdığımız regex ifadesi ile santimetre değerlerini metinden çıkardık.
- Art arda kullandığımız replace() fonksiyonu ile de önce değerin başındaki, sonra da sonundaki parantezi temizledik.
- value_counts() ile en yaygın rastlanan 10 değere baktığımızda işlemin başarılı bir şekilde gerçekleştiğini söyleyebiliriz.
Temiz ve düzenli bir veri, veri bilimi/analizi projeleri için kritik öneme sahiptir. Eğer devamlı bir veri akışı var ise veri temizliği prosedürleri düzenli aralıklarla elden geçirilmeli, veri kalitesinin artırılması için çalışmalar yapılmalıdır.
Harika bir çalışma oluşmuş. Her bir adım çok sade bir biçimde açıklanmış. Emekleriniz için teşekkür ederim.
Harika bir çalışma olmuş. Her bir adım olabilecek en sade şekilde açıklanmış. Emekleriniz için teşekkür ederim.