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énycsomagokat használni. A Python szintaxisá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 file-t, ami Python-függvények, esetleg -osztályok definícióit tartalmazza. Rendszerint ez egy .py kiterjeszésű file. Több ilyen definíciókat tartalmazó file-ok ö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. Lássunk erre egy példát!


In [1]:
import numpy

A fenti parancs betöltötte a numpy modult és ezzel a modulban definiált függvények és adatstruktúrák rendelkezésünkre állnak! A numpy csomag sok gyakran használt matematikai függvény implementációját is tartalmazza Határozzuk meg például, hogy mennyi $\sin(3)$ :


In [2]:
numpy.sin(3)


Out[2]:
0.14112000805986721

Amint a fenti példa is illusztrálja egy betöltött modul függvényeit az alábbi szintaxis szerint kell használni

modul_neve.fuggveny_neve(...)

Egy modul nem csak függvényeket hanem előre definiált változókat is tartalmazhat. Például a numpy modul definiálja a matematikában és a fizikában is gyakran használt $e$ és $\pi$ számokat. Ezekre - talán nem túl meglepő módon - így hivatkozunk:


In [3]:
numpy.e


Out[3]:
2.718281828459045

In [4]:
numpy.pi


Out[4]:
3.141592653589793

Nagy moduloknak sok almodulja is van. Az almodulokra az alábbi szintaxis alapján hivatkozunk:

modul_neve.almodul_neve.fuggveny_neve(...)

Például a numpy véletlenszámokat generáló függvényei a random almodul függvényeiként vannak definiálva. Az alábbi kódcella $0$ és $99$ között generál egy véletlen számot, ehhez a numpy modul random almoduljának a randint fügvényét használja.


In [5]:
numpy.random.randint(100)


Out[5]:
82

Előfordulhat, hogy egy modulból csak bizonyos függvényeket szeretnénk betölteni. Erre ad lehetőséget a

from modul_neve import egyik_fuggveny_neve, masik_fuggveny_neve

konstrukció. Például ha a numpy csomagból csak a $\sin$ és $\cos$ függvényeket szeretnénk használni, akkor ezt az alábbi módon tehetjük meg:


In [6]:
from numpy import sin,cos

Ezután a modulnév használata nélkül rendelkezésünkre állnak a $\sin()$ és $\cos()$ függvények!


In [7]:
sin(23)**2+cos(23)**2


Out[7]:
1.0

Egy modulból akár betölthetjük az összes függvényt és változót is:


In [8]:
from numpy import *

Ekkor már például hivatkozhatunk a $\pi$ értékét tároló pi változóra a numpy kiírása nélkül.


In [9]:
pi


Out[9]:
3.141592653589793

A kurzus további részében - eltekintve néhány speciális esettől - célszerű lesz minden notebookot néhány sokszor használt modul betöltésével kezdeni. Ezt a jupyter notebookokban az alábbi paranccsal ( cell magic-el ) tehetjük meg:


In [10]:
%pylab inline


Populating the interactive namespace from numpy and matplotlib

A fenti utasítás a következő importálásokat tartalmazza:

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 *

Azaz a numpy numerikus csomagon túl többek közt betölti a matplotlib ábra készítő modult is.

Még egy adatstruktúra: array

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

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


In [11]:
vec=array([1,2,3])
vec


Out[11]:
array([1, 2, 3])

In [12]:
matr=array([[1,1,3],[4,3,5],[6,2,3]])
matr


Out[12]:
array([[1, 1, 3],
       [4, 3, 5],
       [6, 2, 3]])

In [13]:
b=array(['a','b','cd'])
b


Out[13]:
array(['a', 'b', 'cd'], 
      dtype='<U2')

In [14]:
c=array([1,2,3,'sd',])
c


Out[14]:
array(['1', '2', '3', 'sd'], 
      dtype='<U21')

In [15]:
d=array([[1,2],[1,3,4],23])
d


Out[15]:
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 mindig a legáltalánosabb adattípus érvényesül. A vec és matr változókban skalárok vannak, a b és a c válltozóban karakterláncok. A d változó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 [16]:
vec.shape


Out[16]:
(3,)

In [17]:
matr.shape


Out[17]:
(3, 3)

In [18]:
d.shape


Out[18]:
(3,)

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


In [19]:
matr.size


Out[19]:
9

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


In [20]:
vec.min()


Out[20]:
1

In [21]:
vec.max()


Out[21]:
3

In [22]:
vec.mean()


Out[22]:
2.0

In [23]:
vec.std()


Out[23]:
0.81649658092772603

Ha az array alakja nem egydimenziós, mint például a matr változó esetében, akkor a fenti függvényeket soronként vagy oszloponként is hattathatjuk a változóra:


In [24]:
matr.mean(axis=0) #oszlop szerinti átlag


Out[24]:
array([ 3.66666667,  2.        ,  3.66666667])

In [25]:
matr.mean(axis=1) #sor szerint


Out[25]:
array([ 1.66666667,  4.        ,  3.66666667])

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


In [26]:
matr.T


Out[26]:
array([[1, 4, 6],
       [1, 3, 2],
       [3, 5, 3]])

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


In [27]:
vec.sum() # A vec változó összes elemének az összege


Out[27]:
6

In [28]:
matr.prod() # A matr változó összes elemének szorzata


Out[28]:
6480

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


In [29]:
matrS=array([['a','b'],['c','d']])

In [30]:
matrS.prod()


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-30-9768fae74e07> in <module>()
----> 1 matrS.prod()

/opt/conda/lib/python3.5/site-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áljunk. 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 [31]:
linspace(0,pi,10) #10 szám 0 és pi között..


Out[31]:
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(), illetve randint() véletlen számokat tartalmazó array-ket adnak.


In [32]:
rand() #0 és 1 között egy véletlen szám


Out[32]:
0.1262308349286887

In [33]:
randn(3) #3 darab véletlen Normális elpszlású szám


Out[33]:
array([ 0.41822238, -0.74672338, -0.76571842])

In [34]:
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[34]:
array([[5, 1, 0],
       [7, 3, 1]])

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 [35]:
v1=array([1,2,3])
v2=array([2,3,3])
v1*v2


Out[35]:
array([2, 6, 9])

In [36]:
sin(v1)


Out[36]:
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 [37]:
b1=array([True,False,True,False])
b2=array([False,False,True,True])

In [38]:
b1+b2


Out[38]:
array([ True, False,  True,  True], dtype=bool)

In [39]:
b1*b2


Out[39]:
array([False, False,  True, False], dtype=bool)

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 [40]:
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 [41]:
proba1[0:3]


Out[41]:
array([ 0.        ,  1.11111111,  2.22222222])

In [42]:
proba1[-4:-1]


Out[42]:
array([ 6.66666667,  7.77777778,  8.88888889])

A list-ekkel ellentétben itt egy tetszőleges indexlistát is megadhatunk indexelésként:


In [43]:
proba1[[3,5,2]]


Out[43]:
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:


In [44]:
proba1>5 # Ez a kifejezés egy bool típusú array-t ad vissza


Out[44]:
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 [45]:
proba1[proba1>5]


Out[45]:
array([  5.55555556,   6.66666667,   7.77777778,   8.88888889,  10.        ])

Adatbázisok elemzésénél egy igen hasznos művelet valamilyen tulajdonság 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 [46]:
proba1[proba2>0.25]


Out[46]:
array([ 0.        ,  1.11111111,  2.22222222,  4.44444444,  5.55555556,
        6.66666667,  7.77777778,  8.88888889])

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

Ábrákról dióhéjban

Igen sokszor egy program futása végén a kiszámolt eredményeket ábrákban foglaljuk össze. A Python nyelvben sok modul van, ami ábra készítésére alkalmas, ezek közül talán a legelterjedtebb a matplotlib. Az alábbiakban néhány egyszerűbb ábrakészítési feladatot tekintünk át. A plot() függvény, amit a későbbiekben is igen gyakran fogunk használni, a legalapvetőbb ábrakészítő függvény. Ezt a függvény a matplotlib modul pyplot almoduljában található. Tehát ha a matplotlib modult betöltöttük akkor az alábbi módon használható:


In [47]:
x=[1,2,3,4,5]
y=[1,4,13,10,25]
matplotlib.pyplot.plot(x,y)


Out[47]:
[<matplotlib.lines.Line2D at 0x7f8d21942828>]

A fenti parancs tehát az y tömböt ábrázolja az x függvényében.

Mivel a notebook elején lefuttatuk a %pylab inline parancsot, ami többek között a matplotlib.pyplot modulból is betöltötte az összes függvényt, ezért elegendő a plot() függvényt magában hívni!


In [48]:
plot(x,y)


Out[48]:
[<matplotlib.lines.Line2D at 0x7f8d20eb8828>]

A Python-ban konkrét matematikai függvények ábrázolásának az a legegyszerűbb módja, ha két tömböt generálunk, egyet, amely azokat a pontokat tartalmazza, ahol a függvényt ki szeretnénk értékelni, illetve egyet, amely a függvény értékét tartalmazza a kiértékelendő pontokban. Ezeket a plot() függvény segítségével ábrázoljuk. Az alábbi példán a $\sin(t)$ függvényt ábrázoljuk a $[0,2\pi]$ intervallumon tíz darab mintavételezési pontot használva.


In [49]:
t=linspace(0,2*pi,10)
plot(t,sin(t))


Out[49]:
[<matplotlib.lines.Line2D at 0x7f8d2175ccf8>]

Ha több mintavételezési pontot használunk akkor simább függvényt kapunk. Ezt úgy érhetjük el, ha a linspace() függvény harmadik változójának segítségével megnöveljük a legenerált értékek számát.


In [50]:
t=linspace(0,2*pi,100) 
plot(t,sin(t))


Out[50]:
[<matplotlib.lines.Line2D at 0x7f8d200f0860>]

Ha egyszerre több függvényt szeretnénk ábrázolni, akkor egy kódcellán belül több plot() parancsot is kiadhatunk:


In [51]:
plot(t,sin(t))
plot(t,cos(t))


Out[51]:
[<matplotlib.lines.Line2D at 0x7f8d200feba8>]

Ha kétváltozós függvényt szeretnénk ábrázolni, akkor ahhoz a mintavételezést a numpy csomag meshgrid() függvényével tehetjük meg az alábbi szintaxis szerint:


In [52]:
xrange=linspace(-3,3,100) # határok és pontok száma az x irányba
yrange=linspace(-3,3,100) # határok és pontok száma az y irányba
x,y=meshgrid(xrange,yrange) # mintavételezés az x és y síkban

Két változós függvényt a pcolor() matplotlib függvény segítségével tudunk ábrázolni. A fent definiált x és y tömbök segítségével például az $$f(x,y)=\mathrm{e}^{-(x^2+y^2)}$$ kétdimenziós Gauss-görbét az alábbi módon ábrázolhatjuk:


In [53]:
pcolor(x,y,exp(-(x**2+y**2)))


Out[53]:
<matplotlib.collections.PolyCollection at 0x7f8d21475f60>

Végül a statisztikus problémák vizsgálatakor számtalanszor használt hisztogramkészítéssel ismerkedjünk. Erre a matplotlib modul a hist() függvényét fogjuk használni. Előszöris gyártsunk a numpy modul random almoduljának randn() függvényével ezer darab véletlen számot. A randn() függvény Gauss-eloszlás szerint generál véletlen számokat. A numpy random modulja sok egyéb más eloszlás szerint is tud véletlenszámokat generálni, a rendelkezésre álló generátorokról itt található információ.


In [54]:
gauss_eloszlas=random.randn(1000)

Egy adatsor hisztogramját egyszerűen a hist() függvénnyel lehet legyártani:


In [55]:
hist(gauss_eloszlas)


Out[55]:
(array([   1.,    6.,   14.,   79.,  197.,  271.,  273.,  117.,   34.,    8.]),
 array([-4.29185613, -3.54341105, -2.79496596, -2.04652088, -1.29807579,
        -0.54963071,  0.19881438,  0.94725946,  1.69570454,  2.44414963,
         3.19259471]),
 <a list of 10 Patch objects>)

A fenti ábrán az ábrázolt intervallumot a hist parancs, az alapértelmezésnek megfelelően 10 alintervallumra osztja, és azt jeleníti meg, hogy az adott alintervallumban hány érték található a bemeneti gauss_eloszlas tömbben.

Fontos megjegyezni hogy a plot(), pcolor() éshist() parancsoknak a fent tárgyalt legegyszerűbb használatán túl számos alapértelmezett értékekkel ellátott kulcsszavas argumentuma van, melyekről egy rövid leírás a megfelelő függvény dokumentációjában ( a docstringjében ) található.