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
Verileri pandas
paketiyle işlemeye başlamadan önce, veriyi hazırlamak ve veri yapıları oluşturacak şekilde birleştirmek gerekir. 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.
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ş dersimizde görmüştük. Ayrıca dosyalardan veriyi pandas.read_csv
fonksiyonuyla okuyarak veriçerçevelerine yüklemeyi de örneklendirmiştik. 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.
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.
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
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örebileceğiniz 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
fonksiyonunu herhangi bir sütunu belirtmeden kullandık. Çoğu zaman entegrasyonu hangi sütun üzerinden yapacağımızı belirlememiz 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.
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çevesiin 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.
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
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 |
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.
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
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 |
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.
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")
pd.merge(yldz_mV_sptype,yldz_par_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 |
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.
print("Tayf Turu Uzerinden Baglanmis Vericercevesi")
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='sptype')
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 |
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.
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
pd.merge(yldz_mV_sptype, yldz_par_sptype, left_on='yildiz', right_on='id')
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 |
pd.merge(yldz_mV_sptype, yldz_par_sptype, left_on='tayfturu', right_on='sptype')
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 bir SQL diliyle "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.
yldz_mV_sptype.columns = ['id','sptype','mV']
yldz_mV_sptype
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 |
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id')
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 |
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how="outer")
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.
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how="left")
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.
pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how="right")
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.
pd.merge(yldz_mV_sptype,yldz_par_sptype, on=['id','sptype'], how="outer")
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 |
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
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 |
pd.merge(yldz_mV_sptype,yldz_d_sptype,on=['id','sptype'], how="outer")
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 |
pd.merge(yldz_mV_sptype,yldz_d_sptype,on=['id','sptype'], how="inner")
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 |
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.
pd.merge(yldz_mV_sptype,yldz_par_sptype, how="inner", right_index=True, left_index=True)
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 |
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.
yldz_mV_sptype.columns = ['yldiz','tayfturu','V']
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.
yldz_mV_sptype.join(yldz_par_sptype, how="inner")
yldiz | 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.
yldz_mV_sptype.columns = ['id','sptype','mV']
yldz_mV_sptype.join(yldz_par_sptype, rsuffix="_sag", lsuffix="_sol", how="outer")
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 |
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.
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.
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.452209 2 0.254997 3 0.021037 4 0.303095 5 0.752578 6 0.139055 7 0.391757 8 0.210496 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.
print(pd.concat([seri1,seri2],axis = 1))
0 1 1 0.452209 NaN 2 0.254997 NaN 3 0.021037 NaN 4 0.303095 NaN 5 NaN 0.752578 6 NaN 0.139055 7 NaN 0.391757 8 NaN 0.210496
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.
print(pd.concat([seri1,seri1],axis = 1, join='inner'))
0 1 1 0.452209 0.452209 2 0.254997 0.254997 3 0.021037 0.021037 4 0.303095 0.303095
Ucuca eklenen seri ya da veriçerçevelerinin ucuca eklendikleri yerleri belirlemek üzere keys
parametresi ile hiyerarşik bir indeksleme yapılabilir.
print(pd.concat([seri1,seri2], keys=[1,2]))
1 1 0.452209 2 0.254997 3 0.021037 4 0.303095 2 5 0.752578 6 0.139055 7 0.391757 8 0.210496 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.
pd.concat([seri1,seri2], axis = 1, keys=['A','B'])
A | B | |
---|---|---|
1 | 0.452209 | NaN |
2 | 0.254997 | NaN |
3 | 0.021037 | NaN |
4 | 0.303095 | NaN |
5 | NaN | 0.752578 |
6 | NaN | 0.139055 |
7 | NaN | 0.391757 |
8 | NaN | 0.210496 |
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.
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])
A | B | C | ||
---|---|---|---|---|
1 | 1 | 0.030857 | 0.051200 | 0.948841 |
2 | 0.027254 | 0.847349 | 0.069779 | |
3 | 0.578225 | 0.715439 | 0.934747 | |
2 | 4 | 0.760747 | 0.891984 | 0.358125 |
5 | 0.281105 | 0.824692 | 0.946473 | |
6 | 0.969698 | 0.451519 | 0.304067 |
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.
pd.concat([df1, df2], axis=1)
A | B | C | A | B | C | |
---|---|---|---|---|---|---|
1 | 0.030857 | 0.051200 | 0.948841 | NaN | NaN | NaN |
2 | 0.027254 | 0.847349 | 0.069779 | NaN | NaN | NaN |
3 | 0.578225 | 0.715439 | 0.934747 | NaN | NaN | NaN |
4 | NaN | NaN | NaN | 0.760747 | 0.891984 | 0.358125 |
5 | NaN | NaN | NaN | 0.281105 | 0.824692 | 0.946473 |
6 | NaN | NaN | NaN | 0.969698 | 0.451519 | 0.304067 |
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.
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'])
A | B | C | ||
---|---|---|---|---|
X | 1 | 0.866158 | 0.992807 | 0.927414 |
2 | 0.405679 | 0.177814 | 0.158940 | |
3 | 0.219667 | 0.338554 | 0.974884 | |
Y | 1 | 0.058511 | 0.870601 | 0.906014 |
2 | 0.743442 | 0.848941 | 0.051455 | |
3 | 0.854454 | 0.996620 | 0.499426 |
Satırda ucuca eklemede ortak sütun isimleri korunarak, 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.
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.
pd.concat([df1, df2])
A | B | C | |
---|---|---|---|
1 | 0.866158 | 0.992807 | 0.927414 |
2 | 0.405679 | 0.177814 | 0.158940 |
3 | 0.219667 | 0.338554 | 0.974884 |
1 | 0.058511 | 0.870601 | 0.906014 |
2 | 0.743442 | 0.848941 | 0.051455 |
3 | 0.854454 | 0.996620 | 0.499426 |
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:
pd.concat([df1, df2], ignore_index=True)
A | B | C | |
---|---|---|---|
0 | 0.866158 | 0.992807 | 0.927414 |
1 | 0.405679 | 0.177814 | 0.158940 |
2 | 0.219667 | 0.338554 | 0.974884 |
3 | 0.058511 | 0.870601 | 0.906014 |
4 | 0.743442 | 0.848941 | 0.051455 |
5 | 0.854454 | 0.996620 | 0.499426 |
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.
pd.concat([df1, df2], axis=1)
A | B | C | A | B | C | |
---|---|---|---|---|---|---|
1 | 0.866158 | 0.992807 | 0.927414 | 0.058511 | 0.870601 | 0.906014 |
2 | 0.405679 | 0.177814 | 0.158940 | 0.743442 | 0.848941 | 0.051455 |
3 | 0.219667 | 0.338554 | 0.974884 | 0.854454 | 0.996620 | 0.499426 |
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
).
pd.concat([df1, df2], axis=1, ignore_index=True)
0 | 1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|---|
1 | 0.866158 | 0.992807 | 0.927414 | 0.058511 | 0.870601 | 0.906014 |
2 | 0.405679 | 0.177814 | 0.158940 | 0.743442 | 0.848941 | 0.051455 |
3 | 0.219667 | 0.338554 | 0.974884 | 0.854454 | 0.996620 | 0.499426 |
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.
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]
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.
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
Her ne kadar efektif bir yöntem olmadığı için çok sık kullanılmasa da veri birleştirmek için pandas
veriçerçevesi ve serileri üzerinde tanımlı bir de append
metodu bulunmaktadır. Bu metod da üzerine uygulandığı veri nesnesini değiştirmek yerine yeni bir veri nesnesi oluşturur. Dolayısıyla sonuç saklanmak isteniyorsa istenen isimde yeni bir veri nesnesine atanmalıdır.
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'])
df3 = df1.append(df2)
print(df3)
A B C 1 0.947124 0.314900 0.536996 2 0.863446 0.508219 0.011356 3 0.291395 0.093490 0.364441 4 0.039078 0.254814 0.573366 5 0.457333 0.641519 0.668856 6 0.629089 0.939781 0.361068
<ipython-input-37-c35bac3a50e8>:3: FutureWarning: The frame.append method is deprecated and will be removed from pandas in a future version. Use pandas.concat instead. df3 = df1.append(df2)
Bir veriçerçevesini (ya da seriyi) diğerinin ucuna eklerken çakışan indeksleri bir kenara bırakıp çakışmayanları ikinciyi birincinin sonuna eklemek üzere birinci nesnenin üzerinde combine_first()
fonksiyonu tanımlanmıştır. Örneklerle nasıl çalıştığını anlayalım.
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.923485 2 0.279921 3 0.660125 4 0.275568 5 0.956989 dtype: float64 Seri 2: 2 0.431431 4 0.801527 5 0.272681 6 0.112464 dtype: float64 seri1.combine_first(seri2)
1 0.923485 2 0.279921 3 0.660125 4 0.275568 5 0.956989 6 0.112464 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.
print("seri2.combine_first(seri1)")
seri2.combine_first(seri1)
seri2.combine_first(seri1)
1 0.923485 2 0.431431 3 0.660125 4 0.801527 5 0.272681 6 0.112464 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.
print(seri1[:3].combine_first(seri2[:3]))
1 0.923485 2 0.279921 3 0.660125 4 0.801527 5 0.272681 dtype: float64
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), satırlar da sütunlara dönüştürülebilir (unstacking). Konuyu bir örnekle anlamaya çalışalım.
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.
df.stack()
ogr1 odev 65 vize 45 final 70 ogr2 odev 80 vize 74 final 90 ogr3 odev 44 vize 37 final 62 dtype: int64
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.
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.
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.
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.
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.
tablo2 = tablo1.pivot('iyon','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 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.
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.
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ım 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.
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
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.
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()
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.
cizgi_listesi['iyon'].duplicated()
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.
cizgi_listesi[cizgi_listesi['iyon'].duplicated()]
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.
cizgi_listesi.drop_duplicates('iyon')
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.
cizgi_listesi.drop_duplicates('iyon', keep='last')
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.
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.
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.
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
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 |
listeadi = {'Custom':'User'}
cizgi_listesi.replace(listeadi, inplace=True)
cizgi_listesi
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.
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
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.
liste_referanslari = {'VALD':'Ryabchikova (1997)', 'NIST':'NIST ASD Team (2019)', 'User':'this study'}
cizgi_listesi['referanslar'] = cizgi_listesi['liste'].map(liste_referanslari)
cizgi_listesi
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 |
İ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.
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'}
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.
sutunlar = {'dalgaboyu':'wavelength',
'iyon':'ion',
'liste':'linelist',
'referanslar':'reference'}
cizgi_listesi.rename(columns=sutunlar)
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.
Serilerde birden, veriçerçevelerinde ikiden fazla boyutun istenmesi durumunda iyi bir çözüm çoklu indeksleme (ing. Multi-indexing) kullanmaktır.
df = pd.DataFrame(np.random.rand(4, 2),
index=[['x', 'x', 'y', 'y'], [0, 1, 0, 1]],
columns=['veri1', 'veri2'])
df
veri1 | veri2 | ||
---|---|---|---|
x | 0 | 0.592923 | 0.153785 |
1 | 0.105717 | 0.006569 | |
y | 0 | 0.542591 | 0.801150 |
1 | 0.316408 | 0.459188 |
Ç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.
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
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.
df_2D = df.unstack()
df_2D
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.
df_2D.stack()
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.
df.index.names = ["gezegen","teleskop"]
df
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.
df.index
MultiIndex([('HAT-P-36b', 'T100'), ('HAT-P-36b', 'T80'), ( 'XO-3b', 'T100'), ( 'XO-3b', 'T80'), ( 'KELT-16b', 'T100'), ( 'KELT-16b', 'T80')], names=['gezegen', 'teleskop'])
df.loc["HAT-P-36b"]
derinlikler | |
---|---|
teleskop | |
T100 | 0.0206 |
T80 | 0.0220 |
df.loc["HAT-P-36b","T80"]
derinlikler 0.022 Name: (HAT-P-36b, T80), dtype: float64
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.
import pandas as pd
import numpy as np
og = pd.read_csv("veri/PSCompPars_2022.03.22_01.51.21.csv", comment="#", skipinitialspace=True,
index_col="pl_name")
og.head()
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 | 326.03000 | 0.32 | -0.32 | ... | -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.20 | -3.20 | ... | -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 | 185.84000 | 0.23 | -0.23 | ... | -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 | 1773.40002 | 2.50 | -2.50 | ... | -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.00 | -1.00 | ... | -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.
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.07 (+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.
og[['pl_bmassj','pl_radj']].dropna().describe()
pl_bmassj | pl_radj | |
---|---|---|
count | 4971.000000 | 4971.000000 |
mean | 1.357814 | 0.488845 |
std | 12.299134 | 0.472060 |
min | 0.000060 | 0.026000 |
25% | 0.012000 | 0.154000 |
50% | 0.025600 | 0.239000 |
75% | 0.404500 | 0.970500 |
max | 752.000000 | 6.900000 |
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.
og.columns
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.
og.groupby("discoverymethod")
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7f770efef7f0>
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.
og.groupby("discoverymethod").count()
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 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | ... | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
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 | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 15 | 15 | 16 | ... | 15 | 15 | 15 | 15 | 14 | 14 | 14 | 15 | 15 | 15 |
Imaging | 58 | 58 | 58 | 58 | 58 | 58 | 14 | 10 | 10 | 14 | ... | 57 | 48 | 48 | 47 | 56 | 56 | 55 | 53 | 53 | 53 |
Microlensing | 129 | 129 | 129 | 129 | 129 | 129 | 9 | 7 | 7 | 9 | ... | 125 | 12 | 10 | 9 | 13 | 12 | 12 | 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 | 915 | 915 | 915 | 915 | 915 | 915 | 915 | 911 | 911 | 915 | ... | 914 | 915 | 912 | 911 | 913 | 913 | 904 | 904 | 903 | 903 |
Transit | 3845 | 3845 | 3845 | 3845 | 3845 | 3845 | 3843 | 3382 | 3382 | 3843 | ... | 3737 | 3843 | 3840 | 3839 | 3840 | 3814 | 3812 | 3813 | 3812 | 3812 |
Transit Timing Variations | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 10 | 10 | 22 | ... | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 | 22 |
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.
og.groupby("discoverymethod")['pl_orbeccen'].mean()
discoverymethod Astrometry 0.345000 Disk Kinematics NaN Eclipse Timing Variations 0.152879 Imaging 0.387500 Microlensing 0.075000 Orbital Brightness Modulation 0.000000 Pulsar Timing 0.057117 Pulsation Timing Variations 0.075000 Radial Velocity 0.217448 Transit 0.025776 Transit Timing Variations 0.083486 Name: pl_orbeccen, dtype: float64
Görüldüğü üzere geçiş ($e_{ort} \sim 0.026$), pulsar zamanlaması ($e_{ort} \sim 0.057$), pulsasyon frekansı değişimi ($e_{ort} \sim 0.075$), kütleçekimsel mercek yöntemi ($e_{ort} \sim 0.075$) gibi yöntemlerde yörünge dış merkezliliği küçük gezegenler keşfedilirken; astrometri ($e_{ort} \sim 0.345$), doğrudan görüntüleme ($e_{ort} \sim 0.388$), dikine hız ($e_{ort} \sim 0.217$) 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.
og.groupby("discoverymethod")['pl_orbper'].median()
discoverymethod Astrometry 246.360000 Disk Kinematics NaN Eclipse Timing Variations 3661.550000 Imaging 45250.000000 Microlensing 2557.000000 Orbital Brightness Modulation 0.811610 Pulsar Timing 45.901950 Pulsation Timing Variations 1005.000000 Radial Velocity 325.803000 Transit 8.689307 Transit Timing Variations 20.308700 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.69$ 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 45250$ 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.
for (teknik, grup) in og.groupby('discoverymethod'):
print("{0:30s} shape={1}".format(teknik, grup.shape))
Astrometry shape=(1, 83) Disk Kinematics shape=(1, 83) Eclipse Timing Variations shape=(16, 83) Imaging shape=(58, 83) Microlensing shape=(129, 83) Orbital Brightness Modulation shape=(9, 83) Pulsar Timing shape=(7, 83) Pulsation Timing Variations shape=(2, 83) Radial Velocity shape=(915, 83) Transit shape=(3845, 83) Transit Timing Variations shape=(22, 83)
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.
og.groupby('discoverymethod')['disc_facility'].describe()
count | unique | top | freq | |
---|---|---|---|---|
discoverymethod | ||||
Astrometry | 1 | 1 | Paranal Observatory | 1 |
Disk Kinematics | 1 | 1 | Atacama Large Millimeter Array (ALMA) | 1 |
Eclipse Timing Variations | 16 | 7 | Multiple Observatories | 7 |
Imaging | 58 | 16 | Paranal Observatory | 17 |
Microlensing | 129 | 5 | OGLE | 71 |
Orbital Brightness Modulation | 9 | 1 | Kepler | 9 |
Pulsar Timing | 7 | 4 | Arecibo Observatory | 3 |
Pulsation Timing Variations | 2 | 2 | Kepler | 1 |
Radial Velocity | 915 | 27 | La Silla Observatory | 259 |
Transit | 3845 | 29 | Kepler | 2679 |
Transit Timing Variations | 22 | 4 | Kepler | 18 |
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 kullınal 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.
og.groupby('discoverymethod')['pl_orbsmax'].describe().unstack()
discoverymethod count Astrometry 1.0 Disk Kinematics 1.0 Eclipse Timing Variations 15.0 Imaging 56.0 Microlensing 127.0 ... max Pulsar Timing 23.0 Pulsation Timing Variations 1.7 Radial Velocity 23.0 Transit 4.5 Transit Timing Variations 4.2 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
nesnneleri ü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.
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.
import numpy as np
og.groupby('discoverymethod')[['pl_bmassj','pl_radj']].aggregate(['min', np.median, max])
pl_bmassj | pl_radj | |||||
---|---|---|---|---|---|---|
min | median | max | min | median | max | |
discoverymethod | ||||||
Astrometry | 28.50000 | 28.50000 | 28.50 | 1.060 | 1.0600 | 1.060 |
Disk Kinematics | 2.50000 | 2.50000 | 2.50 | 1.180 | 1.1800 | 1.180 |
Eclipse Timing Variations | 1.90000 | 6.29000 | 23.70 | 1.070 | 1.1400 | 1.200 |
Imaging | 2.00000 | 11.25000 | 752.00 | 0.920 | 1.1300 | 6.900 |
Microlensing | 0.00450 | 0.64000 | 18.00 | 0.099 | 1.1500 | 1.280 |
Orbital Brightness Modulation | 0.00140 | 1.25000 | 2.10 | 0.068 | 1.1900 | 1.360 |
Pulsar Timing | 0.00006 | 0.01353 | 2.50 | 0.030 | 0.1655 | 1.240 |
Pulsation Timing Variations | 3.20000 | 7.50000 | 11.80 | 1.100 | 1.1350 | 1.170 |
Radial Velocity | 0.00220 | 1.05300 | 55.59 | 0.081 | 1.1400 | 1.390 |
Transit | 0.00012 | 0.02000 | 144.00 | 0.026 | 0.2100 | 2.085 |
Transit Timing Variations | 0.00009 | 0.10350 | 22.00 | 0.108 | 0.2810 | 1.210 |
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.
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.
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.
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.
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.3450 246.36 0.360000 Imaging 0.3875 29968145.90 553.467857
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.
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.
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
donusum_fonksiyonu = lambda x: x - x.mean()
df.groupby('kategori').transform(donusum_fonksiyonu)
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.
def donusum_fonksiyonu(x):
return x / x.mean()
og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper']
<ipython-input-87-ccc8aaf5d6c3>:3: FutureWarning: Dropping invalid columns in DataFrameGroupBy.transform is deprecated. In a future version, a TypeError will be raised. Before calling .transform, select only columns which should be valid for the function. og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper'] <ipython-input-87-ccc8aaf5d6c3>:3: FutureWarning: Dropping invalid columns in DataFrameGroupBy.transform is deprecated. In a future version, a TypeError will be raised. Before calling .transform, select only columns which should be valid for the function. og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper'] <ipython-input-87-ccc8aaf5d6c3>:3: FutureWarning: Dropping invalid columns in DataFrameGroupBy.transform is deprecated. In a future version, a TypeError will be raised. Before calling .transform, select only columns which should be valid for the function. og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper'] <ipython-input-87-ccc8aaf5d6c3>:3: FutureWarning: Dropping invalid columns in DataFrameGroupBy.transform is deprecated. In a future version, a TypeError will be raised. Before calling .transform, select only columns which should be valid for the function. og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper'] <ipython-input-87-ccc8aaf5d6c3>:3: FutureWarning: Dropping invalid columns in DataFrameGroupBy.transform is deprecated. In a future version, a TypeError will be raised. Before calling .transform, select only columns which should be valid for the function. og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper'] <ipython-input-87-ccc8aaf5d6c3>:3: FutureWarning: Dropping invalid columns in DataFrameGroupBy.transform is deprecated. In a future version, a TypeError will be raised. Before calling .transform, select only columns which should be valid for the function. og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper'] <ipython-input-87-ccc8aaf5d6c3>:3: FutureWarning: Dropping invalid columns in DataFrameGroupBy.transform is deprecated. In a future version, a TypeError will be raised. Before calling .transform, select only columns which should be valid for the function. og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper']
pl_name 11 Com b 0.310318 11 UMi b 0.491342 14 And b 0.176884 14 Her b 1.687936 16 Cyg B b 0.760019 ... ups And b 0.004395 ups And c 0.229631 ups And d 1.214945 ups Leo b 0.366636 xi Aql b 0.130160 Name: pl_orbper, Length: 5005, dtype: float64
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.
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.
def normalizedonem(x):
return x['pl_orbper'] / x['pl_orbper'].mean()
og.groupby('discoverymethod').apply(normalizedonem)
discoverymethod pl_name Astrometry DENIS-P J082303.1-491201 b 1.000000 Disk Kinematics HD 97048 b NaN Eclipse Timing Variations 2MASS J19383260+4603591 b 0.105868 DE CVn b 1.042930 DP Leo b 2.600898 ... Transit Timing Variations Kepler-539 c 4.760590 Kepler-82 f 0.360529 TOI-2202 c 0.117465 WASP-126 c 0.036323 WASP-18 c 0.010263 Name: pl_orbper, Length: 5005, dtype: float64