{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 800100715151 Astronomide Veritabanları #\n",
"\n",
"## Ders - 04 Pandas Paketiyle Veri İşleme ##"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Doç. Dr. Özgür Baştürk
\n",
"Ankara Üniversitesi, Astronomi ve Uzay Bilimleri Bölümü
\n",
"obasturk at ankara.edu.tr
\n",
"http://ozgur.astrotux.org"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Bu derste neler öğreneceksiniz? #\n",
"## Pandas Paketi İleri Konular ##\n",
"\n",
"* [Pandas Paketiyle Veriyi İşlenmek Üzere Hazırlama](#Pandas-Paketiyle-Veriyi-İşlenmek-Üzere-Hazırlama)\n",
"* [Verinin Birbirine Bağlanması: Merging](#Verinin-Birbirine-Bağlanması:-Merging) \n",
" * [Veri Çerçevelerini Bağlama Türleri](#Veri-Çerçevelerini-Bağlama-Türleri)\n",
" * [Bire bir Bağlama](#Bire-bir-Bağlama)\n",
" * [Çoktan bire Bağlama](#Çoktan-bire-Bağlama)\n",
" * [Çoktan çoka Bağlama](#Çoktan-çoka-Bağlama)\n",
" * [Farklı Sütunlar Üzerinden Bağlama](#Farklı-Sütunlar-Üzerinden-Bağlama)\n",
" * [İndeks Üzerinden Bağlama İşlemleri](#İndeks-Üzerinden-Bağlama-İşlemleri)\n",
" * [join Metodu](#join-Metodu)\n",
"* [Veriçerçevesi ve Serileri Ucuca Ekleme: Concatenating](#Veriçerçevesi-ve-Serileri-Ucuca-Ekleme:-Concatenating)\n",
" * [Veriçerçevelerinin Ucuca Eklenmesi](#Veriçerçevelerinin-Ucuca-Eklenmesi)\n",
" * [Kesişim ve Bileşke Yöntemleriyle Ucuca Ekleme](#Kesişim-ve-Bileşke-Yöntemleriyle-Ucuca-Ekleme)\n",
" * [Append Metodu](#Append-Metodu)\n",
"* [Veri Çerçevelerini Birleştirme: Combining](#Veri-Çerçevelerini-Birleştirme:-Combining)\n",
"* [Sütun ve Satırlarda Döndürme: Pivoting](#Sütun-ve-Satırlarda-Döndürme:-Pivoting)\n",
" * [Uzun ve Geniş Tablolar: Longframe ve Wideframe](#Uzun-ve-Geniş-Tablolar:-Longframe-ve-Wideframe)\n",
"* [Veri çıkarma: Removing](#Veri-çıkarma:-Removing)\n",
" * [Tekrarlayan Satırların Atılması](#Tekrarlayan-Satırların-Atılması)\n",
"* [Eşleştirme](#Eşleştirme)\n",
" * [Eşleşenleri Değiştirme](#Eşleşenleri-Değiştirme)\n",
" * [Eşleştirme Yaparak Yeni Bir Sütun Ekleme](#Eşleştirme-Yaparak-Yeni-Bir-Sütun-Ekleme)\n",
" * [Eşleştirme Yaparak İndeks İsimlerini Değiştirme](#Eşleştirme-Yaparak-İndeks-İsimlerini-Değiştirme)\n",
"* [Çoklu İndeksleme](#Çoklu-İndeksleme)\n",
"* [Kaynaklar](#Kaynaklar)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Pandas Paketiyle Veriyi İşlenmek Üzere Hazırlama #\n",
"\n",
"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. \n",
"\n",
"* Veriyi Yükleme \n",
"* Veriyi Bir Araya Getirme
\n",
" • Bağlama (ing. merging)
\n",
" • Ucuca Ekleme (ing. concatenating)
\n",
" • Birleştirme (ing. combining)\n",
"* Veri Yapısını Yeniden Şekillendirme (ing. pivoting)\n",
"* Veri Çıkarma (ing. removing)\n",
"\n",
"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.\n",
"\n",
"`pandas` nesnelerinde tutulan veriler farklı şekillerde bir araya getirilebilirler:\n",
"\n",
"* 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.\n",
"\n",
"* Veriyi ucuca ekleme (ing. concatenating): `pandas.concat()` fonksiyonu veri nesnelerini (Series ve DataFrame) bir eksen boyunca ucuca ekler.\n",
"\n",
"* 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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Verinin Birbirine Bağlanması: Merging #\n",
"\n",
"SQL'e aşinalığı olanlar için [$JOIN$](https://blog.codinghorror.com/a-visual-explanation-of-sql-joins/) 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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"yldz_mV = pd.DataFrame(\\\n",
" {'id':['Arcturus','Deneb','Betelgeuse','Spica','Rigel','Merak', 'Vega', 'Altair','Bellatrix'],\\\n",
" 'mV': [-0.05,1.25,0.42,0.97,0.13,2.37,0.03,0.76,1.64]}) \n",
"yldz_par = pd.DataFrame(\\\n",
" {'id':['Arcturus','Betelgeuse','Rigel','Deneb','Vega','Fomalhaut','Polaris'],\\\n",
" 'par': [88.83,6.55,3.78,2.31,130.23,129.01,7.54]}) \n",
"print(\"Gorsel Parlakliklar:\")\n",
"print(yldz_mV)\n",
"print(\"Paralakslar:\")\n",
"print(yldz_par)\n",
"print(\"Baglanmis Vericercevesi\")\n",
"pd.merge(yldz_mV,yldz_par)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Veri Çerçevelerini Bağlama Türleri ##\n",
"\n",
"### Bire bir Bağlama ###\n",
"\n",
"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. \n",
"\n",
"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. \n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"gezegen_yorunge = pd.DataFrame(\\\n",
" {'id':['Merkur','Venus','Dunya','Mars','Jupiter','Saturn', 'Uranus', 'Neptun'],\\\n",
" 'e': [0.205, 0.007, 0.017, 0.094, 0.049, 0.057, 0.046, 0.011],\\\n",
" 'i [derece]': [7.0, 3.4, 0.0, 1.9, 1.3, 2.5, 0.8, 1.8],\\\n",
" 'P [gun]' : [88.0, 224.7, 365.2, 687.0, 4331, 10747, 30589, 59800],\\\n",
" 'psi [derece]': [0.034, 177.4, 23.4, 25.2, 3.1, 26.7, 97.8, 28.3]})\n",
"gezegen_fiziksel = pd.DataFrame(\\\n",
" {'id':['Merkur','Venus','Dunya','Mars','Jupiter','Saturn', 'Uranus', 'Neptun'],\\\n",
" 'Mp [x10^24 kg]': [0.330, 4.87, 5.97, 0.642, 1898, 568, 86.8, 102],\\\n",
" 'Rp [km]': [2439.5, 6052., 6378., 3396., 71492., 60268., 25559., 24764.],\\\n",
" 'rho [g/cm^3]': [5.427, 5.243, 5.514, 3.933, 1.326, 0.687, 1.271, 1.638],\\\n",
" 'Prot [saat]': [1407.6, -5832.5, 23.9, 24.6, 9.9, 10.7, -17.2, 16.1]})\n",
"print(\"Baglanmis Vericercevesi\")\n",
"pd.merge(gezegen_yorunge,gezegen_fiziksel, on='id')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Çoktan bire Bağlama ###\n",
"\n",
"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.\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"gezegen_fiziksel['tur'] = ['kayac', 'kayac', 'kayac', 'kayac', 'gaz', 'gaz', 'buz', 'buz']\n",
"gezegen_tur = pd.DataFrame(\\\n",
" {'tur' : ['kayac','gaz','buz'],\\\n",
" 'yer' : ['ic gezegen','dis gezegen','dis gezegen']})\n",
"print(\"Baglanmis Vericercevesi\")\n",
"pd.merge(gezegen_fiziksel,gezegen_tur, on=\"tur\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Çoktan çoka Bağlama ###\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yldz_mV_sptype = pd.DataFrame(\\\n",
" {'id':['Arcturus','Deneb','Betelgeuse','Spica','Rigel','Merak', 'Vega', 'Altair','Bellatrix'],\\\n",
" 'sptype':['K1.5 III', 'A2 Ia', 'M1 Iab', 'B1 V', 'B8 Ia','A0 V', 'A0 V', 'A7 V', 'B1 V'],\\\n",
" 'mV': [-0.05,1.25,0.42,0.97,0.13,2.37,0.03,0.76,1.64]})\n",
"yldz_par_sptype = pd.DataFrame(\\\n",
" {'id':['Arcturus','Betelgeuse','Rigel','Deneb','Vega','Fomalhaut','Polaris'],\\\n",
" 'sptype':['K1.5 III', 'M1 Iab', 'B8 Ia', 'A2 Ia', 'A0 V', 'A0 V', 'F8 Ib'],\\\n",
" 'par': [88.83,6.55,3.78,2.31,130.23,129.01,7.54]})\n",
"print(\"Gorsel Parlakliklar ve Tayf Turleri:\")\n",
"print(yldz_mV_sptype)\n",
"print(\"Paralakslar ve Tayf Turleri:\")\n",
"print(yldz_par_sptype)\n",
"print(\"ID Uzerinden Baglanmis Vericercevesi\")\n",
"pd.merge(yldz_mV_sptype,yldz_par_sptype)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Farklı Sütunlar Üzerinden Bağlama ##\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"Tayf Turu Uzerinden Baglanmis Vericercevesi\")\n",
"pd.merge(yldz_mV_sptype,yldz_par_sptype, on='sptype')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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.\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yldz_mV_sptype.columns = ['yildiz','tayfturu','V']\n",
"print(yldz_mV_sptype)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype, yldz_par_sptype, left_on='yildiz', right_on='id')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype, yldz_par_sptype, left_on='tayfturu', right_on='sptype')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yldz_mV_sptype.columns = ['id','sptype','mV']\n",
"yldz_mV_sptype"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how=\"outer\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how=\"left\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_par_sptype, on='id', how=\"right\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Birden fazla anahtar (sütun adı) üzerinden entegrasyon amaçlandığında istenen sütunlar `on` parametresine bir liste dahilinde sağlanır."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_par_sptype, on=['id','sptype'], how=\"outer\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yldz_d_sptype = pd.DataFrame(\\\n",
" {'id':['Arcturus','Betelgeuse','Rigel','Deneb','Vega','Fomalhaut','Polaris'],\\\n",
" 'sptype':['K1.5 III', 'M1 Iab', 'B8 Ia', 'A1 I', 'A0 V', 'A0 V', 'F8 Ib'],\\\n",
" 'd': 1 / (yldz_par_sptype['par']*1e-3)})\n",
"yldz_d_sptype"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_d_sptype,on=['id','sptype'], how=\"outer\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_d_sptype,on=['id','sptype'], how=\"inner\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## İndeks Üzerinden Bağlama İşlemleri ##\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.merge(yldz_mV_sptype,yldz_par_sptype, how=\"inner\", right_index=True, left_index=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### join Metodu ###\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yldz_mV_sptype.columns = ['yldiz','tayfturu','V']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yldz_mV_sptype.join(yldz_par_sptype, how=\"inner\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yldz_mV_sptype.columns = ['id','sptype','mV']\n",
"yldz_mV_sptype.join(yldz_par_sptype, rsuffix=\"_sag\", lsuffix=\"_sol\", how=\"outer\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Veriçerçevesi ve Serileri Ucuca Ekleme: Concatenating #\n",
"\n",
"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.\n",
"\n",
"Önce serilerle örnekleyelim."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"seri1 = pd.Series(np.random.rand(4), index=[1,2,3,4])\n",
"seri2 = pd.Series(np.random.rand(4), index=[5,6,7,8])\n",
"print(pd.concat([seri1,seri2]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(pd.concat([seri1,seri2],axis = 1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(pd.concat([seri1,seri1],axis = 1, join='inner'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ucuca eklenen seri ya da veriçerçevelerinin ucuca eklendikleri yerleri belirlemek üzere `keys` parametresi ile hiyerarşik bir indeksleme yapılabilir."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(pd.concat([seri1,seri2], keys=[1,2]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.concat([seri1,seri2], axis = 1, keys=['A','B'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Veriçerçevelerinin Ucuca Eklenmesi ##\n",
"\n",
"`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.\n",
"\n",
"Yine klasik bir ucuca ekleme işleminin öncelikle varsayılan modda (satırda) nasıl yapıldığına bir örnekle bakalım."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"df1 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[1,2,3], columns=['A','B','C'])\n",
"df2 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[4,5,6], columns=['A','B','C'])\n",
"pd.concat([df1, df2], keys=[1,2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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. \n",
"\n",
"Aynı veriçerçevelerini `axis` parametresini $1$ değerine ayarlayarak bu kez sütunda ucuca getirmeyi deneyelim."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
" pd.concat([df1, df2], axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"df1 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[1,2,3], columns=['A','B','C'])\n",
"df2 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[1,2,3], columns=['A','B','C'])\n",
"pd.concat([df1, df2], keys=['X','Y'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" pd.concat([df1, df2], verify_integrity=True)\n",
"except ValueError as e:\n",
" print(\"ValueError:\", e)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.concat([df1, df2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.concat([df1, df2], ignore_index=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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. \n",
"\n",
"Bu kez birleştirmeyi sütunda yapmayı deneyelim ve `axis` parametresini $'col'$ değerine ayarlayalım."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.concat([df1, df2], axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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`)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.concat([df1, df2], axis=1, ignore_index=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Kesişim ve Bileşke Yöntemleriyle Ucuca Ekleme ###\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df1 = pd.DataFrame({'e':[0.205, 0.007, 0.017, 0.094],\n",
" 'i': [7.0, 3.4, 0.0, 1.9],\n",
" 'P': [88.0, 224.7, 365.2, 687.0],\n",
" 'psi': [0.034, 177.4, 23.4, 25.2]},\n",
" index=['merkur','venus','dunya','mars'])\n",
"df2 = pd.DataFrame({'e':[0.049, 0.057, 0.046, 0.011],\n",
" 'P': [4331, 10747, 30589, 59800],\n",
" 'rho': [1.326, 0.687, 1.271, 1.638]},\n",
" index=['jupiter','saturn','uranus','neptun'])\n",
"print(\"Ic gezegenler:\")\n",
"print(df1)\n",
"print(\"----------------\")\n",
"print(\"Dıs gezegenler:\")\n",
"print(df2)\n",
"print(\"---------------------\")\n",
"print(\"pd.concat([df1,df2]\")\n",
"pd.concat([df1,df2], sort=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"pd.concat([df1,df2], join='inner')\")\n",
"print(pd.concat([df1,df2], join='inner', sort=False))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Append Metodu ##\n",
"\n",
"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. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df1 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[1,2,3], columns=['A','B','C'])\n",
"df2 = pd.DataFrame(np.random.rand(9).reshape(3,3), index=[4,5,6], columns=['A','B','C'])\n",
"df3 = df1.append(df2)\n",
"print(df3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Veri Çerçevelerini Birleştirme: Combining #\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"seri1 = pd.Series(np.random.rand(5),index=[1,2,3,4,5])\n",
"print(\"Seri 1:\")\n",
"print(seri1)\n",
"seri2 = pd.Series(np.random.rand(4),index=[2,4,5,6])\n",
"print(\"Seri 2:\")\n",
"print(seri2)\n",
"print(\"seri1.combine_first(seri2)\")\n",
"seri1.combine_first(seri2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"seri2.combine_first(seri1)\")\n",
"seri2.combine_first(seri1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(seri1[:3].combine_first(seri2[:3]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Sütun ve Satırlarda Döndürme: Pivoting #\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"df = pd.DataFrame(np.array([65,45,70,80,74,90,44,37,62]).reshape(3,3),\n",
" index=['ogr1','ogr2','ogr3'],\n",
" columns=['odev','vize','final'])\n",
"print(df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df.stack()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"df2 = df.stack()\n",
"print(\"Sutundan satira donusum (stack):\")\n",
"print(df2)\n",
"print(\"Satirdan sutuna donusum (unstack):\")\n",
"df3 = df2.unstack()\n",
"print(df3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"İ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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df4 = df2.unstack(0)\n",
"print(df4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uzun ve Geniş Tablolar: Longframe ve Wideframe ##\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tablo1 = pd.DataFrame({ 'iyon':['FeI','FeI','FeI',\n",
" 'NaI','NaI','NaI',\n",
" 'CaII','CaII','CaII'],\n",
" 'liste':['NIST','VALD','Custom',\n",
" 'NIST','VALD','Custom',\n",
" 'NIST','VALD','Custom'],\n",
" 'dalgaboyu': [5292.512, 5292.509,5292.513,\n",
" 5682.256, 5682.261, 5682.258,\n",
" 5167.615, 5167.618, 5167.620]})\n",
"print(tablo1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tablo2 = tablo1.pivot('iyon','liste')\n",
"print(tablo2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Bu şekilde her bir iyonun hangi listede hangi dalgaboyunda bulunduğunu görmek çok daha kolaylaştı."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Veri çıkarma: Removing #\n",
"\n",
"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.\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"df = pd.DataFrame(np.array([65,45,70,80,74,90,44,37,62]).reshape(3,3),\n",
" index=['ogr1','ogr2','ogr3'],\n",
" columns=['odev','vize','final'])\n",
"print(df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"del df['odev']\n",
"print(df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"İ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. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df2 = df.drop('ogr3')\n",
"print(\"ogr3 'un olmadıgi liste:\")\n",
"print(df2)\n",
"print(\"orjinal liste:\")\n",
"print(df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tekrarlayan Satırların Atılması ##\n",
"\n",
"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.\n",
"\n",
"Ö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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"cizgi_listesi = pd.DataFrame({ 'iyon':['FeI','FeI','FeI',\n",
" 'NaI','NaI','NaI',\n",
" 'CaII','CaII','CaII'],\n",
" 'liste':['NIST','VALD','Custom',\n",
" 'NIST','VALD','Custom',\n",
" 'NIST','VALD','Custom'],\n",
" 'dalgaboyu': [5292.512, 5292.509,5292.513,\n",
" 5682.256, 5682.261, 5682.258,\n",
" 5167.615, 5167.618, 5167.620]})\n",
"cizgi_listesi.duplicated()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ancak istersek herhangi bir sütunda tekrar eden değerleri de `duplicated()` fonksiyonuyla bulabiliriz."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cizgi_listesi['iyon'].duplicated()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cizgi_listesi[cizgi_listesi['iyon'].duplicated()]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"İstendiği takdirde bu tekrar satırları veriçerçevesinden çıkarılabilir. `drop_duplicates()` metodu bu amaçla kullanılır."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cizgi_listesi.drop_duplicates('iyon')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop_duplicates.html) 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. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cizgi_listesi.drop_duplicates('iyon', keep='last')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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. \n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Eşleştirme #\n",
"\n",
"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. \n",
"\n",
"## Eşleşenleri Değiştirme ##\n",
"\n",
"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()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.replace.html) 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. \n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"cizgi_listesi = pd.DataFrame({ 'iyon':['FeI','FeI','FeI',\n",
" 'NaI','NaI','NaI',\n",
" 'CaII','CaII','CaII'],\n",
" 'liste':['NIST','VALD','Custom',\n",
" 'NIST','VALD','Custom',\n",
" 'NIST','VALD','Custom'],\n",
" 'dalgaboyu': [5292.512, 5292.509,5292.513,\n",
" 5682.256, 5682.261, 5682.258,\n",
" 5167.615, 5167.618, 5167.620]})\n",
"cizgi_listesi"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"listeadi = {'Custom':'User'}\n",
"cizgi_listesi.replace(listeadi, inplace=True)\n",
"cizgi_listesi"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`replace()` fonksiyonu `NaN` gibi değerleri 0'la değiştirmek için sıklıkla kullanılır. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"seri = pd.Series([10,2.3,np.nan,-4,0.6,np.nan,7.5])\n",
"print(seri)\n",
"seri.replace(np.nan,0, inplace=True)\n",
"print(\"Degisiklik sonrasi:\")\n",
"print(seri)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Eşleştirme Yaparak Yeni Bir Sütun Ekleme ##\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"liste_referanslari = {'VALD':'Ryabchikova (1997)', 'NIST':'NIST ASD Team (2019)', 'User':'this study'}\n",
"cizgi_listesi['referanslar'] = cizgi_listesi['liste'].map(liste_referanslari)\n",
"cizgi_listesi"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Eşleştirme Yaparak İndeks İsimlerini Değiştirme ##\n",
"\n",
"İ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.\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"yeni_indeksler = {}\n",
"for i in range(len(cizgi_listesi.index)):\n",
" yeni_indeksler[i] = cizgi_listesi.at[i,'iyon']+\"_\"+cizgi_listesi.at[i,'liste']\n",
"print(yeni_indeksler)\n",
"cizgi_listesi.rename(yeni_indeksler)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sutunlar = {'dalgaboyu':'wavelength',\n",
" 'iyon':'ion',\n",
" 'liste':'linelist',\n",
" 'referanslar':'reference'}\n",
"cizgi_listesi.rename(columns=sutunlar)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Yine aynı şekilde bu değişikliklerin kalıcı hale getirilmek istendiği durumlarda `inplace` parametresine başvurulur."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Çoklu İndeksleme\n",
"\n",
"Serilerde birden, veriçerçevelerinde ikiden fazla boyutun istenmesi durumunda iyi bir çözüm çoklu indeksleme (ing. Multi-indexing) kullanmaktır."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df = pd.DataFrame(np.random.rand(4, 2),\n",
"index=[['x', 'x', 'y', 'y'], [0, 1, 0, 1]],\n",
"columns=['veri1', 'veri2'])\n",
"df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ç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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"index = [('HAT-P-36b', 'T100'), ('HAT-P-36b', \"T80\"),\n",
"('XO-3b', \"T100\"), ('XO-3b', \"T80\"),\n",
"('KELT-16b', \"T100\"), ('KELT-16b', \"T80\")]\n",
"derinlikler = [0.0206, 0.0220,\n",
"0.0096, 0.0100,\n",
"0.0152, 0.0170]\n",
"index = pd.MultiIndex.from_tuples(index)\n",
"df = pd.DataFrame({\"derinlikler\":derinlikler}, index=index)\n",
"df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df_2D = df.unstack()\n",
"df_2D"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Tekrar 3-boyuta $stack$ fonksiyonuyla dönülür."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df_2D.stack()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Bu indekslere isim verilip daha da kullanışlı hale getirilebilir."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df.index.names = [\"gezegen\",\"teleskop\"]\n",
"df"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"İndeksleme ve dilimleme n-boyutlu `numpy` dizilerindekine benzerdir."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"df.index"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df.loc[\"HAT-P-36b\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df.loc[\"HAT-P-36b\",\"T80\"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Veri Gruplama #\n",
"\n",
"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. \n",
"\n",
"Öncelikle `pandas` 'ta bu tür sayısal gruplamaların nasıl yapıldığını ötegezegenler veritabanı üzerinden örnekleyelim. Yine [NASA Exoplanet Archive](https://exoplanetarchive.ipac.caltech.edu/) 'dan indirebileceğiniz keşifleri onaylanmış (ing. confirmed) ötegezegenler veritabanı üzerinde çalışalım."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"og = pd.read_csv(\"veri/PSCompPars_2022.03.22_01.51.21.csv\", comment=\"#\", skipinitialspace=True,\n",
" index_col=\"pl_name\")\n",
"og.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Öncelikle geçiş yapan ötegezegenlerin yörüngelerinin dış merkezliliklerinin ortalamasını bilmek istiyor olalım."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"Otegezegenlerin ortalama dis merkezliligi: {:.2f} (+{:.2f} , {:.2f})\".\n",
" format(og['pl_orbeccen'].mean(),og['pl_orbeccenerr1'].mean(),og['pl_orbeccenerr2'].mean())) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og[['pl_bmassj','pl_radj']].dropna().describe()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## GroupBy Fonksiyonuyla Veri Gruplama ##\n",
"\n",
"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.\n",
"\n",
"Ö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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og.columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Görüldüğü gibi keşif yöntemi pl_discmethod başlıklı sütunda yer almaktadır."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og.groupby(\"discoverymethod\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og.groupby(\"discoverymethod\").count()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og.groupby(\"discoverymethod\")['pl_orbeccen'].mean()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og.groupby(\"discoverymethod\")['pl_orbper'].median()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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.\n",
"\n",
"`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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for (teknik, grup) in og.groupby('discoverymethod'):\n",
" print(\"{0:30s} shape={1}\".format(teknik, grup.shape))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og.groupby('discoverymethod')['disc_facility'].describe()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"og.groupby('discoverymethod')['pl_orbsmax'].describe().unstack()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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. \n",
"\n",
"### aggregatge Metodu ###\n",
"\n",
"`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. \n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"og.groupby('discoverymethod')[['pl_bmassj','pl_radj']].aggregate(['min', np.median, max])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### filter Metodu ###\n",
"\n",
"`DataFrameGroupBy` nesneleri üzerinde tanımlı `filter()` metoduyla yapılan filtreleme işlemi, grup özelliklerine göre veri atmanıza olanak sağlar. \n",
"\n",
"Aşağıdaki örneği inceleyelerek `filter()` metodunun nasıl çalıştığını anlamaya çalışalım."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"rng = np.random.RandomState(0)\n",
"df = pd.DataFrame({'kategori': ['A', 'B', 'C', 'A', 'B', 'C'],\n",
" 'veri1': range(6),\n",
" 'veri2': rng.randint(0, 10, 6)},\n",
" columns = ['kategori', 'veri1', 'veri2'])\n",
"print(df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ö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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"filtre_fonksiyonu = lambda x: x['veri2'].std() < 4\n",
"print(df.groupby('kategori').filter(filtre_fonksiyonu))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(df.groupby('kategori').std())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ş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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"filtre_fonksiyonu = lambda x: x['pl_orbeccen'].mean() > 0.25\n",
"print(og.groupby('discoverymethod').\\\n",
"filter(filtre_fonksiyonu)[['discoverymethod','pl_orbeccen','pl_orbper','pl_orbsmax']].\\\n",
"groupby('discoverymethod').mean())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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.\n",
"\n",
"Sonuç olarak `filter()` fonksiyonu uygulandığı veriçerçevesinin bir şekilde sınırlandırılmış (kısaltılmıış) bir versiyonunu döndürür."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### trasformation Metodu ###\n",
"\n",
"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. \n",
"\n",
"Ö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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"rng = np.random.RandomState(0)\n",
"df = pd.DataFrame({'kategori': ['A', 'B', 'C', 'A', 'B', 'C'],\n",
" 'veri1': range(6),\n",
" 'veri2': rng.randint(0, 10, 6)},\n",
" columns = ['kategori', 'veri1', 'veri2'])\n",
"print(df)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"donusum_fonksiyonu = lambda x: x - x.mean()\n",
"df.groupby('kategori').transform(donusum_fonksiyonu)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def donusum_fonksiyonu(x):\n",
" return x / x.mean()\n",
"og.groupby('discoverymethod').transform(donusum_fonksiyonu)['pl_orbper']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### apply Metodu ###\n",
"\n",
"`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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def normalizasyon(x):\n",
" x['veri1'] /= x['veri2'].sum()\n",
" return(x)\n",
"print(df.groupby('kategori').apply(normalizasyon))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def normalizedonem(x):\n",
" return x['pl_orbper'] / x['pl_orbper'].mean()\n",
"og.groupby('discoverymethod').apply(normalizedonem)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Başa Dön](#Bu-derste-neler-öğreneceksiniz?)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Kaynaklar #\n",
"\n",
"* Python Data Science Handbook, Jake VanderPlas, 2017, O’Reilly Media, Inc.\n",
"* Python Data Analytics, 2015, Fabio Nelli, Apress\n",
"* [Real Python Tutorial on combining data in pandas](https://realpython.com/pandas-merge-join-and-concat/)\n",
"* [pandas Paketi](https://pandas.pydata.org/)\n",
"* [pandas Kullanıcı El Kitabı](https://pandas.pydata.org/docs/user_guide/index.html)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 1
}