800100715151 Astronomide Veritabanları

Ders - 04 Pandas Paketiyle Veri İşleme

Doç. Dr. Özgür Baştürk
Ankara Üniversitesi, Astronomi ve Uzay Bilimleri Bölümü
obasturk at ankara.edu.tr
http://ozgur.astrotux.org

Pandas Paketiyle Veriyi İşlenmek Üzere Hazırlama

Verileri pandas paketiyle işlemeye başlamadan önce, veriyi hazırlamak ve veri yapıları oluşturacak şekilde birleştirmek gerekir. Ayrıca bu işlemler veri biliminin de temel adımlarından birini oluşturur. Böylece daha sonra pandas kütüphanesi tarafından sağlanan araçlar ile manipüle edilebilirler. Veri hazırlama için farklı prosedürler aşağıda listelenmiştir.

  • Veriyi Yükleme
  • Veriyi Bir Araya Getirme
    • Bağlama (ing. merging)
    • Ucuca Ekleme (ing. concatenating)
    • Birleştirme (ing. combining)
  • Veri Yapısını Yeniden Şekillendirme (ing. pivoting)
  • Veri Çıkarma (ing. removing)

Veri yükleme için çeşitli yöntemlerle (sözlükler, listeler, numpy dizileri kullanarak) pandas serileri (pandas.Series) ve veriçerçeveleri (pandas.DataFrame) oluşturmayı Pandas'a Giriş dersinde görmüştünüz. Ayrıca verinin sütunlar şeklinde yapılandırılmış dosyalardan pandas.read_csv fonksiyonuyla okunarak veriçerçevelerine yüklenmesi de örneklendirilmişti. Ancak, farklı kaynaklarda ve muhtemelen farklı formatlarda saklanan veriyi aldıktan sonra bir veriçerçevesinde (DataFrame'de) birleştirmek gibi hazırlık işlemlerine de ihtiyaç duyulabilir. Örneğin bir ötegezegene ve barınak yıldızına ilişkin pek çok bilgi exoplanet.eu ya da NASA Exoplanet Archive gibi kataloglardan alınabiliyor olsa da özellikle yıldıza ilişkin bilgilerin daha güncel olarak bulunabileceği Gaia veritabanı ya da tayfsal parametrelerinin bulunduğu veritabanlarından alınarak yıldıza ilişkin bazı bilgilerin güncellenmesi ya da diğer bazılarının eklenmesi istenebilir. Bu bölümde verilerin bütünleşik bir veri yapısına dönüştürülmesi için yapılması gerekli işlemler tartışılacaktır.

pandas nesnelerinde tutulan veriler farklı şekillerde bir araya getirilebilirler:

  • Veriyi bağlama (ing. merging): pandas.merge() fonksiyonu, bir veya daha fazla anahtara bağlı olarak bir veritabanı çerçevesindeki (DataFrame'deki) satırları birbirine bağlar. SQL dilindeki $join$ metoduna benzer niteliktedir.

  • Veriyi ucuca ekleme (ing. concatenating): pandas.concat() fonksiyonu veri nesnelerini (Series ve DataFrame) bir eksen boyunca ucuca ekler.

  • Veriyi birleştirme (ing. combining): pandas.DataFrame.combine_first() fonksiyonu, başka bir yapıdan veri alarak veri yapısındaki eksik değerleri doldurmak ya da güncelleme için bağlamanızı sağlayan bir yöntemdir.

Verinin Birbirine Bağlanması: Merging

SQL'e aşinalığı olanlar için $JOIN$ işlemine karşılık gelen bu birleştirme işlemi, bir veriçerçevesindeki satırların bir veya daha fazla anahtar kullanarak bağlanmasını sağlar. pandas fonksiyonlarından merge() bu amaçla kullanılır. Örnek olarak, iki veriçerçevesi ($DataFrame$) tanımlayalım ve bunları birbirine pandas.merge() fonksiyonuyla bağlayalım.

In [1]:
import pandas as pd
yldz_mV = pd.DataFrame(\
            {'id':['Arcturus','Deneb','Betelgeuse','Spica','Rigel','Merak', 'Vega', 'Altair','Bellatrix'],\
             'mV': [-0.05,1.25,0.42,0.97,0.13,2.37,0.03,0.76,1.64]}) 
yldz_par = pd.DataFrame(\
            {'id':['Arcturus','Betelgeuse','Rigel','Deneb','Vega','Fomalhaut','Polaris'],\
             'par': [88.83,6.55,3.78,2.31,130.23,129.01,7.54]}) 
print("Gorsel Parlakliklar:")
print(yldz_mV)
print("Paralakslar:")
print(yldz_par)
print("Baglanmis Vericercevesi")
pd.merge(yldz_mV,yldz_par)
Gorsel Parlakliklar:
           id    mV
0    Arcturus -0.05
1       Deneb  1.25
2  Betelgeuse  0.42
3       Spica  0.97
4       Rigel  0.13
5       Merak  2.37
6        Vega  0.03
7      Altair  0.76
8   Bellatrix  1.64
Paralakslar:
           id     par
0    Arcturus   88.83
1  Betelgeuse    6.55
2       Rigel    3.78
3       Deneb    2.31
4        Vega  130.23
5   Fomalhaut  129.01
6     Polaris    7.54
Baglanmis Vericercevesi
Out[1]:
id mV par
0 Arcturus -0.05 88.83
1 Deneb 1.25 2.31
2 Betelgeuse 0.42 6.55
3 Rigel 0.13 3.78
4 Vega 0.03 130.23

Sonuçtan da görülebileceği gibi, döndürülen veriçerçevesi, iki veriçerçevesinde de bulunan her iki sütunun yalnızca ortak olan yıldız adlarındaki tüm satırlarını içermektedir. Bu durumda merge fonksiyonu birleştirmenin yapılacağı herhangi bir sütunun adı belirtilmeden kullanılmıştır. Çoğu zaman entegrasyonun hangi sütun üzerinden yapılacağının da belirlenmesi gerekir. Bunun nasıl yapılabileceğine ilişkin seçenekleri görmeden önce iki veriçerçevesinin birbirine ne şekillerde bağlanabileceğine bakalım.

Veri Çerçevelerini Bağlama Türleri

Bire bir Bağlama

Yukarıda bir örneğini gördüğümüz bu bağlama türü (ing. one-to-one join) en basiti olup bağlanmak istenen iki sütunun tek bir sütuna dönüştürülmesi sırasında doğrudan bire-bir eşleşebilen ve birbirini tekrar etmeyen (ing. duplicate) satırların her iki veriçerçevesinde de bulunan sütunlardaki bilgilerinin de eklenmesiyle yeni bir veriçerçevesinin oluşmasını sağlar.

Yukarıdaki örnek bu şekilde bir birleştirmedir. Her iki veriçerçevesinde de $id$ sütununda yer alan yıldız adları tektir ve birbirlerini karşılar. Daha sonra dıştan bağlama (outer join) işleminde göreceğiniz gibi her iki veriçerçevesindeki yıldızların da bulunduğu bir veriçerçevesi elde edilebilir. Ancak pandas.merge() fonksiyonunun doğal davranış şekli içten bağlama (inner join), bir başka deyişle "kesişim" almadır. Dolayısıyla yukarıdaki örnekte her iki veriçerçevesinde de bulunan yıldızlar yeni veriçerçevesine alınmıştır.

Bir başka örnek için bu kez her iki veriçerçevesinde de aynı satırların bulunduğu ve yine satırların birbirini tekrar etmediği bir durumu inceleyelim.

In [2]:
gezegen_yorunge = pd.DataFrame(\
            {'id':['Merkur','Venus','Dunya','Mars','Jupiter','Saturn', 'Uranus', 'Neptun'],\
             'e': [0.205, 0.007, 0.017, 0.094, 0.049, 0.057, 0.046, 0.011],\
             'i [derece]': [7.0, 3.4, 0.0, 1.9, 1.3, 2.5, 0.8, 1.8],\
             'P [gun]' : [88.0, 224.7, 365.2, 687.0, 4331, 10747, 30589, 59800],\
             'psi [derece]': [0.034, 177.4, 23.4, 25.2, 3.1, 26.7, 97.8, 28.3]})
gezegen_fiziksel = pd.DataFrame(\
            {'id':['Merkur','Venus','Dunya','Mars','Jupiter','Saturn', 'Uranus', 'Neptun'],\
             'Mp [x10^24 kg]': [0.330, 4.87, 5.97, 0.642, 1898, 568, 86.8, 102],\
             'Rp [km]': [2439.5, 6052., 6378., 3396., 71492., 60268., 25559., 24764.],\
             'rho [g/cm^3]': [5.427, 5.243, 5.514, 3.933, 1.326, 0.687, 1.271, 1.638],\
             'Prot [saat]': [1407.6, -5832.5, 23.9, 24.6, 9.9, 10.7, -17.2, 16.1]})
print("Baglanmis Vericercevesi")
pd.merge(gezegen_yorunge,gezegen_fiziksel, on='id')
Baglanmis Vericercevesi
Out[2]:
id e i [derece] P [gun] psi [derece] Mp [x10^24 kg] Rp [km] rho [g/cm^3] Prot [saat]
0 Merkur 0.205 7.0 88.0 0.034 0.330 2439.5 5.427 1407.6
1 Venus 0.007 3.4 224.7 177.400 4.870 6052.0 5.243 -5832.5
2 Dunya 0.017 0.0 365.2 23.400 5.970 6378.0 5.514 23.9
3 Mars 0.094 1.9 687.0 25.200 0.642 3396.0 3.933 24.6
4 Jupiter 0.049 1.3 4331.0 3.100 1898.000 71492.0 1.326 9.9
5 Saturn 0.057 2.5 10747.0 26.700 568.000 60268.0 0.687 10.7
6 Uranus 0.046 0.8 30589.0 97.800 86.800 25559.0 1.271 -17.2
7 Neptun 0.011 1.8 59800.0 28.300 102.000 24764.0 1.638 16.1

Çoktan bire Bağlama

Bağlanan sütunlardan birinin tekrarlanan (ing. duplicate) satırlar içerdiği bağlama türüdür (ing. many-to-one joins). Bu bağlama türüne bir örnek vermek üzere yeni bir veriçerçevesi tanımlayıp yukarıdaki çerçevelerle bağlayalım.

Aşağıdaki örnekte gezegen_fiziksel veriçerçevesine gezegenlerin türünü göstermek üzere yeni eklenen $tur$ sütununda kayaç, gaz ve buz gezegenler birden fazla kez yer almaktadır. Buna mukabil gezegen_tur veriçerçevesinde bu sütun birer kez tekrarlanan türleri içermektedir.

In [3]:
gezegen_fiziksel['tur'] = ['kayac', 'kayac', 'kayac', 'kayac', 'gaz', 'gaz', 'buz', 'buz']
gezegen_tur = pd.DataFrame(\
            {'tur' : ['kayac','gaz','buz'],\
             'yer' : ['ic gezegen','dis gezegen','dis gezegen']})
print("Baglanmis Vericercevesi")
pd.merge(gezegen_fiziksel,gezegen_tur, on="tur")
Baglanmis Vericercevesi
Out[3]:
id Mp [x10^24 kg] Rp [km] rho [g/cm^3] Prot [saat] tur yer
0 Merkur 0.330 2439.5 5.427 1407.6 kayac ic gezegen
1 Venus 4.870 6052.0 5.243 -5832.5 kayac ic gezegen
2 Dunya 5.970 6378.0 5.514 23.9 kayac ic gezegen
3 Mars 0.642 3396.0 3.933 24.6 kayac ic gezegen
4 Jupiter 1898.000 71492.0 1.326 9.9 gaz dis gezegen
5 Saturn 568.000 60268.0 0.687 10.7 gaz dis gezegen
6 Uranus 86.800 25559.0 1.271 -17.2 buz dis gezegen
7 Neptun 102.000 24764.0 1.638 16.1 buz dis gezegen

Çoktan çoka Bağlama

Bağlanan sütunlardan her ikisinde de tekrarlanan (ing. duplicate) satırların içerildiği bağlama türüdür (ing. many-to-many joins). Aşağıdaki örnekte daha önce oluşturduğumuz yldz_mV ve yldz_par_sptype veriçerçevelerine her iki tarafta da tekrarlanan satırlar içeren ve yıldızların tayf türünü gösteren $sptype$ sütunu eklenmiştir. Bu durumda yapılan birleştirme bir many-to-many join işlemidir.

In [4]:
yldz_mV_sptype = pd.DataFrame(\
            {'id':['Arcturus','Deneb','Betelgeuse','Spica','Rigel','Merak', 'Vega', 'Altair','Bellatrix'],\
             'sptype':['K1.5 III', 'A2 Ia', 'M1 Iab', 'B1 V', 'B8 Ia','A0 V', 'A0 V', 'A7 V', 'B1 V'],\
             'mV': [-0.05,1.25,0.42,0.97,0.13,2.37,0.03,0.76,1.64]})
yldz_par_sptype = pd.DataFrame(\
            {'id':['Arcturus','Betelgeuse','Rigel','Deneb','Vega','Fomalhaut','Polaris'],\
             'sptype':['K1.5 III', 'M1 Iab', 'B8 Ia', 'A2 Ia', 'A0 V', 'A0 V', 'F8 Ib'],\
             'par': [88.83,6.55,3.78,2.31,130.23,129.01,7.54]})
print("Gorsel Parlakliklar ve Tayf Turleri:")
print(yldz_mV_sptype)
print("Paralakslar ve Tayf Turleri:")
print(yldz_par_sptype)
print("ID Uzerinden Baglanmis Vericercevesi")
print(pd.merge(yldz_mV_sptype,yldz_par_sptype))
print("Tayf turu Uzerinden Baglanmis Vericercevesi")
print(pd.merge(yldz_mV_sptype,yldz_par_sptype,on="sptype"))
Gorsel Parlakliklar ve Tayf Turleri:
           id    sptype    mV
0    Arcturus  K1.5 III -0.05
1       Deneb     A2 Ia  1.25
2  Betelgeuse    M1 Iab  0.42
3       Spica      B1 V  0.97
4       Rigel     B8 Ia  0.13
5       Merak      A0 V  2.37
6        Vega      A0 V  0.03
7      Altair      A7 V  0.76
8   Bellatrix      B1 V  1.64
Paralakslar ve Tayf Turleri:
           id    sptype     par
0    Arcturus  K1.5 III   88.83
1  Betelgeuse    M1 Iab    6.55
2       Rigel     B8 Ia    3.78
3       Deneb     A2 Ia    2.31
4        Vega      A0 V  130.23
5   Fomalhaut      A0 V  129.01
6     Polaris     F8 Ib    7.54
ID Uzerinden Baglanmis Vericercevesi
           id    sptype    mV     par
0    Arcturus  K1.5 III -0.05   88.83
1       Deneb     A2 Ia  1.25    2.31
2  Betelgeuse    M1 Iab  0.42    6.55
3       Rigel     B8 Ia  0.13    3.78
4        Vega      A0 V  0.03  130.23
Tayf turu Uzerinden Baglanmis Vericercevesi
         id_x    sptype    mV        id_y     par
0    Arcturus  K1.5 III -0.05    Arcturus   88.83
1       Deneb     A2 Ia  1.25       Deneb    2.31
2  Betelgeuse    M1 Iab  0.42  Betelgeuse    6.55
3       Rigel     B8 Ia  0.13       Rigel    3.78
4       Merak      A0 V  2.37        Vega  130.23
5       Merak      A0 V  2.37   Fomalhaut  129.01
6        Vega      A0 V  0.03        Vega  130.23
7        Vega      A0 V  0.03   Fomalhaut  129.01

Farklı Sütunlar Üzerinden Bağlama

Görüldüğü gibi her iki veri çerçevesinde ortak olan yıldızlar (Arcturus, Deneb, Betelgeuse, Rigel ve Vega) olduğu için iki veriçerçevesi birbirine $ID$ sütunu üzerinden bağlandı. Ancak biz aynı olan tayf türlerinden bağlamak istiyor da olabliirdik. Bunu on parametresini istenen sütun adına ($sptype$) ayarlayarak yapabiliriz. Her ne kadar bu parametre herhangi bir sütuna verilmediğinde her iki veri setinde aynı olan ilk sütunlar bağlama için seçilse de hangi sütun üzerinden bağlama yapılmak istendiğini her zaman belirtmekte, bir başka deyişle her zaman on parametresini hangi sütun üzerinden bağlama yapılmak isteniyorsa ona ayarlamakta fayda vardır.

In [5]:
print("Tayf Turu Uzerinden Baglanmis Vericercevesi")
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='sptype')
Tayf Turu Uzerinden Baglanmis Vericercevesi
Out[5]:
id_x sptype mV id_y par
0 Arcturus K1.5 III -0.05 Arcturus 88.83
1 Deneb A2 Ia 1.25 Deneb 2.31
2 Betelgeuse M1 Iab 0.42 Betelgeuse 6.55
3 Rigel B8 Ia 0.13 Rigel 3.78
4 Merak A0 V 2.37 Vega 130.23
5 Merak A0 V 2.37 Fomalhaut 129.01
6 Vega A0 V 0.03 Vega 130.23
7 Vega A0 V 0.03 Fomalhaut 129.01

Görüldüğü gibi her iki verçevesi tayf türü üzerinden bağlandığı zaman yıldız adları farklı olabilmekte, bu durumda da merge işlemiyle yeni oluşturulan veriçerçevesinde iki ayrı id sütunu oluşmaktadır (id_x ve id_y). İstendiği takdirde sondaki "_x" ve "_y" yerine istenen başka bir metin de kullanılabilir, bunun için suffixes parametresi istenen metinlere ayarlanır. Özet olarak $merge$ işleminin sonucu hangi kriterle bağlama yapıldığına bağlı olarak değişebilmektedir.

Bununla birlikte, genellikle, bunun tersi bir problemle karşılaşılır: iki farklı veri setinde aynı bilgiyi içeren iki farklı sütun adının olması! Bu durumu gidermek için birinci ve ikinci veriçerçevesi için anahtar sütunu belirten left_on ve right_on seçeneklerinin kullanılması gerekir. Öncelikle yldz_mV_sptype veriçerçevesindeki sütun isimlerini değiştirelim.

In [6]:
yldz_mV_sptype.columns = ['yildiz','tayfturu','V']
print(yldz_mV_sptype)
       yildiz  tayfturu     V
0    Arcturus  K1.5 III -0.05
1       Deneb     A2 Ia  1.25
2  Betelgeuse    M1 Iab  0.42
3       Spica      B1 V  0.97
4       Rigel     B8 Ia  0.13
5       Merak      A0 V  2.37
6        Vega      A0 V  0.03
7      Altair      A7 V  0.76
8   Bellatrix      B1 V  1.64
In [7]:
pd.merge(yldz_mV_sptype, yldz_par_sptype, left_on='yildiz', right_on='id')
Out[7]:
yildiz tayfturu V id sptype par
0 Arcturus K1.5 III -0.05 Arcturus K1.5 III 88.83
1 Deneb A2 Ia 1.25 Deneb A2 Ia 2.31
2 Betelgeuse M1 Iab 0.42 Betelgeuse M1 Iab 6.55
3 Rigel B8 Ia 0.13 Rigel B8 Ia 3.78
4 Vega A0 V 0.03 Vega A0 V 130.23
In [8]:
pd.merge(yldz_mV_sptype, yldz_par_sptype, left_on='tayfturu', right_on='sptype')
Out[8]:
yildiz tayfturu V id sptype par
0 Arcturus K1.5 III -0.05 Arcturus K1.5 III 88.83
1 Deneb A2 Ia 1.25 Deneb A2 Ia 2.31
2 Betelgeuse M1 Iab 0.42 Betelgeuse M1 Iab 6.55
3 Rigel B8 Ia 0.13 Rigel B8 Ia 3.78
4 Merak A0 V 2.37 Vega A0 V 130.23
5 Merak A0 V 2.37 Fomalhaut A0 V 129.01
6 Vega A0 V 0.03 Vega A0 V 130.23
7 Vega A0 V 0.03 Fomalhaut A0 V 129.01

Varsayılan olarak, merge fonksiyonu SQL diliyle bir "inner join" işlemi gerçekleştirir; bir başka deyiişle, sonuçtaki kesişen satırlar alınır. Her iki veriçerçevesinde de bulunmayan satırlar ise kaybedilir. Diğer olası seçenekler "left join", "right join" ve "outer join" 'dir ve how parametresinin uygun şekilde ayarlanmasıyla kullanılabilir. Bu seçeneklerin nasıl çalıştğını örnekleyerek anlamak için öncelikle yldz_mV_sptype veriçerçevesinin başlıklarını eski haline dönüştürelim.

In [9]:
yldz_mV_sptype.columns = ['id','sptype','mV']
yldz_mV_sptype
Out[9]:
id sptype mV
0 Arcturus K1.5 III -0.05
1 Deneb A2 Ia 1.25
2 Betelgeuse M1 Iab 0.42
3 Spica B1 V 0.97
4 Rigel B8 Ia 0.13
5 Merak A0 V 2.37
6 Vega A0 V 0.03
7 Altair A7 V 0.76
8 Bellatrix B1 V 1.64
In [10]:
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id')
Out[10]:
id sptype_x mV sptype_y par
0 Arcturus K1.5 III -0.05 K1.5 III 88.83
1 Deneb A2 Ia 1.25 A2 Ia 2.31
2 Betelgeuse M1 Iab 0.42 M1 Iab 6.55
3 Rigel B8 Ia 0.13 B8 Ia 3.78
4 Vega A0 V 0.03 A0 V 130.23
In [11]:
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how="outer")
Out[11]:
id sptype_x mV sptype_y par
0 Arcturus K1.5 III -0.05 K1.5 III 88.83
1 Deneb A2 Ia 1.25 A2 Ia 2.31
2 Betelgeuse M1 Iab 0.42 M1 Iab 6.55
3 Spica B1 V 0.97 NaN NaN
4 Rigel B8 Ia 0.13 B8 Ia 3.78
5 Merak A0 V 2.37 NaN NaN
6 Vega A0 V 0.03 A0 V 130.23
7 Altair A7 V 0.76 NaN NaN
8 Bellatrix B1 V 1.64 NaN NaN
9 Fomalhaut NaN NaN A0 V 129.01
10 Polaris NaN NaN F8 Ib 7.54

Görüldüğü gibi dıştan bağlamada ($outer$) karşılıklı gelemeyen satırlar için sol ve sağ tarafta $NaN$ değerleri oluşmuştur. outer join, özünde bir "bileşke" işlemidir ve her iki veriçerçevesinde ortak olmayan satırlar da alınır. Bu sırasıyla $left$ ve $right$ seçeneklerinin nasıl çalıştığı konusunda fikir verir. Özünde bu her iki seçenek de birer outer join seçeneğidir.

In [12]:
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how="left")
Out[12]:
id sptype_x mV sptype_y par
0 Arcturus K1.5 III -0.05 K1.5 III 88.83
1 Deneb A2 Ia 1.25 A2 Ia 2.31
2 Betelgeuse M1 Iab 0.42 M1 Iab 6.55
3 Spica B1 V 0.97 NaN NaN
4 Rigel B8 Ia 0.13 B8 Ia 3.78
5 Merak A0 V 2.37 NaN NaN
6 Vega A0 V 0.03 A0 V 130.23
7 Altair A7 V 0.76 NaN NaN
8 Bellatrix B1 V 1.64 NaN NaN

Görüldüğü gibi sol taraftaki (fonksiyona ilk olarak verilen) yldz_mV_sptype veriçerçevesinde bulunan tüm yıldızların bilinen parametreleri entegre çerçevede bulunurken; sağdaki (fonksiyona ikinci olarak geçirilen) yldz_par_sptype veriçerçevesindeki sütunlarda bilgisi olan sadece Vega yıldızının paralaks ve tayf türü bilgileri geçirilmiştir. Sol dış birleştirme (left outer join) kullanıldığında oluşan yeni veriçerçevesinde sol taraftaki (fonksiyona ilk sırada sağlanan) tüm satırlar bulunurken, sağ taraftaki veriçerçevesinde sadece sol taraftaki veriçerçevesinde de bulunan satırlar alınır; eşleşmeyen satırlar ise alınmaz. Ayrıca sütunların sıralamasında da öncelik sol taraftaki veriçerçevesinin sütunlarındadır. Bu durum $right$ seçeneğinde tam ters şekilde çalışır.

In [13]:
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how="right")
Out[13]:
id sptype_x mV sptype_y par
0 Arcturus K1.5 III -0.05 K1.5 III 88.83
1 Betelgeuse M1 Iab 0.42 M1 Iab 6.55
2 Rigel B8 Ia 0.13 B8 Ia 3.78
3 Deneb A2 Ia 1.25 A2 Ia 2.31
4 Vega A0 V 0.03 A0 V 130.23
5 Fomalhaut NaN NaN A0 V 129.01
6 Polaris NaN NaN F8 Ib 7.54

Birden fazla anahtar (sütun adı) üzerinden entegrasyon amaçlandığında istenen sütunlar on parametresine bir liste dahilinde sağlanır.

In [14]:
pd.merge(yldz_mV_sptype,yldz_par_sptype, on=['id','sptype'], how="outer")
Out[14]:
id sptype mV par
0 Arcturus K1.5 III -0.05 88.83
1 Deneb A2 Ia 1.25 2.31
2 Betelgeuse M1 Iab 0.42 6.55
3 Spica B1 V 0.97 NaN
4 Rigel B8 Ia 0.13 3.78
5 Merak A0 V 2.37 NaN
6 Vega A0 V 0.03 130.23
7 Altair A7 V 0.76 NaN
8 Bellatrix B1 V 1.64 NaN
9 Fomalhaut A0 V NaN 129.01
10 Polaris F8 Ib NaN 7.54
In [15]:
yldz_d_sptype = pd.DataFrame(\
            {'id':['Arcturus','Betelgeuse','Rigel','Deneb','Vega','Fomalhaut','Polaris'],\
             'sptype':['K1.5 III', 'M1 Iab', 'B8 Ia', 'A1 I', 'A0 V', 'A0 V', 'F8 Ib'],\
             'd': 1 / (yldz_par_sptype['par']*1e-3)})
yldz_d_sptype
Out[15]:
id sptype d
0 Arcturus K1.5 III 11.257458
1 Betelgeuse M1 Iab 152.671756
2 Rigel B8 Ia 264.550265
3 Deneb A1 I 432.900433
4 Vega A0 V 7.678722
5 Fomalhaut A0 V 7.751337
6 Polaris F8 Ib 132.625995
In [16]:
pd.merge(yldz_mV_sptype,yldz_d_sptype,on=['id','sptype'], how="outer")
Out[16]:
id sptype mV d
0 Arcturus K1.5 III -0.05 11.257458
1 Deneb A2 Ia 1.25 NaN
2 Betelgeuse M1 Iab 0.42 152.671756
3 Spica B1 V 0.97 NaN
4 Rigel B8 Ia 0.13 264.550265
5 Merak A0 V 2.37 NaN
6 Vega A0 V 0.03 7.678722
7 Altair A7 V 0.76 NaN
8 Bellatrix B1 V 1.64 NaN
9 Deneb A1 I NaN 432.900433
10 Fomalhaut A0 V NaN 7.751337
11 Polaris F8 Ib NaN 132.625995
In [17]:
pd.merge(yldz_mV_sptype,yldz_d_sptype,on=['id','sptype'], how="inner")
Out[17]:
id sptype mV d
0 Arcturus K1.5 III -0.05 11.257458
1 Betelgeuse M1 Iab 0.42 152.671756
2 Rigel B8 Ia 0.13 264.550265
3 Vega A0 V 0.03 7.678722

İndeks Üzerinden Bağlama İşlemleri

Bazı durumlarda, bir veriçerçevesinin sütunları anahtar olarak düşünülmek yerine, indeksleri veriçerçevelerini birbirine bağlamak için kullanılmak istenebilir. Hangi indekslerin dikkate alınacağını belirtmek için, left_index veya right_index seçeneklerini etkinleştirmek (True olarak ayarlamak) gerekir. Bu durumda aynı indekse sahip satırlar birleştirilirken, left_index soldaki (fonksiyona ilk geçirilen) veriçerçevesinin aynı sütunlarını $x$, sağdaki veriçerçevesinin sütunlarını $y$ ile isimlendirir. Örnekte sağdaki veriçerçevesinin (yldz_par_sptype) 0-6 indeksleri arasında kayıtlar olduğundan bağlanmış veriçerçevesinde de sadece bu indeksler vardır. Soldaki veriçerçevesinin (yldz_mV_sptype) 7 ve 8. indekslerindeki $Altair$ ve $Bellatrix$ bağlanmış veriçerçevesinde yoktur.

In [18]:
pd.merge(yldz_mV_sptype,yldz_par_sptype, how="inner", right_index=True, left_index=True)
Out[18]:
id_x sptype_x mV id_y sptype_y par
0 Arcturus K1.5 III -0.05 Arcturus K1.5 III 88.83
1 Deneb A2 Ia 1.25 Betelgeuse M1 Iab 6.55
2 Betelgeuse M1 Iab 0.42 Rigel B8 Ia 3.78
3 Spica B1 V 0.97 Deneb A2 Ia 2.31
4 Rigel B8 Ia 0.13 Vega A0 V 130.23
5 Merak A0 V 2.37 Fomalhaut A0 V 129.01
6 Vega A0 V 0.03 Polaris F8 Ib 7.54

join Metodu

DataFrame nesneleri, indeksler üzerinden bağlamak istediğinizde merge() fonksiyonu altyapısını kullanan ve onun işlevini genişleten join() metoduna sahiptir. Bu metodun kullanımını örneklemeden önce yine birinci veriçerçevesinin sütun başlıklarını farklı olacak şekilde ayarlayalım.

In [19]:
yldz_mV_sptype.columns = ['yildiz','tayfturu','V']
In [20]:
yldz_mV_sptype
Out[20]:
yildiz tayfturu V
0 Arcturus K1.5 III -0.05
1 Deneb A2 Ia 1.25
2 Betelgeuse M1 Iab 0.42
3 Spica B1 V 0.97
4 Rigel B8 Ia 0.13
5 Merak A0 V 2.37
6 Vega A0 V 0.03
7 Altair A7 V 0.76
8 Bellatrix B1 V 1.64
In [21]:
yldz_par_sptype
Out[21]:
id sptype par
0 Arcturus K1.5 III 88.83
1 Betelgeuse M1 Iab 6.55
2 Rigel B8 Ia 3.78
3 Deneb A2 Ia 2.31
4 Vega A0 V 130.23
5 Fomalhaut A0 V 129.01
6 Polaris F8 Ib 7.54

join arkaplanda merge fonksiyonunu kullanır ancak ondan farklı olarak veriçerçevesi ve seriler üzerinde tanımlanmış bir metot yapısındadır. Yukarıdaki gibi bir isim değişikliği yapılmaz ve her iki veriçerçevesinde de aynı sütunlar bulunursa $join$ işleminin yapıldığı tarafa göre sütunların yeniden isimlendirilmesi için lsuffix ve rsuffix parametreleri istenen metinlere ayarlanır.

In [22]:
yldz_mV_sptype.join(yldz_par_sptype, how="inner")
Out[22]:
yildiz tayfturu V id sptype par
0 Arcturus K1.5 III -0.05 Arcturus K1.5 III 88.83
1 Deneb A2 Ia 1.25 Betelgeuse M1 Iab 6.55
2 Betelgeuse M1 Iab 0.42 Rigel B8 Ia 3.78
3 Spica B1 V 0.97 Deneb A2 Ia 2.31
4 Rigel B8 Ia 0.13 Vega A0 V 130.23
5 Merak A0 V 2.37 Fomalhaut A0 V 129.01
6 Vega A0 V 0.03 Polaris F8 Ib 7.54

Görüldüğü gibi $join$ ile yapılan bağlama indeksler üzerindendir. Bağlama yapılırken kesişim istendiğini belirtmek üzere how parametresi $inner$ seçeneğine ayarlanmıştır. Bu nedenle her iki veriçerçevesinde de bulunan $0,1,..,6$ numaralı indekslerdeki satırlar her iki veriçerçevesinden de alınarak bir bağlama işlemi yapılmıştır. Eğer bir önceki örnekte olduğu gibi tüm sütun isimleri farklı değil de bazıları aynı olsaydı, bağlama işlemi yaparken sol ve sağ taraftan gerekecek aynı isimli sütunları yeniden isimlendirmek için hangi metnin sırasıyla bu sütun isimlerinin sonuna ekleneceğini belirtmek için lsuffix ve rsuffix parametrelerini istenen metinlere ayarlamak gerekecekti.

$join$ ile bağlamaların indeksler üzerinden yapılma zorunluluğu nedeniyle, bağlanacak (ikinci, "other") veriçerçevesinin indeks sütunu değiştirilip, üzerinden bağlama yapılacak (birinci, "caller") veriçerçevesinin bu sütuna karşılık gelen sütun adı on parametresine verilerek bağlamada istenen sütunların kullanılması sağlanabilir. Bu değişiklik pandas veriçerçeveleri üzerinde tanımlı set_index metoduyla yapılabilir. Metod değişikliği kalıcı hale getirmeden kullanılabileceği için indesk değişimi de kalıcı olmamış olur.

In [23]:
yldz_mV_sptype.join(yldz_par_sptype.set_index("id"), how="inner", on="yildiz")
Out[23]:
yildiz tayfturu V sptype par
0 Arcturus K1.5 III -0.05 K1.5 III 88.83
1 Deneb A2 Ia 1.25 A2 Ia 2.31
2 Betelgeuse M1 Iab 0.42 M1 Iab 6.55
4 Rigel B8 Ia 0.13 B8 Ia 3.78
6 Vega A0 V 0.03 A0 V 130.23

Ayrıca bu bağlamada dıştan bağlama ("outer join") (bileşke) işlemi uuygulandığından sol taraftaki veriçerçevesinin bütun satırları alınmış, sağ tarafta sol tarafla eşleşmeyen tüm satır değerleri $NaN$ olarak belirlenmiştir.

In [24]:
yldz_mV_sptype.columns = ['id','sptype','mV']
yldz_mV_sptype.join(yldz_par_sptype, rsuffix="_sag", lsuffix="_sol", how="outer")
Out[24]:
id_sol sptype_sol mV id_sag sptype_sag par
0 Arcturus K1.5 III -0.05 Arcturus K1.5 III 88.83
1 Deneb A2 Ia 1.25 Betelgeuse M1 Iab 6.55
2 Betelgeuse M1 Iab 0.42 Rigel B8 Ia 3.78
3 Spica B1 V 0.97 Deneb A2 Ia 2.31
4 Rigel B8 Ia 0.13 Vega A0 V 130.23
5 Merak A0 V 2.37 Fomalhaut A0 V 129.01
6 Vega A0 V 0.03 Polaris F8 Ib 7.54
7 Altair A7 V 0.76 NaN NaN NaN
8 Bellatrix B1 V 1.64 NaN NaN NaN

Veriçerçevesi ve Serileri Ucuca Ekleme: Concatenating

Seri halinde veri saklayan diğer tüm Python nesnelerinde olduğu gibi pandas veriçerçeveleri ve serileri de ucuca eklenebilir. Bunun için pandas.concatenate fonksiyonu kullanılır.

Önce serilerle örnekleyelim.

In [25]:
import numpy as  np
import pandas as pd
seri1 = pd.Series(np.random.rand(4), index=[1,2,3,4])
seri2 = pd.Series(np.random.rand(4), index=[5,6,7,8])
print(pd.concat([seri1,seri2]))
1    0.728995
2    0.775793
3    0.959097
4    0.138579
5    0.577445
6    0.777666
7    0.035385
8    0.897081
dtype: float64

concatenate fonksiyonu varsayılan olarak $axis = 0$ parametresiyle çalışır ve satır üzerinden ucuca ekleme yapar. axis parametresi $axis = 1$ ya da $axis = 'col'$ değerlerine ayarlanırsa ucuca ekleme sütun üzerinden yapılır. Sütunda ucuca ekleme sırasında oluşacak her yeni sütuna $0$'dan başlanarak $1, 2, ... $ numaralı indeksler atanır. Satırda aynı olmayan indekslere NaN değeri (ing. "not a number") verilir.

In [26]:
print(pd.concat([seri1,seri2],axis = 1))
          0         1
1  0.728995       NaN
2  0.775793       NaN
3  0.959097       NaN
4  0.138579       NaN
5       NaN  0.577445
6       NaN  0.777666
7       NaN  0.035385
8       NaN  0.897081

Varsayılan bu davranış şekli join parametresinin $inner$ değerine ayarlanmasıyla değiştirilebilir. Gerek SQL, gerekse de pandas 'ta inner join özünde bir kesişim işlemidir. Burada dikkat edilmesi gereken bu şekilde (içten) ucuca eklenecek iki serinin indekslerinin aynı olması gereğidir, aksi takdirde bir kesişim işlemi gerçekleşmez. Bu nedenle ilk örnek olarak iki aynı seriyi burada ucuca ekledik.

In [27]:
print(pd.concat([seri1,seri1],axis = 1, join='inner'))
          0         1
1  0.728995  0.728995
2  0.775793  0.775793
3  0.959097  0.959097
4  0.138579  0.138579

Ucuca eklenen seri ya da veriçerçevelerinin ucuca eklendikleri yerleri belirlemek üzere keys parametresi ile hiyerarşik bir indeksleme yapılabilir.

In [28]:
print(pd.concat([seri1,seri2], keys=[1,2]))
1  1    0.728995
   2    0.775793
   3    0.959097
   4    0.138579
2  5    0.577445
   6    0.777666
   7    0.035385
   8    0.897081
dtype: float64

Dıştan birleştirmelerde ($axis = 1$, "outer join") bu anahtarlar sütun ismi olur. Ucuca ekleme işlemlerinde varsayılan birleştirme (join) metodu "outer join" olduğu için bunu ayrıca how parametresi üzerinden belirlemek gerekmez.

In [29]:
pd.concat([seri1,seri2], axis = 1, keys=['A','B'])
Out[29]:
A B
1 0.728995 NaN
2 0.775793 NaN
3 0.959097 NaN
4 0.138579 NaN
5 NaN 0.577445
6 NaN 0.777666
7 NaN 0.035385
8 NaN 0.897081

Veriçerçevelerinin Ucuca Eklenmesi

concatenate fonksiyonu seri nesnesi ($pd.Series$) üzerinde çalıştığı şekliyle veriçerçevesi ($pd.DataFrame$) nesneleri üzerinde de çalışır. Ancak veriçerçevesi en az iki boyutlu bir nesne olduğu için yapılabilecek işlemlerin karmaşıklığı da daha fazla olabilmektedir.

Yine klasik bir ucuca ekleme işleminin öncelikle varsayılan modda (satırda) nasıl yapıldığına bir örnekle bakalım.

In [30]:
import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[1,2,3], columns=['A','B','C'])
df2 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[4,5,6], columns=['A','B','C'])
pd.concat([df1, df2], keys=[1,2])
Out[30]:
A B C
1 1 0.490788 0.464997 0.748811
2 0.836915 0.479361 0.641746
3 0.979305 0.856202 0.163091
2 4 0.952128 0.659635 0.692526
5 0.947335 0.467892 0.992781
6 0.622717 0.504856 0.808872

Görüldüğü gibi ucuca eklenen iki veri setinden birincisi ($df1$) ilk üç satırda yer alırken, keys parametresi ile bu verisetine ikinci bir indeks olarak $1$ değeri atanmıştır. İkinci veri seti birincinin ucuna satırda eklenince, indeksleri $4,5,6$ olduğundan bir sorun çıkmamış, son üç satırda indeksleriyle birlikte yer almıştır. keys parametresi ile bu verisetine de ikinci bir indeks olarak $2$ değeri atanmıştır.

Aynı veriçerçevelerini axis parametresini $1$ değerine ayarlayarak bu kez sütunda ucuca getirmeyi deneyelim.

In [31]:
 pd.concat([df1, df2], axis=1, keys=[1,2])
Out[31]:
1 2
A B C A B C
1 0.490788 0.464997 0.748811 NaN NaN NaN
2 0.836915 0.479361 0.641746 NaN NaN NaN
3 0.979305 0.856202 0.163091 NaN NaN NaN
4 NaN NaN NaN 0.952128 0.659635 0.692526
5 NaN NaN NaN 0.947335 0.467892 0.992781
6 NaN NaN NaN 0.622717 0.504856 0.808872

Görüldüğü gibi bu kez veriçerçeveleri sütunda ucuca getirilmiş, birinci veriçerçevesi $1,2,3$ indesklerine sahipken, ikinci veriçerçevesinin sahip olduğu $4,5,6$ indekslerine sahip olmadığı için bu indesklere NaN değeri gelmiş, tersi durum sütunda birincinin ucuna eklenen ikinci veriçerçevesi için de gerçekleşmiştir. Eğer bu iki veriçerçevesi tanımlanırken indeksleri de aynı tanımlanmış olsaydı nasıl bir durumla karşılaşacağımıza bakalım.

In [32]:
import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[1,2,3], columns=['A','B','C'])
df2 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[1,2,3], columns=['A','B','C'])
pd.concat([df1, df2], keys=['X','Y'])
Out[32]:
A B C
X 1 0.581774 0.330418 0.713601
2 0.138608 0.265059 0.155960
3 0.881244 0.862327 0.442467
Y 1 0.980460 0.594537 0.724819
2 0.211729 0.045210 0.983485
3 0.871971 0.922965 0.548481

Satırda ucuca eklemede ortak sütun isimleri ve ikinci veriçerçevesindeki indeksler aynı şekilde korunarak birincinin ucuna eklenmiş ve son üç satıra gelmiştir. Bu durum aynı indekslerden ikişer tane olmasına yol açmaktadır. Bu durumdan keys parametresi sırasıyla $X$ ve $Y$ değerlerine atanmak suretiyle yapılan çoklu indesklemeyle kaçınabileceği gibi bu durum hiç bir şekilde istenmiyorsa kodun hata vermesi de sağlanabilir. verify_integrity parametresi bu durumu denetler. Birden fazla satırın aynı indekse sahip olması her zaman istenen bir durum değildir; zira veriçerçevesinin bütünlüğünü ("integrity") bozar. Bu parametre $True$ değerine ayarlandığında ucuca ekleme işlemi aynı indeksten iki tane oluşmasıyla sonuçlanacaksa bu engellenir ve bir hata mesajı üretilebilir.

In [33]:
try:
    pd.concat([df1, df2], verify_integrity=True)
except ValueError as e:
    print("ValueError:", e)
ValueError: Indexes have overlapping values: Int64Index([1, 2, 3], dtype='int64')

Ancak bu durumun bir sorun oluşturmayacağı değerlendiriliyorsa aynı indekse sahip istendiği sayıda satır veriçerçevesinde bulunabilir. Bu durum veriçerçevelerinin bir esnekliği olsa da indeksleme, dilimleme ve çağırma işlemlerinde bariz sorunlara yol açacağı gerekçesiyle veriçerçevesinin bütünlüğünü bozar.

In [34]:
pd.concat([df1, df2])
Out[34]:
A B C
1 0.581774 0.330418 0.713601
2 0.138608 0.265059 0.155960
3 0.881244 0.862327 0.442467
1 0.980460 0.594537 0.724819
2 0.211729 0.045210 0.983485
3 0.871971 0.922965 0.548481

Aynı indeksten birden fazla oluşmasına engel olmak için bir başka çözüm indeksleri bütünüyle gözardı etmek ve indeks yönetimini pandas 'a bırakmaktır. Bunun için ignore_index parametresi $True$ değerine ayarlanır. Yukarıdaki örnekte olduğu gibi indekslerin önem taşımadığı ve zaten tamsayı olduğu durumlarda bu kolaylıkla uygulanabilir; ancak bu durumun sorun olarak değerlendirilebileceği indekslerin metinler olduğu örneklerle de karşılaşılabilir. Bir örnekle görelim:

In [35]:
pd.concat([df1, df2], ignore_index=True)
Out[35]:
A B C
0 0.581774 0.330418 0.713601
1 0.138608 0.265059 0.155960
2 0.881244 0.862327 0.442467
3 0.980460 0.594537 0.724819
4 0.211729 0.045210 0.983485
5 0.871971 0.922965 0.548481

Görüldüğü üzere her iki veriçerçevesinin indeksleri göz ardı edilmiş ve 0'dan başlanarak yeni bir indeksleme yapılmıştır.

Bu kez birleştirmeyi sütunda yapmayı deneyelim ve axis parametresini $'col'$ değerine ayarlayalım.

In [36]:
pd.concat([df1, df2], axis=1)
Out[36]:
A B C A B C
1 0.581774 0.330418 0.713601 0.980460 0.594537 0.724819
2 0.138608 0.265059 0.155960 0.211729 0.045210 0.983485
3 0.881244 0.862327 0.442467 0.871971 0.922965 0.548481

Görüldüğü üzere aynı isimde sütunların varlığı da bir problem olmamakta sütunlar yanyana eklenebilmektedir. Yine istenirse bu durumdan ignore_index ya da çoklu indeksleme (keys parametresi üzerinden) yoluyla kaçınılabileceği gibi kodun hata vermesi de sağlanabilir (verify_integrity = True).

In [37]:
pd.concat([df1, df2], axis=1, ignore_index=True)
Out[37]:
0 1 2 3 4 5
1 0.581774 0.330418 0.713601 0.980460 0.594537 0.724819
2 0.138608 0.265059 0.155960 0.211729 0.045210 0.983485
3 0.881244 0.862327 0.442467 0.871971 0.922965 0.548481

Kesişim ve Bileşke Yöntemleriyle Ucuca Ekleme

Veriçerçeve ve serilerinin bağlanmasında (ing. merging) gördüğünüz join metodu, aynı lineer cebir işlemlerine karşılık gelecek şekilde verilerin ucuca eklenmesinde de kullanılabilmektedir. Yukarıdaki örnekte olduğu gibi aynı ismi taşıyan sütunlardaki verilerin doğrudan alta alta yazılması (bileşkesinin alınması) yerine bir kesişiminin de alınması istenebilir. Bu durumun pandas.merge fonksiyonundaki join metodununun $inner$ (kesişim) ve $outer$ (bileşke) değerlerine atanmasıyla sağlandğını hatırlıyorsunuz. Ucucua eklemede de join metodu bu değerlere atanarak amaçlanan şekilde işlemin yapılması sağlanabilir. Birer örnekle görelim.

In [38]:
df1 = pd.DataFrame({'e':[0.205, 0.007, 0.017, 0.094],
                  'i': [7.0, 3.4, 0.0, 1.9],
                  'P': [88.0, 224.7, 365.2, 687.0],
                  'psi': [0.034, 177.4, 23.4, 25.2]},
                    index=['merkur','venus','dunya','mars'])
df2 = pd.DataFrame({'e':[0.049, 0.057, 0.046, 0.011],
                  'P': [4331, 10747, 30589, 59800],
                  'rho': [1.326, 0.687, 1.271, 1.638]},
                  index=['jupiter','saturn','uranus','neptun'])
print("Ic gezegenler:")
print(df1)
print("----------------")
print("Dıs gezegenler:")
print(df2)
print("---------------------")
print("pd.concat([df1,df2]")
pd.concat([df1,df2], sort=False)
Ic gezegenler:
            e    i      P      psi
merkur  0.205  7.0   88.0    0.034
venus   0.007  3.4  224.7  177.400
dunya   0.017  0.0  365.2   23.400
mars    0.094  1.9  687.0   25.200
----------------
Dıs gezegenler:
             e      P    rho
jupiter  0.049   4331  1.326
saturn   0.057  10747  0.687
uranus   0.046  30589  1.271
neptun   0.011  59800  1.638
---------------------
pd.concat([df1,df2]
Out[38]:
e i P psi rho
merkur 0.205 7.0 88.0 0.034 NaN
venus 0.007 3.4 224.7 177.400 NaN
dunya 0.017 0.0 365.2 23.400 NaN
mars 0.094 1.9 687.0 25.200 NaN
jupiter 0.049 NaN 4331.0 NaN 1.326
saturn 0.057 NaN 10747.0 NaN 0.687
uranus 0.046 NaN 30589.0 NaN 1.271
neptun 0.011 NaN 59800.0 NaN 1.638

Görüldüğü üzere aksi söylenmediği takdirde yapılan işlem satırları altalta dizerken tüm sütunları kullanmak (outer join) ve bir satırın herhangi bir sütun için verisi yoksa onu NaN değerine atamaktır. Eğer ucuca ekleme sırasında sadece ortak olan sütunlar kullanılmak isteniyorsa bu davranış join = 'inner' ile aşılabilir.

In [39]:
print("pd.concat([df1,df2], join='inner')")
print(pd.concat([df1,df2], join='inner', sort=False))
pd.concat([df1,df2], join='inner')
             e        P
merkur   0.205     88.0
venus    0.007    224.7
dunya    0.017    365.2
mars     0.094    687.0
jupiter  0.049   4331.0
saturn   0.057  10747.0
uranus   0.046  30589.0
neptun   0.011  59800.0

Veri Çerçevelerini Birleştirme: Combining

Bir veriçerçevesini (ya da seriyi) diğerinin ucuna eklerken çakışan indeksleri bir kenara bırakıp, çakışmayanlar için ikinciyi birincinin sonuna eklemek üzere birinci nesnenin üzerinde combine_first() fonksiyonu tanımlanmıştır. Örneklerle nasıl çalıştığını anlayalım.

In [40]:
import pandas as pd
import numpy as np
seri1 = pd.Series(np.random.rand(5),index=[1,2,3,4,5])
print("Seri 1:")
print(seri1)
seri2 = pd.Series(np.random.rand(4),index=[2,4,5,6])
print("Seri 2:")
print(seri2)
print("seri1.combine_first(seri2)")
seri1.combine_first(seri2)
Seri 1:
1    0.407357
2    0.298891
3    0.306653
4    0.852370
5    0.833057
dtype: float64
Seri 2:
2    0.600602
4    0.060694
5    0.808498
6    0.230424
dtype: float64
seri1.combine_first(seri2)
Out[40]:
1    0.407357
2    0.298891
3    0.306653
4    0.852370
5    0.833057
6    0.230424
dtype: float64

Görüldüğü gibi $1, 2, 3, 4, 5$ indeksleri birinci seriden ($seri1$), $6$ indeksi ise ikinci seriden ($seri2$) alınarak seriler kombine edilmiştir. Bunun tersine bakalım.

In [41]:
print("seri2.combine_first(seri1)")
seri2.combine_first(seri1)
seri2.combine_first(seri1)
Out[41]:
1    0.407357
2    0.600602
3    0.306653
4    0.060694
5    0.808498
6    0.230424
dtype: float64

Görüldüğü üzere $2, 4, 5, 6$ indeksleri ikinci seriden alınırken, onda bulunmayan $1$ ve $3$ indeksleri ise birinci seriden alınmıştır. Serilerin belirli bölümleri de dilimleme yoluyla birleştirilebilir. Aşağıdaki örnekte ilk serinin son üç elemanına kadar olan bölümünün ikinci serinin son üç elemanına kadar olan bölümünü "overlap" etmesi, sonrasında serinin geri kalanının $seri2$ elemanları tarafından doldurulması istenmiştir.

In [42]:
print(seri1[:3].combine_first(seri2[:3]))
1    0.407357
2    0.298891
3    0.306653
4    0.060694
5    0.808498
dtype: float64

Sütun ve Satırlarda Döndürme: Pivoting

Veriyi analize hazırlarken sıklıkla başvurulan bir grup başka işlem de sütunlarla satırların yerlerini değiştirme (pivoting) işlemleridir. Bu işlemlerde sütunlar satırlara dönüştürülebildiği (stacking) gibi satırlar da sütunlara dönüştürülebilir (unstacking). Konuyu bir örnekle anlamaya çalışalım.

In [43]:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.array([65,45,70,80,74,90,44,37,62]).reshape(3,3),
                  index=['ogr1','ogr2','ogr3'],
                  columns=['odev','vize','final'])
print(df)
      odev  vize  final
ogr1    65    45     70
ogr2    80    74     90
ogr3    44    37     62

Veriçerçeveleri (DataFrame) üzerinde tanımlıstack() metodunu kullanarak, sütunları satırlar haline dönüştürmek suretiyle birden fazla (hiyerarşik) indeksi bulunan bir seri üretilmesi mümkündür.

In [44]:
df.stack()
Out[44]:
ogr1  odev     65
      vize     45
      final    70
ogr2  odev     80
      vize     74
      final    90
ogr3  odev     44
      vize     37
      final    62
dtype: int64
In [45]:
df_ogrs = df.stack()
df_ogrs['ogr1']['odev']*0.2 + df_ogrs['ogr1']['vize']*0.3 + df_ogrs['ogr1']['final']*0.5
Out[45]:
61.5

Bunun tersi işlem de tahmin edilebileceği gibi unstack() fonksiyonu ile yapılır. Örneğimizde bu işlem satırları toplayıp sütuna dönüştüreceğinden veriçerçevesinin eski halinde görüntülenmesini sağlayacaktır. Bir not olarak veriçerçevesinin değiştirilemez (immutable) bir nesne olduğunu hatırlatmakta fayda vardır. Dolayısıyla yukarıda yaptığımız işlem sadece bir görüntüleme işlemidir ve veriçerçevemiz dönüşmüş değildir. Bu noktaya dikkat çekerek tersi işlem olan unstack() işlemini örnekleyebilmek üzere stack() edilmiş veriçerçevisini bir başka veri çerçevesine aktaralım ve unstack() işlemini bunun sonrasında yapalım.

In [46]:
df2 = df.stack()
print("Sutundan satira donusum (stack):")
print(df2)
print("Satirdan sutuna donusum (unstack):")
df3 = df2.unstack()
print(df3)
Sutundan satira donusum (stack):
ogr1  odev     65
      vize     45
      final    70
ogr2  odev     80
      vize     74
      final    90
ogr3  odev     44
      vize     37
      final    62
dtype: int64
Satirdan sutuna donusum (unstack):
      odev  vize  final
ogr1    65    45     70
ogr2    80    74     90
ogr3    44    37     62

İstendiği takdirde unstack() fonksiyonunu tersi yönde uygulamak suretiyle orjinal veriye dönmek yerine sütun isimlerini satırlara, satırları da sütunlara veriyi doğru şekilde hizalayarak almak da tercih edilebilir. Örneğimizde bu ödev, vize ve final notlarını satırlara, öğrenci isimlerini de sütunlara almaya karşılık gelecektir.

In [47]:
df4 = df2.unstack(0)
print(df4)
       ogr1  ogr2  ogr3
odev     65    80    44
vize     45    74    37
final    70    90    62

Görüldüğü üzere bu tür işlemler, bir öğrenci için ağırlıklı ortalama almak ya da sınıf için belirli bir sınavın (örneğin vizenin) ortalamasını almak gibi işlemleri yaparken esneklik sağlar. Karşılaşılan probleme göre bu tür esneklikler sağlayan fonksiyonların farkında olunarak döngüler yerine bu fonskiyonların kullanılmlası önerilir.

Bu türden (satırda geri döndürme) bir davranış, transpose ile de sağlanabilir.

In [48]:
df.transpose()
Out[48]:
ogr1 ogr2 ogr3
odev 65 80 44
vize 45 74 37
final 70 90 62

Uzun ve Geniş Tablolar: Longframe ve Wideframe

Satır ve sütunların yerdeğiştirme işlemlerinden bir başkası özellikle benzer ya da aynı elemanların bulunduğu veriçerçevelerinde kullanışlı hale gelir. Okuması zor olan bir tablo türü yerine okuması ve diğer tablolarla yukarıda anlatılan yöntemlerle etkileşimi daha kolay olan bir tablo türüne geçmek tercih edilebillir.

In [49]:
tablo1 = pd.DataFrame({ 'iyon':['FeI','FeI','FeI',
                                 'NaI','NaI','NaI',
                                 'CaII','CaII','CaII'],
                       'liste':['NIST','VALD','Custom',
                               'NIST','VALD','Custom',
                               'NIST','VALD','Custom'],
                       'dalgaboyu': [5292.512, 5292.509,5292.513,
                                5682.256, 5682.261, 5682.258,
                                5167.615, 5167.618, 5167.620]})
print(tablo1)
   iyon   liste  dalgaboyu
0   FeI    NIST   5292.512
1   FeI    VALD   5292.509
2   FeI  Custom   5292.513
3   NaI    NIST   5682.256
4   NaI    VALD   5682.261
5   NaI  Custom   5682.258
6  CaII    NIST   5167.615
7  CaII    VALD   5167.618
8  CaII  Custom   5167.620

Farklı iyonların farklı çizgi listelerindeki dalgaboyu değerlerini veren bu tabloyu oluşturulduğu haliyle okumak kolay olmayabilir. Ancak birden fazla indeks kullanarak bu tabloyu geniş formda gösterdiğimizde okuması çok daha kolay gelebilir. Bunun için hangi özelliğe göre döndürme işlemi yapılmak isteniyorsa pivot() foksiyonuna sağlanmak suretiyle istenen döndürme işlemi yapılabilir.

In [50]:
tablo2 = tablo1.pivot(index='iyon',columns='liste')
print(tablo2)
      dalgaboyu                    
liste    Custom      NIST      VALD
iyon                               
CaII   5167.620  5167.615  5167.618
FeI    5292.513  5292.512  5292.509
NaI    5682.258  5682.256  5682.261

Bu şekilde her bir iyonun hangi listede hangi dalgaboyunda bulunduğunu görmek çok daha kolaylaştı.

Veri çıkarma: Removing

Veri hazırlamanın son aşaması sütunların ve satırların gerektiğinde kaldırılmasıdır (removing). Buna ilişkin örnekler Ders 2: Pandas'a Giriş bölümünde görmüştük. Bununla birlikte, veriyi analize hazırlamak için gerekli işlemleri tamamlamak adına burada yeni örnekler verilecektir.

Aşağıda daha önce de tanımladığımız 3 öğrencinin ödev, vize ve final notlarını gösterir veriçerçevesini tekrar tanımlayarak satır ve sütun çıkarma işlemlerini tekrarlamaya yönelik örnekler bulunmaktadır.

In [51]:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.array([65,45,70,80,74,90,44,37,62]).reshape(3,3),
                  index=['ogr1','ogr2','ogr3'],
                  columns=['odev','vize','final'])
print(df)
      odev  vize  final
ogr1    65    45     70
ogr2    80    74     90
ogr3    44    37     62

Tıpkı numpy dizilerinde olduğu gibi del komutu veriçerçevelerinden de sütun silmek için kullanılabilir. del sadece veriçerçeveleri üzerinde tanımlı bir metod ya da fonksiyon olmadığı için kullanılır kullanılmaz etkili olur ve başka bir veriçerçevesine ya da değişkene kopyalama gib bir önlem alınmadıysa, geri alınması mümkün değildir.

In [52]:
del df['odev']
print(df)
      vize  final
ogr1    45     70
ogr2    74     90
ogr3    37     62

İstenmeyen bir satırı kaldırmak içinse drop() metodu kullanılır. drop() veriçerçeveleri üzerinde tanımlı bir metod olduğu ve veriçerçevesi ($DataFrame$) nesnesi de değiştirilemez (immutable) olduğu için doğrudan etkili olmaz. Eğer satır silme işlemi korunmak isteniyorsa işlem sonrası oluşan veriçerçevesi bir değişkene (eskisini kaybetmek koşuluyla istenirse aynı isimli) alınmalıdır.

In [53]:
df2 = df.drop('ogr3')
print("ogr3 'un olmadıgi liste:")
print(df2)
print("orjinal liste:")
print(df)
ogr3 'un olmadıgi liste:
      vize  final
ogr1    45     70
ogr2    74     90
orjinal liste:
      vize  final
ogr1    45     70
ogr2    74     90
ogr3    37     62

Tekrarlayan Satırların Atılması

Bir pandas serisi ya da veriçerçevesinde tekrar eden satırları (ya da bir sütun içindeki değerleri) yakalamak için duplicated() metodu kullanılır. Metod, tekrarlayan değerlerin arandığı yerdeki bu tür değerleri True diğerlerini ise False olarak işaretlemek suretiyle bir şablon (mask) oluşturur. Tıpkı belirli koşullara dayalı olarak o koşulu sağlayan ve sağlamayan satırların filtrelenmesinde olduğu gibi, bu maske ilgilenilen veri setinin filtrelenmesinde kullanılabilir.

Örnek olarak çeşitli tekrar eden satır ve sütunlar barındıran çizgi listesi örneğimize geri dönelim. Bu veriçerçevesinde kendini tekrar eden bir satır olmadığı için tüm satırlar $False$ olarak işaretlenecektir.

In [54]:
import pandas as pd
cizgi_listesi = pd.DataFrame({ 'iyon':['FeI','FeI','FeI',
                                 'NaI','NaI','NaI',
                                 'CaII','CaII','CaII'],
                       'liste':['NIST','VALD','Custom',
                               'NIST','VALD','Custom',
                               'NIST','VALD','Custom'],
                       'dalgaboyu': [5292.512, 5292.509,5292.513,
                                5682.256, 5682.261, 5682.258,
                                5167.615, 5167.618, 5167.620]})
cizgi_listesi.duplicated()
Out[54]:
0    False
1    False
2    False
3    False
4    False
5    False
6    False
7    False
8    False
dtype: bool

Ancak istersek herhangi bir sütunda tekrar eden değerleri de duplicated() fonksiyonuyla bulabiliriz.

In [55]:
cizgi_listesi['iyon'].duplicated()
Out[55]:
0    False
1     True
2     True
3    False
4     True
5     True
6    False
7     True
8     True
Name: iyon, dtype: bool

Bu şekilde oluşturduğumuz şablonu daha sonra veriçerçevesine vererek tekrarlayan satırları alabiliriz. Örneğimizde $iyon$ sütununda her bir iyon için verilen ilk satır $False$ değeri ile maskelenmiş iken, daha sonra bu iyona ilişkin diğer çizgi listesindeki değerlerin bulunduğu satırlar $True$ olarak işaretlenmiştir. Dolayısıyla bu şablon cizgi_listesi veriçerçevesine sağlandığında sadece tekrar eden satırlar filtrelenip görüntülenir.

In [56]:
cizgi_listesi[cizgi_listesi['iyon'].duplicated()]
Out[56]:
iyon liste dalgaboyu
1 FeI VALD 5292.509
2 FeI Custom 5292.513
4 NaI VALD 5682.261
5 NaI Custom 5682.258
7 CaII VALD 5167.618
8 CaII Custom 5167.620

İstendiği takdirde bu tekrar satırları veriçerçevesinden çıkarılabilir. drop_duplicates() metodu bu amaçla kullanılır.

In [57]:
cizgi_listesi.drop_duplicates('iyon')
Out[57]:
iyon liste dalgaboyu
0 FeI NIST 5292.512
3 NaI NIST 5682.256
6 CaII NIST 5167.615

Veriçerçevesi üzerinde yaptığımız bu tür filtre işlemlerinin veriçerçevesini değiştirmediğini, işlemin sonucunu saklanması istendiğinde yeni bir veriçerçevesine alınması gerektiğini unutmayınız. Filtre işleminin sonucunun saklanması istendiğinde inplace parametresinin $False$ olan varsayılan değeri True ile değiştirilebilir. Bu durumda yapılan değişiklik kalıcı hale getirilmiş olur. drop_duplicates() tekrarlanan satırların her seferinde sadece ilkinin ($'first'$) korunması yerine sonuncusunun korunmasını ($'last'$) ya da tamamının ($False$) filtrelenmesine de keep parametresi üzerinden olanak sağlar.

In [58]:
cizgi_listesi.drop_duplicates('iyon', keep='last')
Out[58]:
iyon liste dalgaboyu
2 FeI Custom 5292.513
5 NaI Custom 5682.258
8 CaII Custom 5167.620

Gördüğünüz gibi sadece kullanıcının oluşturduğu "Custom" isimli çizgi listesindeki benzersiz (unique) satırlar kalmıştır; zira bu çizgi listesi son sıradadır.

Eşleştirme

Veriçerçeveleri üzerinde çalışırken zaman zaman belirli bir değere karşılık gelen tüm değerleri değiştirmek (ing. replace), değerleri eşleştirerek yeni bir sütun oluşturmak (ing. map) ya da indeks değerlerini istenen değerlerle değiştirmek (ing. rename) istenebilir. pandas tümü özünde "eşleştirme" (ing. mapping) olan bu işlemler için çeşitli fonksiyonlar sunar.

Eşleşenleri Değiştirme

Bir veriçerçevesindeki değerlerden bazılarını istenen başka bir değerle değiştirmek için veri nesneleri üzerinde tanımlı replace() metodu kullanılır. Değiştirilmek istenen değeri anahtar (key) yerine koymak istenen değeri değer (value) kabul eden bir sözlük kullanmak bu tür değişimler için ideal bir çözümdür.

Aşağıdaki örnekle bu tür işlemlerin nasıl yapıldığını görelim. cizgi_listesi veriçerçevemizde kullanıcı tanımlı çizgi listesinde yer alan tüm çizgiler için $Custom$ yerine $User$ değerini koymak istiyor olalım ve bu değişimi kalıcı hale getirmek için inplace parametresini de $True$ değerine ayarlayalım.

In [59]:
import pandas as pd
cizgi_listesi = pd.DataFrame({ 'iyon':['FeI','FeI','FeI',
                                 'NaI','NaI','NaI',
                                 'CaII','CaII','CaII'],
                       'liste':['NIST','VALD','Custom',
                               'NIST','VALD','Custom',
                               'NIST','VALD','Custom'],
                       'dalgaboyu': [5292.512, 5292.509,5292.513,
                                5682.256, 5682.261, 5682.258,
                                5167.615, 5167.618, 5167.620]})
cizgi_listesi
Out[59]:
iyon liste dalgaboyu
0 FeI NIST 5292.512
1 FeI VALD 5292.509
2 FeI Custom 5292.513
3 NaI NIST 5682.256
4 NaI VALD 5682.261
5 NaI Custom 5682.258
6 CaII NIST 5167.615
7 CaII VALD 5167.618
8 CaII Custom 5167.620
In [60]:
listeadi = {'Custom':'User'}
cizgi_listesi.replace(listeadi, inplace=True)
cizgi_listesi
Out[60]:
iyon liste dalgaboyu
0 FeI NIST 5292.512
1 FeI VALD 5292.509
2 FeI User 5292.513
3 NaI NIST 5682.256
4 NaI VALD 5682.261
5 NaI User 5682.258
6 CaII NIST 5167.615
7 CaII VALD 5167.618
8 CaII User 5167.620

replace() fonksiyonu NaN gibi değerleri 0'la değiştirmek için sıklıkla kullanılır.

In [61]:
import numpy as np
seri = pd.Series([10,2.3,np.nan,-4,0.6,np.nan,7.5])
print(seri)
seri.replace(np.nan,0, inplace=True)
print("Degisiklik sonrasi:")
print(seri)
0    10.0
1     2.3
2     NaN
3    -4.0
4     0.6
5     NaN
6     7.5
dtype: float64
Degisiklik sonrasi:
0    10.0
1     2.3
2     0.0
3    -4.0
4     0.6
5     0.0
6     7.5
dtype: float64

Eşleştirme Yaparak Yeni Bir Sütun Ekleme

Bir sütunda verilen değerleri kullanarak yeni bir sütun eklemek de sıklıkla ihtiyaç duyulan bir yöntemdir. pandas veri nesneleri üzerinde tanımlı map() metodu kullanılarak bu amaca erişilebilir.

In [62]:
liste_referanslari = {'VALD':'Ryabchikova (1997)', 'NIST':'NIST ASD Team (2019)', 'User':'this study'}
cizgi_listesi['referanslar'] = cizgi_listesi['liste'].map(liste_referanslari)
cizgi_listesi
Out[62]:
iyon liste dalgaboyu referanslar
0 FeI NIST 5292.512 NIST ASD Team (2019)
1 FeI VALD 5292.509 Ryabchikova (1997)
2 FeI User 5292.513 this study
3 NaI NIST 5682.256 NIST ASD Team (2019)
4 NaI VALD 5682.261 Ryabchikova (1997)
5 NaI User 5682.258 this study
6 CaII NIST 5167.615 NIST ASD Team (2019)
7 CaII VALD 5167.618 Ryabchikova (1997)
8 CaII User 5167.620 this study

Eşleştirme Yaparak İndeks İsimlerini Değiştirme

İndeks isimlerini yenileriyle değiştirmek istendiğinde de aynı yönteme başvurulabilir. Bu kez kullanılması gereken fonksiyon yine veri nesneleri üzerinde tanımlı rename() metodudur.

Yine cizgi_listesi veriçerçevemiz üzerinde bir örnekle görelim. Yeni indekslerimizi $iyon$ sütunundaki iyon isimleriyle $liste$ sütunundaki liste isimlerini "_" ile birleştirerek oluşturalım.

In [63]:
yeni_indeksler = {}
for i in range(len(cizgi_listesi.index)):
    yeni_indeksler[i] = cizgi_listesi.at[i,'iyon']+"_"+cizgi_listesi.at[i,'liste']
print(yeni_indeksler)
cizgi_listesi.rename(yeni_indeksler)
{0: 'FeI_NIST', 1: 'FeI_VALD', 2: 'FeI_User', 3: 'NaI_NIST', 4: 'NaI_VALD', 5: 'NaI_User', 6: 'CaII_NIST', 7: 'CaII_VALD', 8: 'CaII_User'}
Out[63]:
iyon liste dalgaboyu referanslar
FeI_NIST FeI NIST 5292.512 NIST ASD Team (2019)
FeI_VALD FeI VALD 5292.509 Ryabchikova (1997)
FeI_User FeI User 5292.513 this study
NaI_NIST NaI NIST 5682.256 NIST ASD Team (2019)
NaI_VALD NaI VALD 5682.261 Ryabchikova (1997)
NaI_User NaI User 5682.258 this study
CaII_NIST CaII NIST 5167.615 NIST ASD Team (2019)
CaII_VALD CaII VALD 5167.618 Ryabchikova (1997)
CaII_User CaII User 5167.620 this study

Sütun isimleri de aynı şekilde değiştirilebilir. Bu kez columns parametresi değişiklik yapılmak istenen sütun isimlerinin bulunduğu sözlüğe ayarlanır.

In [64]:
sutunlar = {'dalgaboyu':'wavelength',
            'iyon':'ion',
            'liste':'linelist',
            'referanslar':'reference'}
cizgi_listesi.rename(columns=sutunlar)
Out[64]:
ion linelist wavelength reference
0 FeI NIST 5292.512 NIST ASD Team (2019)
1 FeI VALD 5292.509 Ryabchikova (1997)
2 FeI User 5292.513 this study
3 NaI NIST 5682.256 NIST ASD Team (2019)
4 NaI VALD 5682.261 Ryabchikova (1997)
5 NaI User 5682.258 this study
6 CaII NIST 5167.615 NIST ASD Team (2019)
7 CaII VALD 5167.618 Ryabchikova (1997)
8 CaII User 5167.620 this study

Yine aynı şekilde bu değişikliklerin kalıcı hale getirilmek istendiği durumlarda inplace parametresine başvurulur.

Çoklu İndeksleme

Serilerde birden, veriçerçevelerinde ikiden fazla boyutun istenmesi durumunda iyi bir çözüm çoklu indeksleme (ing. Multi-indexing) kullanmaktır.

In [65]:
df = pd.DataFrame(np.random.rand(4, 2),
index=[['x', 'x', 'y', 'y'], [0, 1, 0, 1]],
columns=['veri1', 'veri2'])
df
Out[65]:
veri1 veri2
x 0 0.746613 0.403287
1 0.950982 0.022387
y 0 0.129968 0.780553
1 0.263632 0.558567

Çoklu indeksler oluşturmak için bir başka yöntem $MultiIndex$ fonksiyonununu kullanmaktır. Demet değişkenler şeklinde tanımlanmış çoklu indeskler için aşağıdaki örnek verilebilir.

In [66]:
index = [('HAT-P-36b', 'T100'), ('HAT-P-36b', "T80"),
('XO-3b', "T100"), ('XO-3b', "T80"),
('KELT-16b', "T100"), ('KELT-16b', "T80")]
derinlikler = [0.0206, 0.0220,
0.0096, 0.0100,
0.0152, 0.0170]
index = pd.MultiIndex.from_tuples(index)
df = pd.DataFrame({"derinlikler":derinlikler}, index=index)
df
Out[66]:
derinlikler
HAT-P-36b T100 0.0206
T80 0.0220
XO-3b T100 0.0096
T80 0.0100
KELT-16b T100 0.0152
T80 0.0170

Bu durumda 2 boyutlu tablo, 2 katlı indeksleme ((x,y),(0,1)) ile 3 boyuta çıkarılmış olur. İstendiğinde "unstack" edilerek bu tablo tekrar 2 boyutlu bir tabloya aşağıdaki şekilde dönüştürülebiir.

In [67]:
df_2D = df.unstack()
df_2D
Out[67]:
derinlikler
T100 T80
HAT-P-36b 0.0206 0.022
KELT-16b 0.0152 0.017
XO-3b 0.0096 0.010

Tekrar 3-boyuta $stack$ fonksiyonuyla dönülür.

In [68]:
df_2D.stack()
Out[68]:
derinlikler
HAT-P-36b T100 0.0206
T80 0.0220
KELT-16b T100 0.0152
T80 0.0170
XO-3b T100 0.0096
T80 0.0100

Bu indekslere isim verilip daha da kullanışlı hale getirilebilir.

In [69]:
df.index.names = ["gezegen","teleskop"]
df
Out[69]:
derinlikler
gezegen teleskop
HAT-P-36b T100 0.0206
T80 0.0220
XO-3b T100 0.0096
T80 0.0100
KELT-16b T100 0.0152
T80 0.0170

İndeksleme ve dilimleme n-boyutlu numpy dizilerindekine benzerdir.

In [70]:
df.index
Out[70]:
MultiIndex([('HAT-P-36b', 'T100'),
            ('HAT-P-36b',  'T80'),
            (    'XO-3b', 'T100'),
            (    'XO-3b',  'T80'),
            ( 'KELT-16b', 'T100'),
            ( 'KELT-16b',  'T80')],
           names=['gezegen', 'teleskop'])
In [71]:
df.loc["HAT-P-36b"]
Out[71]:
derinlikler
teleskop
T100 0.0206
T80 0.0220
In [72]:
df.loc["HAT-P-36b","T80"]
Out[72]:
derinlikler    0.022
Name: (HAT-P-36b, T80), dtype: float64

Veri Gruplama

Veri işlemenenin son aşamasını veri gruplama (ing. data aggregation) teknikleri oluşturur. Veri gruplama kavramıyla amaçlanan bir dizi veriden tek bir gruba dönüşüm yapmaktır. Örneğin $sum()$, $mean()$, $count()$, $min()$, $max()$ gibi numpy fonksiyonlarıyla yapılan da bir çeşit gruplamadır. Tüm bu fonksiyonlar bir grup veriyi tek bir sayıyla ifade eder. Sayısal bu tür gruplamaların yanı sıra doğal olarak, aynı türden olduğu düşünülen verilerin, tek bir grupla ifade edildiği kategorik gruplamalar da düşünülebilir.

Öncelikle pandas 'ta bu tür sayısal gruplamaların nasıl yapıldığını ötegezegenler veritabanı üzerinden örnekleyelim. Yine NASA Exoplanet Archive 'dan indirebileceğiniz keşifleri onaylanmış (ing. confirmed) ötegezegenler veritabanı üzerinde çalışalım.

In [73]:
import pandas as pd
import numpy as np
og = pd.read_csv("veri/PSCompPars_2024.04.02_02.08.30.csv", comment="#", skipinitialspace=True,
                index_col="pl_name")
og.head()
Out[73]:
hostname sy_snum sy_pnum discoverymethod disc_year disc_facility pl_controv_flag pl_orbper pl_orbpererr1 pl_orbpererr2 ... sy_disterr2 sy_vmag sy_vmagerr1 sy_vmagerr2 sy_kmag sy_kmagerr1 sy_kmagerr2 sy_gaiamag sy_gaiamagerr1 sy_gaiamagerr2
pl_name
11 Com b 11 Com 2 1 Radial Velocity 2007 Xinglong Station 0 323.21000 0.06000 -0.05000 ... -1.9238 4.72307 0.023 -0.023 2.282 0.346 -0.346 4.44038 0.003848 -0.003848
11 UMi b 11 UMi 1 1 Radial Velocity 2009 Thueringer Landessternwarte Tautenburg 0 516.21997 3.20000 -3.20000 ... -1.9765 5.01300 0.005 -0.005 1.939 0.270 -0.270 4.56216 0.003903 -0.003903
14 And b 14 And 1 1 Radial Velocity 2008 Okayama Astrophysical Observatory 0 186.76000 0.11000 -0.12000 ... -0.7140 5.23133 0.023 -0.023 2.331 0.240 -0.240 4.91781 0.002826 -0.002826
14 Her b 14 Her 1 2 Radial Velocity 2002 W. M. Keck Observatory 0 1765.03890 1.67709 -1.87256 ... -0.0073 6.61935 0.023 -0.023 4.714 0.016 -0.016 6.38300 0.000351 -0.000351
16 Cyg B b 16 Cyg B 3 1 Radial Velocity 1996 Multiple Observatories 0 798.50000 1.00000 -1.00000 ... -0.0111 6.21500 0.016 -0.016 4.651 0.016 -0.016 6.06428 0.000603 -0.000603

5 rows × 83 columns

Öncelikle geçiş yapan ötegezegenlerin yörüngelerinin dış merkezliliklerinin ortalamasını bilmek istiyor olalım.

In [74]:
print("Otegezegenlerin ortalama dis merkezliligi: {:.2f} (+{:.2f} , {:.2f})".
      format(og['pl_orbeccen'].mean(),og['pl_orbeccenerr1'].mean(),og['pl_orbeccenerr2'].mean()))      
Otegezegenlerin ortalama dis merkezliligi: 0.08 (+0.07 , -0.06)

Bir başka örnek olarak istenen sütunların genel istatistikleri için değeri NaN olan satırları da eledikten sonra describe() metodunu kullanalım.

In [75]:
og[['pl_bmassj','pl_radj']].dropna().describe()
Out[75]:
pl_bmassj pl_radj
count 5563.000000 5563.000000
mean 1.387069 0.505955
std 7.470618 0.472643
min 0.000060 0.028000
25% 0.012700 0.159000
50% 0.027370 0.247000
75% 0.503500 1.044000
max 282.000000 6.900000

GroupBy Fonksiyonuyla Veri Gruplama

Gruplama işlemi özünde üç aşamalı bir işlemdir. Öncelikle veri istenen sütundaki eş değerlere göre gruplara bölünür (ing. split), daha sonra istenen gruplama işlemi (toplama, sayma vs.) her bir gruba uygulanır (ing. apply) ve daha sonra gruplar birleştirilir (ing. combine). Programcılıkta da veritabanı yönetiminde de bir amaca ulaşmanın birden çok yöntemi vardır. Burada tarif edilen bu işlemler dizisine ayrı ayrı fonksiyonlar (koşula göle maskeleme, fonksiyona (sum, mean gibi) maskelenmiş veri nesnelerini gönderme, sonra birleştirme (merge, join ya da concat gibi fonksiyonlarla) fonksiyonları kullanılarak gerçekleştirilebilir. Bu fonksiyonların bir sıra dahilinde planlanmasıyla da amaçlanan sonuca erişilebilir. İyi programcılık, programlama dilinin sağladığı olanakların farkında olup bunları gerektiği zaman gereğince kullanmaya dayanır. pandas veri nesneleri üzerinde tanımlı GroupBy metodu bütun bu adımları sırayla gerçekleştirebilir.

Örneğin ötegezegenler ($og$) veriçerçevemizi keşif yöntemine göre gruplayalım. Bunun için öncelikle veriçerçevesinin sütunlarının adlarını görüntüleyip, keşif yönteminin hangi sütun isminde yer aldığını bulmalıyız.

In [76]:
og.columns
Out[76]:
Index(['hostname', 'sy_snum', 'sy_pnum', 'discoverymethod', 'disc_year',
       'disc_facility', 'pl_controv_flag', 'pl_orbper', 'pl_orbpererr1',
       'pl_orbpererr2', 'pl_orbperlim', 'pl_orbsmax', 'pl_orbsmaxerr1',
       'pl_orbsmaxerr2', 'pl_orbsmaxlim', 'pl_rade', 'pl_radeerr1',
       'pl_radeerr2', 'pl_radelim', 'pl_radj', 'pl_radjerr1', 'pl_radjerr2',
       'pl_radjlim', 'pl_bmasse', 'pl_bmasseerr1', 'pl_bmasseerr2',
       'pl_bmasselim', 'pl_bmassj', 'pl_bmassjerr1', 'pl_bmassjerr2',
       'pl_bmassjlim', 'pl_bmassprov', 'pl_orbeccen', 'pl_orbeccenerr1',
       'pl_orbeccenerr2', 'pl_orbeccenlim', 'pl_insol', 'pl_insolerr1',
       'pl_insolerr2', 'pl_insollim', 'pl_eqt', 'pl_eqterr1', 'pl_eqterr2',
       'pl_eqtlim', 'ttv_flag', 'st_spectype', 'st_teff', 'st_tefferr1',
       'st_tefferr2', 'st_tefflim', 'st_rad', 'st_raderr1', 'st_raderr2',
       'st_radlim', 'st_mass', 'st_masserr1', 'st_masserr2', 'st_masslim',
       'st_met', 'st_meterr1', 'st_meterr2', 'st_metlim', 'st_metratio',
       'st_logg', 'st_loggerr1', 'st_loggerr2', 'st_logglim', 'rastr', 'ra',
       'decstr', 'dec', 'sy_dist', 'sy_disterr1', 'sy_disterr2', 'sy_vmag',
       'sy_vmagerr1', 'sy_vmagerr2', 'sy_kmag', 'sy_kmagerr1', 'sy_kmagerr2',
       'sy_gaiamag', 'sy_gaiamagerr1', 'sy_gaiamagerr2'],
      dtype='object')

Görüldüğü gibi keşif yöntemi pl_discmethod başlıklı sütunda yer almaktadır.

In [77]:
og.groupby("discoverymethod")
Out[77]:
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x760bb36f2b90>

GroupBy metodunun sonucu bir DataFrameGroupBy nesnesidir. Bu nesne ilgilenilen veriçerçevesinin özel bir görüntüsüdür. Bu nesnenin üzerine istenen işlem uygulanarak (ing. apply) gruplama işleminin ikinci aşaması da gerçekleştirilebliir.

In [78]:
og.groupby("discoverymethod").count()
Out[78]:
hostname sy_snum sy_pnum disc_year disc_facility pl_controv_flag pl_orbper pl_orbpererr1 pl_orbpererr2 pl_orbperlim ... sy_disterr2 sy_vmag sy_vmagerr1 sy_vmagerr2 sy_kmag sy_kmagerr1 sy_kmagerr2 sy_gaiamag sy_gaiamagerr1 sy_gaiamagerr2
discoverymethod
Astrometry 3 3 3 3 3 3 3 3 3 3 ... 3 2 2 2 3 3 3 3 3 3
Disk Kinematics 1 1 1 1 1 1 0 0 0 0 ... 1 1 1 1 1 1 1 1 1 1
Eclipse Timing Variations 17 17 17 17 17 17 17 16 16 17 ... 16 16 16 16 15 15 15 16 16 16
Imaging 68 68 68 68 68 68 21 17 17 21 ... 65 57 57 56 64 64 63 61 61 61
Microlensing 210 210 210 210 210 210 10 8 8 10 ... 206 12 10 9 14 13 13 1 1 1
Orbital Brightness Modulation 9 9 9 9 9 9 9 6 6 9 ... 8 9 9 9 9 4 4 9 9 9
Pulsar Timing 7 7 7 7 7 7 6 6 6 6 ... 6 1 1 1 1 1 1 1 1 1
Pulsation Timing Variations 2 2 2 2 2 2 2 2 2 2 ... 2 2 2 2 2 2 2 2 2 2
Radial Velocity 1088 1088 1088 1088 1088 1088 1088 1084 1084 1088 ... 1086 1088 1085 1082 1086 1086 1077 1075 1074 1074
Transit 4168 4168 4168 4168 4168 4168 4168 3715 3715 4168 ... 4053 4165 4162 4161 4162 4132 4130 4134 4133 4133
Transit Timing Variations 29 29 29 29 29 29 29 17 17 29 ... 28 29 29 29 29 29 29 28 28 28

11 rows × 82 columns

Görüldüğü gibi aslında bir DataFrameGroupBy nesnesi de bir veriçerçevesi gibi kullanılabilir. Veriçerçevelerinden sütun seçilebildiği gibi bu nesneden de istenen sütunlar seçilebilir ve üzerinde istenen işlemler düşünülebliir. Örneğin daha önce elde ettiğimiz ötegezegenlerin ortalama dış merkezliliğini keşif yöntemine göre ayrı ayrı elde edebilir, karşılaştırmalar yapabilir ve bunlardan istatistiki çıkarımlara yönebiliriz.

In [79]:
og.groupby("discoverymethod")['pl_orbeccen'].mean()
Out[79]:
discoverymethod
Astrometry                       0.547667
Disk Kinematics                       NaN
Eclipse Timing Variations        0.147519
Imaging                          0.393474
Microlensing                     0.190000
Orbital Brightness Modulation    0.000000
Pulsar Timing                    0.057117
Pulsation Timing Variations      0.075000
Radial Velocity                  0.220069
Transit                          0.032682
Transit Timing Variations        0.090232
Name: pl_orbeccen, dtype: float64

Görüldüğü üzere geçiş ($e_{ort} \sim 0.033$), pulsar zamanlaması ($e_{ort} \sim 0.075$), pulsasyon frekansı değişimi ($e_{ort} \sim 0.075$) gibi yöntemlerde yörünge dış merkezliliği küçük gezegenler keşfedilirken; astrometri ($e_{ort} \sim 0.548$), doğrudan görüntüleme ($e_{ort} \sim 0.393$), dikine hız ($e_{ort} \sim 0.220$) gibi yöntemlerde ise ortalama yörünge dış merkezliliği daha büyük olan ötegezegenler keşfedilebilmektedir. Benzer şekilde yörünge dönemlerini de görmek isteyebiliriz. Bu kez grupladıktan sonra medyan alalım.

In [80]:
og.groupby("discoverymethod")['pl_orbper'].median()
Out[80]:
discoverymethod
Astrometry                         284.390000
Disk Kinematics                           NaN
Eclipse Timing Variations         3160.000000
Imaging                          37000.000000
Microlensing                      2900.000000
Orbital Brightness Modulation        0.811610
Pulsar Timing                       45.901950
Pulsation Timing Variations       1005.000000
Radial Velocity                    348.360000
Transit                              8.484808
Transit Timing Variations           22.264920
Name: pl_orbper, dtype: float64

Farklı tekniklerin farklı yörünge karakteristiklerine sahip ötegezegenleri keşfetmeye daha duyarlı oldukları, bu anlamda birbirlerinin keşif yanlılıklarını dengeleme kapasiteleri görülebilmektedir. Örneğin geçiş yöntemi yörünge dönemi küçük ($P_{orb,medyan} \sim 8.49$ gün) gezegenleri keşfetmeye yanlı iken doğrudan görüntüleme tekniğinin yanlılığı tam ters yöndedir ($P_{orb,medyan} \sim 37000$ gün). Her ne kadar doğrudan görüntüleme yöntemiyle daha az gezegen keşfediliyor olsa da geçiş yönteminin üzerinden çıkarımlar yapılmak istenen ötegezegenler popülasyonunu dengelemesi açısından önemi açıktır.

DataFrameGroupBy nesnesi tıpkı liste (list), demet değişken (tuple), sözlük (dictionary) ya da numpy dizileri (numpy.array) gibi üzerinde iteratif işlemler uygulanabilen bir nesnedir.

In [81]:
for (teknik, grup) in og.groupby('discoverymethod'):
    print("{0:30s} shape={1}".format(teknik, grup.shape))
Astrometry                     shape=(3, 83)
Disk Kinematics                shape=(1, 83)
Eclipse Timing Variations      shape=(17, 83)
Imaging                        shape=(68, 83)
Microlensing                   shape=(210, 83)
Orbital Brightness Modulation  shape=(9, 83)
Pulsar Timing                  shape=(7, 83)
Pulsation Timing Variations    shape=(2, 83)
Radial Velocity                shape=(1088, 83)
Transit                        shape=(4168, 83)
Transit Timing Variations      shape=(29, 83)
In [82]:
og['discoverymethod'].value_counts()
Out[82]:
Transit                          4168
Radial Velocity                  1088
Microlensing                      210
Imaging                            68
Transit Timing Variations          29
Eclipse Timing Variations          17
Orbital Brightness Modulation       9
Pulsar Timing                       7
Astrometry                          3
Pulsation Timing Variations         2
Disk Kinematics                     1
Name: discoverymethod, dtype: int64

Görüldüğü üzere her bir teknikten kaçar gezegen keşfedildiği ve her gruba karşılık gelen teknikle keşfedilen gezegenlerin kaç sütunda (71) parametrelerinin bulunduğu tek tek ekrana getirilebilmektedir. Her bir tekniğin en başarıyla uygulandığı gözlemevi ve teleskoplara ilişkin istatistiki bilgi edinmek için aşağıdaki gibi bir gruplama ifadesi kullanılabilir.

In [83]:
og.groupby('discoverymethod')['disc_facility'].describe()
Out[83]:
count unique top freq
discoverymethod
Astrometry 3 3 Paranal Observatory 1
Disk Kinematics 1 1 Atacama Large Millimeter Array (ALMA) 1
Eclipse Timing Variations 17 6 Multiple Observatories 9
Imaging 68 17 Paranal Observatory 20
Microlensing 210 6 OGLE 90
Orbital Brightness Modulation 9 1 Kepler 9
Pulsar Timing 7 4 Arecibo Observatory 3
Pulsation Timing Variations 2 2 Kepler 1
Radial Velocity 1088 29 La Silla Observatory 280
Transit 4168 34 Kepler 2746
Transit Timing Variations 29 4 Kepler 21

Her bir yöntemle keşif yapan gözlemevi ya da teleskopların sayısı, en çok keşif yapan gözlemevi / teleskoplar ve kaçar keşif yaptıkları gibi bilgilere describe() metoduyla ulaşılır. Bu istatistikler için kullanılan gözlemevi / teleskop bilgisi içeren disc_facility sütunu sayısal olmayan kategorik veri içerdiği gerekçesiyle elde edilen istatistiksel parametreler de ($count$, $unique$, $top$, $freq$) kategorik veriler için geçerli istatistiki parametrelerdir. Daha önceki örneklerden sayısal veri içeren sütunlar söz konusu olduğunda bu istatistiklerin de farklı olduğunu bilyorsunuz. Aşağıdaki örnekte maksimum yörünge büyüklüğü Astronomi Birimi'nde farklı keşif tekniği grupları için farklı istatistikler dahilinde sunulmaktadır. unstack metodu her bir istatistiği bir diğerinden ayırarak keşif gruplarına birer ikincil indeks haline getirmektedir.

In [84]:
og.groupby('discoverymethod')['pl_orbsmax'].describe().unstack()
Out[84]:
       discoverymethod            
count  Astrometry                       3.000
       Disk Kinematics                  1.000
       Eclipse Timing Variations       14.000
       Imaging                         67.000
       Microlensing                   207.000
                                       ...   
max    Pulsar Timing                   23.000
       Pulsation Timing Variations      1.700
       Radial Velocity                 36.984
       Transit                          4.500
       Transit Timing Variations        4.200
Length: 88, dtype: float64

Gördüğünüz tüm bu gruplama işlemlerinde sırasıyla gruplara ayırma (ing. split), işlem yapma (ing. apply) ve birleştirme (ing. combine) işlemleri DataFrameGroupBy nesneleri üzerinde tanımlı groupby() fonksiyonuyla sırasıyla gerçekeştirilmektedir. DataFrameGroupBy nesnneleri üzerinde birleştirme (combine) işleminin yanı sıra filtreleme (filter()), dönüştürme (transform()) ve fonksiyon uygulama (apply()) işlemleri de tanımlıdır.

aggregatge Metodu

DataFrameGroupBy nesneleri üzerinde tanımlı bu işlemlerin nasıl çalıştığını anlamaya aggregate() metoduyla başlayalım. Bu metod istenen liste, metin (fonksiyon adı) ya da sözlük gibi bir yapıda gönderilen tüm fonksiyonları ilgili gruplara uygular.

NASA Exoplanet Archive ötegezegenler veritabanındaki ötegezegenleri keşif yöntemlerine göre gruplandırıp bu kez bazı fiziksel özelliklerinin (kütle, yarıçap, yoğunluk) minimum, medyan ve maksimum değerlerine bakalım. Fonksiyonun gruplandırma dahilinde birden fazla fonksiyonu, birden fazla sütuna aynı anda uygulayabildiğine ilişkin bu örnek aşağıdaki ifadeyle verilebilir.

In [85]:
import numpy as np
og.groupby('discoverymethod')[['pl_bmassj','pl_radj']].aggregate(['min', 'median', 'max'])
Out[85]:
pl_bmassj pl_radj
min median max min median max
discoverymethod
Astrometry 2.26000 5.00000 28.500 1.060 1.1500 1.19
Disk Kinematics 2.50000 2.50000 2.500 1.180 1.1800 1.18
Eclipse Timing Variations 1.61000 5.90000 23.700 1.070 1.1400 1.21
Imaging 2.00000 11.72900 30.000 0.934 1.1200 6.90
Microlensing 0.00302 0.55000 29.000 0.089 1.1100 1.28
Orbital Brightness Modulation 0.00140 1.25000 2.100 0.068 1.1900 1.36
Pulsar Timing 0.00006 0.01353 2.500 0.030 0.1655 1.24
Pulsation Timing Variations 3.20000 7.50000 11.800 1.100 1.1350 1.17
Radial Velocity 0.00220 1.05650 29.365 0.081 1.1200 1.39
Transit 0.00012 0.02070 282.000 0.028 0.2125 2.10
Transit Timing Variations 0.00135 0.14334 22.000 0.071 0.3280 1.25

filter Metodu

DataFrameGroupBy nesneleri üzerinde tanımlı filter() metoduyla yapılan filtreleme işlemi, grup özelliklerine göre veri atmanıza olanak sağlar.

Aşağıdaki örneği inceleyelerek filter() metodunun nasıl çalıştığını anlamaya çalışalım.

In [86]:
import numpy as np
rng = np.random.RandomState(0)
df = pd.DataFrame({'kategori': ['A', 'B', 'C', 'A', 'B', 'C'],
                   'veri1': range(6),
                   'veri2': rng.randint(0, 10, 6)},
                  columns = ['kategori', 'veri1', 'veri2'])
print(df)
  kategori  veri1  veri2
0        A      0      5
1        B      1      0
2        C      2      3
3        A      3      3
4        B      4      7
5        C      5      9

Örnek olarak bu veri çerçevesinde olup ikinci veri setinde ($veri2$) standart sapması 4'ten büyük olan ölçümleri filtreleyebiliriz. Bunun için öncelikle bir filtre fonksiyonu tanımlayalım.

In [87]:
filtre_fonksiyonu = lambda x: x['veri2'].std() < 4
print(df.groupby('kategori').filter(filtre_fonksiyonu))
  kategori  veri1  veri2
0        A      0      5
3        A      3      3

Görüldüğü gibi standart sapması 4'ten küçük olan tek grup A kategorisindeki ölçümlerdir. Tüm standart sapma değerlerine bakarak bunu doğrulayabiliriz. Filtre fonksiyonu (filtering function) bir grubun (burada $A$, $B$ ve $C$ kategorileri) sağlaması beklenen şartı tanımladığı için mutlaka Boolean ($True$ ya da $False$) döndüren bir fonksiyon olmaldıır. filter fonksiyonu bu şartı sağlayan grupları alır, sağlamayanları eler.

In [88]:
print(df.groupby('kategori').std())
            veri1     veri2
kategori                   
A         2.12132  1.414214
B         2.12132  4.949747
C         2.12132  4.242641

Şimdi bu örnekle öğrendiklerimizi yine ötegezegenler ($og$) veriçerçevemize uygulayarak pekiştirelim. Veriçerçevimizi gruplama için yine discoverymethod sütununda verilen keşif yöntemini kullanalım. Sonrasında filtrelemeyi keşfettiği gezegenlerin yörüngelerinin ortalama dış merkezliliği 0.25'ten büyük olan tekniklerle ($e_{ort} > 0.25$) keşfedilen gezegenleri göstermek üzere yapalım. Bu gezegenlerin de tüm sütunlardaki değerlerini değil sadece keşif yöntemi (discoverymethod), yörünge dış merkezliliği (pl_orbeccen), yörünge dönemi (pl_orbper) ve yörünge büyüklüğünü (pl_orbsmax) veren sütunları gösterelim.

In [89]:
filtre_fonksiyonu = lambda x: x['pl_orbeccen'].mean() > 0.25
print(og.groupby('discoverymethod').\
filter(filtre_fonksiyonu)[['discoverymethod','pl_orbeccen','pl_orbper','pl_orbsmax']].\
groupby('discoverymethod').mean())
                 pl_orbeccen     pl_orbper  pl_orbsmax
discoverymethod                                       
Astrometry          0.547667  2.805500e+02    0.599550
Imaging             0.393474  2.026245e+07  474.523254

Görüldüğü gibi $e_{ort} > 0.25$ koşulunu sağlayan iki yöntem doğrudan görüntüleme (ing. Imaging) ve astrometri yöntemleridir. Ekrana bu yöntemlerle keşfedilen gezegenlerin istenen parametreleri, DataFrameGroupBy nesnesi üzerinde tanımlı filter() fonksiyonunun sonucu bir veriçerçevesi türünde nesne olduğu için getirilebilmiştir.

Sonuç olarak filter() fonksiyonu uygulandığı veriçerçevesinin bir şekilde sınırlandırılmış (kısaltılmıış) bir versiyonunu döndürür.

trasformation Metodu

Filtreleme verilen veri gruplarını bir filtre fonksiyonuna tabi tutup, bu filtre fonksiyonuyla belirlenen şartı sağlayanlarını alır ve gerisini filtreleyip elerken, transformation() metodu grubu ilgili fonksiyon çerçevesinde dönüştürür.

Örneğin bir veri setindeki her bir veriden o verinin dahil olduğu grubun (örneğin $A$ kategorisinin) ortalamasını çıkararak ilgili sütun ve satıra yazarak veriçerçevesini dönüştürmek (transform etmek) için aşağıdaki gibi bir ifade kullanılabilir. Yine bu işlemin sonucunun yeni bir veriçerçevesi tanımladığını ve saklanmak isteniyorsa bu veriçerçevesine bir isim verilmesi gerektiğini unutmayınız.

In [90]:
import numpy as np
rng = np.random.RandomState(0)
df = pd.DataFrame({'kategori': ['A', 'B', 'C', 'A', 'B', 'C'],
                   'veri1': range(6),
                   'veri2': rng.randint(0, 10, 6)},
                  columns = ['kategori', 'veri1', 'veri2'])
print(df)
  kategori  veri1  veri2
0        A      0      5
1        B      1      0
2        C      2      3
3        A      3      3
4        B      4      7
5        C      5      9
In [91]:
donusum_fonksiyonu = lambda x: x - x.mean()
df.groupby('kategori').transform(donusum_fonksiyonu)
Out[91]:
veri1 veri2
0 -1.5 1.0
1 -1.5 -3.5
2 -1.5 -3.0
3 1.5 -1.0
4 1.5 3.5
5 1.5 3.0

Yöntemi öntegezegen veritabanı üzerinde, her bir yöntemin keşfettiği gezegenlerin parametrelerini o yöntemle bu parametre için keşfedilen gezegenlerin ortalama değerine normalize ederek (bölerek) vermek için kullanalım ve sonucu yörünge dönemi (pl_orbper) için görüntüleyelim.

In [92]:
def donusum_fonksiyonu(x):
    return x / x.mean()
og.groupby('discoverymethod')[['pl_orbeccen','pl_orbper','pl_bmassj']].transform(donusum_fonksiyonu)['pl_orbper']
Out[92]:
pl_name
11 Com b      0.179790
11 UMi b      0.287154
14 And b      0.103888
14 Her b      0.981825
16 Cyg B b    0.444176
                ...   
ups And b     0.002568
ups And c     0.134203
ups And d     0.710047
ups Leo b     0.214272
xi Aql b      0.076191
Name: pl_orbper, Length: 5602, dtype: float64

apply Metodu

apply() metodu herhangi bir fonksiyonun bir DataFrameGroupBy nesnesine uygulanmasını sağlar. Her bir grubun birinci veri setindeki ölçümlerini ($veri1$) ikinci veri setindeki ölçümlerin toplamına ($veri2$) normalize etmek istiyor olalım. Aşağıdaki $normalizasyon$ fonksiyonunun df.groupby('kategori') ifadesi ile elde edilen DataFrameGroupBy nesnesine uygulanması sonucu $veri1$ sütunundaki veri seti her bir grup için o grubun $veri2$ veri setindeki ölçümlerin toplamına bölünmesi (normalize edilmesi) sağlanmıştır.

In [93]:
def normalizasyon(x):
    x['veri1'] /= x['veri2'].sum()
    return(x)
print(df.groupby('kategori').apply(normalizasyon))
  kategori     veri1  veri2
0        A  0.000000      5
1        B  0.142857      0
2        C  0.166667      3
3        A  0.375000      3
4        B  0.571429      7
5        C  0.416667      9

apply() fonksiyonu oldukça kullanışlı bir fonksiyon olup, herhangi bir fonksiyonun sadece DataFrameGroupBy nesnelerine değil, istenen tüm veriçerçevelerine uygulanmasını sağlar. Sonuç istenirse bir pandas veri nesnesi istenirse de skaler bir değer olabilir.

Bir önceki bölümde transformation() metoduyla bütun sütunlara uyguladığımız normalizasyonu, sadece yörünge dönemleri bilinen tüm gezegenlerin yörünge dönemlerinin, keşfedildikleri yöntemle keşfedilen tüm gezegenlerin yörünge dönemi ortalamasına oranını hesaplamak üzere kullanabiliriz.

In [94]:
def normalizedonem(x):
    return x['pl_orbper'] / x['pl_orbper'].mean()
og.groupby('discoverymethod').apply(normalizedonem)
Out[94]:
discoverymethod            pl_name                   
Astrometry                 DENIS-P J082303.1-491201 b    0.878132
                           GJ 896 A b                    1.013687
                           HIP 66074 b                   1.108180
Disk Kinematics            HD 97048 b                         NaN
Eclipse Timing Variations  2MASS J19383260+4603591 b     0.115566
                                                           ...   
Transit Timing Variations  Kepler-82 f                   0.376320
                           TOI-199 c                     1.359993
                           TOI-2202 c                    0.122610
                           WASP-126 c                    0.037914
                           WASP-18 c                     0.010712
Name: pl_orbper, Length: 5602, dtype: float64

Kaynaklar