Pandas ile ilgili bu yazımızda veri çerçevesi (DataFrame) isimli veri yapısını ele alacağız. Önceki yazımızda etiketli verilerden oluşan serileri görmüştük. Seriler tek boyutlu bir veri yapısıyken, veri çerçevelerini her sütunu bir seriden oluşan iki boyutlu bir matris olarak düşünebiliriz. Serilerde bahsettiğimiz birçok yöntemi veri çerçevelerinde de kullanmak mümkün. Ayrıca SQL tablolarında kullanılan tablo birleştirme (JOIN) gibi işlemleri de Pandas ile yapmak mümkün.
Veri Çerçeveleri
Veri çerçevelerini birden fazla serinin bir araya gelmiş hali olarak düşünebiliriz. Veri çerçeveleri de seriler gibi etiketli bir veri yapısıdır, ancak serilerden farkı iki boyutlu olmasıdır. Serilerdeki gibi etiket değerlerine indeks (index) denir. Veri çerçeveleri index alanının yanında columns alanını da içeriyor. columns sütunların isimlerini içeren bir sıralı nesne.
Veri çerçevesi oluşturmak için serileri kullanmak mümkün. Linkten erişebileceğiniz IMF’nin gayrisafi yurt içi hasıla verisi ile ilk denemeyi yapabiliriz. 2016 ve 2017 yılları için serileri anlattığımız yazıdaki veri kümelerini oluşturalım.
# Pandas paketini yükleyelimimport pandas as pd import numpy as np# GSYİH değeri en yüksek olan 10 ülkenin değerlerini kullanacağız.gdp_data_2017 = [19362.13, 11937.56, 4884.49, 3651.87, 2574.81, 2565.05, 2439.01, 2080.92, 1921.14, 1640.39]# İndeks değerlerini içeren listeyi oluşturalım.gdp_index_2017 = ['ABD', 'Çin', 'Japonya', 'Almanya', 'Fransa', 'Birleşik Krallık', 'Hindistan', 'Brezilya', 'İtalya', 'Kanada']# Seriyi oluşturalım. İndeks değerini vermediğimizde, Pandas 0'dan başlayarak veriyi indeksler.gdp_2017 = pd.Series(gdp_data_2017, index = gdp_index_2017, name ='GDP_2017')# Aynı işlemi 2016 yılı için yapalım.d = {'ABD':18624.45,'Çin':11232.11,'Japonya':4936.54,'Almanya':3479.23,'Fransa':2466.47,'Birleşik Krallık':2629.19,'Hindistan':2263.79,'Brezilya':1798.62,'İtalya':1850.74,'Kanada':1529.76,'Kore':1411.04}gdp_2016 = pd.Series(d, name ='GDP_2016')print(gdp_2016)print(gdp_2017)
ABD 18624.45
Almanya 3479.23
Birleşik Krallık 2629.19
Brezilya 1798.62
Fransa 2466.47
Hindistan 2263.79
Japonya 4936.54
Kanada 1529.76
Kore 1411.04
Çin 11232.11
İtalya 1850.74
Name: GDP_2016, dtype: float64
ABD 19362.13
Çin 11937.56
Japonya 4884.49
Almanya 3651.87
Fransa 2574.81
Birleşik Krallık 2565.05
Hindistan 2439.01
Brezilya 2080.92
İtalya 1921.14
Kanada 1640.39
Name: GDP_2017, dtype: float64
Oluşturduğumuz serileri birleştirmek için iki yol kullanacağız. Bunlardan biri pandas altındaki concat fonksiyonu, diğeri ise serilerden oluşan bir sözlük tanımlamak olacak.
concat fonksiyonunu kullanırken sütunların isimlerini ayrıca belirtmemize gerek yok. Bunun yerine serilerin name alanı sütun isimleri olarak atanıyor. Bir seride olup diğerinde olmayan değerler (Kore gibi) NaN değeriyle gösterilir.
GDP_2017 GDP_2016
ABD 19362.13 18624.45
Almanya 3651.87 3479.23
Birleşik Krallık 2565.05 2629.19
Brezilya 2080.92 1798.62
Fransa 2574.81 2466.47
Hindistan 2439.01 2263.79
Japonya 4884.49 4936.54
Kanada 1640.39 1529.76
Kore NaN 1411.04
Çin 11937.56 11232.11
İtalya 1921.14 1850.74
Veri çerçevesindeki değerlerin serilerdeki yerinden bağımsız olarak indeks alanına göre eşleştirildiğini görüyoruz. Ondalık işaretini virgüle çevirmek için aşağıdaki yerel ayarları kullanabiliriz. Aşağıdaki kutucukta yerel ayarları değiştiriyoruz ve ondalıklı sayıları formatlıyoruz. Son satırdaki grouping = False binlik işaretini kullanmayacağımızı belirtiyor. Yerel ayarların formatlama ile kullanılması konusunda Kaan’ın sıralı nesneler ve dizeler için yazdığı yazıları incelemenizi tavsiye ederim.
import localeloc = locale.getlocale()# Yerel ayarları Türkiye standartlarına çeviriyoruz.locale.setlocale(locale.LC_ALL, "Turkish_Turkey.1254")# Küsuratlı sayıları virgülden sonra iki basamak içerecek şekilde formatlıyoruz.# Binlik işaretini kullanmayacağız.pd.set_option('display.float_format', lambda x: locale.format('%.2f', x, grouping =False))
Aşağıdaki örnekte, veri çerçevesini serilerden oluşan bir sözlük yardımıyla oluşturuyoruz. Sözlüğün anahtarı sütunun ismine atanır.
d = {'2017' : gdp_2017,'2016' : gdp_2016}df = pd.DataFrame(d)print(df)
2016 2017
ABD 18624,45 19362,13
Almanya 3479,23 3651,87
Birleşik Krallık 2629,19 2565,05
Brezilya 1798,62 2080,92
Fransa 2466,47 2574,81
Hindistan 2263,79 2439,01
Japonya 4936,54 4884,49
Kanada 1529,76 1640,39
Kore 1411,04 nan
Çin 11232,11 11937,56
İtalya 1850,74 1921,14
Ondalık işaretinin değiştiğini görebilirsiniz.
Sözlük listeleri de veri çerçevesi oluşturmak için kullanılabilir. Aşağıdaki örnekteki listedeki her bir sözlük bir ülkenin GSYİH değerine denk geliyor. Hangi ülkeler olduğunu ise veri çerçevesini oluştururken söylüyoruz. Belirtmediğimiz değerler de NaN değerini alıyor.
data = [{'GDP_2016':3651.87, 'GDP_2017': 3479.23}, {'GDP_2016' : 2466.47}]df = pd.DataFrame(data, index = ['Almanya', 'Fransa'])print(df)
GDP_2016 GDP_2017
Almanya 3651,87 3479,23
Fransa 2466,47 nan
Veri çerçevelerinin matrisler gibi iki boyutlu bir veri yapısı olduğundan bahsetmiştik. Matrisler de veri çerçeveleri oluşturmak için kullanılabilir.
# 2x2'lik rassal sayılardan oluşan bir matris oluşturalım.data = np.random.rand(2,2)# Index ve columns değerlerini veri çerçevesini oluştururken tanımlayabiliriz.df = pd.DataFrame(data, index = ['1. satır', '2. satır'], columns=['1. sütun', '2. sütun'])print(df)
Uygulamalarınızda kullanacağınız boyuttaki verileri bahsettiğimiz yöntemlerle veri çerçevesine atamanın zorluğunu farketmişsinizdir. Neyse ki pandas çeşitli formatlardaki dosyaları okumanızı sağlayacak fonksiyonlar içeriyor.
Dosya okuma
pandas paketi csv, Excel, JSON formatlarındaki dosyaların yanında Stata, SAS programlarıyla oluşturulmuş dosyaları da okuyarak içeriğini bir veri çerçevesine atamanızı sağlayan fonksiyonlar içeriyor. Benim sıklıkla kullandığım read_clipboard fonksiyonu ise kopyaladığınız bir veriyi (örneğin bir Excel tablosunu) veri çerçevesine dönüştürüyor.
read_csv fonksiyonunda sıklıkla kullanabileceğiniz parametreleri aşağıda bulabilirsiniz:
sep: kolonları ayıran karakter ya da kurallı ifade (varsayılan değer ','),
header: sütun isimlerini içeren satırın numarası (varsayılan değer 0, eğer böyle bir satır yoksa None),
index_col: indeks değerlerini içeren sütunun numarası,
names: sütun isimlerini içeren sıralı nesne.
read_excel fonksiyonunda ek olarak birden fazla sayfa içeren dokümanlarda hangi sayfaları okutacağınızı belirten sheet_name argümanını da kullanabilirsiniz. sheet_name değeri sayfanın adı, indeksi (ya da bunların bir listesi) olabilir. Ayrıca None değerini kullanmanız halinde bütün sayfaları okutabilirsiniz. Örnek olarak linkten indirebileceğiniz bir çevrimiçi alışveriş veri kümesini kullandım.
# Dosya tek bir sayfa içerdiği için sheet_name'e ihtiyaç duymadık.# Benim bilgisayarımda Excel dosyası ile Jupyter Notebook dosyası # aynı klasörde olduğundan adres belirtmedim.df = pd.read_excel('Online Retail.xlsx', decimal =',')print(df.head())
InvoiceNo StockCode Description Quantity \
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6
1 536365 71053 WHITE METAL LANTERN 6
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6
InvoiceDate UnitPrice CustomerID Country
0 2010-12-01 08:26:00 2,55 17850,00 United Kingdom
1 2010-12-01 08:26:00 3,39 17850,00 United Kingdom
2 2010-12-01 08:26:00 2,75 17850,00 United Kingdom
3 2010-12-01 08:26:00 3,39 17850,00 United Kingdom
4 2010-12-01 08:26:00 3,39 17850,00 United Kingdom
FaturaNo UrunNo Tanim Adet \
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6
1 536365 71053 WHITE METAL LANTERN 6
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6
FaturaTarihi BirimFiyat MusteriNo Ulke
0 2010-12-01 08:26:00 2,55 17850,00 United Kingdom
1 2010-12-01 08:26:00 3,39 17850,00 United Kingdom
2 2010-12-01 08:26:00 2,75 17850,00 United Kingdom
3 2010-12-01 08:26:00 3,39 17850,00 United Kingdom
4 2010-12-01 08:26:00 3,39 17850,00 United Kingdom
Temel Metotlar ve Erişim
Veri çerçevelerinde, serilerde gördüğümüz dim, shape, size alanlarını ve erişim için kullandığımız loc ve iloc fonksiyonları kullanabiliriz. Serilerden farklı olarak veri çerçeveleri iki boyutlu, size alanı da bu nedenle sütun ve satır sayısının çarpımına eşit.
Erişim için de yine serilerde gördüğümüz iloc ve loc metotlarını kullanabiliriz. Aynı zamanda Kaan’ın Sıralı Nesneler İşlemler yazısında bahsettiği dilimleme yöntemlerini kullanabiliriz.
print('Veri çerçevesinin 0,0 indeksindeki değer: {}'.format(df.iloc[0,0]))print('Veri çerçevesinin ilk satırındaki değerler:\n{}'.format(df.iloc[0,:]))
Veri çerçevesinin 0,0 indeksindeki değer: 536365
Veri çerçevesinin ilk satırındaki değerler:
FaturaNo 536365
UrunNo 85123A
Tanim WHITE HANGING HEART T-LIGHT HOLDER
Adet 6
FaturaTarihi 2010-12-01 08:26:00
BirimFiyat 2,55
MusteriNo 17850,00
Ulke United Kingdom
Name: 0, dtype: object
Verideki eksik değerler özel ilgi göstermemizi gerektirecek durumlara işaret edebilir. Şimdi sütunlardaki eksik değerlerin sayısına bakalım. isnull veri çerçevesinin boyutunda 0 ve 1 (True/False) değerlerinden oluşan bir veri çerçevesi döndürüyor. Bu veri çerçevesi NaN olan hücreler için True, diğer hücreler için False değerine sahip. Sütunlar üzerinden toplarsak eksik değer içeren hücre sayısını bulabiliriz.
print('Eksik hücre sayısı')print(df.isnull().sum())
Eksik hücre sayısı
FaturaNo 0
UrunNo 0
Tanim 1454
Adet 0
FaturaTarihi 0
BirimFiyat 0
MusteriNo 135080
Ulke 0
dtype: int64
Müşteri numarası olmayan 135080 satır var. Bu satırları veri setinden çıkaralım.
df = df[~df['MusteriNo'].isnull()]print('Şekil: {}'.format(df.shape))print('Eksik hücre sayısı')print(df.isnull().sum())
Şekil: (406829, 8)
Eksik hücre sayısı
FaturaNo 0
UrunNo 0
Tanim 0
Adet 0
FaturaTarihi 0
BirimFiyat 0
MusteriNo 0
Ulke 0
dtype: int64
Alternatif olarak eksik değerleri istediğiniz başka bir değerle değiştirmeniz de mümkün. Bu amaçla fillna fonksiyonunu kullanabilirsiniz. Örnek olarak eksik değerleri 0 değeriyle doldurmak için df.fillna(0, inplace = True) yazmanız yeterli. Buradaki inplace argümanı değişikliğin veri çerçevesi üzerinde yapılmasını sağlar. Bunu kullanmak istemezseniz bir atama yapmanız gerekecek (df = df.fillna(0)). İki seçeneği de kullanmamanız haline orijinal veri çerçevesi değişmeyecektir.
Müşteri numaraları tamsayı olması gerekirken ondalıklı olarak okunduğu için tamsayıya çevirelim.
# Aşağıdaki satırda sütunlara ulaşmanın iki farklı yolunu görüyoruz. # Veri_çerçevesi['sütun_adı'] ve Veri_çerçevesi.Sütun_adıdf['MusteriNo'] = df.MusteriNo.astype('int')print(df.head())
FaturaNo UrunNo Tanim Adet \
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6
1 536365 71053 WHITE METAL LANTERN 6
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6
FaturaTarihi BirimFiyat MusteriNo Ulke
0 2010-12-01 08:26:00 2,55 17850 United Kingdom
1 2010-12-01 08:26:00 3,39 17850 United Kingdom
2 2010-12-01 08:26:00 2,75 17850 United Kingdom
3 2010-12-01 08:26:00 3,39 17850 United Kingdom
4 2010-12-01 08:26:00 3,39 17850 United Kingdom
Veri İşleme
Veri çerçevesinde ürünler için birim fiyat ve satın alınan adet değerleri var. Müşterilerin o ürün için toplam harcamasını içeren bir sütun ekleyelim. Yeni sütunun adı Miktar olsun.
FaturaNo UrunNo Tanim Adet \
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6
1 536365 71053 WHITE METAL LANTERN 6
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6
FaturaTarihi BirimFiyat MusteriNo Ulke Miktar
0 2010-12-01 08:26:00 2,55 17850 United Kingdom 15,30
1 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
2 2010-12-01 08:26:00 2,75 17850 United Kingdom 22,00
3 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
4 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
FaturaNo ve o faturaya ait toplam miktarı içeren yeni bir veri çerçevesi oluşturalım. groupby fonksiyonuyla her alışverişin toplam miktarını bulabiliriz. Groupby fonksiyonunun kullanımını Veri Defteri’nin ilk yazılarından birinde anlatmıştık.
İlk veri çerçevesine ToplamMiktar sütununu eklemek için pandas paketinin sunduğu birleştirme işlemlerinden yararlanabiliriz. Bu amaçla merge fonksiyonunu kullanacağız.
merge işlemi için iki veri çerçevesinin hangi sütunlarının nasıl birleştirileceğini belirtmek gerekiyor. merge fonksiyonu SQL tablolarında kullanılan birleştirme (JOIN) işlemlerini destekliyor. İki veri çerçevesini birleştirmek için FaturaNo anahtarını kullanalım (on = 'FaturaNo'). Farklı isimlere sahip sütunlar üzerinden birleştirme yapmak için right_on = ... ve left_on = ... şeklinde veri çerçevelerindeki sütun isimlerini fonksiyona ekleyebilirsiniz.
how argümanı için kullanabileceğiniz değerler:
“inner”: Sadece iki tabloda da bulunan anahtar değerlerini birleştirir. Bir tabloda olmayan değerler silinir.
“right”: İlk tabloda bulunan değerler korunur ve ikinci tabloda eşleşen değerler tabloya eklenir. İkinci tabloda bulunmayan değerler NaN ile belirtilir.
“left”: İkinci tabloda bulunan değerler korunur ve ilk tabloda eşleşen değerler tabloya eklenir. İlk tabloda bulunmayan değerler NaN ile belirtilir.
“outer”: İki tablodan en az birinde bulunan değerler korunur. Bir tabloda olmayan eksik değerler NaN ile belirtilir.
df_yeni = pd.merge(df,df_fis, how='inner', on ='FaturaNo')print(df_yeni.head())print('Şekil: {}'.format(df_yeni.shape))print(df_yeni[df_yeni['FaturaNo'] ==536365])
FaturaNo UrunNo Tanim Adet \
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6
1 536365 71053 WHITE METAL LANTERN 6
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6
FaturaTarihi BirimFiyat MusteriNo Ulke Miktar \
0 2010-12-01 08:26:00 2,55 17850 United Kingdom 15,30
1 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
2 2010-12-01 08:26:00 2,75 17850 United Kingdom 22,00
3 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
4 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
ToplamMiktar
0 139,12
1 139,12
2 139,12
3 139,12
4 139,12
Şekil: (406829, 10)
FaturaNo UrunNo Tanim Adet \
0 536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6
1 536365 71053 WHITE METAL LANTERN 6
2 536365 84406B CREAM CUPID HEARTS COAT HANGER 8
3 536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6
4 536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6
5 536365 22752 SET 7 BABUSHKA NESTING BOXES 2
6 536365 21730 GLASS STAR FROSTED T-LIGHT HOLDER 6
FaturaTarihi BirimFiyat MusteriNo Ulke Miktar \
0 2010-12-01 08:26:00 2,55 17850 United Kingdom 15,30
1 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
2 2010-12-01 08:26:00 2,75 17850 United Kingdom 22,00
3 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
4 2010-12-01 08:26:00 3,39 17850 United Kingdom 20,34
5 2010-12-01 08:26:00 7,65 17850 United Kingdom 15,30
6 2010-12-01 08:26:00 4,25 17850 United Kingdom 25,50
ToplamMiktar
0 139,12
1 139,12
2 139,12
3 139,12
4 139,12
5 139,12
6 139,12
Tekrar eden verileri tekilleştirmek için drop_duplicates fonksiyonunu kullanabiliriz. Aşağıda müşteri numaralarını tekilleştirerek kaç müşteri olduğunu buluyoruz. Ben sütunları kopyalamayı tercih ettim. Bunu yapmamanız durumunda Python referans modelinden kaynaklı bir uyarı mesajı alacaksınız. Bu konu hakkında Kaan’ın yazısını okumanızı tavsiye ederim.
# Sadece ürün bilgilerini içeren sütunları alıyoruz.# Burada copy işlemini kullanmazsak bir uyarı mesajı alıyoruz.df_musteri = df[['MusteriNo']].copy()df_musteri.drop_duplicates(inplace =True)print(df_musteri.head())print('Şekil: {}'.format(df_musteri.shape))print('Müşteri sayısı: {}'.format(df_musteri.shape[0]))
Veri çerçeveleri scikit-learn, statsmodel gibi bir çok paket tarafından destekleniyor. Tensorflow ve lightgbm gibi popüler paketlerle de veri çerçevelerini kullanabilirsiniz. Yapay öğrenmede sıklıkla kullanılan bu paketlerden scikit-learn paketiyle ilgili bir giriş yazısı yazmıştık. İleride, diğer paketlerle ilgili yazılar yazmayı planlıyoruz.
Bu yazının Jupyter Notebook dosyasına Github dizinimizden ulaşabilirsiniz.