Processing math: 100%

Modulok: amikor egy hétköznapi felhasználó óriások vállán áll

A pythonban, mint minden más modern nyelvben, szokás előre megírt hasznos függvényeket és függvény csomagokat használni. A python szntaxisában az import parancs segítségével tudunk külső függvényeket, csomagokat vagy modulokat betölteni. Modulnak nevezünk egy python nyelven írt filet ami python függvények, esetleg osztályok definícióit tartalmazza. Rendszerint ez egy .py kiterjeszésű file. Több ilyen definíciót tartalmazó fileok összességét hívjuk csomagnak. Nagy, sok függvénydefiníciót tartalmazó, csomagokat szokás alcsomagokra osztani. Ebben a notebookban a numpy a matplotlib és a csv csomagok néhány hasznos függvényével és adatstruktúrájával fogunk megismerkedni.
Mielőtt ezeket használni tudnánk be kell őket tölteni. Ezt legegyszerűbben az alábbi parancsal tehetjük meg:

In [1]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib

A fenti parancs az alábbi modul importálásokat és rövidítés definíciókat végzi el:

import numpy
   import matplotlib
   from matplotlib import pylab, mlab, pyplot
   np = numpy
   plt = pyplot

   from IPython.display import display
   from IPython.core.pylabtools import figsize, getfigs

   from pylab import *
   from numpy import *

Az első két import parancs hatása a következő: mind a matplotlib mind pedig a numpy csomag fő csomagjának függvényeit fel tudjuk használni az alábbi szintaxis szerint:

In [2]:
sajatlista=[1,2,3]
sajatarray=numpy.array(sajatlista)
sajatarray
Out[2]:
array([1, 2, 3])

A fenti példa egy listából csinál array típusú válltozót. Az array-ek amint azt később látni is fogjuk, egy a listához hasonló adatstruktúrák amelyek a numpy csomag egyik alapvető objektumai.

Van arra is lehetőség hogy egy nagy, több modult is tartalmazó csomagból csak egy modult töltsünk be. Erre példa az importos pédákból a harmadik sor:

from matplotlib import pylab mlab pyplot

Ez az utasítás sor azt mondja hogy a matplotlib csomagból töltsük be a pylab mlab és pyplot modulokat, melyek az ábra készítéshez használt függvényeket tartalmaznak. Viszgáljunk meg egy példát és ábrázoljuk a fent definiált sajatarray tömböt. Ezt a matplotlib csomag pyplot moduljának plot parancsával tehetjük meg. Ahogy az előző példában itt is a "."-al egymás után fűzve a csomag/alcsomag modul és függvény neveket tudjuk meghívni a megfelelő függvényt:

In [3]:
matplotlib.pyplot.plot(sajatarray)
Out[3]:
[<matplotlib.lines.Line2D at 0x7fc1f76aaa58>]

Hosszú modul és csomagnevek gépelésének elkerülése végett be lehet vezetni rövidítéseket, erre való az importálások következő két sora:

np = numpy
 plt = pyplot
In [4]:
plt.plot(sajatarray)
Out[4]:
[<matplotlib.lines.Line2D at 0x7fc1f563b048>]

Az utolsó két sor

from pylab import *
from numpy import *

pedig egy adott modulból vagy csomagból úgy tölt be minden ott definiált függvényt vagy osztályt hogy azokra a modul neve nélkül is lehet hivatkozni! Azaz a plot parancs a fenti importálások hatására mindenféle előtag nélkül is használható!

In [5]:
plot(sajatarray)
Out[5]:
[<matplotlib.lines.Line2D at 0x7fc1f55976a0>]

Ezentúl ahol csak lehet meg fogjuk tenni a fenti importálásokat és, az átláthatóság kedvéért törekszünk úgy megoldani mintafeladatokat hogy az előforduló csomagok neveire ne kelljen hivatkozni. Azért amikor egy nem saját magunk által írt függvényt használunk gondoljunk mindíg azokra az "óriásokra" akik órákat/heteket/éveket őltek abba hogy könnyen használható és jól működő csomagokat írtak.

Még egy adatstruktúra : array

A numpy csomag egy a list-ekhez hasonló adatstruktúrával gazdagítja a repertoárunkat. Ennek az új adatstruktúrának a neve: array. Tekintsük át az array-ek viselkedésének néhány alapvető tulajdonságait! (Részletesebb leírások angol és magyar nyelven. )

A List-ek hez hasonlóan számok vagy más objektumok listáinak segítségével tudjuk őket definiálni:

In [6]:
vec=array([1,2,3])
vec
Out[6]:
array([1, 2, 3])
In [7]:
matr=array([[1,1,3],[4,3,5],[6,2,3]])
matr
Out[7]:
array([[1, 1, 3],
       [4, 3, 5],
       [6, 2, 3]])
In [8]:
b=array(['a','b','cd'])
b
Out[8]:
array(['a', 'b', 'cd'], 
      dtype='<U2')
In [9]:
c=array([1,2,3,'sd',])
c
Out[9]:
array(['1', '2', '3', 'sd'], 
      dtype='<U21')
In [10]:
d=array([[1,2],[1,3,4],23])
d
Out[10]:
array([[1, 2], [1, 3, 4], 23], dtype=object)

Az utolsó pár példában a kiírásban megjelent dtype= azt jelzi hogy az adott array-ben milyen jellegő dolgok vannak csoportosítva. Egy array-ban mindíg a legáltalánosabb adat típus érvényesül. A vec és matr változókban skalárok vannak a b és a c válltozóban karkakterláncok. A d válltozóban pedig, mivel skalár (az utolsó szám) is van benne meg listák is (az első két vektor) ezért általános "objektumok" halmazának csoportjaként jelenik meg.

Egy pár hasznos array művelet:

Egy array alakjáról a shape attribútum segítségével érdeklődhetünk:

In [11]:
vec.shape
Out[11]:
(3,)
In [12]:
matr.shape
Out[12]:
(3, 3)
In [13]:
d.shape
Out[13]:
(3,)

A size attribútum az elemek összes számáról ad tájékoztatást:

In [14]:
matr.size
Out[14]:
9

Számosrok statisztikai vizsgálatában segít a min max (leg kisebb és legnagyobb elem értéke) illetve a mean (átlag) és az std (szórás) függvények

In [15]:
vec.min()
Out[15]:
1
In [16]:
vec.max()
Out[16]:
3
In [17]:
vec.mean()
Out[17]:
2.0
In [18]:
vec.std()
Out[18]:
0.81649658092772603

Ha az array alakja nem dimenziós mint például a matr esetében akkor a fenti függvények soronként vagy oszloponként is elvégezhetőek:

In [19]:
matr.mean(axis=0) #oszlop szerint
Out[19]:
array([ 3.66666667,  2.        ,  3.66666667])
In [20]:
matr.mean(axis=1) #sor szerint
Out[20]:
array([ 1.66666667,  4.        ,  3.66666667])

A matr mátrix transzponáltját a T attribútum segítségével kapjuk.

In [21]:
matr.T
Out[21]:
array([[1, 4, 6],
       [1, 3, 2],
       [3, 5, 3]])

Egy array-ban jelen lévő elemek szorzata s összege a sum illetve a prod segítségével kapható:

In [22]:
vec.sum()
Out[22]:
6
In [23]:
matr.prod()
Out[23]:
6480

Azonban vigyázni kell mert nem minden array függvény alkalmazható minden array-ra! Ha karaktereket tartalmazó mátrix produktumára vagyunk kíváncsiak akkro hibát fogunk kapni!

In [24]:
matrS=array([['a','b'],['c','d']])
In [25]:
matrS.prod()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-9768fae74e07> in <module>()
----> 1 matrS.prod()

/usr/local/lib/python3.4/dist-packages/numpy/core/_methods.py in _prod(a, axis, dtype, out, keepdims)
     33 
     34 def _prod(a, axis=None, dtype=None, out=None, keepdims=False):
---> 35     return umr_prod(a, axis, dtype, out, keepdims)
     36 
     37 def _any(a, axis=None, dtype=None, out=None, keepdims=False):

TypeError: cannot perform reduce with flexible type

Néhány függvény segíti hogy valamilyen előre megadott struktúrával rendelkező array-ek generálását. Lássunk erre néhány példát! A linspace() függvény egy megadott kezdő és vég érték között megadott számú egyenletesen mintavételezett számot ad:

In [26]:
linspace(0,pi,10) #10 szám 0 és pi között.. 
#IGEN az importálásoknak hála pi egy előre definiált változó!!! (numpy.pi  a numpy csomagból)
Out[26]:
array([ 0.        ,  0.34906585,  0.6981317 ,  1.04719755,  1.3962634 ,
        1.74532925,  2.0943951 ,  2.44346095,  2.7925268 ,  3.14159265])

A rand() függvény és a hozzá hasonló randn() randint() véletlen számokat tartalmazó array-ket adnak

In [27]:
rand() #0 és 1 között egy véletlen szám
Out[27]:
0.4050786113877549
In [28]:
randn(3) #3 darab véletlen Normális elpszlású szám 
Out[28]:
array([-0.49261188,  1.87296543, -0.66932503])
In [29]:
randint(0,9,(2,3)) # egy 2x3 as véletlen mátrix amely 0 és 9 közötti egész számokat tartalmaz
Out[29]:
array([[7, 5, 2],
       [6, 8, 3]])

Fontos megjegyezni hogy bizonyos alapműveletek (összeadás, kivonás, szorzás és osztás ) és alapvető matematikai függvények (például a sin, cos és exp függvények) array típusú változókra elemenként hatnak!

In [30]:
v1=array([1,2,3])
v2=array([2,3,3])
v1*v2
Out[30]:
array([2, 6, 9])
In [31]:
sin(v1)
Out[31]:
array([ 0.84147098,  0.90929743,  0.14112001])

Bool típusú változók array-ának összege elemenkénti or szorzata elemenkénti and műveletnek felel meg:

In [32]:
b1=array([True,False,True,False])
b2=array([False,False,True,True])
In [33]:
b1+b2 
Out[33]:
array([ True, False,  True,  True], dtype=bool)
In [34]:
b1*b2
Out[34]:
array([False, False,  True, False], dtype=bool)

Ahogy azt már fentebb is láttuk a plot() parancs segítségével ábrákat tudunk készíteni! Későbbiekben számos alkalommal kell ábrán valamit ábrázolni. Lássunk egy array-ket használó páldát egy egyszerű függvény ábrázolására:

In [35]:
x=linspace(-pi,pi,100)
plot(x,sin(x))
Out[35]:
[<matplotlib.lines.Line2D at 0x7fc1f54e4c88>]

Ezt a fenti konstrukciót, azaz hogy linspace-el gyártunk egy mintavételezést majd ezt plot parancs segítségével egy függvény ábrázolására használjuk a későbbiekben igen sokszor fogjuk használni!!

A feladatok során találkozni fogunk néhány adatbázis statisztikai elemzésével. Az átlagképzés és a szórás meghatározásán kívül magának az eloszlásnak a vizsgálata is fontos információkat hordozhat. Az eloszlás grafikus ábrázolása legegyszerűbben a matplotlib csomag hist függvényével tehető meg, amely az adatok alapján hisztogrammot állít elő:

In [36]:
veletlen=rand(10000) #10000 darab egyenletes eloszlású véletlen válltozó
hist(veletlen)
Out[36]:
(array([ 1031.,  1017.,   963.,  1028.,   981.,   966.,  1005.,  1028.,
          989.,   992.]),
 array([  1.11388721e-04,   1.00091373e-01,   2.00071356e-01,
          3.00051340e-01,   4.00031324e-01,   5.00011308e-01,
          5.99991292e-01,   6.99971276e-01,   7.99951259e-01,
          8.99931243e-01,   9.99911227e-01]),
 <a list of 10 Patch objects>)

Ha egy olyan függvényünk van ami két változótól is függ akkor ahoz a mintvételezést a meshgrid függvény segítségével tudjuk legyártani:

In [37]:
x,y=meshgrid(linspace(-pi,pi,30),linspace(-1,1,30))
z=sin(x)*y

Megjeleníteni például az imshow függvény segítségével tudjuk.

In [38]:
imshow(z,extent=(x.min(),x.max(),y.min(),y.max())) #az extent kulcsszóra a helyes méretezés végett van szükség
Out[38]:
<matplotlib.image.AxesImage at 0x7fc1f53c7cc0>

Array típusú változók indexelései

Az array típusú változók legfontosabb tulajdonsága hogy a list-eknél jóval gazdagabb indexelési módszerekkel rendelkeznek. Nézzünk ezekre néhány példát! Előszöris definiáljunk néhány változót:

In [39]:
proba1=linspace(0,10,10) #1-től 10-ig 10 db egyenletes szám
proba2=rand(10) #10 véleteln szám
proba3=randint(0,10,(5,5)) #5x5 ös véletlen mátrix

A list-eknél megszokott szeletelések itt is működnek:

In [40]:
proba1[0:3]
Out[40]:
array([ 0.        ,  1.11111111,  2.22222222])
In [41]:
proba1[-4:-1]
Out[41]:
array([ 6.66666667,  7.77777778,  8.88888889])

A list-ekkel ellentétben itt egy tetszőleges index listát is megadhatunk indexelés ként:

In [42]:
proba1[[3,5,2]]
Out[42]:
array([ 3.33333333,  5.55555556,  2.22222222])

Egy másik hasznos dolog hogy egy array-ból Bool típusú array segítségével valamilyen kritériumokat teljesítő elemeket választhatunk ki egy array-ból:

In [43]:
proba1>5 # Ez a kifejezés egy bool típusú array-t ad vissza
Out[43]:
array([False, False, False, False, False,  True,  True,  True,  True,  True], dtype=bool)

ha a fenti Bool típusú array-t mint indexet használjuk a proba1 hasában akkor egy olyan array-t kapunk amely a proba1 nek csak azon elemeit tartalmazza ahol a Bool érték True volt azaz az utolsó 5 elemet!

In [44]:
proba1[proba1>5]
Out[44]:
array([  5.55555556,   6.66666667,   7.77777778,   8.88888889,  10.        ])

Adatbázisok elemzésénél egy igen hasznos művelet valamilyen tulajdonsá szerint válogatni az adatbázisban. Ezen feladatok sokszor megfogalmazhatóak úgy mint egy array elemeinek egy másik array elemei szerinti szelektálása! Vizsgáljuk meg például a proba1 azon elemeit amelyeknek megfelelő elemek a proba2-ben 0.25-nél nagyobbak:

In [45]:
proba1[proba2>0.25]
Out[45]:
array([  4.44444444,   5.55555556,   6.66666667,   8.88888889,  10.        ])

Végül áljon itt egy pár grafikus példa magasabb dimmenziójú array változók indexeléseire:

Egy array vagy egy lista elemein elemenként végrehajthatunk műveleteket. Erre a map függvéyn egy igen általános példa. A map egy olyan függvény amelyiknek az első bemeneti változója egy függvény a második pedig egy lista vagy array amely minden elemére az első válltozóban beadott függvényt hattatni kívánjuk. például:

In [46]:
array(list(map(str,proba1))) # Egy számokat tartalmazó array konvertálása karakterláncokat tartalmazóra.
Out[46]:
array(['0.0', '1.11111111111', '2.22222222222', '3.33333333333',
       '4.44444444444', '5.55555555556', '6.66666666667', '7.77777777778',
       '8.88888888889', '10.0'], 
      dtype='<U13')

Sokszor előfordul, tipikusan a map és hozzá hasonló függvények kapcsán hogy nincs kész függvény arra a feladatra amit el szeretnénk végezni az array vagy a lista elemeivel, de egy rövid kifejezés segítségével megoldható lenne a dolog. Például a proba1 array elemeinek a sinus-ának a 2/3 részét szeretnénk képezni. Ekkor használhatjuk a python nyelv anonim függvényeit vagyis a lambda kifejezéseket:

In [47]:
(lambda x:sin(x)*5/2)(pi/2)  # ez nem más mint a 5/2*sin(x) függvény kiértékelva a pi/2 helyen azaz 5/2!!
Out[47]:
2.5

map() függvény hasába így írunk lambda kifejezést:

In [48]:
array(list(map(lambda x:sin(x)*2/3,proba1)))
Out[48]:
array([ 0.        ,  0.59746147,  0.5301467 , -0.12704531, -0.64287808,
       -0.44340101,  0.24943415,  0.66473193,  0.34040379, -0.36268074])

Így tudjuk megnézni hogy egy számsorozat melyik eleme osztható 3-al:

In [49]:
szamok=randint(0,100,10)#10 db véletlen szám 0 és 100 között
szamok
Out[49]:
array([87, 65, 17, 71, 86, 82, 98, 53, 31, 75])
In [50]:
list(map(lambda x:x%3==0,szamok))
Out[50]:
[True, False, False, False, False, False, False, False, False, True]

Bővebben a lambda kifejezésekől itt találtok olvasnivalót angolul és magyarul.

Adatok ki-be olvasása, feldolgozása és ábrázolása

Számtalanszor előforduló probléma hogy valamilyen, formázott vagy feldolgozandó formátumú file-ban tárolt adatokkal kell valamilyen műveletet végrehalytani. Az alábbi példák során megvizsgálunk, néhány függvény melyek file-ok és file-ban tárolt adatok feldolgozásában nyújthatnak segítséget.

A Baumgartner ugrás elemzése: Egyszerű adatfile-ok kezelése

Baumgartner Jump with data

Elemezzük Felix Baumgartner ugrásának adatait. Az ugrás megtett út idő adatait a BAUMGARTNER/h_vs_t file-ban találjuk. ( Vizsgáljuk meg magát a filet is ! ) A file két oszlop számot tartalmaz. Az első oszlop az idő s-ben a második oszlop az adott időben mért magasság m-ben. Ilyen jellegű egyszerű struktúrájú file-ok beolvasására és az adatok array-ba való töltésére a numpy csomag loadtxt függvényét használhatjuk:

In [51]:
baum_data=loadtxt('BAUMGARTNER/h_vs_t')

A baum_data változó array formájában tartalmazza a két oszlopot. Válasszuk külön az idő és a magasság adatokat:

In [52]:
t=baum_data[:,0] # idő
h=baum_data[:,1] #magasság

Jelenítsük meg az adatokat a plot parancs segítségével!

In [53]:
plot(t,h)
Out[53]:
[<matplotlib.lines.Line2D at 0x7fc1f53b99e8>]

Az ugrással kapcsolatban egy igen fontos kérdés volt hogy vajon sikerült-e szabadesésben átlépni a hangsebességet? Vizsgáljuk meg hogy ezen adatok alapján vajon átlépte-e Felix Baumgartner a hanghatárt! Először is szükség van a sebesség időfüggésére. Ezt a magasság idő függvény numerikus deriváltjával fogjuk meghatározni. Ha egy y(x) függvényt mintavételezésével véges darab xi,yi párt kapunk akkor az y(x) függvény numerikus deriváltját a következő differencia hányadossal közelíthetjük:

dydx|xi=yi+1yixi+1xi

In [54]:
# numerikus derivált függvény
def nderiv(y,x):
    "Első szomszéd differenciál"
    n = len(y) # adatpontok száma
    d = zeros(n) # változó inicializálás. A zeros() függvény tetszőleges alakú és 0-kat tartalmazó arrayt gyárt
    # mivel a legegyszerűbb numerikus differenciálás nem szimmetrikus a végpontokat
    # kicsit másképp kezeljük mint a tömb belsejében lévő pontokat
    for i in range(1,n-1):
        d[i] = (y[i+1]-y[i])/(x[i+1]-x[i])   #egy általános pont deriváltja
    d[0] = (y[1]-y[0])/(x[1]-x[0])           #az első pont deriváltja
    d[n-1] = (y[n-1]-y[n-2])/(x[n-1]-x[n-2]) # az utolsó pont deriváltja
    return d

Az nderiv függvény segítségével a sebesség meghatározható.

In [55]:
v=nderiv(h,t)

Vizsgáljuk meg a sebesség idő függvényt!

In [56]:
plot(t,v)
Out[56]:
[<matplotlib.lines.Line2D at 0x7fc1f5329940>]

Vajon mi okozta a hirtelen ugrást 250 s után ? Mivel általában a hang terjedési sebessége függ a magasságtól ezért annak érdekében hogy megtudjuk hogy sikerült-e áttörni a hanghatárt célszerű a sebességet a magasság függvényében ábrázolni:

In [57]:
plot(h,abs(v))
Out[57]:
[<matplotlib.lines.Line2D at 0x7fc1f528d7b8>]

A wikipédián található adatok alapján a hangsebesség 25km magasságban valamivel 300 m/s alatt van. Ezen a magasságon Felix sebessége 350 m/s körül mozgott tehát a rekord, a mérési adatok alapján, sikerült! A sebesség maximális értéke:

In [58]:
v.max()
Out[58]:
2.2779043280182107

Ezt a sebességet pedig a következő magasságon érte el:

In [59]:
h[abs(v)==abs(v).max()][0] #Vajon mit csinál a [0] a kifejezés végén ?
Out[59]:
27488.0

Az ugrástól számított eltelt idő a sebesség maximumának elérésénél pedig:

In [60]:
t[abs(v)==abs(v).max()][0]
Out[60]:
55.880200000000002

Ha a sebességet is lederiváljuk a gyorsulást kapjuk:

In [61]:
a=nderiv(v,t)

Ez a függvény a halmozódó numerikus hibák miatt már eléggé zajos:

In [62]:
plot(t,a)
Out[62]:
[<matplotlib.lines.Line2D at 0x7fc1f52762b0>]

Vizsgáljuk meg a gyorsulás adatsorának első néhány értékét:

In [63]:
a[0:13]
Out[63]:
array([  0.        ,   0.        ,  -0.57651242,  -7.4946614 ,
        -8.07129639, -10.9536965 ,  -9.80015077,  -9.80218623,
       -10.37769626, -10.94906968,  -8.65258847, -13.26038966, -13.26038966])

Illetve ezen értékek átlagát (egyszerű fizikai meggondolások alapján milyen értéket várnánk ide?):

In [64]:
a[0:13].mean()  
Out[64]:
-7.9383567266924846

Napfoltok változása: adatfile formázással

Ebben a mintapéldában egy fileból soronkénti olvasunk be adatokat és a számunkra releváns sorokból extraktáljuk a hasznos információkat.

Már az 1700-évek eleje óta elég megbízható adatok állnak rendelkezésre a napfoltok számának időbeni válltozásáról. Elemezzük ezen adatokat! Az adatbázisból letöltött havi átlagos napfolt szám értékeket megtaláljuk a NAPFOLT/SN_m_tot_V2.0.txt fileban. (Vizsgáljuk meg magát a file-t!) A python open függvénye segítségével megnyithatjuk a file-t olvasásra:

In [65]:
file = open('NAPFOLT/SN_m_tot_V2.0.txt')    #file megnyitása

Egy megnyitott file minden sorát mint soronkénti karakterláncok listáját a readlines függvény segítségével betöltjük a sorok listába:

In [66]:
sorok = file.readlines()                    #a file összes sorának beolvasása egy stringekből álló list-be

Mivel már nincs szükség a afile-ra ezért be is csukjuk azt!

In [67]:
file.close()                                #mivel már nem kell az adatfile be is zárjuk

Vizsgáljuk meg a sorok lista első 10 elemét! Azaz a file első 10 sorát!

In [68]:
sorok[0:10]                                  #mi volt a file-ban ? itt használhatnánk print-et is
Out[68]:
['#1. oszlop ev  ho  T[ev]  napfolt_szam\n',
 '#2. oszlop honap\n',
 '#3. oszlop az ido evben merve \n',
 '#4. oszlop Napfoltok szamanak atlaga\n',
 '#5. oszlop Napfoltok szamanak szorasa\n',
 '#6. oszlop Napfolt meresek szama\n',
 '1749 01 1749.042   96.7  -1.0    -1  \n',
 '1749 02 1749.123  104.3  -1.0    -1  \n',
 '1749 03 1749.204  116.7  -1.0    -1  \n',
 '1749 04 1749.288   92.8  -1.0    -1  \n']

Amint a #-al kezdődő sorokból kiderül a napfoltok száma a 4. oszlopban van illetve a 3. oszlop az időt tartalmazza években mérve (azaz az első két oszlopot kombinálja). Szükségünk lesz tehát erre a két oszlopra! Az első értékes sor a 7.:

In [69]:
sorok[6]
Out[69]:
'1749 01 1749.042   96.7  -1.0    -1  \n'

Egy karakterláncot a split() függvény segítségével "szóközök" mentén fel tudunk szabdalni: Tehát ez most már egy lista:

In [70]:
sorok[6].split()
Out[70]:
['1749', '01', '1749.042', '96.7', '-1.0', '-1']

a már ismert float függvény pedig számot készít a számunkra megfelelő oszlopból:

In [71]:
float(sorok[6].split()[3])
Out[71]:
96.7

Ahoz hogy elemezni tudjuk az adatokat szükségünk van a 7. sortól a 3. és 4. oszlopokra, ezt egy for ciklussal nyerhetjük ki:

In [72]:
num_napfolt=[]
meresi_ido=[]
for sor in sorok[6:]:
    num_napfolt.append( float(sor.split()[3]) )
    meresi_ido.append(  float(sor.split()[2]) )
In [73]:
plot(meresi_ido,num_napfolt)
Out[73]:
[<matplotlib.lines.Line2D at 0x7fc1f514f5c0>]

A fenti for konstrukció kiváltható egy kicsit tömörebb kóddal a map függvényt és a lambda kifejezést használva:

In [76]:
data=array(list(map(lambda x:list(map(float,x.split())), sorok[6:] )))

Az ábra plot ugyan az mint előtte!

In [77]:
plot(data[:,2],data[:,3])
Out[77]:
[<matplotlib.lines.Line2D at 0x7fc1f501aac8>]

Amint az ábrán jól látszik a napfoltok száma az évek során körülbelül 11 éves periódussal oszcillál.

Érettségi adatok elemzése: CSV file feldolgozása

Az alábbiakban az elmúlt pár év éretségi statisztikai adatait fogjuk megvizsgálni. Ez a példa sok szempontból jól illusztrál olyan problémákat amely valós adatbázis elemzések kapcsán felmerülhet. Ilyen például a hiányzó adatok kezelése, vagy a nem egészen kompatibilis adatbázisok egységes kezelése. Az érettségi adatokat a fenti honlap úgynevezett pontosvesszővel tagolt (comma separated value, röviden csv) formátumban teszi elérhetővé. Amint a név is sugallja ez a formátum az egyes adatokat pontosvesszővel (vagy más előre meghatározott szeparáló karakterrel ) tagolja. Rendszerint a file-ok elsősora oszlop címkéket a többi sor pedig a tényleges adatokat tartalmazza. Rendszerint ezt a formátumot nem túl nagy adatbázisok tárolására szokták használni. Sok közismert alkalmazás pl az Excel fel van készítve az ilyen formátumú file-ok kezelésére. A csv formátum egyik fő előnye hogy szabad szemmel olvasható. A python nyelvben a csv modul segítségével tudunk alapvető file műveleteket végrehajtani csv formátumú fileokon.

In [ ]:
import csv

Az ERETTSEGI_ADATOK könyvtárban található file-ok tartalmazzák az elmúlt 5 év kétszintes fizika érettségit tevő diákjainak statisztikai adatait. Általában évente két vizsga alkalommal lehet érettségit tenni emelt illetve középszinten. A file-ok nevei ezen adatok alapján vannak elnevezve. Például a dat_2015-E-1 a 2015-ben emelt szintű érettségit tevők listáját tartalmazza az első (azaz tavaszi) vizsga alkalmról, a dat_2014-K-2 pedig a 2014 őszi vizsga alkalmának közép szintű eredményeit tartalmazza. Nyissunk meg egy-két ilyen filet és vizsgáljuk meg a struktúráikat! Az első sor a adatok címkéit tartalmazza ";"-el elválasztva. A legtöbb file ban szerepel például olyan cimke hogy vizsgázó neme vagy vizsgázó pontszáma. Azonban mivel az emeltszintű és a közép szintű érettségi vizsga kicsit más, már a címkékben is vannak külömbségek az "E"-s illetve a "K"-s file-ok között.

Az alábbiakban a 2014 es emelt színtű érettségi második vizsga alkalmának statisztikai tulajdonságait fogjuk megvizsgálni. Ehez elősször beolvassuk a megfelelő adatfilet és egy olyan struktúrába foglaljuk ami egyszerű adatbázis jellegű operációkat lehetővé tesz. Ez a formátum pedig nem más mint egy python szótár (dict) melynek kulcsszavait a csv file első sorai adják és minden egyes kulcsszóhoz python array formájában tárolódnak az adatok.

A már ismerős open parancs segítségével készítünk fel egy file-t olvasásra:

In [ ]:
file = open('ERETTSEGI_ADATOK/dat_2014-E-2',encoding='iso-8859-2')    #az encoding kulcsszóra az ékezetes szavak miatt van szükség

A csv modul reader függvényével egy előre meghatározott oszlop választó karakter ";" alapján felosztja a file olvasandó sorait oszlopokra:

In [ ]:
olvaso = csv.reader(file,delimiter=';')

Az adatbeolvasást itt most egy for ciklussal tesszük meg. Az ADAT válltozó egy ezek után egy lista lesz melynek minden sora szintén egy lista!

In [ ]:
ADAT=[]
for sor in olvaso:
    ADAT.append(sor)
In [ ]:
ADAT[0] # Ez a kulcsszavak listája
In [ ]:
ADAT[1] # Ez pedig az első értékes adatsor!

A file-t most bezárjuk:

In [ ]:
file.close()

Ezek után az ADAT változó tartalmazza az adatbázisunkat. Készítsünk belőle dict-ek és array-k segítségével egy kicsit könnyebben kezelhető adatstruktúrát!

In [ ]:
erettsegi={}; # ez lesz az új szótár neve
for keys  in range(len(ADAT[0])): # a szótár kulcsszavait az ADAT változó első sora tartalmazza
    erettsegi[ADAT[0][keys]]=[]                                #Minden kulcsszóhoz feltöltjük a hozzá tartozó oszlopot
    for i in range(1,len(ADAT)):
        erettsegi[ADAT[0][keys]].append(ADAT[i][keys])         #Ezen a ponton még csak listák vannak a kulcsszavak mögött
    
    erettsegi[ADAT[0][keys]]=array(erettsegi[ADAT[0][keys]])  #itt a listákat array-be konvertáljuk

Nézzünk néhány példát a most elészített erettsegi változó használatára! Nézzük meg például hogy a Budapesten vizsgázott hallgatók milyen írásbeli pontszámokat értek el! Ezt az erettsegi szótár írásbeli pontszám kulcsának egy bool array szerinti szűrésével tehetjük meg:

In [ ]:
erettsegi['írásbeli pontszám'][ erettsegi['intézmény városa']=='Budapest' ]

Azok a diákok akiknél '-' érték szerepel nem jöttek el a vizsgára! Erről meggyőződhetünk az alábbiak alapján is:

In [ ]:
erettsegi['vizsgázó részvétele'][ erettsegi['intézmény városa']=='Budapest' ]

Vizsgáljuk meg a viszgán megjelent diákok pontszámainak a diákok neme szerinti eloszlását! Két kritérium szerinti szűrést két bool array * -ával tudunk elvégezni:

In [ ]:
#fiúk adatai
erettsegi['össz pontszám'][(erettsegi['vizsgázó neme']=='férfi')*(erettsegi['vizsgázó részvétele']=='megjelent')]
In [ ]:
#lanyok adatai
erettsegi['össz pontszám'][(erettsegi['vizsgázó neme']=='nő')*(erettsegi['vizsgázó részvétele']=='megjelent')]

A map függvény és egy jól megválogatott lambda konstrukcióval egész számmá tudjuk konvertálni a fenti karakterláncokat tartalmazó listát (azaz megszabadolunk a felesleges egyenlőség és idézőjelektől!):

In [ ]:
fiuk_eredmenyei=list(
    map(lambda x:int(x[2:-1]),
        erettsegi['össz pontszám'][
          (erettsegi['vizsgázó neme']=='férfi')*(erettsegi['vizsgázó részvétele']=='megjelent') 
                              ])
)

lanyok_eredmenyei=list(
    map(lambda x:int(x[2:-1]),
        erettsegi['össz pontszám'][
            (erettsegi['vizsgázó neme']=='nő')*(erettsegi['vizsgázó részvétele']=='megjelent') 
                              ])
)

Ezek után már tudjuk alkalmazni a hist() és mean() függvényeket a pontszámok eloszlásának és átlagának vizsgálására:

In [ ]:
hist(fiuk_eredmenyei);
print("A fiúk átlagaos pontszáma:",mean(fiuk_eredmenyei))
In [ ]:
hist(lanyok_eredmenyei);
print("A lányok átlagaos pontszáma:",mean(lanyok_eredmenyei))

Azt látjuk tehát hogy ezen vizsgatípus és ezen vizsga alkalom során a lányok átlagosan jobb eredményeket értek el!

Végül határozzuk meg hogy egy városra bontva hogy alakulnak az átlagos pontszámok! Szükség lesz ehhez a városok listájára. Mivel egy városban több diák is vizsgázhatott ezért szükséges a város nevek array-ából az egyedi értékeket kiválasztani, ezt a unique függvény teszi meg:

In [ ]:
print("unique nélkül:")
print(erettsegi['intézmény városa'])
print("unique-al:")
print(unique(erettsegi['intézmény városa']))

Az alábbi for ciklus végig megy az egyedi városneveken, megnézi hogy abban a városban eljött e valaki vizsgázni, és az összes vizsgázó átlagolt pontszámát kiírja az adott város neve mellé:

In [ ]:
for varos in unique(erettsegi['intézmény városa']):
    if ((erettsegi['intézmény városa']==varos)*(erettsegi['vizsgázó részvétele']=='megjelent')).any():
        varos_atlag=mean(list(map(lambda x:float(x[2:-1]),
        erettsegi['össz százalék'][(erettsegi['intézmény városa']==varos)*(erettsegi['vizsgázó részvétele']=='megjelent')]
       )))
        print(varos,varos_atlag)

Ebben az adatsorban tehát a legjobban teljesítő város Győr a legrosszabbul teljesítő város pedig Kecskemét volt!

A feladatokban lessz rá példa hogy egy könyvtárból több file-t is be kell olvasni és feldolgozni. Ebben a feladatban némi könnyebséget jelenthet a jupyter notebook kód celláiban a ! használata amivel egyszerű shell parancsokat hajthatunk végre a notebook könyvtárjában:

In [ ]:
!ls

a ! -es parancsok kimenete mint python változó a rendelkezésünkre áll ! Például egy alkönyvtárban lévő file-ok listáját az alábbi parancs

In [ ]:
fileok=!ls ERETTSEGI_ADATOK/

mint python lista adja vissza:

In [ ]:
fileok
In [ ]: