1 Teadusarvutused¶
1.1 Mõiste¶
Teadusarvutusetes mis on IT valdkonna haru, kasutatakse moodsaid arvutustehnika võimalusi keerukate füüsika probleemide mõistmiseks ja lahendamiseks.
Teadusavastuste kolm põhikomponenti on järgmised: teooria, eksperiment, arvutused (õpid või alustad varsti nende konponentide õpingutega oma õppekava raames ja -mahus). Praktikas hõlmavad teadusarvutused järgmisi probleeme ja tegevusi:
- Teadusarvutused:
- Keeruliste ülesannete lahendamine (mittelineaarsus, mittelineaarne dünaamika, kaos, komplekssüsteemid)
- Kallite või eetiliselt tundlikke eksperimentide simulatsioon
- Arvutusvõimsus ja raudvara
- Arvutiklastrid, võrkandmetöötlus
- Paralleelarvutused, lihtsamal juhul ka hargtöötlus (multithreading)
- Algoritmid
- Teadusarvutuste teegid + enda loodud algoritmid ja moodulid
- Andmete analüüs
- Visualiseerimine
- Modelleerimine
Lisainfo: https://en.wikipedia.org/wiki/Computational_science
1.2 Keeruline probleem¶
Keeruliste ülesannete lahendamine nõuab lähenemist, mis arvestab mittelineaarsuse dünaamika ja kaose omavahelist põimumist. Mittelineaarsed süsteemid ei reageeri muutustele proportsionaalselt – väike algtingimuste erinevus võib tuua kaasa märkimisväärselt erineva tulemuse, mida tuntakse kaose peamise omadusena.
Lisaks kaootilistele probleemidele eksisteerivad nn. komplekssüsteemid. Komplekssüsteemides mõjutavad paljud omavahel seotud elemendid üksteist pidevalt, luues mustreid, mida ei saa lihtsalt üksikosade põhjal ette ennustada. Selliste ülesannete lahendamisel kasutatakse sageli arvutisimulatsioone, iteratiivseid meetodeid ja süsteemset mõtlemist, mis aitavad tuvastada peidetud korrapära. Nii ei otsita mitte üht ainsat täpset lahendust, vaid terviklikku arusaama süsteemi käitumisest.
1.3 Probleemi lahandamine ja algoritm¶
Probleemi lahendamine ja algoritm käivad käsikäes. Probleemi lahendamisel tuleb esmalt mõista olukorda, eristada olulised detailid ebaolulistest ning sõnastada eesmärk. Seejärel koostatakse algoritm – täpne, üheselt mõistetav juhiste jada, mis viib samm-sammult soovitud tulemuseni. Hästi üles ehitatud algoritm teeb keerulise ülesande lihtsamaks, võimaldab lahendust korrata, kontrollida ning vajadusel täiustada. Nii muutub abstraktne probleem konkreetseks ja lahendatavaks tegevusplaaniks.
Definitsioon Loengust 3:
Algoritm (=lahenduseeskiri) on fikseeritud juhiste jada, millega teisendatakse algoritmi sisendandmed algoritmi väljundandmeteks. Algoritmi esitatakse kas juhiste loendina või graafiliselt kasutades voodiagrammi. Algoritmi rakendatakse probleemi lahendamiseks.
Vikipeedia definitsioon:
Algoritm on sammsammuline tegevusjuhis, juhend, eeskiri mingi tegevuse sooritamiseks või eesmärgi saavutamiseks. Kõige sagedamini kasutatakse seda terminit matemaatilise ülesande lahendamiseks mõeldud eeskirja kohta.
Algoritmi esitust mingis formaalses keeles, tavaliselt programmeerimiskeeles või masinkoodis, nimetatakse arvutiprogrammiks.
Allikas Vikipeedia: https://et.wikipedia.org/wiki/Algoritm
Allpool esitama kaks algotirmi näidet.
1.3.1 Algoritm: Listi suurima arvu leidmine¶
Sisend: List ja listi esimene arv (eeldame, et suurim).
Väljund: Listi suurim arv.
Tegevused:
A. Üle listi itereerimine.
B. Arvu võrdlemine eelmisega.
Algoritm:
- Alusta oletades, et listi esimene arv on suurim.
- Itereeri üle listis olevate arvude ükshaaval (A).
- Kui leiad suurema arvu siis kirjuta eelmine suurim arv üle (B).
- Peale kõigi arvude kontrollimist oled leidnud suurima arvu. Tagasta see.
Joonis 1 kujutab argoritmi voodiagrammina.
| Joonis 1. Algoritm voodiagrammina: Listi suurima arvu leidmine. Halli värviga on kujutatud taustal automaatselt teostatud tegevused. |
Algoritm esitatuna programmikoodi abil:
def find_max(numbers):
# Samm 1: Oleta et esimene arv on suurim.
maximum = numbers[0]
# Samm 2: Itereeri üle kõigi arvude.
for num in numbers:
# Samm 3: Salvesta arv kui see on suurem kui eelmised.
if num > maximum:
maximum = num
# Samm 4: Tagasta tulemus.
return maximum
data = [3, 8, 1, 5, 9, 2]
print("Suurim arv on:", find_max(data))
Suurim arv on: 9
1.3.2 Algoritm: Lambi põlema panemine¶
Sisend: Lamp.
Väljund: Lamp annab valgust.
Tegevused:
A. Vaata kas lamp põleb?
B. Lüliti lülitamine (vajadusel pirni vahetus ja lambi parandus).
Algoritm:
- Kontrolli, kas lamp põleb (A). Kui jah, siis on valgus. Kui ei, siis kontrolli, kas lülit on sisse lülitatud. Kui ei, siis
- lülita lülit sisse (B) ja korda A. Kui jah, siis lülita see välja ja kontrolli, kas lambipirn on terve. Kui jah, siis vaheta lambipirn ja korda B. Kui ei, siis paranda lamp ja korda B.
Joonis 2 kujutab eelmainitud algoritmi voodiagrammina.
| Joonis 2. Algoritm voodiagrammina: Lambi põlema panemine. |
Algoritm esitatuna programmikoodi abil:
def lamp_põleb(lamp):
valgus = lamp['lylit'] and lamp['pirn'] and lamp['korras']
print(f'Lambi olek: {lamp} --> {valgus}')
if valgus: # Sama mis valgus == True.
return True
if lamp['lylit']:
lamp['lylit'] = False
if lamp['pirn']:
lamp['korras'] = True
else:
lamp['pirn'] = True
else:
lamp['lylit'] = True
return lamp_põleb(lamp)
lambi_olek = dict(lylit=False, pirn=False, korras=False)
lamp_põleb(lambi_olek)
Lambi olek: {'lylit': False, 'pirn': False, 'korras': False} --> False
Lambi olek: {'lylit': True, 'pirn': False, 'korras': False} --> False
Lambi olek: {'lylit': False, 'pirn': True, 'korras': False} --> False
Lambi olek: {'lylit': True, 'pirn': True, 'korras': False} --> False
Lambi olek: {'lylit': False, 'pirn': True, 'korras': True} --> False
Lambi olek: {'lylit': True, 'pirn': True, 'korras': True} --> True
True
1.4 Teadusandmete visualiseerimine¶
Teadusandmete visualiseerimine on oluline tööriist keerukate nähtuste mõistmisel, sest see muudab mahukad ja sageli raskesti hoomatavad andmekogumid intuitiivselt arusaadavaks. Joonised, graafikud, interaktiivsed visualiseeringud ja ruumilised mudelid aitavad esile tuua mustrid, seosed ja trendid, mida pelgalt numbriliste tabelite põhjal oleks keeruline märgata. Hästi läbimõeldud visualiseering toetab nii teadlaste omavahelist kommunikatsiooni kui ka uurimistulemuste selget edastamist laiemale publikule, võimaldades keerulisi ideid esitada lihtsas ja visuaalselt atraktiivses vormis.
Visualiseerimise abil muutuvad näiteks kursuse kodulehelt leitavas failis data_L14.txt olevad andmed kergesti mõistetavaks.
import matplotlib.pyplot as plt
%config InlineBackend.figure_format='retina' # Vajadusel sisesta Spyderi konsooli, maagiline käsk.
with open('data_L14.txt', 'r') as f:
data = eval(f.read()) # Sõne --> pesastatud list.
plt.figure() # Joonise loomine vt. allpool.
plt.imshow(data, cmap='gray')
plt.colorbar(shrink=0.7)
plt.show()
Märkus: Sisesta arenduskeskkonna Spyder konsooli või Jupyter koodirakku maagiline käsk %config InlineBackend.figure_format='retina' kui sul on kõrge resulutsiooniga kuvar. Käsk muudab joonised teravamaks.
2 Teadusarvutused Pythonis¶
Python on tänapäeval üks olulisemaid tööriistu teadusarvutustes, sest see ühendab endas kasutuslihtsuse ja võimsa ökosüsteemi. Erinevalt traditsioonilisematest teaduskeeltest, nagu Fortran või Matlab, võimaldab Python kiiresti prototüüpida, visualiseerida ja analüüsida andmeid, ilma et kasutaja peaks tegelema keeruka süntaksiga. Selle paindlikkus muudab ta sobivaks nii akadeemiliste kui ka rakenduslikke mudelite testimiseks ja simulatsioonide loomiseks.
Teadusarvutuste tugevuseks Pythoni maailmas on lai valik spetsialiseeritud teeke. NumPy pakub kiiret andmemassiividega arvutust, SciPy lisab matemaatilisi ja optimeerimisalgoritme, Matplotlib võimaldab luua professionaalseid graafikutega jooniseid ning Pandas toetab erinevaid (sisend)andmestruktuure ja andmeanalüüsi. Kõrgetasemelised tööriistad nagu TensorFlow (https://www.tensorflow.org), PyTorch (https://pytorch.org), scikit-learn (https://scikit-learn.org/stable/) ja JAX (https://docs.jax.dev/en/latest/index.html) avavad lisaks võimaluse rakendada masinõpet ja kiiret differentsiaalvõrrandite integreerimist mis on vajalikud moodsates teaduslike simulatsioonid kodeerimisel. Koos moodustavad need ökosüsteemi, mis muudab Pythoni üheks kõige populaarsemaks ja kättesaadavamaks teadustarkvara platvormiks maailmas.
Allpool tutvustame valiku teeke. Me oleme osasid neist juba eelnevalt maininud, vt. Loeng 10. Samas loengus seletasime ka kuidas neid mooduleid ja/või teeka hallata kasutades konsoolis käske (tarkvara) pip ja conda. Joonis 3 kujutab siinmainitud teekide logosid.
Valik olulisemaid teadusarvutuste mooduleid ja teeke millest räägime selles ja järgmises loengus:
- NumPy $-$ andmete esitus massiividena, https://numpy.org
- pandas $-$ andmete säilitamine ja analüüs (kasutab NumPy), https://pandas.pydata.org
- SciPy $-$ teadusarvutuste algoritmid, https://www.scipy.org
- IPython $-$ interaktiivne andmete analüüs, https://ipython.org
- SymPy $-$ sümbolarvutus (CAS, computer algebra system), https://www.sympy.org/en/index.html
- matplotlib $-$ andmete analüüs ja visualiseerimine, https://matplotlib.org
- seaborn $-$ andmete analüüs ja visualiseerimine (kasutab matplotlib) http://seaborn.pydata.org/index.html
| Joonis 3. Teadusarvutustes kasutatud teekide ja nende projektide logod, seisuga 2023. |
Eelmainitud teekide ja tööriistade kasutamine:
- Dokumentatsioon (DocString), abiinfo, näited
- Lähtekood
- Arendajate ja kautajate kogukond
Enamus teadusarvutuste teeke on vaikimisi kaasas Anaconda Pythoni distributsiooniga või allaletavad konsoolis kasutadesconda't või pip'i (Python Package Index (PyPI) manager). Pythoni distributsiooni haldamisest rääkisime Loengus 10.
2.1 Teegi NumPy ülevaade¶
Kasutusjuhend veebis https://numpy.org/doc/stable/index.html Ära unusta ka kohalikult instaleerituid abivahendeid, vt. Loeng 9.
Andmetüüp numpy.ndarray ehk andmemassiiv (multidimensional array)
- Andmetüüp, optimeeritud kiirusele ja ekonoomsusele
- Täisarvud, erinevad arvu laiused (bitdepth)
- Ujuvkomaarvud (kümnendmurdarvud), erinevad arvu laiused ehk bitisügavus
- ...
- Andmemassiivide dimensioonaalsus
- Ligipääs andmetele ja nende analüüs
- Indekseerimine
- Aritmeetilised tehted massiividega
- Arvutusmeetodid
- Massiivi võtmesõnaline ehk nimega argument
axis
Alampaketid:
fft$-$ kiire Fourier teisendusedlinalg$-$ lineaaralgebramatlib$-$ maatriksidrandom$-$ juhuslikud arvud- ...
import numpy as np # Soovituslik nimekasutus.
massiiv = np.array([1, 2, 3]) # Teisendame Pythoni listi andmemassiiviks.
print(type(massiiv)) # Mis andmetüüp?
print(massiiv) # Näeb konsoolis ilus välja.
massiiv
<class 'numpy.ndarray'> [1 2 3]
array([1, 2, 3])
Funktsiooniga np.arange (a range mitte arange) saame luua jäjestatud massiive:
a = np.arange(0, 5, 0.5) # Parem kui Pythoni range, murdarvuline samm.
print(a)
a
[0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5]
array([0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5])
Loodud massiivi kopeerimine:
b = a # Viide originaalile.
c = a.copy() # Massiivi meetod.
print(id(a))
print(id(b))
print(id(c))
4527947568 4527947568 4540346800
d = np.array(a) # Sisuliselt konstruktorfunktsioon.
print(id(a))
print(id(b))
print(id(c))
print(id(d))
4527947568 4527947568 4540346800 4540347088
Siin töötab lõikudeks jagamine sarnaselt Pythoni listile (ja muud jadad). Kogu massiivi siule viitamine loog uue objekti sarnaselt Pythoni listile, vt. Loeng 7.
e = a[:] # Lõikamine (slicing), uus objekt.
print(id(a))
print(id(b))
print(id(c))
print(id(d))
print(id(e))
4527947568 4527947568 4540346800 4540347088 4540348240
2.1.1.2 Massiivi kuju ehk dimensinaalsus¶
Me saame luua mitmemõõtmelisi massiive:
data = [[1, 2, 3],
[4, 5, 6]]
a = np.array(data) # Kaks rida, kolm veergu.
print(a)
a
[[1 2 3] [4 5 6]]
array([[1, 2, 3],
[4, 5, 6]])
Andmemassiivi meetodi reshape kasutus:
a = np.arange(15).reshape(3, 5) # Kolm rida, viis veergu.
print(a)
a
[[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]]
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
Massiivi atribuudid:
a.shape # Massiivi kuju...
(3, 5)
a.ndim # Mitu dimensiooni massiivis?
2
a.dtype.name # Salvestatud andmete ehk numbrite andmetüüp.
'int64'
a.itemsize # Massiivi andmete suurus bittides.
8
a.size # Mitu elementi massiivis?
15
2.1.1.3 Kahemõõtmeline massiiv¶
Kahemõõtmelise massiivi defineerimine. Massiiv toetab kõiki arvude tüüpe k.a. kompleksarve.
data = [[1, 2, 3+2j],
[4, 5, 6.0]]
a = np.array(data) # Vaikimisi käitumine.
print(a)
a
[[1.+0.j 2.+0.j 3.+2.j] [4.+0.j 5.+0.j 6.+0.j]]
array([[1.+0.j, 2.+0.j, 3.+2.j],
[4.+0.j, 5.+0.j, 6.+0.j]])
Massiivi elemente saab teisendada teistesse andmetüüpidesse või määrata elementide andmetüüpi defineerimisel:
a = np.arange(4).reshape(2, 2)
b = a.astype(complex) # Massiivi meetod.
print(b)
np.array([[1, 2, 3], [4, 5, 6]], dtype=complex) # Määrame andmetüübi ise.
[[0.+0.j 1.+0.j] [2.+0.j 3.+0.j]]
array([[1.+0.j, 2.+0.j, 3.+0.j],
[4.+0.j, 5.+0.j, 6.+0.j]])
2.1.1.4 Spetsiaalsete ja eriotstarbega massiivide defineerimine¶
Alguse ja lõpu vahele kindla arvu ühtlaselt jaotatud arvudega massiiv:
np.linspace(-1, 1, num=6) # Algus, lõpp, jagame võrdselt kuue arvu vahel.
array([-1. , -0.6, -0.2, 0.2, 0.6, 1. ])
Nullmassiiv:
np.zeros((2, 3)) # Nullmassiiv.
array([[0., 0., 0.],
[0., 0., 0.]])
a = np.arange(4).reshape(2, 2)
np.zeros_like(a) # Sama dimensioon ja kuju mis massiivil.
array([[0, 0],
[0, 0]])
Arvuga 1 täidetud massiiv:
np.ones((2, 3))
array([[1., 1., 1.],
[1., 1., 1.]])
Algväärtustamata massiiv:
np.empty((2, 3)) # Algväärtustamata massiiv kujuga (2, 3).
array([[1., 1., 1.],
[1., 1., 1.]])
Juhuslikke arvudega täidetud massiivid (NB! eksisteerib uuem, parem ja kiirem lähenemine vt. NEP 19):
np.random.randint(1, 10, 3) # Proovi ka 3 -> (3,) -> (3, 2). NB! 3, või (3,) tuple singleton syntax.
array([8, 9, 1])
np.random.uniform(1, 10, 3) # Proovi ka 3, -> (3,) -> (3, 2). NB! 3, või (3,) tuple singleton syntax.
array([9.35457342, 2.45462331, 4.81398564])
np.random.rand(2, 3) # Juhuslike arvude gen., ühtlane jaotus = valge müra.
array([[0.10447531, 0.61598736, 0.77679626],
[0.48909235, 0.04249337, 0.43644786]])
np.random.randn(2, 3) # Juhuslike arvude gen., normaaljaotus.
array([[-0.81453337, 0.72418434, -0.49177093],
[ 0.76991135, 0.63407207, 1.61567227]])
Massiivi elemente saame genereerida kasutades funktsioone:
np.fromfunction(lambda i, j: i + j, (2, 3)) # Itereerib üle massiivi andmete indeksite, rida i, veerg j.
array([[0., 1., 2.],
[1., 2., 3.]])
2.1.1.5 Iteraatortugi¶
Loodud NumPy massiivid on iteraatortoega objektid:
m = np.array([1, 2, 3])
for i in m:
print(i)
1 2 3
for i in np.arange(3):
print(i)
0 1 2
for i in np.ones(3):
print(i)
1.0 1.0 1.0
a = np.arange(4).reshape(2, 2)
for rida in a:
print(rida)
[0 1] [2 3]
for rida in a:
for veerg in rida:
print(veerg)
0 1 2 3
2.1.1.6 Toetatud andmetüübid¶
Massiivides toetatakse erinevaid andmetüüpi arve (täisarvud, ujuvkomakohaarvud). Toetatud on erinevate bitisügavusega arvud:
np.array([1, 2, 3], dtype=np.int8)
array([1, 2, 3], dtype=int8)
np.array([1, 2, 3], dtype=np.int64) # Lisaks kõik kahe astmed mis jäävad 8 ja 64 biti vahele.
array([1, 2, 3])
np.array([1, 2, 3], dtype=np.float16)
array([1., 2., 3.], dtype=float16)
np.array([1, 2, 3], dtype=np.float64) # Lisaks vahepealsed ja eksisteerib ka float128.
array([1., 2., 3.])
np.array([1, 2, 3], dtype=np.complex64)
array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
np.array([1, 2, 3], dtype=np.complex128) # Lisaks vahepealsed ja eksisteerib ka float256.
array([1.+0.j, 2.+0.j, 3.+0.j])
2.1.1.7 Massiivi elementide andmetüüpide segakasutus¶
Mitme erineva andmetüübiga massiivi elementide korral valitakse vaikimisi ühine andmetüüp mis rakendatakse kõikidele elementidele. Valitud andmetüüp toetab kõiki elemente.
np.array([1, 3.14, 2+1j])
array([1. +0.j, 3.14+0.j, 2. +1.j])
np.array([1, 3.14, 1+1j, 'Juulius'])
array(['1', '3.14', '(1+1j)', 'Juulius'], dtype='<U64')
Loomulikult võime proovida teisendada andmetüüpi kasutades meetodit astype:
np.array(['1', 2, 3]).astype(np.int64)
array([1, 2, 3])
Kui soovid luua massiivi kus on salvestatud erinevad andmetüübi objektid:
a = np.array([1, 2, "Juulius"], dtype=object)
print(a)
[1 2 'Juulius']
Märkus: Massiiv elemendid on nüüd puhta Pythoni objektid. Massiiviga töötamine on nüüd aeglasem.
Fikseeritud heterogeensete massiivi defineerimine on parem lahendus:
dt = np.dtype([('arv', 'i4'), ('ujuvkoma', 'f4'), ('nimi', 'U10')])
a = np.array([(1, 3.14, 'Juulius')], dtype=dt)
print(a)
print(a['arv'], a['ujuvkoma'], a['nimi'])
print(a[0][0], a[0][1], a[0][2])
[(1, 3.14, 'Juulius')] [1] [3.14] ['Juulius'] 1 3.14 Juulius
Selline kood säilitab NumPy massiivide suhtelise arvutusliku kiiruse.
2.1.1.8 Aritmeetika- ja võrdlusoperaatorid¶
Operaatorid võivad käituda erinevalt puhta Pythoni operaatorites. Üldjuhul teostatakse tehteid element-element haaval. NumPy andmemassiivid ja teegis NumPy ülekirjutatud aritmeetika- ja võrdlusoperaatorid:
a = np.array([1, 1, 10, 10])
b = np.arange(4) # --> [0 1 2 3]
a - b # a + b, a * b, b / a, a**2, jne. Element-element haaval.
array([1, 0, 8, 7])
a + b
array([ 1, 2, 12, 13])
2 * a # Element haaval, proovi ka /, %, //, jne.
array([ 2, 2, 20, 20])
a + 1
array([ 2, 2, 11, 11])
a += b # -=, *=, /=, **=, jne. Itereerib ise element-element haaval.
a
array([ 1, 2, 12, 13])
print(a == a) # peavad omama sama kuju.
a == 12 # Element haaval, kahe massiivi korral võrdleb vastavaid elemente.
[ True True True True]
array([False, False, True, False])
a < 10 # >, <=, jne.
array([ True, True, False, False])
~(a < 10) # Eitus, biti NOT on siin ülekirjutatatud.
array([False, False, True, True])
(a > 1) & (a < 13) # bitwise AND üle kirjutataud.
array([False, True, True, False])
NB! Milleks on sellised tõeväärtustega massiivid kasulikud? Saame valida elemente vastavalt meelevaldselt valitud tingimustele:
filter = a < 10
a[filter] # Või a[a < 10].
array([1, 2])
Massiivide omavahelised korrutustehted:
A = np.array([[1, 2], [3, 4]])
A.dot(A) # Maatrikskorrutis (dot product).
array([[ 7, 10],
[15, 22]])
A @ A # Sama mis eelmine, Pythoni kohahoidja operaatori kasutus.
array([[ 7, 10],
[15, 22]])
A * A # Korrutamine element-element kaupa.
array([[ 1, 4],
[ 9, 16]])
A**2 # Astendamine element-element kaupa.
array([[ 1, 4],
[ 9, 16]])
Maatriksi astendamine (lineaaralgebrast teada reeglid):
from numpy.linalg import matrix_power
matrix_power(A, 3)
array([[ 37, 54],
[ 81, 118]])
A.T # Maatriksi transponeerimine.
array([[1, 3],
[2, 4]])
2.1.1.9 NumPy massiivide meetodid ja funktsioonid¶
Valik NumPy funktsioone mis itereerivad automaatselt üle massiivi andmete:
a = np.array([1, 2, 3])
print(type(a))
a.min() # Meetodile vastab ka funktsioon np.min.
<class 'numpy.ndarray'>
1
a.max() # Meetodile vastab ka funktsioon np.max. Allpool ka...
3
a.sum()
6
Kumulatiivne summeerimine:
a.cumsum()
array([1, 3, 6])
Aritmeetiline keskmine:
a.mean() # Aritmeetiline keskmine.
2.0
Standardhälve:
a.std() # Standardhälve (standard deviation).
0.816496580927726
Dispersioon:
a.var() # Dispersioon (variance).
0.6666666666666666
2.1.2 Andmemassiivide teljed ja nimega argument axis¶
2.1.2.1 NumPy argument axis¶
Nimega argument axis ja eelmainitud funktsioonid, itereerimine kindlas andmemassiivi telje suunas. Kiire ülevaade:
A = np.array([[1, 2], [3, 4]])
A.sum(axis=0) # Veeru andmed.
array([4, 6])
A.sum(axis=1) # Ridade andmed.
array([3, 7])
A.sum() # Kõigi elementide summa.
10
2.1.2.2 Ühemõõtmelise massiivi telg axis=0¶
Ühemõõtmeline (1D) massiiv ja selle telg axis=0. Kuna tegemist on massiivi ainukese teljega siis pole sellele viitamine kohustuslik. Joonis 4 kujutab seda massiivi telge graafiliselt.
Joonis 4. Ühemõõtmeline massiiv ja selle ainukene telg axis=0. |
2.1.2.3 Kahemõõtmelise massiivi teljed axis=0 ja axis=1¶
Kahemõõtmeline (2D) massiiv ja selle teljed axis=0 ja axis=1. Joonsi 5 kujutab eelmainitud massiivi telgi graafiliselt.
np.arange(2, 10).reshape(2, 4)
array([[2, 3, 4, 5],
[6, 7, 8, 9]])
Joonis 5. Kahemõõtmeline massiiv ja selle teljed axis=0 ja axis=1. |
2.1.2.4 Kolmemõõtmelise massiivi teljed axis=0, axis=1 ja axis=2¶
Kolmemõõtmeline (3D) massiiv ja selle teljed axis=0, axis=1 ja axis=2. Joonis 6 kujutab eelmainitud massiivi telgi graafiliselt.
massiiv = np.arange(24).reshape(4, 3, 2)
massiiv
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]],
[[12, 13],
[14, 15],
[16, 17]],
[[18, 19],
[20, 21],
[22, 23]]])
Joonis 6. Kolmemõõtmeline massiiv ja selle teljed axis=0, axis=1 ja axis=2. |
massiiv.sum(axis=2) # Kontrollime.
array([[ 1, 5, 9],
[13, 17, 21],
[25, 29, 33],
[37, 41, 45]])
Kui sa tahad kasutada kõiki elemente ehk pakkida see lahti (flattened array, collapsed into 1D array) vali axis = None või ära kasuta argumenti:
massiiv.sum(axis=None) # Sama mis massiiv.sum()
276
2.1.3 Massiivi indekseerimine¶
Osaliselt erineb Pythoni listide ja teiste iteraatortoega objektide omast.
x = np.arange(10)
print(x)
x[2] # Sama mis Pythoni listis.
[0 1 2 3 4 5 6 7 8 9]
2
x[-2] # Toetab negatiivset indeksit.
8
x.shape = (2, 5) # Muudan 2D-ks
x
array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
x[1][3] # Toetab Pythoni indekseerimist.
8
Toetab ka sellist mugavat viitamist, viide elemendile:
x[1, 3] # NumPy enda lahendus: rida, veerg. Palju mugavam.
8
x[1, -1] # Rida, veerg.
9
x[0] # Esimene rida.
array([0, 1, 2, 3, 4])
Lõikudeks jagamine:
x = np.arange(10)
x[2:5] # Lõige kahest viieni (5 mitte-kaasaarvatud). Slicing.
array([2, 3, 4])
x[:-7] # Kuni indeksini, -7 mitte-kaasaarvatud.
array([0, 1, 2])
Toetab ka sammu sarnaselt Pythoni jadadega. Süntaks x[algus:lõpp:samm]:
x[1:7:2] # Toetab sammu, iga teine.
array([1, 3, 5])
Elemente saab valida kasutades elemendi indeksit, luuakse uus massiiv valitud andmetega:
x[[1, 3, 5, 9]] # Valib indeksite järgi.
array([1, 3, 5, 9])
y = np.arange(20).reshape(5, 4)
print(y)
print(y[[1], [0, 1]])
print(y[[1], [0]])
y[[1, 2], :3]
[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] [12 13 14 15] [16 17 18 19]] [4 5] [4]
array([[ 4, 5, 6],
[ 8, 9, 10]])
y[[0, 0, 1], [1, 3, 3]] # Valib indeksite järgi ja mõlema koordinaadi järgi [[rida], [veerg]]
array([1, 3, 7])
Lõikumise abil valitud elemendid ja uus massiiv:
y[1:5:2, ::3] # Rida, veerg.
array([[ 4, 7],
[12, 15]])
2.2 Teegi SciPy ülevaade¶
SciPy kasutusjuhendid veebis (reference guide): https://docs.scipy.org/doc/scipy/reference/ Ära unusta ka kohalikult instaleerituid abivahendeid, vt. Loeng 9.
SciPy $-$ kogum matemaatilisi, statistilisi ja optimeerimise algoritme ja muid abivahendeid:
cluster$-$ andmekaeveconstants$-$ füüsika ja matemaatika konstandidfftpack$-$ kiire Fourier algoritmidintegrate$-$ integraatorid ja ODE lahendajadinterpolate$-$ interpo latsioon, splainidio$-$ sisend-väljundlinalg$-$ lineaaralgebrandimage$-$ N-mõõtmeline pildianalüüsodr$-$ regressiooni analüüsoptimize$-$ optimeerimine, võrrandisüsteemide lahendaminesignal$-$ signaalitöötlussparse$-$ hõredad maatriksidspatial$-$ arvutusgeomeetriaspecial$-$ erifunktsioonidstats$-$ statistika jaotused ja funktsioonid
2.2.1 Näide: Algebralise võrrandisüsteem lahendamine¶
Probleem: Lahenda algebraline võrrandisüsteem kujul:
$$ \begin{cases} x + 3y = 1,\\ 2x + 5y = 2, \end{cases} $$mida saame esitada ka maatrikskujul: $$ \begin{pmatrix} 1 & 3\\ 2 & 5 \end{pmatrix}\!\cdot\! \begin{pmatrix} x\\ y \end{pmatrix}= \begin{pmatrix} 1\\ 2 \end{pmatrix}, $$
s.t., et leia tundmatud $x$ ja $y$ või vektor $(x, y)^T$.
from scipy import linalg
A = np.array([[1, 3], [2, 5]]) # Süsteemi koefitsientide maatriks.
vec = [1, 2] # Pythoni list, saad kasutada ka massiivi numpy.ndarray.
linalg.solve(A, vec) # --> LAHEND: [1., 0.]
array([1., 0.])
Kontrollime tulemust:
A.dot(np.array([1, 0]))
array([1, 2])
2.2.2 Näide: Maatriksi manipulaarimine¶
Mida veel saab teha? Leiame eelmises lõigus loodud maatriksi A determinandi:
linalg.det(A) # Determinant.
-1.0000000000000002
Pöördmaatriksi leidmine:
linalg.inv(A) # Pöördmaatriks (inverse matrix).
array([[-5., 3.],
[ 2., -1.]])
2.3 Joonised ja graafikud, andmete visualiseerimine¶
2.3.1 Teegi matplotlib ülevaade¶
Juhend ja näited veebis: https://matplotlib.org/stable/users/index.html Ära unusta ka kohalikult instaleeritud abivahendeid, vt. Loeng 9.
Matplotlib $-$ andmete analüüsi tööriistad ja visualiseerimine
- 2-mõõtmelised (3-mõõtmelised) joonised, teljed konfigureeritavad
- Teaduspublikatsioonile nõutav kvaliteet, $\LaTeX$'i tugi, vektorgraafika tugi, EPS salvestus, jm.
- Lihtne kasutada, Matlab'le sarnane süntaks
Teatavasti skriptimiskeelt $\LaTeX$ see kursus ei käsitle, vt. iseseisvalt $\LaTeX$ projekti koduleht https://www.latex-project.org/ Lisaks mainisime $\LaTeX$'it Loeng 1 lisades.
Matplotlib joonise loomise töövoog:
- Joonise tekitamine
- Telgede lisamine joonisele, telgede konfigureerimine
- Graafikute lisamine telgedele
- Joonise salvestamine ja kuvamine
Järgmine nädal laiendame jooniste loomise teemat.
2.3.1.1 Joonise objekti, graafiku teljepaari ja graafiku loomine¶
Teek matplotlib võimaldab luua teljepaare ja graafikuid kasutades kahte koodi süntaksi kirjutamise kuju:
- Detailsem koodi süntaks kus opereerime joonise, teljepaaride ja muude objektidega detailsemalt ja
- lihtsam koodi süntaks mis võimaldab jooniseid kiiresti luua.
Joonise loomine: Lihtne graafik ja matplotlibi lihtsam süntaks.
import matplotlib.pyplot as plt
x = np.linspace(0.0, 2.0, 100) # Vaikimisi, num=50.
y = 1 + np.sin(3 * np.pi * x) # Funktsioon sin toetab massiivi.
plt.figure() # Joonise objekti loomine.
plt.plot(x, y) # Graafiku defineerimine.
plt.xlabel(r'Argument $x$') # LaTeXi exit tag on '$'.
plt.ylabel(r'Funktsioon $y(x)$')
plt.title(r'Pealkiri $y(x) = \int_{-10}^{x}(t-b)^2dt$')
plt.grid() # Ruudustiku lisamine, pole kohustuslik.
plt.tight_layout() # Aitab joonise elementide paigutusega.
plt.savefig('test.pdf') # Joonise salvestamine, vali faililaiend ise.
plt.show() # Joonise kuvamine.
2.3.1.2 Mitme graafiku lisamine samale teljepaarile¶
Märkus: Joonise objekti loomisel saad määrata selle suuruse (laiuse, kõrguse) tollides.
x = np.linspace(0.0, 2.0, 100)
y = 1 + np.sin(3 * np.pi * x)
y2 = 1.2 + np.sin(4 * np.pi * x)
plt.figure(figsize=(4.5, 1)) # Argument figsize määrab joonise suurus tollides.
plt.plot(x, y)
plt.plot(x, y2) # Võid mitu graafikut lisada.
#plt.plot(x, y, x, y2) # Töötab ka nii.
plt.xlabel('x')
plt.ylabel('y')
plt.title('Pealkiri')
plt.show()
Tutvu dokumentatsiooniga. DocString sisaldab infot selle kohta kuidas nt. lisada andmepunktide markeeringuid, jpm.
#help(plt.plot) # või plt.plot?
Lisame eespool loodud graafikutele andmepunktide markerid:
x = np.linspace(0.0, 2.0, 100)
y = 1 + np.sin(3 * np.pi * x)
y2 = 1.2 + np.sin(4 * np.pi * x)
plt.figure(figsize=(4.5, 3))
plt.plot(x, y, '^') # Loe dokumentatsiooni.
plt.plot(x, y2, '-og')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Pealkiri')
plt.show()
Argumendi telje ehk horisontaalse telje andmed pole kohustuslikud. Nende puudumisel kasutatakse massiivide või listide indekseid.
y = np.array([1, 2, 3, 4, 5, 4, 3, 2, 1]) # Saab ka tavalist Pythoni listi kasutada.
plt.figure(figsize=(4.5, 3))
plt.plot(y, '-or') # Argument x pole kohustuslik, kasutab indekseid.
plt.xlabel('Indeks')
plt.ylabel('Andmed y')
plt.title('Pealkiri')
plt.show()
2.3.1.2 Mitme teljepaari lisamine joonisele¶
Matplotlib lubab luua mitme teljepaariga jooniseid.
Kaks teljepaari paigutatud veergu:
x = np.linspace(0, 2*np.pi, 300)
y = np.sin(x**2)
plt.figure(figsize=(4.5, 4))
plt.subplot(2, 1, 1) # 2 rida, 1 veerg, esimene teljepaar.
plt.plot(x, y, label='legend 1')
plt.legend(loc=1)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Graafiku pealkiri')
plt.subplot(2, 1, 2) # 2 rida, 1 veerg, teine teljepaar.
plt.scatter(x, y, label='legend 2') # Graafiku tüüp, saab asendada plt.plot'ga.
plt.legend(loc=3)
plt.xlabel('x')
plt.suptitle('Joonise peakiri')
plt.tight_layout() # Aitab joonise elementide paigutusega.
plt.savefig('test.pdf') # Joonise salvestamine.
plt.show()
Neli teljepaari millest lisamine joonisele kolm:
x = np.linspace(0, 2*np.pi, 300)
y = np.sin(x**2)
plt.figure(figsize=(4.5, 4))
plt.subplot(2, 2, 1) # 2 rida, 2 veerg, esimene teljepaar.
plt.plot(x, y, label='legend 1')
plt.legend(loc=1)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Graafiku pealkiri')
plt.subplot(2, 2, 2) # 2 rida, 2 veerg, teine teljepaar.
plt.scatter(x, y, label='legend 2') # Graafiku tüüp, saab asendada plt.plot'ga.
plt.legend(loc=3)
plt.xlabel('x')
# Kolmandat teljepaari ei defineeri.
plt.subplot(2, 2, 4) # 2 rida, 2 veerg, neljas teljepaar.
plt.plot(x, y, '.', label='legend 2')
plt.legend(loc=3)
plt.xlabel('x')
plt.suptitle('Joonise peakiri')
plt.tight_layout() # Aitab joonise elementide paigutusega.
plt.savefig('test.pdf')
plt.show()
Kerge vaevaga saame viimase teljepaari paigutust parandada. Paigutame teljepaari mis oli positsioonil 4 nii, et see oleks positsioonidel 3 ja 4:
x = np.linspace(0, 2*np.pi, 300)
y = np.sin(x**2)
plt.figure(figsize=(4.5, 4))
plt.subplot(2, 2, 1) # 2 rida, 2 veerg, esimene teljepaar.
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.subplot(2, 2, 2) # 2 rida, 2 veerg, teine teljepaar.
plt.scatter(x, y) # Graafiku tüüp, saab asendada plt.plot'ga.
plt.xlabel('x')
plt.subplot(2, 2, (3, 4)) # 2 rida, 2 veerg, kolmas teljepaar asetseb positsioonidel 3 ja 4.
plt.plot(x, y, '.')
plt.xlabel('x')
plt.ylabel('y')
plt.suptitle('Joonise peakiri')
plt.tight_layout() # Aitab joonise elementide paigutusega.
plt.show()
Või paigutame teljepaari 2 positsioonidele 2 ja 4:
x = np.linspace(0, 2*np.pi, 300)
y = np.sin(x**2)
plt.figure(figsize=(4.5, 3.5))
plt.subplot(2, 2, 1) # 2 rida, 2 veerg, esimene teljepaar.
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.subplot(2, 2, (2, 4)) # 2 rida, 2 veerg, teine teljepaar asetseb positsioonidel 2 ja 4.
plt.scatter(x, y) # Graafiku tüüp, saab asendada plt.plot'ga.
plt.xlabel('x')
plt.subplot(2, 2, 3) # 2 rida, 2 veerg, kolmas teljepaar.
plt.plot(x, y, '.')
plt.xlabel('x')
plt.ylabel('y')
plt.suptitle('Joonise peakiri')
plt.tight_layout() # Aitab joonise elementide paigutusega.
plt.show()
2.4 Matplotlib joonised ja IPythoni konsool¶
2.4.1 Jooniste kuvamine¶
Nagu eelnevast selgus kuvatakse Matplotlib jooniseid meetodiga plt.show. Olenevalt sinu arenduskeskkonnast ja selle seadetest võib see juhtuda erinevatel viisidel. Arenduskeskonnas Spyder saame joonistega töötada ja neid avada kolmel erineval viisil. Arenduskeskonnas Jupyteris (JupyterLab, Jupyter Notebook) kahel erineval viisil.
2.4.2 Integreeritud arenduskeskkond Spyder¶
Spyder saab kuvada Matplotlib jooniseid:
- Plots aknas (üla-paremas aknas), vali vastav sakk.
- IPythoni konsooli väljatrüki osana (inline).
- Eraldi avanevas Matplotlib'i aknas koos Matplotlib'i graafilise kasutajaliidesega (auto).
Olemasolevate võimaluste valimiseks saame kasutame nn. maagilisi konsooli käske. Konsooli sisseehitatud maagilised käsud (Built-in magic console commands, line magic commands) muudavad arenduskeskkonna käitumist. Meile olulised maagilised käsud (Spyder IDE konsool):
%matplotlib inlinevõimatplotlib inline$-$ graafikud kujutatakse konsooli aknas%matplotlib autovõimatplotlib auto$-$ graafikud avatakse eraldi aknas koos graafilise kasutajaliidesega (andmete visuaalne analüüs ja manipulatsioon; zoom, pan, crop, jne.)
Kui sa soovid järgi vaadata kõiki defineeritud maagilisi käske siis sisesta konsooli %lsmagic või lsmagic.
Loomulikult saab graafikute kuvamise viisi kontrollida ka läbi Spydery graafilise kasutajaliidese (vt. Preferences $\to$ IPython console). Lisaks on Spyderi uuemates versioonides loodud programmi akna allosas olevale inforibale nupp millega saab jooniste kuvamise režiimi muuta, vt, Joonis 7.
| Joonis 7. Nupp mille abil saab juhtida joonisete kujutamise viisi. Lülita ümber režiimide auto ja inline vahel. |
Joonised 8, 9 ja 10 kujutavad arenduskeskkonna Spyder kolme erinevat jooniste kuvamise moodust.
Joonis 8. Maagiliste käskude %matplotlib inline või matplotlib inline esile kutsutud joonise kujutamine konsooli aknas. |
Joonis 9. Maagiliste käskude %matplotlib auto või matplotlib auto esile kutsutud joonise kujutamine eraldi aknas koos graafilise kasutajaliidesega. |
Joonis 9 näitab kuidas saab joonised konsooli väljatrükist eemaldada kasutades graafilist kasutajaliidest.
| Joonis 10. Graafikute kujutamine ainult arenduskeskkonna Spyder "Plots" aknas. |
2.4.3 Integreeritud arenduskeskkond Jupyter¶
Jupyter Notebook ja JupyterLab saab kuvada jooniseid:
- IPythoni konsooli väljatrüki osana ehk koodirakku alla (vaikimisi)
- Eraldi Matplotlibi aknas koos Matplotlib'i graafilise kasutajaliidesega
Olemasolevate võimaluste valimiseks saame kasutame nn. maagilisi konsooli käske. Konsooli sisseehitatud maagilised käsud (Built-in magic console commands, line magic commands) muudavad arenduskeskkonna käitumist. Meile olulised maagilised käsud (Jupyteri koodirakk):
%matplotlib inlinevõimatplotlib inline$-$ graafikud kujutatakse konsooli aknas%matplotlib autovõimatplotlib auto$-$ graafikud avatakse eraldi aknas koos graafilise kasutajaliidesega (andmete visuaalne analüüs ja manipulatsioon; zoom, pan, crop, jne.)
Loomulikult saab graafikute kuvamise viisi kontrollida ka läbi Spyder'i graafilise kasutajaliidese.
2.4.4 Muud arenduskeskkonnad¶
Muud arenduskeskkonnad peaksid võimaldama sarnaseid joonistega töötamise võimalusi, kuid ei pruugi.