Doç. Dr. Özgür Baştürk
Ankara Üniversitesi, Astronomi ve Uzay Bilimleri Bölümü
obasturk at ankara.edu.tr
http://ozgur.astrotux.org
pandas
Python programlama dili için yazılmış, BSD-lisanslı açık kaynak kodlu, yüksek performanslı, kolay kullanılabilen veri yapıları ve analiz araçları sağlayan bir kütüphanedir (pandas dokümantasyonu). Üzerine kurulu olduğu, bir yaygın sayfa yapısına benzer, sütunların isimlendirildiği veri çerçevesi nesnesi (ing. DataFrame
) sayesinde veriyle kolay ve hızlı etkileşim sağlar.
Seriler (series
) etiketlendirlmiş birer bir boyutlu (1D) dizidir. Yapısı sözlük (dictionary) yapısına benzer, tıpkı numpy dizileri gibi sabit bir uzunluğa sahiptir. Değer almayan ya da 'N/A', 'NaN', '-' gibi nümerik olmayan değerler alan sütunların yönetimine de olanak sağlar. Numpy ya da Python'un satandart işlem ya da fonksiyonlarının üzerinde kullanılabildiği bir yapıdır. Örneğin sözlüklerde (dictionary) bu işlem ve fonksiyonların çok büyük bir bölümü kullanılamaz. pandas modülünün üzerine kurulu olduğu DataFrame
yapısının temelinde pandas serileri vardır. pandas serileri temel olarak veri çerçevelerinin sütunları olarak kullanılabilmekle birlikte kendi başlarına veri yapıları olarak da kullanılabilirler.
İşe bir pandas.Series
nesnesi oluşturarak başlayalım.
import pandas as pd
import numpy as np
gzgn_kutleler = pd.Series([0.330, 4.87, 5.97, 0.642, 1898, 568, 86.8, 102], \
index=['merkur', 'venus', 'dunya','mars','jupiter', 'saturn', 'uranus', 'neptun'], \
name="Kutle(10^24 kg)")
print(gzgn_kutleler)
print(gzgn_kutleler.index)
Görüldüğü gibi gzgn_kutleler pandas serisini oluşturmak üzere bir listeden sağlanan etiketler (labels
ya da index
) her bir gezegeni, değerler ise yine bir listeden sağlanan ($10^{24}$ kg cinsinden) kütle değerlerini göstermektedir. Değerleri bir numpy dizisi gibi itere edilebilir (iterable) başka bir değişken türünden de sağlamak mümkündür. Bu anlamda, pandas modülünün Series
nesnesi serbest olarak indekslenmiş birer numpy
dizisi gibi de düşünülebilir.
pandas serileri etiketsiz (ya da başlıksız) başlatılabileceği gibi, ki bu durumda başlıklar 0'dan başlanarak tam sayılarla numaralandırılır, bir sözlük (dictionary) değişleni üzerinden de tanımlanabilir. Yapıları nedeniyle Pandas series
nesnesi, özelleştirilmiş birer dictionary
(sözlük) nesnesi gibi de düşünülebilir. Her bir indeks söz konusu seride bir satırdaki değere karşılık gelmektedir.
import numpy as np
s1 = pd.Series(np.random.randn(5)*10)
print(s1)
yldz_mV = {'Sirius' : -0.5, 'Vega' : 0.0, 'Proxima' : 13.6}
print(type(yldz_mV), yldz_mV)
yldz_mV = pd.Series((yldz_mV))
print(yldz_mV)
pd.Series(yldz_mV, index=['Vega', 'Sirius', 'Altair', 'Proxima'])
print(yldz_mV.name)
Tıpkı bir numpy dizisi gibi pandas series
nesnesi üzerinde işlemler yapılabilir ve fonksiyonlar uygulanabilir, indeksleme ve dilimleme özellikleri kullanılabilir.
print(gzgn_kutleler[::2])
print(gzgn_kutleler > gzgn_kutleler['dunya'])
print(gzgn_kutleler[gzgn_kutleler < gzgn_kutleler['uranus'] ])
print(yldz_mV[1:-1])
print("-----------------")
print(yldz_mV[yldz_mV > yldz_mV.median()])
y1 = 'Vega'
y2 = 'Proxima'
print("{:s} ile {:s}'nin aki oranlari {:g}.".\
format(y1, y2, 10**(-0.4*(yldz_mV[y1]-yldz_mV[y2]))))
'Betelgeuse' in yldz_mV
print("Verilen yildizlarin ortalama parlakligi: {:g}, standart sapmasi: {:g}".\
format(yldz_mV.mean(), yldz_mV.std()))
s2 = pd.Series(np.random.randn(5)*2.5)
print("s2: \n", s2)
print("s2 + s2: \n" , s2 + s2)
print("s2^2: \n", s2**2)
Bu işlemlerde her iki tarafta aynı başlıkların (indekslerin ya da etiketlerin) olmaması sorunu da pandas tarafından etkin bir şekilde yönetilir.
s2[1:] + s2[:-2]
print(yldz_mV.name)
yldz_mV = yldz_mV.rename('mV')
print(yldz_mV.name)
pandas serilerine (series) yeni seriler append
metoduyla eklenir.
print(gzgn_kutleler)
yeni_cisimler = {'pluto':0.0146, 'ay':0.073}
print("------------")
gzgn_kutleler = gzgn_kutleler.append(pd.Series(yeni_cisimler))
print(gzgn_kutleler)
print("Saturn'un kutlesi, {:.4f} Jupiter kutlesidir.".format(gzgn_kutleler['saturn'] / gzgn_kutleler['jupiter']))
Pandas serileri her ne kadar veri üzerinde çalışmak için etkin ve hızlı bir yol sağlasa da Pandas'ın asıl veri nesnesi veri çerçeveleridir (DataFrame
). Seriler çoğu zaman hızlı bir şekilde veri çerçeveleri oluşturmak üzere ve her veri çerçevesinin sütunları olarak kullanılırlar ve bir boyutludurlar, veri çerçeveleri ise tablolar ve yaygın sayfalar (spreadsheet) olarak düşünülebilir. Bu yapıları nedeniyle Pandas DataFrame
nesnelerini, özelleştirilmiş birer dictionary
(sözlük) nesnesi olarak da düşünmek mümkündür. Her bir indeks söz konusu veri çerçevesinde bir sütuna karşılık gelir.
Bir DataFrame
pek çok açıdan sütun ve satıların isimlerinin olduğu yaygın sayfalara benzer. Yapıları gereği iki boyutuludur (2D). Pandas'la veri analizi büyük ölçüde veri çerçevesi nesnesi üzerine kurulu olduğundan en sık kullanılan nesneler de DataFrame
nesneleridir.
Veri çerçeveleri
Dolayısı ile Güneş Sistemi gezegenlerinin bazı özelliklerini saklamak istediğimiz gzgn_kutleler
serisi gibi yapıları saklamak için DataFrame
daha esnek ve kolay analize olanak sağlayan bir yapı sağlar. Bu seri daha geniş ve gezegenlere ilişkin başka özelliklerin de saklandığı bir gezegenler
veri çerçevesinin bir sütununu oluşturabilir.
gezegenler = {'Mg' : gzgn_kutleler,
'Rg' : pd.Series([2439.5, 6052., 6378., 1737.5, 3396., 71492., 60268., 25559., 24764., 1185.],\
index=['merkur','venus','dunya', 'ay', 'mars', 'jupiter', 'saturn', 'uranus', 'neptun','pluto']),\
'Prot' : pd.Series([1407.6, -5832.5, 23.9, 655.7, 24.6, 9.9, 10.7, -17.2, 16.1, -153.3],\
index=['merkur','venus','dunya', 'ay', 'mars', 'jupiter', 'saturn', 'uranus', 'neptun','pluto']),\
'Porb' : pd.Series([88.0, 224.7, 365.2, 27.3, 687.0, 4331, 10747, 30589, 59800, 90560], \
index=['merkur','venus','dunya', 'ay', 'mars', 'jupiter', 'saturn', 'uranus', 'neptun','pluto'])}
gunes_sistemi = pd.DataFrame(gezegenler)
Veri çerçeveleri pandas serileri kullanılarak oluşturulabileceği gibi listelerden, numpy dizilerinden veya sözlüklerden de oluşturulabilir.
# Sozlukler uzerinden veri cercevesi olusturma
veri = {
'ogrno' : ['05','12','23','04'],
'arasinav' : [35, 72, 60, 100],
'final' : [70, 43, 57, 82]
}
ders1 = pd.DataFrame(veri)
print(ders1)
# Listeler uzerinden veri cercevesi olusturma
sehirler = pd.DataFrame(data = [
['01', 'Adana', 'Kebap'],
['34', 'Istanbul', 'Kalabalik'],
['06', 'Ankara', 'Memurlar'],
['35', 'Izmir', 'Gevrek'],
['44', 'Malatya', 'Kayisi'],
['16', 'Bursa', 'Uludag']
], columns = ['Plaka', 'Sehir', 'Ozellik'])
print(sehirler)
# Numpy dizileri uzerinden veri cercevesi olusturma
tarihler = pd.date_range('20200219', periods=5)
df = pd.DataFrame(np.random.randn(5, 4), index=tarihler, columns=list('ABCD'))
print(df)
# Birden fazla nesne turu kullanarak veri cercevesi olusturma
df2 = pd.DataFrame({'A': 1.,
'B': pd.Timestamp('20190219'),
'C': pd.Series(1, index=list(range(4)), dtype='float32'),
'D': np.array([3] * 4, dtype='int32'),
'E': pd.Categorical(["AST413", "AST415", "AST416", "AST515"]),
'F': 'Asterosismoloji'})
print(df2)
Veri çerçevelerinin tamamını, bir bölümünü, bir sütununu, bir satırını ya da bir elemanını görüntülemek için pek çok fonksiyon bulunduğu gibi listelerde ve dizilerde geçerli dilimleme ve indeksleme seçenekleri de kullanılabilmektedir.
print("Tun Gunes Sistemi")
print(gunes_sistemi)
print("---------------------")
print("Veri cercevesindeki ilk 5 gezegen")
print(gunes_sistemi.head())
print("---------------------")
print("Veri cercevesindeki son 3 gezegen")
print(gunes_sistemi.tail(3))
print("---------------------")
print("Kutleler : ")
print(gunes_sistemi['Mg']*1e24)
print("Ortalama Kutle: {:g} kg".format(gunes_sistemi['Mg'].mean()*1e24))
print("----------------------")
print(gunes_sistemi.columns)
describe
metodu ile veri setinin bazı istatistiksel parametrelerini görmek ve veri seti hakkında hızlı bir yorumda bulunmak da mümkün olabilir.
print(gunes_sistemi.describe())
Veri çerçevesini herhangi bir eksene göre sıralamak için sort_values
fonksiyonu kullanılır.
gunes_sistemi.sort_values(by='Rg')
İndeksleme ve dilimleme işlemleri tıpkı numpy
, list
, tuple
nesenlerinde ve pd.Series
nesnesinde yapıldığı şekliyle yapılabilir. Bunun dışındaki işlemler için:
df[col]
(ilgili sütundaki seriyi getirir)df.loc[label]
(ilgii satırdaki seriyi getirir)df.iloc[loc]
(ilgii satırdaki seriyi getirir)df[5:10]
(Veri çerçevesinin dilimlenmiş bölümünü getirir)df[kosul]
(Veri çerçevesinin koşula uyan bölümünü)yapıarı da kullanılabilir. Öncelikle numpy
dizilerindeki indeksleme tarzı indeksleme seçeneklerini gözden geçirelim.
gunes_sistemi[::2]
gunes_sistemi[-1:0:-2]
İndekslemede tamsayı indekslere başvurarak verilerin yeri üzerinden yukarıdaki gibi indeksleme yapılabileeği gibi (ing. implicit indexes), Series
ve DataFrame
nesnelerinin indeksleri (satır isimleri olarak da düşünülebilir) olduğu düşüncesinden hareketle bu indeksler üzerinden de dilimleme ve indeksleme (ing. explicit indexes) yapılabilir.
gunes_sistemi['jupiter':'pluto']
Bu durumda özellikle tamsayı indeksler seçilerek kullanıcı tarafından oluşturulmuş veri çerçeveleri ve seriler üzerinde çalışılırken, kullanıcının belirlediği indekslerin mi yoksa verinin bulunduğu yerin indekslerinin mi geçerli olduğu gibi bir soru ortaya çıkar. Bunu basit bir örnekle görelim.
seri_tamindeks = pd.Series(['merkur', 'dunya', 'jupiter'], index=[1, 3, 5])
#Indeksleme yapilirken kullanici tanimli indeksler kullanilirken
print(seri_tamindeks[1])
print("---------------")
#Dilimlemede verinin yerine dayali indeksler kullanilir
print(seri_tamindeks[0:2])
Bu karışıklığı önlemek için Pandas, bazı dizinleme şemalarını esas alan özel dizin oluşturucu özellikler (attributes
) sağlar. Bunları pandas nesneleri üzerinde tanımlı fonksiyonlar metotlar (methods
) olarak değil, o nesneye özel özellikler olarak düşünmek gerekir.
loc
attribute'ı her zaman kullanıcı tarafından verilen indekslere referans vermek için kullanılır.
# seri_tamindeks uzerinde bu kez kullanici indeksiyle veri secelim
print(seri_tamindeks.loc[5])
# Gunes Sistemi veri cercevesi uzerinde
# Herhangi satir(lar)i indeksiyle secmek icin
gunes_sistemi.loc['jupiter']
Dilimlemede kullanıcı tanımlı indeksler üzerinden dilimleme yapılırken verilen aralığın her iki ucu da (sonu da!) dilimlememey dahil edilirken, numpy dizisi (ya da liste) tarzı, verinin bulunduğu konuma göre yapılan indekslemede verilen aralığın sonu dilimlemeye dahil edilmez (örn. $dizi[1:4]$ şeklinde bir indekslemede dizinin 1, 2 ve 3 numaralı indekslerinde bulunan değerlerden bir dizi dilimi oluşturulur, 4 numaralı indeksteki değer alınmaz!).
# Veri cercevesinin sadece bir bolumunu secmek icin
gunes_sistemi.loc['jupiter':'merkur', ['Mg', 'Rg']]
gunes_sistemi.loc['ay', ['Prot','Porb']]
loc
kullanılarak sütun ya da satır adıyla seçim yapılabileceği gibi indeks numarası kullanılarak verinin bulundğu konum üzerinden de seçim yapılabilir; bunun için iloc
attribute'una başvurulur.
# seri_tamindeks serisi uzerinde
seri_tamindeks.iloc[2]
seri_tamindeks.iloc[0:2]
gunes_sistemi.iloc[4]
gunes_sistemi.sort_values('Porb').iloc[3]
gunes_sistemi.sort_values('Porb').iloc[1:5,2:4]
gunes_sistemi.iloc[[4,3,0],-1]
# Belirli bir deger icin
print(gunes_sistemi.loc['venus','Rg'])
print(gunes_sistemi.iloc[-1, -1])
# Bu islemi daha hizli yapabilen iki metot at ve iat metotlaridir
print(gunes_sistemi.at['dunya','Mg'])
print(gunes_sistemi.iat[1,2]) #1: dunya 2: Porb
Tıpkı numpy dizilerinde olduğu gibi pandas veri çerçevelerinde de bir koşula dayanan dilimleemler yapılabilir.
print(gunes_sistemi['Porb'] > 365.25)
print(gunes_sistemi[gunes_sistemi['Mg'] > 10.00]['Rg'])
Bir koşula göre dilimlemelerde dahi numpy tarzı dilimlemeler ve indekslemelerin dahi pandas tarafından sağlanan loc
, iloc
, ix
, at
, iat
özellikleri (attribute) kullanılması önerilmektedir.
print(gunes_sistemi.loc[gunes_sistemi['Prot'] < 24.0])
Herhangi bir değerin bir sütunda olup olmadığını belirlemek için isin
fonksiyonu kullanılır.
gunes_sistemi[gunes_sistemi['Porb'].isin([365.2, 88.00])]
print(df)
s1 = [1, 2, 3, 4, 5]
df['E'] = s1
print(df)
Ancak indeksleri vererek yeni bir sütun eklemek daha güvenilir ve tutarlı bir yoldur.
gunes_sistemi['e'] = pd.Series([0.205, 0.007, 0.017, 0.055, 0.094, 0.049, 0.057, 0.046, 0.011, 0.244], \
index = ['merkur','venus', 'dunya', 'ay', 'mars', 'jupiter', 'saturn', 'uranus', 'neptun', 'pluto'])
print(gunes_sistemi)
# g/cm^3 cinsinden hacimleri yeni bir sutuna ekleyelim
gunes_sistemi['V'] = gunes_sistemi['Mg']*1e27 / (4./3*np.pi*(gunes_sistemi['Rg']*1e5)**3)
print(gunes_sistemi)
Şimdi aslında bir cüce gezegen olmasına karşın Ceres'in parametrelerini yeni bir satır olarak veri çerçevemize ekleyelim ama Ceres'in yörünge dönemini ($P_{orb}$) ve dış merkezliliğini ($e$) girmeyi unutmuş olalım. Ayrıca diğer parameterleri de bir miktar "karışık" bir sırada verelim. Bunun yanı sıra Ceres'in yarıçapını da Dünya yarıçapı cinsinden ($R = 0.074 R_{dunya}$) biliyor olalım.
ceres = {'Prot' : pd.Series([9.1], index=['ceres']),\
'Rg' : pd.Series([0.074*gunes_sistemi.at['dunya','Rg']], index=['ceres']),\
'Mg': pd.Series([0.94e-3], index=['ceres'])}
gunes_sistemi = gunes_sistemi.append(pd.DataFrame(ceres), sort=True)
print(gunes_sistemi)
Eklemeyi bir veri serisi üzerinden değil bir sözlük üzerinden de yapabiliriz. Güneş Sistemi'nin en büyük uydusu Ganymede'in bildiğimiz parametrelerini de bu şekilde ekleyelim.
ganymede = {'Mg' : 148.2e-3, 'Rg' : 2631., 'e' : 0.001}
gunes_sistemi = gunes_sistemi.append(pd.DataFrame(ganymede, index=['ganymede']), \
sort=True)
print(gunes_sistemi)
İki büyük uydunun (Titan, Triton) parametrelerini daha ekleyelim.
iki_buyuk_uydu = [{'Mg':134.6e-3, 'Rg':2575, 'Prot':382.7, 'e': 0.029}, \
{'Rg':1352.5, 'Prot':-141.0, 'e':0.000, 'Mg':21.5e-3}]
gunes_sistemi = gunes_sistemi.append(pd.DataFrame(iki_buyuk_uydu, \
index=['titan','triton']), sort=True)
print(gunes_sistemi)
Şimdi bakınca Ganymede'in dönme dönemini girmediğimizi farketmiş ve girmek istiyor olalım. Uydular için yörünge dönemi gezegenlerinin etrafındaki yörüngelerinin dönemi olduğu için girmemeyi tercih ettik (Ay hariç).
gunes_sistemi.at['ganymede', 'Prot'] = 171.7
print(gunes_sistemi)
Veri çerçevelerinden sütun silmek için del
komutu (zira bir veri çerçevesi değiştirilemez (ing. immutable) bir nesnedir), satır silmek için ise drop
metodu kullanılır.
print(sehirler)
sehirler['Bolge'] = ['Akdeniz', 'Marmara', 'Ic Anadolu', 'Ege', 'Guneydogu Anadolu', 'Marmara']
print(sehirler)
del sehirler['Ozellik']
print(sehirler)
sehirler.drop(4, axis=0)
print(sehirler)
Pandas özünde numpy dizileriyle de birlikte çalışmak üzere tasarlanmış bir veri yönetim paketi olduğu için diziler üzerinde çalışabilen ön tanımlı (built-in) ya da kullanıcı tanımlı fonksiyonlar Pandas veri nesneleri üzerinde çallışır.
import pandas as pd
import numpy as np
rstglvr = np.random.RandomState(42)
rstglsr = pd.Series(rstglvr.randint(0, 20, 5))
rstglsr
rstgldf = pd.DataFrame(rstglvr.randint(0, 20, (4, 5)),
columns=['A', 'B', 'C', 'D', 'E'])
rstgldf
print(np.sin(rstgldf/16. + np.pi / 2))
Herhangi bir fonksiyon ya da aritmetik işlem birden fazla seri ya da veri çerçevesi arasında uygulanmak istendiğinde Pandas iki veri nesnesinin indekslerini hizalar.
yuzolcum = pd.Series({'Birlesik Krallik': 242495, 'Fransa': 551695, 'Almanya': 357386,
'Ispanya': 498511, 'Turkiye': 783562}, name='yuzolcum')
nufus = pd.Series({'Turkiye': 84200851, 'Ispanya': 46767543, 'Birlesik Krallik': 67803450,
'Almanya': 83792987, 'Fransa': 65227357}, name='yuzolcum')
nufus_yogunlugu = nufus / yuzolcum
print(nufus_yogunlugu)
gdp = pd.Series({'Almanya': 3863344, 'Fransa': 2707074, 'Italya': 1988636,
'Izlanda': 24280, 'Turkiye': 743708}, name='yuzolcum')
gdp_kisibasi = gdp / nufus
print(gdp_kisibasi*1e6)
Bir veri çerçevesi ile bir seri arasında işlem yaparken işlem varsayılan olarak satırda gerçekleşir ve indeksler hizalanır.
A = rstglvr.randint(20, size=(4, 5))
dfA = pd.DataFrame(A, columns=list('XYZTW'))
dfA - dfA.iloc[0]
İşlem sütun üzerinde yapılmak istendiğinde axis
anahtarı işlemin sütun üzerinden yapılacağını belirtmek üzere $0$ 'a eşitlenir
print(dfA)
dfA.subtract(dfA['Z'], axis=0)
apply
metodu herhangi bir fonksiyonun tüm veriçerçevesi ya da seri üzerine uygulanmasını sağlar.
import pandas as pd
import numpy as np
rstglvr = np.random.RandomState(42)
print(rstglvr)
A = rstglvr.randn(20)
dfA = pd.DataFrame(A.reshape((4,5)), columns=list('XYZTW'))
print(dfA)
print(dfA.apply(np.cumsum))
Bir veri tablosuna ya da pandas diliyle veri çerçevesinde karşılığı bulunmayan bir verinin nasıl girileceği tartışmalı bir konu olup, tek bir çözümü de yoktur. Veri türüne bağlı olarak bazı durumlarda $-1$, $9.9999$, $-99999$ gibi "nöbetçi değer" (ing. sentinel) kullanılabileceği gibi, boş bırakmak, '-', 'NA', 'N/A'gibi belirteçler kullanmak da tercih edilebilmektedir. Bu çözümlerin her birinin avantaj ve dezavantajları bulunur. Örneğin tüm verilerin pozitif olarak girileceği bir sütunda değeri olmayanların yerine $-1$ girmek sütunlar üzerinde matematiksel işlemler yapılırken bu sütunun da dikkate alınması ve CPU'ya bu nedenle ek yük binmesi anlamına gelir.
Pandas, bir veri çerçevesi ya da seride bulunmayacak verilerin yerine $None$ ve $NaN$ nesnelerini kullanır. Ancak $None$ kullanıldığına veri tipi de nesne ($object$) olur ki bu işlemlerin daha kısa sürede yapılmasına olanak sağlayan $NaN$ seçeneğine göre dezavantajlıdır.
for dtype in ['object', 'int']:
print("dtype =", dtype)
%timeit np.arange(1E6, dtype=dtype).sum()
print()
$NaN$ veri türü IEEE kayan noktalı sayı standartları arasında tanımlanmış ve tüm programlama dilleri ve dijital işlemlerde kullanılan bir veri türüdür. $NaN$ veri türü ile yapılan tüm işlemler sonuç döndürür.
x = np.array([1, np.nan, 3, 4])
print("1 + x = ", 1 + x)
print("0x = ", 0*x)
print("ln(x) = ", np.log(x))
print("min(x) = ", np.min(x))
print("nanmax(x) = ", np.nanmax(x))
print("SIGMA(x) = ", np.sum(x))
print("nansum(x) = ", np.nansum(x))
pandas modülünde girilmeyen ya da NaN
(ing. Not a Number) veya None
olarak girilen verileri (Null) yönetmek üzere de özel fonksiyonlar (isnull
, notnull
,dropna
,fillna
) bulunmaktadır.
veri2 = {
'ogrno' : ['08','16','32','74'],
'odev' : [56, 72, 60, 84],
'proje' : [70, 43, 57, 71]
}
ders2 = pd.DataFrame(veri2)
ders2
ders2 = ders2.append(pd.DataFrame({'ogrno' : 18, 'proje' : 37},
index=[4]), sort=True)
ders2
ders2 = ders2.append(pd.DataFrame({'ogrno' : 24, 'odev' : 65},
index=[5]), sort=True)
ders2
Nan
olan değerlerin tablodaki yerini isna
fonksiyonuyla görebiliriz.
# isna fonksiyonu Null degeri veren elemanlari True
# digerlerini False isaretleyerek bir maske (mask) olusturur
pd.isna(ders2)
dropna
$NaN$ değer bulunan satır (varsayılan) ya da sütunları silmek için kullanılır.
ders2.dropna(subset=['proje'])
Varsayılan davranışında satır silen drop
fonksiyonu NaN
sütunları silmek için de kullanılabilir. $axis = 1$ ya da $axis = columns$ verilerek NaN
barındıran tüm sütunlar silinebilir.
ders2.dropna(axis=1)
dropna
fonksiyonunun how
parametresi en az bir değeri ($how = 'any'$) veya tüm değerleri ($how = 'all'$) NaN
olan, satır veya sütunun veri çerçevesinden kaldırılıp kaldırılmayacağını belirler.
ders2.dropna(how='any')
Bazen $NA$ değerlerini silmek yerine, geçerli bir değerle değiştirmek tercih edilebilir. Bu değer, sıfır gibi tek bir sayı olabilir veya çevredeki değerler kullanılarak yapılabilecek bir interpolasyon sonucu elde edilen değer olabilir. Aslında bu işlem isnull
fonksiyonu ile bir maske (ing. mask) üretilerek $NaN$ değerler belirlendikten sonra istenen değerlerle değiştirilerek de yapılabilir. Ancak bu çok sık kullanılan işlem olduğu için Pandas dizinin bir kopyasını $null$ değerlerle değiştiren fillna
metodunu sağlamıştır.
veri = pd.Series([1, np.nan, 2, None, 3, np.nan], index=list('PQRSTU'))
veri
# NaN degerleri 0 ile degistir
veri.fillna(0)
# Nan degerleri bir sonraki deger ile degistir
veri.fillna(method='ffill')
# Nan degerleri bir onceki deger ile degistir
veri.fillna(method='bfill')
ders2.fillna(0, axis=1)
Pandas modülünden tablo yapısındaki ascii (salt metin), sütunlardaki değerleri virgülle ayrılan (csv), yaygın sayfa programlarıyla oluşturulan pek çok formattaki dosyadan veri çekilip, üzerinde işlemler yapılabilir. Öncelikle sütunlardaki değerleri virgülle ayrılmış bir tablo yapısı (csv) üzerinde bir örnekle çalışalım. ders_notlar.csv dosyasında bir derste öğrencilerin aldığı notlar (iki ödev, bir arasınav ve bir final olmak üzere) verilmiştir. Öncelikle bu dosyadaki veriyi alıp, bir veri çerçevesine aktaralım. Sonrasında buradaki verinin üzerinde işlemler yapabilir ve analiz edebiliriz.
Pandas modülünün csv
fonksiyonu metin değişkenlere aktarılan sütun değerlerinin başında boşluk varsa (whitespace
) bunları da metne dahil ettiğinden bu davranışı değiştirmek üzere skipinitialspace
parametresi True değerine ayarlanmalıdır.
import pandas as pd
notlar = pd.read_csv('ders_notlar.csv', index_col="ad", skipinitialspace=True)
notlar
Eğer ders_notlar.csv dosyasını bir metin editörle açacak olursanız hem NaN
olarak girilen değerler (sınava girmeyenler için), hem de boş bırakılan değerler (durum sütununda henüz durumu belirlenmemiş öğrenciler; W: dersten çekilenleri göstermektedir) göreceksiniz. Pandas boş bırakılan değerleri de NaN
olarak yorumlamaktadır. Tüm sütun isimleri veri dosyasından çekilirken ad sütunu indeks olarak işaretlenmiştir. Başlığı olmayan ya da atlanmak istenen veri dosyaları için csv
fonksiyonunun header
parametresinde çeşitli seçenekler tanımlanmıştır.
# indekslenen sutun
print(notlar.index)
print("-----------------")
# tüm sutunlar
print(notlar.columns)
print("-----------------")
# Herhangi bir ogrencinin butun notlari
print(notlar.loc['ogrenci28'])
print("-----------------")
# Tum arasinav notlari
print(notlar['arasinav'])
Şimdi bir analiz işlemi yapabiliriz. Örneğin aldığı tüm notlardan öğrencinin ağırlıklı genel ortalamasını çıkarabiliriz. Bu ders için her iki ödevin %10'ar (toplamda %20), arasınavın %30, finalin ise %50 ağırlığının olduğunu varsayalım
notlar['genel_ortalama'] = notlar['odev1']*0.1 + notlar['odev2']*0.1 + notlar['arasinav']*0.3 + notlar['final']*0.5
notlar['genel_ortalama']
Bu ortalamalar hesaplanırken önemli bir şansımız arasınava girmeyen tüm öğrencilerin finale de girmemiş olması ve finale girmeyen tüm öğrencilerin de dersten çekilmiş (durum: W) olmalarıdır. Durumun bu olması zorunlu değildir. Örneğin bir öğrenci arasınava girmeyip derse devam etmiş, finale girmiş olabilir. Bu durumda arasınav notunu 0 yapmak gerekir. Bu tür ayarlamalar koşul yapılarıyla (if
- elif
- else
) kontrol edilebilir. Ayrıca pandas bu tür kontroller için pratik fonksiyonlar da sağlamaktadır. Örneğin her öğrenci için ağırlıklı ortalama çıkarmak bakımından arasınav ve finale girmeyenlerin notlarını 0 varsaymak iyi bir fikir olacaktır. Öğrencinin dersten çekilip çekilmediği durum sütununda kontrol edilebildiği için (W) bu önemli bir avantaj sağlayacaktır. Bu amaçla veri çerçeveleri üzerine tanımlı fillna
metodu kullanılabilir.
notlar = notlar.fillna(0)
notlar['genel_ortalama'] = notlar['odev1']*0.1 + notlar['odev2']*0.1 + notlar['arasinav']*0.3 + notlar['final']*0.5
notlar
Bu durumda durum sütununda da W olmayan satırlar 0 ile doldurulmuştur. Ayrıca arasınav ya da finale girmeyen öğrencilerin genel ortalaması NaN
olarak belirlenmiş olduğu için onlar da 0'a dönüştürülmüştür. Bu nedenle genel_ortalama sütununu tekrar hesaplamakta yarar görülmüştür. Şimdi, durum sütunu ortalama notlarına bağlı olarak verilecek olan harf notları ile güncellenebilir.
ogrenciler = notlar.index.values
print(ogrenciler)
for i, ortalama in enumerate(notlar['genel_ortalama']):
if ortalama > 89.500:
notlar.at[ogrenciler[i], 'durum'] = 'A'
notlar
Aynı sonucu dilimleme için koşul kullanmak suretiyle de almak mümkündür. Veri çerçeveleri üzerinde tanımlı loc
metodu herhangi bir veya birden fazla sütunda istenen koşulu sağlayan satırları seçmek için kullanılablir. Bu satırlar seçildikten sonra bu koşulların sağlandığı satırda yer alan istenen sütun(lar) yeni değerleri ile güncellenebilir. Bu noktada birden fazla koşulun and
ya da or
gibi bir bağlaçla bağlanarak kombine koşullar oluşturulması mümkün olmakla birlikte pandas'ın and
ve or
yerine bitwise boolean operatörlerini ( &
ve |
) kullanıyor olmasına dikkat etmek gerekir.
notlar.loc[(notlar['genel_ortalama'] >= 87.500) & (notlar['genel_ortalama'] < 89.500), ['durum']] = 'A-'
notlar.loc[(notlar['genel_ortalama'] >= 84.000) & (notlar['genel_ortalama'] < 87.500), ['durum']] = 'B+'
notlar.loc[(notlar['genel_ortalama'] >= 79.500) & (notlar['genel_ortalama'] < 84.500), ['durum']] = 'B'
notlar.loc[(notlar['genel_ortalama'] >= 76.500) & (notlar['genel_ortalama'] < 79.500), ['durum']] = 'B-'
notlar.loc[(notlar['genel_ortalama'] >= 72.500) & (notlar['genel_ortalama'] < 76.500), ['durum']] = 'C+'
notlar.loc[(notlar['genel_ortalama'] >= 69.500) & (notlar['genel_ortalama'] < 72.500), ['durum']] = 'C'
notlar.loc[(notlar['genel_ortalama'] >= 64.500) & (notlar['genel_ortalama'] < 69.500), ['durum']] = 'C-'
notlar.loc[(notlar['genel_ortalama'] >= 59.500) & (notlar['genel_ortalama'] < 64.500), ['durum']] = 'D+'
notlar.loc[(notlar['genel_ortalama'] >= 54.500) & (notlar['genel_ortalama'] < 59.500), ['durum']] = 'D'
notlar.loc[(notlar['genel_ortalama'] < 54.500) & (notlar['durum'] != 'W'), ['durum']] = 'F'
notlar
pandas'ın istatistiki analize yönelik pek çok fonksiyonu bulunmaktadır. Bunlardan bazılarını örnekleyelim.
notlar.describe()
notlar.sort_values(by="final")
notlar.mean()
notlar.median()
notlar.max()
Biraz da analiz yapmak üzere derste alınan notları inceleyelim.
notlar["durum"].value_counts()
Bu veriçerçevesi kategorik veri taşıdığı için doğrudan hist
fonksiyonunu kullanamasak da bir histogram görmek için plot
fonksiyonunun bar
seçeneğini kullanabiliriz.
notlar['durum'].value_counts().plot(kind='bar')
Veri çerçevesi nesnelerinin üzerinde çizim fonksiyounu (plot
) da tanımlıdır. Bunun için ayrıca matplotlib kütüphanesi çağırmaya gerek yoktur, çünkü çizim fonksiyonu matplotlib'i kendi çağırır.
%matplotlib inline
notlar['final'].plot()
notlar.plot.scatter(x="arasinav", y="final", marker="o")
pandas veri çerçeveleri kolaylıkla $\LaTeX$ formatındaki tablolara dönüştürülüp, yayınlara aktarılabilir.
notlar_latex = notlar.to_latex()
print(notlar_latex)
Sonuç olarak oluşan notlar veriçerçevesini yine virgülle ayrılmış bir salt metin dosyasına (ders_notlar_sonuc.csv) adıyla yazdıralım.
notlar.to_csv("ders_notlar_sonuc.csv")
exoplanet.eu kataloğunu ödev teslim tarihine kadar herhahgi bir zamanda bilgisayarınıza indirdikten sonra bir pandas veri çerçevesi nesnesine alınız.
Kataloğun ilk 10 ve son 10 satırını ekrana yazdırınız.
Kataloğun tüm sütunları için en temel istatistikleri ekrana yazdırınız.
Kataloğu sadece size verilen gezegen türü için sınırlandırınız.
Söz konusu gezegen türü için kütle ve yarıçapı belirlenmiş olanların yoğunluklarını hesaplayarak, barınak yıldızın metal bolluğuna karşı çizdiriniz. Grafiğinizdeki aykırı noktaları belirleyerek çıkarınız. Grafiği çizdirmeden önce nasıl bir ilişki görmeyi beklerdiniz? Beklentiniz ile çizdirdiğiniz grafiği karşılaştırarak yorumlayınız.
Üzerinde çalıştığınız gezegenlerin barınak yıldızlarının sıcaklıklarının, metal bolluklarının, kütlelerinin, yarıçaplarının ve yaşlarının temel istatistiklerini ekrana yazdırınız.
Bir önceki soruda verilen parametrelerin aykırı noktalarını göstermek üzere kutu grafiklerini çizdiriniz.
Kutu grafiği temelinde aykırı noktalarından ayıkladığınız parametrelerin histogramlarını çizdiriniz.
Üzerinde çalıştığınız gezegen grubu için kütle ve yarıçap ilişkisini bir grafik üzerinde gösteriniz.
Gezegen grupları:
Sıcak Jüpiterler: Kütleleri $0.36 M_{jüp}$'den büyük, yörünge dönemleri $1.3 – 10$ Dünya günü arasında değişen gezegenler: Şeyma Torun
Ilık Jüpiterler: Kütleleri $0.36 M_{jüp}$'den büyük, yörünge dönemleri $10 – 100$ Dünya günü arasında değişen gezegenler: Meltem Yıldız
Satürnler: Kütleleri $0.30 M_{jüp}$'den, yyğunlukları $0.8 g/cm^3$ 'ten küçük gezegenler: Javid Bashirzade
Süper DünyalarKütleleri $2.0 M_{yer}$ ile $10.0 M_{yer}$, yarıçapları $0.8 R_{yer}$ ile $1.25 R_yer$ arasında değişen gezegenler: Uğur Şenaslan
Mini-neptünlerKütleleri $6.0 M_{yer}$ ile $12.0 M_{yer}$, yarıçapları $1.6 R_{yer}$ ile $2.00 R_yer$ arasında değişen gezegenler: Furkan Tomak
Soğuk Jüpiterler: Kütleleri $0.36 M_{jüp}$'den, yörünge dönemleri $100$ Dünya gününden büyük gezegenler: Ekrem Murat Esmer