Matplotlib¶
Matplotlib on populaarne Pythoni pakett andmete visualiseerimiseks. Kuna tegemist on väga võimalusterohke tarkvaraga, siis antud peatükk piirdub teegi põhimõtete ja kõige levinumate graafikutüüpide tutvustamisega. Täpsemaid detaile ja keerulisemaid graafikutüüpe uuri paketi ametlikust dokumentatsioonist.
Matplotlib ja NumPy
Kuigi enamasti tutvustatakse Matplotlib-i koos NumPy-ga, siis see pole tingimata vajalik. Lihtsuse huvides ongi selles peatükis kõik andmed esitatud harilike Pythoni listidena, aga kes soovib, võib nende asemel kasutada vabalt ka NumPy massiive.
Paketi paigaldamine¶
Matplotlib-i saab paigaldada pip-i abil. Paketi nimeks on matplotlib, seega installimise käsk on pip install matplotlib
(Windowsi puhul) või pip3 install matplotlib
(Linuxi ja Mac-i puhul).
Põhimõtted¶
Oletame, et meil on andmed mingi firma sissetulekute kohta erinevatel kuudel. Proovime seda infot kõigepealt visualiseerida matplotlib
-i joongraafiku abil:
import matplotlib.pyplot as plt
kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
fig = plt.figure() # Kõigepealt loome joonist tähistava objekti
ax = fig.add_subplot(1,1,1) # ja lisame joonisele joonestusala.
ax.plot(kuud, sissetulekud) # Lisame joonestusalale joondiagrammi
ax.set_xlabel("Kuud") # ja x-telje pealkirja
plt.show() # Kuvame joonise ekraanile.
Käivita see programm ja katseta avanenud aknas olevaid nuppe.
Selle lihtsa näite põhjal saame välja tuua kõige olulisemad matplotlib
-i põhimõtted ja tavad.
- Kõige sagedamini kasutatav matplotlib-i moodul on
matplotlib.pyplot
, mis imporditakse tavaliselt lühema nimeplt
alla. - Imporditud moodulis on funktsioon
figure
, mille abil luuakse joonist tähistav objekt. - Ühel joonisel võib olla mitu graafikut/joonestusala (ing k subplot). Uue joonestusala loomiseks kasutatakse joonise meetodit
add_subplot
, mille argumendid näitavad mitmeks reaks ja veeruks joonis jagada ning mitmes joonestusala luua. Ühe joonestusalaga jooniste puhul on argumendid alati1,1,1
. add_subplot
tagastab objekti klassistAxes
(teljestik), mille abil saab konkreetsele joonestusalale elemente lisada.- Antud näites kasutasime joonestamiseks meetodit
plot
, mis on mõeldud joondiagrammide koostamiseks, agaAxes
oskab joonistada ka sektor-, tulp- ja hajuvusdiagramme, histogramme, lihtsaid kujundeid ja palju muud. - Sama objekti kaudu käib näiteks ka telgede ja legendi seadistamine.
- Antud näites kasutasime joonestamiseks meetodit
- Tulemust saab näha kasutades joonise meetodit
show
. Alternatiivina (või lisaks) võib joonise meetodigasavefig
ka faili salvestada.
Alternatiiv: pyplot-stiil
Internetis ringi vaadates leiate palju matplotlib-i kasutamise näiteid, kus pole meetodeid figure
ja add_subplot
üldse kasutatud. Meie näide võiks olla neis kohtades kirja pandud umbes selliselt:
import matplotlib.pyplot as plt
kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
plt.plot(kuud, sissetulekud) # Lisame joonestusalale joondiagrammi
plt.xlabel("Kuud") # ja x-telje pealkirja
plt.show()
Nagu näha on siin plot
, legend
ja show
võetud otse moodulist matplotlib.pyplot
.
Tegelikult luuakse ka sellise koodi korral joonisele ja joonestusalale vastavad objektid, aga see toimub automaatselt. Teisisõnu matplotlib.pyplot.plot()
, matplotlib.pyplot.legend()
ja matplotlib.pyplot.show()
on veidi kavalamad, kui vastavad Axes
ja Figure
meetodid.
Kuna praktiliselt kõigile Axes
meetoditele olemas vastavad kavalad matplotlib.pyplot
funktsioonid, siis saaks selle stiiliga panna kokku sama keerulisi jooniseid nagu eraldi väljatoodud Axes
objektide abil.
Kui sellised kavalad funktsioonid on olemas, miks siis üldse näha vaeva Figure
ja Axes
objektide loomisega? Tegelikult ei olegi tarvis – eriti just lihtsate, ühekordseks kasutamiseks mõeldud graafikute koostamiseks on pyplot-stiil väga mugav ja asjakohane. Keerulisemate graafikute puhul aga võimaldavad eraldi väljatoodud Figure
ja Axes
objektid (st. objekt-orienteeritud stiil) lahendust selgemalt struktureerida ning teatud puhkudel on nende sissetoomine lausa möödapääsematu. Seepärast ongi selles õpikus matplotlib-i tutvustamiseks valitud objektorienteeritud stiil.
Selle teema kohta saab lähemalt lugeda siit: http://matplotlib.org/faq/usage_faq.html#coding-styles
Joondiagramm¶
Eelmisest näitest nägime, et joondiagramme saab koostada Axes
meetodi plot
abil. Uurime nüüd asja lähemalt.
Teljestiku seadistamine¶
Ülaltoodud koodi käivitamisel pidi ilmuma umbes selline aken:
Nagu näha, on matplotlib seadistanud graafiku teljestiku nii, et etteantud andmepunktid mahuvad parajasti ära, aga see ei pruugi olla alati parim valik – selle pildi järgi tundub jaanuari ja juuni erinevus palju suurem, kui see tegelikult oli. Telgede ulatuse seadistamiseks saame kasutada meetodeid set_xlim
ja set_ylim
, mis määravad vastava telje nähtavuspiirkonna.
Teine probleem on see, et y-teljel ei ole kõikide kuude numbreid. Õnneks saab meetoditega set_xticks
ja set_yticks
määrata, millistesse kohtadesse tuleb telgedel märgid (ticks) kuvada.
Täiendame nüüd oma graafikut neid võimalusi kasutades:
import matplotlib.pyplot as plt
kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
fig = plt.figure() # Kõigepealt loome joonist tähistava objekti
ax = fig.add_subplot(1,1,1) # ja lisame joonisele joonestusala.
ax.plot(kuud, sissetulekud) # Lisame joonestusalale joondiagrammi
ax.set_xlabel("Kuud") # ja x-telje pealkirja
ax.set_ylim(0, 2000) # Määrame y-telje nähtavuspiirkonna
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12]) # ja x-telje märgid
plt.show() # Kuvame joonise ekraanile.
Näide telgede paigutamisest keskele
Harjutus. Märkide seadistamine¶
Uuri meetodit set_xticklabels
ja proovi manada x-teljele kuu numbrite asemel kuu nimed.
Joone ja andmepunktide seadistamine¶
Kui uurid meie firma sissetuleku andmeid lähemalt, siis märkad, et veebruari ja märtsi andmed on puudu ja joondiagrammi vastav lõik on joonistatud lihtsalt jaanuari ja aprilli vahele. Oleks hea, kui graafikust tuleks välja, milliste kuude kohta on tegelikud andmepunktid olemas.
Joone ja andmepunktide välimust saame määrata plot
meetodi kolmanda argumendiga:
import matplotlib.pyplot as plt
kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
fig = plt.figure() # Kõigepealt loome joonist tähistava objekti
ax = fig.add_subplot(1,1,1) # ja lisame joonisele joonestusala.
ax.plot(kuud, sissetulekud, "o-") # Lisame joonestusalale joondiagrammi
ax.set_xlabel("Kuud") # ja x-telje pealkirja
ax.set_ylim(0, 2000) # Määrame y-telje nähtavuspiirkonna
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12]) # ja x-telje märgid
plt.show() # Kuvame joonise ekraanile.
Antud näites o
tähendab seda, et iga andmepunkti kohale tuleb joonistada täpike ja -
tähendab seda, et andmepunktide vahele tuleb tõmmata kriips.
Lisaks punkti ja kriipsu kuju määramisele, saaks sama argumendiga näidata ära ka nende värvi. Näiteks "^--g"
(g nagu green) tekitab rohelised kolmnurksed andmepunktid ja katkendliku joone ning "*r"
tekitab punased tärnikujulised andepunktid ilma jooneta.
Rohkem infot leiab meetodi plot
dokumentatsioonist.
TODO: grid
Mitme näitaja võrdlemine¶
Tuli välja, et firmal on kogutud andmed ka antud kuude väljaminekute kohta. Teeme graafiku, mis näitab sissetulekuid ja väljaminekuid korraga. Selleks, et oleks, selge, kumb joon tähistab kumba näitajat, lisame graafikule ka legendi – selleks lisame plot
väljakutsetele label
argumendid ja kutsume välja joonestusala meetodi legend()
:
import matplotlib.pyplot as plt
kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
väljaminekud = [700, 1160, 1556, 1520, 1415, 1180, 1770, 500, 408, 505]
fig = plt.figure() # Kõigepealt loome joonist tähistava objekti
ax = fig.add_subplot(1,1,1) # ja lisame joonisele joonestusala.
ax.plot(kuud, sissetulekud, "o-", label="Sissetulekud")
ax.plot(kuud, väljaminekud, "^-r", label="Väljaminekud")
ax.set_xlabel("Kuud")
ax.set_ylim(0, 2000) # Määrame y-telje nähtavuspiirkonna
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12]) # ja x-telje märgid
ax.legend()
plt.show() # Kuvame joonise ekraanile.
Tulpdiagramm¶
Tulpdiagrammi koostamiseks on meetod bar
, millele tuleb anda argumendiks tulpade positsioonid x-teljel, tulpade kõrgused ja tulba laius (x-telje skaalal):
import matplotlib.pyplot as plt
kuud = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
ümbrikke_kulunud = [ 2, 6, 2, 7, 6, 2, 3, 2, 4, 4, 1, 13]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.bar(kuud, ümbrikke_kulunud, 0.8)
ax.set_xlabel("Kuud")
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12])
plt.show()
Mitme näitaja tulpdiagramm¶
Kui me tahame tulpadena kõrvuti näha ümbrike ja kirjaklambrite kulusid, siis tuleb lihtsalt meetodit bar
välja kutsuda kaks korda. Seejuures aga tuleb sättida eri näitajate tulbad nii, et nad üksteist varjutama ei hakkaks. Samuti tuleb teha tulbad kitsamaks. Lisaks anname bar
-ile label
argumendi, mille põhjal legend()
teeb joonisele legendi:
import matplotlib.pyplot as plt
kuud = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
ümbrikke_kulunud = [ 2, 6, 2, 7, 6, 2, 3, 2, 4, 4, 1, 13]
kirjaklambreid_kulunud = [ 5, 2, 1, 3, 3, 0, 0, 0, 1, 2, 1, 3]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# Selleks, et tulbad ei satuks üksteise peale, nihutame neid pisut
ax.bar([kuu - 0.2 for kuu in kuud], ümbrikke_kulunud, 0.4, label="Ümbrikke")
ax.bar([kuu + 0.2 for kuu in kuud], kirjaklambreid_kulunud, 0.4, label="Kirjaklambreid")
ax.set_xlabel("Kuud")
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12])
ax.legend()
plt.show()
Tulpade nihutamiseks kasutatud konstruktsiooni ([kuu - 0.2 for kuu in kuud]
) nimetatakse listikomprehensiooniks.
Veel võimalusi¶
- Horisontaalse tulpdiagrammi jaoks on meetod
barh<matplotlib.axes.Axes.barh()
- …
Kahe y-telje kasutamine¶
Siiani tehtud joon- ja tulpdiagrammide kombineerimine ei ole tehniliselt võttes midagi rasket – kuna x-teljel oli kõigil juhtudel sama skaala, siis tuleb lihtsalt kõik elemendid lisada samale joonestusalale. Tuleb vaid arvestada, et rahaasjade andmetes oli meil veebruari ja märtsi kohal auk, aga kontoritarvete puhul oli ka nende kuude jaoks andmed olemas:
import matplotlib.pyplot as plt
raha_kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
väljaminekud = [700, 1160, 1556, 1520, 1415, 1180, 1770, 500, 408, 505]
asjade_kuud = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
ümbrikke_kulunud = [ 2, 6, 2, 7, 6, 2, 3, 2, 4, 4, 1, 13]
kirjaklambreid_kulunud = [ 5, 2, 1, 3, 3, 0, 0, 0, 1, 2, 1, 3]
fig = plt.figure() # Kõigepealt loome joonist tähistava objekti
ax = fig.add_subplot(1,1,1) # ja lisame joonisele joonestusala.
ax.set_ylim(0, 2000) # Määrame y-telje nähtavuspiirkonna
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12]) # ja x-telje märgid
ax.plot(raha_kuud, sissetulekud, "o-", label="Sissetulekud")
ax.plot(raha_kuud, väljaminekud, "^-r", label="Väljaminekud")
ümbriku_tulpade_positsioonid = [kuu - 0.4 for kuu in asjade_kuud]
ax.bar(ümbriku_tulpade_positsioonid, ümbrikke_kulunud, 0.4, label="Ümbrikke")
ax.bar(asjade_kuud, kirjaklambreid_kulunud, 0.4, label="Kirjaklambreid")
ax.set_xlabel("Kuud")
ax.legend()
plt.show() # Kuvame joonise ekraanile.
Kahjuks see lähenemine siiski ei tööta, sest rahasummad on palju suuremad kui kontoritarvete arvud ja seetõttu viimased ei paista üldse välja. Lahenduseks on kahe erineva y-skaala kasutamine (TODO: pikem selgitus ja lingid):
import matplotlib.pyplot as plt
raha_kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
väljaminekud = [700, 1160, 1556, 1520, 1415, 1180, 1770, 500, 408, 505]
asjade_kuud = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
ümbrikke_kulunud = [ 2, 6, 2, 7, 6, 2, 3, 2, 4, 4, 1, 13]
kirjaklambreid_kulunud = [ 5, 2, 1, 3, 3, 0, 0, 0, 1, 2, 1, 3]
fig = plt.figure() # Kõigepealt loome joonist tähistava objekti
ax = fig.add_subplot(1,1,1) # ja lisame joonisele joonestusala.
ax.set_ylim(0, 2000) # Määrame y-telje nähtavuspiirkonna
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12]) # ja x-telje märgid
ax.plot(raha_kuud, sissetulekud, "o-", label="Sissetulekud")
ax.plot(raha_kuud, väljaminekud, "^-r", label="Väljaminekud")
ümbriku_tulpade_positsioonid = [kuu - 0.4 for kuu in asjade_kuud]
ax2 = ax.twinx()
ax2.bar(ümbriku_tulpade_positsioonid, ümbrikke_kulunud, 0.4, label="Ümbrikke")
ax2.bar(asjade_kuud, kirjaklambreid_kulunud, 0.4, label="Kirjaklambreid")
ax.set_xlabel("Kuud")
ax.legend(loc="upper left")
ax2.legend(loc="upper right")
plt.show() # Kuvame joonise ekraanile.
Nüüd häirib tulemuses veel see, et jooned jäävad osaliselt tulpade taha peitu, ning sissetulekute joon on ümbrike tulpadega sama värvi. Õnneks pakub matplotlib lahenduse ka neile muredele:
import matplotlib.pyplot as plt
raha_kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
väljaminekud = [700, 1160, 1556, 1520, 1415, 1180, 1770, 500, 408, 505]
asjade_kuud = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
ümbrikke_kulunud = [ 2, 6, 2, 7, 6, 2, 3, 2, 4, 4, 1, 13]
kirjaklambreid_kulunud = [ 5, 2, 1, 3, 3, 0, 0, 0, 1, 2, 1, 3]
fig = plt.figure() # Kõigepealt loome joonist tähistava objekti
ax = fig.add_subplot(1,1,1) # ja lisame joonisele joonestusala.
ax.set_ylim(0, 2000) # Määrame y-telje nähtavuspiirkonna
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12]) # ja x-telje märgid
ax.plot(raha_kuud, sissetulekud, "o-g", label="Sissetulekud")
ax.plot(raha_kuud, väljaminekud, "^-r", label="Väljaminekud")
ümbriku_tulpade_positsioonid = [kuu - 0.4 for kuu in asjade_kuud]
ax2 = ax.twinx()
ax2.bar(ümbriku_tulpade_positsioonid, ümbrikke_kulunud, 0.4, label="Ümbrikke")
ax2.bar(asjade_kuud, kirjaklambreid_kulunud, 0.4, label="Kirjaklambreid")
ax.set_xlabel("Kuud")
ax.legend(loc="upper left")
ax2.legend(loc="upper right")
# sätime joonte teljestiku tulpade omast ettepoole
ax.set_zorder(ax2.get_zorder() + 1)
ax.patch.set_visible(False)
plt.show() # Kuvame joonise ekraanile.
Rohkem infot
- http://matplotlib.org/examples/api/two_scales.html näitab, kuidas rõhutada telje ja joone seotust värvides erinevate telgede märgendid eri värvi
- http://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html näitab kuidas tekitada graafikule rohkem kui 2 y-telge.
Sektordiagramm¶
Sektordiagrammi koostamiseks on mõeldud meetod pie
, mis võtab esimeseks argumendiks sektorite suurused ja labels
argumendiks sektorite nimed:
import matplotlib.pyplot as plt
kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.pie(sissetulekud, labels=kuud)
plt.show()
Histogramm¶
Jätame nüüd firma rahule ja võtame ette järgmise andmekomplekti. Failis punktid.csv
(kodeering UTF-8) on tudengite poolt mingis aines kogutud punktid. Iga rida tähistab ühe tudengi andmeid. Esimeses veerus on kodutööde punktisumma, teises veerus on testi eest saadud punktid ja kolmandas veerus vaheeksami punktid.
Kõigepealt üritame saada selgust, kuidas jaotusid vaheeksami punktid. Selleks laseme matplotlib-il joonistada histogrammi.
Me võiksime need andmed sisse lugeda harilikke faili- ja tekstioperatsioone kasutades, aga seekord võtame Pythoni standardteegi moodulist csv
appi funktsiooni reader()
, mis teeb CSV-faili lugemise oluliselt lihtsamaks.
Histogrammi joonistamiseks kasutame Axes
meetodit hist
, mis võtab esimeseks argumendiks väärtuste loetelu ja teiseks argumendiks täisarvu, mis näitab, mitmesse gruppi need väärtused tuleks jaotada.
import csv
import matplotlib.pyplot as plt
vaheeksamid = []
with open("punktid.csv", encoding="UTF-8") as f:
reader = csv.reader(f, delimiter=";")
for rida in reader:
try:
vaheeksamid.append(float(rida[2]))
except ValueError:
# Ignoreerime puuduvate või vigaste väärtusega
pass
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.hist(vaheeksamid, 20)
plt.show()
Hajuvusdiagramm¶
Läheme edasi tudengite poolt kogutud punktide analüüsimisega. Nüüd oleks hea teada, kas usin kodutööde lahendamine aitab saada vaheeksamil paremat tulemust – selleks koostame kodutööde ja vaheeksami punktide põhjal ning Axes.scatter
abil hajuvusdiagrammi.
import csv
import matplotlib.pyplot as plt
kodutööd = []
vaheeksamid = []
with open("punktid.csv", encoding="UTF-8") as f:
reader = csv.reader(f, delimiter=";")
for rida in reader:
try:
# Kui tudeng pole ühtegi kodutööd teinud, siis võib vastavas
# lahtris olla 0 asemel ka sidekriips.
# Mõlemal juhul võime öelda, et tudeng on saanud kodutööde eest 0p
if rida[0] == "-":
kodutöö = 0.0
else:
kodutöö = float(rida[0])
# Vaheeksami puhul aga tähendab kriips seda, et tudeng puudus
# vaheeksamilt. Kõige kindlam on praegu neid tudengeid mitte arvestada.
# Seetõttu üritame teisendust ilma lisakontrollita. Kui see ebaõnnestub,
# siis selle tudengi andmeid ei arvestata (ka kontrolltööd mitte)
vaheeksam = float(rida[2])
# Jätame andmed meelde alles siis, kui kõik teisendused õnnestusid.
# See kindlustab selle, et mõlemasse listi tuleb sama palju elemente
# ja samadel indeksitel on sama tudengi andmed, st. listides olevad andmed
# on seotud. See on hajuvusdiagrammi koostamisel oluline.
kodutööd.append(kodutöö)
vaheeksamid.append(vaheeksam)
except ValueError:
# Ignoreerime puuduvate või vigaste väärtustega ridu
pass
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(kodutööd, vaheeksamid)
ax.set_xlabel("Kodutööde punktid")
ax.set_ylabel("Vaheeksami punktid")
plt.show()
Diagramm näitab tõesti, et rohkem kodutöid teinud tudengid said ka rohkem punkte vaheeksamil, aga algandmeid uurides näeme, et on palju tudengeid, kelle kodutööde ja vaheksami punktid kattuvad täpselt mõne teise (või paljude teiste) tudengite punktidega ja seetõttu satuvad vastavad andmepunktid ka graafikul kohakuti. Äkki selle täpi kohal, mis tähistab 0p kodutööde eest ja 20p vaheeksami eest, on tegelikult 50 täppi ja meie eespool tehtud järeldus ei pea paika?
Kõige lihtsam lahendus on kasutada scatter
-i väljakutsel lisaparameetrit alpha
, mis määrab iga täpi läbipaistvuse – 0.0 tähendab täiesti läbipaistvat täppi ja 1.0 täiesti läbipaistmatut täppi. Kui me laseme matplotlibil joonistada osaliselt läbipaistvaid täppe, siis mitut täppi kohakuti pannes saame kokku tumedama täpi. Proovi järele:
...
ax.scatter(kodutööd, vaheeksamid, alpha=0.1)
...
Teine võimalus on näidata punktikombinatsioonide sagedust täppide suurusega. Selleks korjame kokku kõik erinevad punktikombinatsioonid ja nende sagedused, ning kasutame parameetrit s
täppide läbimõõtude määramiseks. Järgneval graafikul ei tähista iga täpp enam mitte ühte tudengit, vaid ühte kodutöö ja vaheeksami punktide kombinatsiooni.
import csv
import matplotlib.pyplot as plt
from collections import Counter
from math import sqrt
kombinatsioonid = []
with open("punktid.csv", encoding="UTF-8") as f:
reader = csv.reader(f, delimiter=";")
for rida in reader:
try:
# Kui tudeng pole ühtegi kodutööd teinud, siis võib vastavas
# lahtris olla 0 asemel ka sidekriips.
# Mõlemal juhul võime öelda, et tudeng on saanud kodutööde eest 0p
if rida[0] == "-":
kodutöö = 0.0
else:
kodutöö = float(rida[0])
# Vaheeksami puhul aga tähendab kriips seda, et tudeng puudus
# vaheeksamilt. Kõige kindlam on praegu neid tudengeid mitte arvestada.
# Seetõttu üritame teisendust ilma lisakontrollita. Kui see ebaõnnestub,
# siis selle tudengi andmeid ei arvestata (ka kontrolltööd mitte)
vaheeksam = float(rida[2])
kombinatsioon = (kodutöö, vaheeksam)
kombinatsioonid.append(kombinatsioon)
except ValueError:
# Ignoreerime puuduvate või vigaste väärtustega ridu
pass
# Toome välja erinevad kombinatsioonid ja täpi suurused vastavalt sagedusele
sagedused = Counter(kombinatsioonid)
kodutööd = []
vaheeksamid = []
täpi_läbimõõdud = []
for kombinatsioon in sagedused:
sagedus = sagedused[kombinatsioon]
kodutööd.append(kombinatsioon[0])
vaheeksamid.append(kombinatsioon[1])
# Kui kombinatsiooni A esineb kaks korda rohkem, kui kombinatsiooni B,
# siis kumb valik oleks õigem?
#
# - A täpi läbimõõt on 2x suurem kui B oma
# - A täpi pindala on 2x suurem kui B oma
#
# On leitud, et täpsema mulje jätab see, kui sagedust näitab pindala,
# seetõttu valime täpi läbimõõdu nii, täpi pindala sõltuks lineaarselt
# vastava kombinatsiooni sagedusest:
täpi_läbimõõdud.append(sqrt(sagedus) * 10)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(kodutööd, vaheeksamid, alpha=0.3, s=täpi_läbimõõdud)
ax.set_xlabel("Kodutööde punktid")
ax.set_ylabel("Vaheeksami punktid")
plt.show()
Sageduste arvutamiseks võtsime appi klassi Counter
Pythoni standardteegi moodulist collections
(aga seda oleksime võinud teha ka primitiivsemate vahenditega).
Kuna täpid võivad nüüd osaliselt ikkagi kattuda, siis jätsime scatter
väljakutsesse ka alpha
argumendi, et pilt tuleks selgem.
Märkus
Argumendile s
saab anda väärtuseks ka ainult ühe arvu – sel juhul tulevad kõik täpid näidatud läbimõõduga. Selline paindlikkus on matplotlib-i puhul tavaline – näiteks alpha
aktsepteerib samuti nii üksikut arvu kui arvude loetelu.
TODO: ka üksikute täppide värvi saab varieerida
Graafikute täiendamine¶
Axes
pakub erinevaid meetodeid graafikute täiendamiseks üksikute joonte, kujundite ja tekstiga. Toome siin ära vaid mõned näited:
axhline
jaaxvline
horisontaalsete / vertikaalsete joonte lisamiseksaxhspan
jaaxvspan
horisontaalsete / vertikaalsete piirkondade esiletõstmisekstext
jaannotate
teksti lisamiseksfill
hulknurkade joonistamiseks
Järgnev näide demonstreerib nende võimaluste kasutamist:
import csv
import matplotlib.pyplot as plt
x = [-3.4, 0.5, -1.3, -4.4, 0.4, -3.8, -2.0, 1.2, 4.5, 4.3, -2.2, -2.9, 1.0,
0.9, 2.1, -2.4, 2.9, 0.5, 3.4, 4.4, 3.7, -1.1, 2.0, 0.4, -1.6, -3.9,
2.0, -0.9, -2.4, 0.1, 1.5, 3.6, 1.8, 4.1, 0.2, -2.8, -4.0, -0.2, -2.6,
-4.1, -2.6, -0.1, 3.3, 3.3, 0.7, -3.3, 4.8, -0.7, 2.5, 1.2]
y = [594, 696, 299, 808, 840, 805, 0, 325, 168, 40, 444, 304, 842, 862, 406,
578, 162, 713, 236, 986, 680, 849, 236, 1, 6, 942, 387, 635, 682, 473, 54,
807, 948, 230, 521, 38, 423, 942, 752, 573, 117, 419, 729, 909, 106, 66,
236, 85, 653, 846]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x, y, zorder=3)
ax.axhline(y=700, color='r', zorder=1)
ax.axvspan(1,3, color="lightgreen", zorder=2)
ax.text(3, 200, "Lihtne tekst")
ax.annotate("Vaata seda!!!", (-3.4, 594), color="r",
arrowprops={"arrowstyle" : "-|>"}, xytext=(-3, 500))
ax.fill([0, -1, 2], [300, 255, 200], zorder=3, color="yellow")
plt.show()
Graafikute kohandamine¶
TODO: fondid, teljed, tickmarks, labels, grids, styles
Eksportimine¶
TODO: fig.savefig
Interaktiivsed graafikud¶
Matplotlibi graafikuid saab panna hiireklõpsudele ja klahvivajutustele reageerima. Järgnev näide demonstreerib ühte võimalust hiireklõpsudele reageerimiseks:
import matplotlib.pyplot as plt
kuud = [ 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
sissetulekud = [710, 1200, 1445, 1690, 1350, 1223, 1470, 1200, 808, 698]
väljaminekud = [700, 1160, 1556, 1520, 1415, 1180, 1770, 500, 408, 505]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# Salvestame jooned muutujatesse, et oleks pärast võimalik vahet teha,
# kummale joonele klõpsati.
# Lisame ka picker argumendi, mis näitab kui lähedale peab klõpsama,
# et klõpsu seostatakse joonega
[sissetulekute_joon] = ax.plot(kuud, sissetulekud, "o-", label="Sissetulekud", picker=3)
[väljaminekute_joon] = ax.plot(kuud, väljaminekud, "^-r", label="Väljaminekud", picker=3)
ax.set_xlabel("Kuud")
ax.set_ylim(0, 2000)
ax.set_xticks([1,2,3,4,5,6,7,8,9,10,11,12])
ax.legend()
# defineerime funktsiooni, mis peaks klõpsudele reageerima
def on_pick(event):
thisline = event.artist
# xdata, ydata ja evend.ind on NumPy massiivid
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
index = event.ind[0]
kuu_nr = xdata[index]
summa = ydata[index]
if thisline == sissetulekute_joon:
näitaja = "sissetulek"
else:
näitaja = "väljaminek"
print(str(kuu_nr) + ". kuu " + näitaja + " oli " + str(summa))
# registreerime funktsiooni klõpsudele reageerima
cid = fig.canvas.mpl_connect('pick_event', on_pick)
plt.show()
Täpsemalt loe siit: http://matplotlib.org/users/event_handling.html
Graafikute integreerimine programmidesse¶
Matplotlib-i graafikuid saab integreerida erinevate kasutajaliidese raamistikega, sh Tkinteriga. Selleks tuleb Figure
objekt luua mitte pyplot
abil, vaid moodulis matplotlib.figure
oleva klassi Figure
abil. Lisaks tuleb luua raamistikuspetsiifilised vidinad graafiku näitamiseks.
Järgnev näide demonstreerib pirukagraafiku lisamist Tkinteri programmi:
import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
def uuenda_graafik():
ax.clear()
# Simuleerin andmete uuendamist juhuslike andmete genereerimisega.
# Reaalne programm loeks andmed kusagilt failist, sensoritest vms.
import random
k = 5
suurused = random.sample([12, 45, 4, 14, 33, 32, 66, 23, 29, 7], k)
nimed = random.sample(["Peeter", "Tiit", "Mari", "Jakob", "Teele",
"Kalle", "Malle", "Reet", "Lauri", "Kati"], k)
ax.pie(suurused, labels=nimed)
canvas.show()
def salvesta_graafik():
fig.savefig("graafik.pdf") # Fail ilmub jooksvasse kausta
# Loon akna
aken = tk.Tk()
aken.title("Demo")
# Loon graafikut tähistavad objektid
fig = Figure(figsize=(5, 5), dpi=100) # 5x5 tolli
ax = fig.add_subplot(1,1,1)
fig.set_facecolor('white')
# Loon pinna graafiku joonistamiseks
# ja paigutan vastava Tk vidina
canvas = FigureCanvasTkAgg(fig, master=aken)
canvas.get_tk_widget().grid(row=0, column=0, columnspan=2)
# Loon nupu andmete laadimiseks
nupp1 = ttk.Button(aken, text="Uuenda graafik", command=uuenda_graafik)
nupp1.grid(row=1, column=0)
# Loon nupu graafiku salvestamiseks
nupp2 = ttk.Button(aken, text="Salvesta graafik", command=salvesta_graafik)
nupp2.grid(row=1, column=1)
# Kuvan andmete esialgse seisu
uuenda_graafik()
# Alustan programmi peatsüklit
aken.mainloop()
Rohkem infot:
Matplotlib + Plotly¶
Plotly on veebiteenus, mis võimaldab interaktiivsete graafikute koostamist ja veebis publitseerimist. Muuhulgas toetab Plotly ka matplotlib-i abil koostatatud graafikute importimist. Selleks saab kasutada Pythoni paketti plotly
, mille saab installida käsuga pip install plotly
.
Järgnev näide demonstreerib, kuidas ühte meie eelpool koostatud graafikutest saata Plotly-sse:
Tähelepanu
Enne selle koodi käivitamist tuleb sul teha enda Plotly konto, ning asendada funktsiooni sign_in
argumendid enda andmetega!
import matplotlib.pyplot as plt
import csv
import matplotlib.pyplot as plt
from collections import Counter
from math import sqrt
# plotly.plotly imporditakse tavaliselt nime py alla
import plotly.plotly as py
kombinatsioonid = []
with open("punktid.csv", encoding="UTF-8") as f:
reader = csv.reader(f, delimiter=";")
for rida in reader:
try:
# Kui tudeng pole ühtegi kodutööd teinud, siis võib vastavas
# lahtris olla 0 asemel ka sidekriips.
# Mõlemal juhul võime öelda, et tudeng on saanud kodutööde eest 0p
if rida[0] == "-":
kodutöö = 0.0
else:
kodutöö = float(rida[0])
# Vaheeksami puhul aga tähendab kriips seda, et tudeng puudus
# vaheeksamilt. Kõige kindlam on praegu neid tudengeid mitte arvestada.
# Seetõttu üritame teisendust ilma lisakontrollita. Kui see ebaõnnestub,
# siis selle tudengi andmeid ei arvestata (ka kontrolltööd mitte)
vaheeksam = float(rida[2])
kombinatsioon = (kodutöö, vaheeksam)
kombinatsioonid.append(kombinatsioon)
except ValueError:
# Ignoreerime puuduvate või vigaste väärtustega ridu
pass
# Toome välja erinevad kombinatsioonid ja täpi suurused vastavalt sagedusele
sagedused = Counter(kombinatsioonid)
kodutööd = []
vaheeksamid = []
täpi_läbimõõdud = []
for kombinatsioon in sagedused:
sagedus = sagedused[kombinatsioon]
kodutööd.append(kombinatsioon[0])
vaheeksamid.append(kombinatsioon[1])
# Kui kombinatsiooni A esineb kaks korda rohkem, kui kombinatsiooni B,
# siis kumb valik oleks õigem?
#
# - A täpi läbimõõt on 2x suurem kui B oma
# - A täpi pindala on 2x suurem kui B oma
#
# On leitud, et täpsema mulje jätab see, kui sagedust näitab pindala,
# seetõttu valime täpi läbimõõdu nii, täpi pindala sõltuks lineaarselt
# vastava kombinatsiooni sagedusest:
täpi_läbimõõdud.append(sqrt(sagedus) * 30)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(kodutööd, vaheeksamid, alpha=0.3, s=täpi_läbimõõdud)
ax.set_xlabel("Kodutööde punktid")
ax.set_ylabel("Vaheeksami punktid")
# Meetod sign_in on vajalik selleks, et graafiks jõuaks õige Plotly konto alla
py.sign_in("sinu_kasutajanimi", "sinu_api_key") # NB! pane siia enda andmed
plot_url = py.plot_mpl(fig) # Saadame graafiku Plotly-sse
print(plot_url)
Kui kõik läks hästi, siis avanes peale selle koodi käivitamist varsti brauseri aken, mis näitas sama graafikut Plotly keskkonnas. Graafiku kõrvalt leiad HTML koodi, mille saad lisada oma veebilehele. Tulemus on midagi sellist (proovi liigutada hiirt punktide kohal):
Märkus
Tuleb arvestada, et plot_mpl
ei saa vähemalt 2016 novembri seisuga kõigi matplotlib-i graafikutega veel hakkama. Kui su graafik toimib show
meetodiga, aga plot_mpl
annab veateate, siis proovi graafikus midagi lihtsustada. Abi võib olla ka veateate guugeldamisest
Täpsem info¶
Ülevaade võimalustest: http://matplotlib.org/users/screenshots.html Väga hea ülevaade matplotlib mõistetest: http://matplotlib.org/faq/usage_faq.html
Hea tut?: http://www.labri.fr/perso/nrougier/teaching/matplotlib/