☠ Ábrakészítés a plotly modul segítségével

Az ábrakészítéshez természetesen az eddig használt matplotlib modul mellett számos másik függvénycsomag is létezik. A lent röviden bemutatott plotly modul előnye, hogy az alapbeállításokat használva is elegáns és interaktív ábrákat tudunk készíteni.

A plotly szintaxisa az eddigiektől azonban némiképp eltér, ennek a rövid bemutatására törekszünk néhány példán keresztül.

A plotly offline és online üzemmódja

A plotly alapvetően egy webes felület, melyen egy ingyenes regisztráció és bejelentkezés után mindenkinek lehetősége nyílik adatok feltöltésére, azok feldolgozására interaktív ábrák formájában, majd a legyártott ábrák megosztására. Ez a módszer jelentősen megkönnyítheti egy csoporton belül a kollaborációt, hiszen a csoporttagok nemcsak az ábrákhoz férnek hozzá, hanem magukhoz az adatokhoz és az ábrákat legyártó kódrészletekhez is. Így ha valaki csak a vonalak színét szeretné egy ábrán megváltoztatni, nem kell e-mailben megkérnie az ábra eredeti gyártóját, hogy ezt tegye meg, hanem a kód átírásával saját maga is megoldhatja.

Néha azonban a vizsgált adatok természetüknél fogva nem tölthetők fel egy publikus tárhelyre, a privát tárhely használatához pedig Pro accountra van szükség, ami értelemszerűen nem ingyenes. Gyakran előfordul olyan probléma is, hogy a feldolgozni kívánt adatfájlok annyira nagyok, hogy feltöltésük (illetve bármilyen mozgatásuk) nem praktikus. Ilyen és ehhez hasonló esetekben szükség lenne egy "lokális" ábrakészítő opcióra, mellyel nem az adatok mennek a plotlyhoz, hanem a plotly jön az adatokhoz. Ennek a megoldására készült a Plotly Offline verziója, mellyel az ábrák és adatok nem egy központi szerveren tárolódnak, hanem a lokális gépen, illetve notebookban. A lenti példákban ezt az offline verziót használjuk.

(A plotly ábrák generálása emellett nemcsak a mostanra megszokott Python nyelven lehetséges, hanem akár R-ben vagy JavaScriptben is. Ezekről az alábbiakban nem ejtünk szót.)

A plotly modul importálása és az offline verzió függvényeinek betöltése


In [11]:
from plotly import *
from plotly.offline import *
init_notebook_mode()


IOPub data rate exceeded.
The notebook server will temporarily stop sending output
to the client in order to avoid crashing it.
To change this limit, set the config variable
`--NotebookApp.iopub_data_rate_limit`.

Egyéb hasznos, már ismert modulok importálása


In [2]:
import numpy as np

Egyszerű ábra készítése

Adatok gyártása

Mint ahogy a matplotlib modulnál is láttuk, minden ábra generálásához elsőként az adatok betöltésére vagy legyártására van szükség. Nézzük a szokásos példánkat: ábrázoljuk a sin(x) függvényt 0 és 2π között.


In [3]:
x_pontok = np.linspace(0,2*np.pi,10)
y_pontok = np.sin(x_pontok)

Az ábra elkészítése

A plotly kicsit másképp viselkedik, mint amit a matplotlib-nél láttunk. Itt egy ábrát alapvetően különféle objektumokkal és egymásba ágyazott dict-ekkel definiálhatunk. Az ábra minden egyes jellemzőjéhez (pl. színek, rács, az adatok, stb.) tartozik egy kulcs-érték páros.

Ezeket a jellemzőket a plotly két kategóriába sorolja, a trace-ekbe és a layout-ba. A trace-ek olyan objektumok, melyek egy adatsort írnak le az ábrán, például egy Scatter vagy egy Heatmap objektum. Egy ábrán természetesen több trace is lehet, ha például kétféle adatsort is ábrázolni szeretnénk. A trace-ek emellett ábrákon belül is kombinálhatók, egyetlen ábrán megjeleníthetünk elszórt mérési pontokat és oszlopdiagramot is. A layout jellemzők pedig az ábra egészére vonatkozó formázási utasítások, például az ábra címe, a háttér színe, a tengelyfeliratok, illetve további annotációk (szövegek).

Az ábrakészítésben a plotly dokumentációja sok segítséget ad.


1. Trace-ek legyártása

Egy ábra definiálásakor tehát elsőként az adatokból le kell gyártanunk a megfelelő trace-eket. A konkrét példában egy olyan ábrát szeretnénk, ahol a fenti y_pontok array az x_pontok array függvényében van ábrázolva, és a pontok folytonos vonallal vannak összekötve. Ezt az alábbi utasítással tehetjük meg:


In [4]:
trace_sin_gorbe = graph_objs.Scatter(x=x_pontok, y=y_pontok, mode='lines')
trace_sin_gorbe


Out[4]:
{'mode': 'lines',
 'type': 'scatter',
 'x': array([ 0.        ,  0.6981317 ,  1.3962634 ,  2.0943951 ,  2.7925268 ,
         3.4906585 ,  4.1887902 ,  4.88692191,  5.58505361,  6.28318531]),
 'y': array([  0.00000000e+00,   6.42787610e-01,   9.84807753e-01,
          8.66025404e-01,   3.42020143e-01,  -3.42020143e-01,
         -8.66025404e-01,  -9.84807753e-01,  -6.42787610e-01,
         -2.44929360e-16])}

Itt tehát legyártottunk egy "gráf objektumot", mely most éppen Scatter típusú. A kiíratásból láthatjuk, hogy bár ezt mint egy objektumot definiáltuk, valójában egy olyan dict, melyben a 'type' kulcshoz a 'scatter' érték tartozik. Tehát tulajdonképpen az alábbi módszer is működne a fenti trace definiálására, az objektumként való megadás csak a kényelmünket szolgálja.


In [5]:
trace_sin_gorbe = {'mode': 'lines',
                  'type' : 'scatter',
                  'x': x_pontok,
                  'y': y_pontok}

Arra viszont figyeljünk, hogy ha a dict-ként való megadást választjuk, akkor a kulcs-érték párokat kettősponttal válasszuk el, a gráf objektumként való megadás esetén pedig egyenlőségjellel.

A mode kulcshoz írt 'lines' azt jelenti, hogy az adatpontokat vonallal szeretnénk az ábrán összekötni. Ha csak be szeretnénk szórni a pontokat az ábrára, akkor ide írjuk a 'markers' kifejezést.

Végül tegyük be az adatok_sin_gorbe listába a legyártott trace objektumokat. Mivel ebben a példában csak egyetlen trace-re volt szükség, így ez elhagyható lenne, de több trace esetén az összeset célszerű egy listába összefűzni:


In [6]:
adatok_sin_gorbe = [trace_sin_gorbe]

2. A layout definiálása

Adjunk az ábránknak címet, a tengelyekre pedig rakjunk tengelyfeliratot. Ezt a Layout objektum specifikálásával tehetjük meg az alábbiak szerint:


In [7]:
layout_sin_gorbe = graph_objs.Layout(title='Ez az ábra címe',
                                    xaxis=graph_objs.XAxis(title='x'), 
                                    yaxis=graph_objs.YAxis(title='sin(x)'))
layout_sin_gorbe


Out[7]:
{'title': 'Ez az ábra címe',
 'xaxis': {'title': 'x'},
 'yaxis': {'title': 'sin(x)'}}

A Layout objektum xaxis változója egy XAxis objektum, melyben már a title változó értéke közvetlenül megadható. Hasonlóan járunk az yaxis változó esetén is. Látjuk azonban, hogy csakúgy mint a trace esetén, ezek az objektumok itt is helyettesíthetők egymásba ágyazott dict-ekkel:


In [8]:
layout_sin_gorbe = {'title': 'Ez az ábra címe',
                  'xaxis': {'title': 'x'},
                  'yaxis': {'title': 'sin(x)'}}

3. A Figure objektum legyártása

Miután az összes szükséges objektum elkészült (az adatokhoz a megfelelő trace-ek, az ábra formázásához pedig a Layout), ezeket összefűzve definiálhatjuk a Figure objektumot:


In [9]:
figure_sin_gorbe = graph_objs.Figure(data=adatok_sin_gorbe, layout=layout_sin_gorbe)
figure_sin_gorbe


Out[9]:
{'data': [{'mode': 'lines',
   'type': 'scatter',
   'x': array([ 0.        ,  0.6981317 ,  1.3962634 ,  2.0943951 ,  2.7925268 ,
           3.4906585 ,  4.1887902 ,  4.88692191,  5.58505361,  6.28318531]),
   'y': array([  0.00000000e+00,   6.42787610e-01,   9.84807753e-01,
            8.66025404e-01,   3.42020143e-01,  -3.42020143e-01,
           -8.66025404e-01,  -9.84807753e-01,  -6.42787610e-01,
           -2.44929360e-16])}],
 'layout': {'title': 'Ez az ábra címe',
  'xaxis': {'title': 'x'},
  'yaxis': {'title': 'sin(x)'}}}

A fentiekhez hasonlóan ez is "csak" egy egymásba ágyazott dict objektum lesz, melyet a hagyományos úton is definiálhattunk volna.


4. Az ábra kirajzoltatása

Az így elkészített Figure objektum már ábrázolható:


In [10]:
iplot(figure_sin_gorbe)



Összefoglalva:

A fentiek talán bonyolultnak tűnnek, de valójában néhány sorban el tudjuk készíteni a fenti ábrát. Láthattuk, hogy a gráfobjektumok definiálása mindig a graph_objs almodul használatával történik. Érdemes tehát az egész almodul összes függvényét importálni, így többé nem kell kiírnunk a graph_objs. részletet.

A fenti ábra persze nagyon "szögletes" egy valódi sin-görbének, de ezen könnyen segíthetünk: növeljük meg a mintavételezési pontok számát és ábrázoljuk újra, most azonban a cos-görbével együtt. Ekkor már két trace objektumot kell gyártanunk.


In [11]:
from plotly.graph_objs import *

In [12]:
x_pontok_uj = np.linspace(0,2*np.pi,50)
y_pontok_sin = np.sin(x_pontok_uj)
y_pontok_cos = np.cos(x_pontok_uj)
trace_sin_gorbe = Scatter(x=x_pontok_uj, y=y_pontok_sin, mode='lines')
trace_cos_gorbe = Scatter(x=x_pontok_uj, y=y_pontok_cos, mode='lines')
adatok = [trace_sin_gorbe, trace_cos_gorbe]
layout_sin_gorbe = Layout(title='Ez az új ábra címe',
                          xaxis=XAxis(title='x'), 
                          yaxis=YAxis(title='sin(x), cos(x)'))
figure_uj = Figure(data=adatok, layout=layout_sin_gorbe)
iplot(figure_uj)


Milyen szép sima görbéket kaptunk! Ellenőrizzük, hogy ez valóban így van-e! Próbáljunk az ábra jobb felső sarkában lévő gombokkal ráközelíteni egy-egy csúcsra. Látható, hogy ilyen skálán még ezek a görbék is szögletesek.

A plotly nagy előnye az ilyen jellegű interaktív nézegetési lehetőség. Ha egy ábrán nagyon sok mérési pont van, akkor csak kellően ráközelítve tudjuk őket megkülönböztetni egymástól. A jobb oldalon a trace 0 és trace 1 feliratok melletti vonalakra kattintva az aktuális görbe az ábráról ideiglenesen eltűntethető, majd ismételt kattintással újra megjeleníthető.

Oszlopdiagram készítése

Ha a fenti ábrát nem vonalakkal szeretnénk elkészíteni, hanem mondjuk a cos függvényt oszlopdiagrammal, csak a trace típusán kell változtatnunk:


In [13]:
trace_sin_gorbe = Scatter(x=x_pontok_uj, y=y_pontok_sin, mode='lines')
trace_cos_gorbe = Bar(x=x_pontok_uj, y=y_pontok_cos)
adatok = [trace_sin_gorbe, trace_cos_gorbe]
layout_sin_gorbe = Layout(title='Ez az oszlopos ábra címe',
                          xaxis=XAxis(title='x'), 
                          yaxis=YAxis(title='sin(x), cos(x)'))
figure_oszlop = Figure(data=adatok, layout=layout_sin_gorbe)
iplot(figure_oszlop)


3D-s ábra készítése

A következő háromdimenziós ábrához az ábrázolni kívánt pontok koordinátáit a plotly_3D.txt szövegfájlban találjuk. Az adatok innen származnak, hasonló ábrák készítéséhez hasznos adatfájlok itt találhatók.

Az adatfájlban az első oszlop az x, a második az y, a harmadik pedig a z koordináta. Elsőként olvassuk be az adatokat. Ehhez a loadtxt függvényt használjuk, mely a numpy modulban található.


In [14]:
data_file = np.loadtxt('data/plotly_3D.txt')
x_tengely = data_file[:,0]
y_tengely = data_file[:,1]
z_tengely = data_file[:,2]

Ezek után már csak az ábrázolás van hátra. Most nem az eddig használt Scatter objektumra van szükség, hanem ennek a 3D-s változatára, a Scatter3d-re.


In [15]:
trace_3D = Scatter3d(x=x_tengely, y=y_tengely, z=z_tengely, mode='markers', marker = dict(size=2))
adatok_3D = [trace_3D]
layout_3D = Layout(width=900,height=500,scene=dict(aspectmode='manual', aspectratio = dict(x=0.2, y=1, z=2/3)))
fig_3D = Figure(data=adatok_3D, layout=layout_3D)
iplot(fig_3D)


Nagyítsuk, forgassuk kedvünkre az ábrát. Próbáljuk meg kitalálni, hogy mit jelenthet a Layout objektumon belül szereplő aspectratio kulcsszó.

☠ Színes kör

Ábrázoljunk scatter pontokkal egy $r=3$ egység sugarú kört, a pontok színét aszerint választva, hogy melyik síknegyedben van a pont.

A kör ábrázolásához elsőként le kell gyártani azokat az $(x,y)$ koordinátapárokat, melyek majd a kört kirajzolják. Ennek a legegyszerűbb módja az alábbi paraméterezés:

$$x = r\cos(\phi)$$$$y = r\sin(\phi)$$

Ahol $\phi$ az x-tengelytől mért szög. Így a $\phi$ értékeit egyenletesen megválasztva a $(0,2\pi)$ intervallumon, az $x$ és $y$ értékeit tartalmazó array-ek a fentiek szerint adódnak:


In [16]:
r = 3
phi = np.linspace(0,2*np.pi,1000)
x = r*np.cos(phi)
y = r*np.sin(phi)

Attól függően, hogy az adott $x$ és $y$ milyen előjelű, 4-féle kategóriába sorolhatók a pontok. (Másként fogalmazva attól függően, hogy a pont melyik síknegyedbe esik.) A tengelyeket mindig a tőlük az óramutató járásával megegyező irányba eső síknegyedhez soroljuk.

Hogy ezeket különböző színnel tudjuk ábrázolni, egy lehetséges megoldás, hogy négy különböző trace objektumot generálunk az alábbiak szerint:


In [17]:
trace_1 = Scatter(x=x[(x<0) * (y<0)], y=y[(x<0) * (y<0)], mode='lines', marker=dict(color='red'))
trace_2 = Scatter(x=x[(x>=0) * (y<0)], y=y[(x>=0) * (y<0)], mode='lines', marker=dict(color='blue'))
trace_3 = Scatter(x=x[(x<0) * (y>=0)], y=y[(x<0) * (y>=0)], mode='lines', marker=dict(color='green'))
trace_4 = Scatter(x=x[(x>=0) * (y>=0)], y=y[(x>=0) * (y>=0)], mode='lines', marker=dict(color='orange'))
data = [trace_1, trace_2, trace_3, trace_4]
layout = Layout(title='Ez egy színes kör', width=500, height=500)
fig = Figure(data=data, layout=layout)
iplot(fig)