Ohjeita Pythonin käyttöön Jupyterilla#

Tähän tiedostoon on koottu joitain tärkeimpiä tietoja Pythonin toiminnasta ja erilaisia funktioita joista voi olla hyötyä alkuun pääsemisessä. Mukaan on laitettu muutamia esimerkkejä niiden toiminnasta.

Tämän dokumentin käytöstä: on tarkoituksenmukaista ajaa ensin kohdasta 2 löytyvät paketit ja sitten siirtyä sinne mitä oli etsimässä. Tästä johtuen erinäisiä funktioita, pääasiassa satunnaisgeneraattoreita, määritellään dokumentissa useassa kohtaa edestakaisin selaamisen välttämiseksi.

Jos et muista miten jokin toiminto näissä notebookeissa menikään, paina h-painiketta kun et ole valinnut mitään soluja ja näet listan Jupyterin pikanäppäimistä.

  1. Lähtökohdat

  2. Paketit

  3. Tietorakenteet ja datan käsittely

  4. Peruslaskutoimitukset

  5. Satunnaisdatan luominen

  6. Kuvaajien piirtäminen

  7. Animaatioiden tekeminen

  8. Kartat ja lämpökartat

  9. Ongelmia? Katso tänne

1. Lähtökohdat#

Ohjelmoinnissa muuttujiin voidaan tallentaa erilaisia arvoja, joita halutaan käyttää tai muuttaa myöhemmin. Erilaisia muuttujatyyppejä ovat esimerkiksi kokonaisluku (int), liukuluku (float) ja merkkijono (string). Pythonissa muuttujien luominen on helppoa, sillä muuttujatyyppiä ei tarvitse erikseen määrittää.

Joskus ohjelmaa ajavaan ytimeen, kerneliin, jää muistiin palasia jotka haittaavat ohjelman toimintaa. Tätä sattuu aina välillä, ei hätää. Paina ylävalikoista ‘Kernel’ ja ‘Restart & Clear Output’, jolloin muistissa olevat prosessit ja tulosteet pyyhitään ja voit aloittaa alusta. Tämä ei kuitenkaan vaikuta mihinkään muutoksiin tekstissä tai koodissa, eli se ei ole virheiden korjausnappi.

2. Paketit#

Python on tieteelliseen laskentaan soveltuva ohjelmointikieli, jolle on kehitetty monenlaisia eri tavalla suoritusteholtaan optimoituja toimintoja. Sen perusperiaatteisiin kuuluu erilaisten pakettien, eli funktiokirjastojen, käyttäminen. Nämä paketit tuodaan import-komennolla käyttöön ja vaikka se saattaakin alkuun tuntua hienoiselta taikatempulta tietää mitä pitäisi kutsua, se selkeytyy nopeasti.

Jos vilkaiset Open Data -projektissa tarjottuja materiaaleja, huomaat että jokaisessa Github-kansiossamme on mukana tekstitiedosto “requirements.txt”, jonka mukaan esimerkiksi MyBinder osaa rakentaa Jupyterille työalustan. Sieltä näkee, että tärkeimmät paketit joita tulemme käyttämään ovat seuraavat:

# Oleellisimmat paketit:

import pandas as pd   # sisältää erilaisia datan lukemiseen liittyviä työkaluja
import numpy as np   # sisältää erilaisia numeerisen laskennan työkaluja
import matplotlib.pyplot as plt  # sisältää kuvaajien (plot) piirtotyökaluja

# Muita hyödyllisiä paketteja:

import random as rand   # sisältää funktioita satunnaislukujen generoimiseen
from scipy import stats    # sisältää tieteellistä laskentaa ja tilastolaskentaa
from scipy.stats import norm    # normaalijakaumatyökaluja
import matplotlib.mlab as mlab  # lisää kuvaajatyökaluja monimutkaisempiin kuvaajiin

# Ei paketti, mutta oleellinen komento joka mahdollistaa tulosten näkymisen nätisti harjoitteissa:

%matplotlib inline

Muista ajaa yllä oleva solu jos haluat että tämän notebookin esimerkit toimivat. Ylläolevan voisi kirjoittaa lyhemminkin ilman as-merkintää, joka nimeää paketit uudelleen, mutta sillä voidaan merkittävästi lyhentää tulevien solujen tekstejä. Jos haluat lukea paketeista tarkemmin, valitse ylempää pudotusvalikko ‘Help’ ja löydät sieltä linkkejä tarkempiin dokumentaatioihin tai muotoiluohjeisiin.

Paketteja on paljon muitakin ja niitä voi tarpeen mukaan googlailla lisää, jos johonkin tarvitsee. Pythonin laajan käyttäjäkunnan ansiosta verkosta löytää helposti tuhansittain esimerkkejä ja hakemistoja. StackExchange ja StackOverflow ovat hyviä paikkoja vastausten etsimiseen.

3. Tietorakenteet ja datan käsittely#

Kooste:

Csv-luku \(\rightarrow\)

nimi = pd.read_csv('polku', muotoiluargumentteja)

Taulukon luku \(\rightarrow\)

pd.read_table('polku', muotoiluargumentteja)

Sisällön tarkastelu \(\rightarrow\)

nimi.head(n)  

Pituus \(\rightarrow\)

len(nimi)  

Muoto \(\rightarrow\)

nimi.shape 

Sarakkeet \(\rightarrow\)

nimi.sarake 
nimi['sarake'] 

Tietyn välin rajaus \(\rightarrow\)

nimi[(nimi.arvosarake >= alaraja) & (nimi.arvosarake <= yläraja)]  

Tekstin etsintä \(\rightarrow\)

vanhaNimi['sarake'].str.contains('haettu_pätkä')  

Lisää sarake \(\rightarrow\)

nimi = nimi.assign(sarake = tiedot) 

Poista sarakkeita \(\rightarrow\)

nimi.drop(['sarake1','sarake2'...], axis = 1)

CMS-kokeen avoin data on .csv-muotoista, eli pilkuilla erotettua tekstipuuroa (comma separated values). Tällainen tieto on hyvin helppoa lukea taulukkona tietokoneen käyttöön pandas-paketin avulla, jolloin koneen muistiin syntyy kaksiulotteinen ns. dataframe-muotoinen taulukko. Tarkempia tietoja täällä, mistä löytyy myös yksityiskohtaisempi lista eri toiminnoista joita valitulle datalle voi tehdä.

Helpoimmat tavat lukea data käyttöön ovat pd.read_csv ja pd.read_table. Jos data on nättiä (erottajana pilkku, otsikointi on tehty, fontit eivät sodi tervettä järkeä vastaan…), yleensä mitään lisätoimenpiteitä ei tarvita.

# Ladataan setti hiukkasdataa muuttujaksi ja määritetään sille haluttu nimi:

kaksoismyonit = pd.read_csv('../Data/Dimuon_DoubleMu.csv')

# Jos haluaisi käyttää suoraa urlia, sijoita tämä 'http://opendata.cern.ch/record/545/files/Dimuon_DoubleMu.csv'
# nimen tilalle.

Tällainen suora muoto hakee tiedoston, joka sijaitsee samassa kansiossa kuin käytössä oleva notebook. Tiedosto voidaan hakea myös suoraan verkko-osoitteen urlista tai toisesta kansiosta ‘../kansio/tiedosto.csv’ -rakenteella.

Mikäli data on toisessa tekstimuodossa, voi kokeilla yleisempää read_table -käskyä, joka ymmärtää muitakin taulukoitavia tekstejä. Yleisin ongelma on, että teksti on eroteltu jollain muulla kuin pilkulla, kuten tabulaattorilla tai puolipisteellä. Tällöin komentoon voi laittaa argumentin sep = ‘x’, missä x on erottimena käytetty merkki. Toinen yleinen ongelma on, että tiedoston rivitys alkaa omituisesti tai sarakkeiden otsikot ovat jossain muualla kuin ensimmäisellä rivillä. Tällöin voidaan laittaa komentoon argumentti header = n, missä n on otsikoiksi halutun rivin järjestysnumero. HUOM! Tietokone aloittaa järjestysluvut aina nollasta jollei toisin mainita.

Tarkempia tietoja mahdollisista argumenteista täällä.

Alla esimerkki datasta, jolla ei ole otsikkoriviä. Datassa näkyy Auringosta tehtyjä havaintoja vuodesta 1992 eteenpäin ja tarkemmat selosteet kunkin sarakkeen merkityksille löytyvät täältä.

# Ladataan setti aurinkodataa ja nimetään se halutulla tavalla:

aurinko = pd.read_table('http://sidc.oma.be/silso/INFO/sndhemcsv.php', sep = ';', encoding = "ISO-8859-1")

Selkeyden vuoksi katsotaan miltä datamme näyttävät. Tähän sopii erinomaisesti nimi.head(n)-komento, joka näyttää n ensimmäistä riviä valitusta datasta. Oletusarvoisesti n = 5, jos laittaa tyhjät sulkeet.

kaksoismyonit.head()
Run Event type1 E1 px1 py1 pz1 pt1 eta1 phi1 ... type2 E2 px2 py2 pz2 pt2 eta2 phi2 Q2 M
0 165617 74601703 G 9.6987 -9.5104 0.3662 1.8633 9.5175 0.1945 3.1031 ... G 9.7633 7.3277 -1.1524 6.3473 7.4178 0.7756 -0.1560 1 17.4922
1 165617 75100943 G 6.2039 -4.2666 0.4565 -4.4793 4.2910 -0.9121 3.0350 ... G 9.6690 7.2740 -2.8211 -5.7104 7.8019 -0.6786 -0.3700 1 11.5534
2 165617 75587682 G 19.2892 -4.2121 -0.6516 18.8121 4.2622 2.1905 -2.9881 ... G 9.8244 4.3439 -0.4735 8.7985 4.3697 1.4497 -0.1086 1 9.1636
3 165617 75660978 G 7.0427 -6.3268 -0.2685 3.0802 6.3325 0.4690 -3.0992 ... G 5.5857 4.4748 0.8489 -3.2319 4.5546 -0.6605 0.1875 1 12.4774
4 165617 75947690 G 7.2751 0.1030 -5.5331 -4.7212 5.5340 -0.7736 -1.5522 ... G 7.3181 -0.3988 6.9408 2.2825 6.9523 0.3227 1.6282 1 14.3159

5 rows × 21 columns

aurinko.head()
1992 01 01.1 1992.001 186 0 186 14.3 1.0 14.3 19 -1 -1.1 1
0 1992 1 2 1992.004 190 18 172 8.2 2.6 7.8 21 -1 -1 1
1 1992 1 3 1992.007 234 26 208 18.3 6.1 17.2 21 -1 -1 1
2 1992 1 4 1992.010 243 54 189 14.8 7.0 13.0 20 -1 -1 1
3 1992 1 5 1992.012 242 58 184 13.8 6.8 12.0 18 -1 -1 1
4 1992 1 6 1992.015 245 79 166 18.7 10.6 15.4 14 -1 -1 1

Ylläolevista nähdään, että aurinko-muuttujan ensimmäinen oikea rivi on kaapattu otsikoiksi, mikä on sikäli ikävää että 1) nyt otsikot ovat hämääviä ja 2) meiltä jää yhden rivin verran dataa käsittelemättä jos haluamme vaikkapa kuvaajan taulukon jostain sarakkeesta. Ratkaistaan asia laittamalla dataa ladattaessa otsikointiargumentiksi header = -1, eli rivi jota ei ole olemassa, jolloin kone tuottaa siihen järjestyslukurivin.

aurinko = pd.read_table('http://sidc.oma.be/silso/INFO/sndhemcsv.php', sep = ';', encoding = "ISO-8859-1", header = None)
aurinko.head()
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 1992 1 1 1992.001 186 0 186 14.3 1.0 14.3 19 -1 -1 1
1 1992 1 2 1992.004 190 18 172 8.2 2.6 7.8 21 -1 -1 1
2 1992 1 3 1992.007 234 26 208 18.3 6.1 17.2 21 -1 -1 1
3 1992 1 4 1992.010 243 54 189 14.8 7.0 13.0 20 -1 -1 1
4 1992 1 5 1992.012 242 58 184 13.8 6.8 12.0 18 -1 -1 1

Jos haluamme muuttaa otsikoinnin selkeämmäksi, voimme tietysti myös nimetä sarakkeet names = [‘nimi’,’nimi2’,’nimi3’]-argumentilla.

aurinko = pd.read_table('http://sidc.oma.be/silso/INFO/sndhemcsv.php', sep=';', encoding = "ISO-8859-1", header = None, 
names = ['Vuosi','Kuukausi','Päivä','Fraktio','$P_{tot}$','$P_{poh}$','$P_{et}$','$\sigma_{tot}$','$\sigma_{poh}$',
         '$\sigma_{et}$','$N_{tot}$','$N_{poh}$','$N_{et}$','Prov'])
aurinko.head()
Vuosi Kuukausi Päivä Fraktio $P_{tot}$ $P_{poh}$ $P_{et}$ $\sigma_{tot}$ $\sigma_{poh}$ $\sigma_{et}$ $N_{tot}$ $N_{poh}$ $N_{et}$ Prov
0 1992 1 1 1992.001 186 0 186 14.3 1.0 14.3 19 -1 -1 1
1 1992 1 2 1992.004 190 18 172 8.2 2.6 7.8 21 -1 -1 1
2 1992 1 3 1992.007 234 26 208 18.3 6.1 17.2 21 -1 -1 1
3 1992 1 4 1992.010 243 54 189 14.8 7.0 13.0 20 -1 -1 1
4 1992 1 5 1992.012 242 58 184 13.8 6.8 12.0 18 -1 -1 1

nimi.heads()-komennon ohella pari muuta pikkukomentoa ovat hyödyllisiä tarkasteltaessa datan muotoa. len(nimi) kertoo rivien määrän eli muuttujan pituuden (length), nimi.shape sekä rivien että sarakkeiden määrän.

# Normaalisti koodisolun tuloste näyttää vain viimeisimmän annetun toiminnon. Print()-komennolla saadaan
# useampia arvoja näkyviin. Voit kokeilla mitä käy jos poistat printin näiden edestä.

print (len(aurinko))
print (aurinko.shape)
11170
(11170, 14)

Kun data on nyt saatu tyydyttävässä muodossa koneelle, sitä voidaan ryhtyä muokkaamaan halutun näköiseksi. Usein ollaan kiinnostuneita jostain yksittäisestä muuttujasta datan sisässä, jolloin pitää pystyä eristämään tiettyjä sarakkeita alkuperäisistä tiedoista tai valita vain ne rivit, joilla jonkin muuttujan arvo on tietyissä rajoissa.

Sarakkeen voi valita kirjoittamalla nimi.sarake tai nimi[‘sarake’]. Hakasulkumuoto on tarpeen, jos sarakkeen nimi esimerkiksi alkaa numerolla, jonka kone saattaa tulkata järjestysluvuksi. Jos haluaa helpottaa elämäänsä eikä välitä muista sarakkeista, kannattaa tämä eristys tehdä uuteen muuttujaan, eli kirjoittaa uusiNimi = nimi.sarake ja käsitellä sitä. Tässä on sekin etu, että dataa on tällöin vähemmän kerrallaan käsittelyssä eikä tietokone mahdollisten kirjoitusvirheiden tapauksessa ala esimerkiksi piirtämään histogrammeja kymmenissä ulottuvuuksissa koko alkuperäisestä datasta ja tukehdu yrittäessään.

# Erotellaan uuteen muuttujaan myonidatan invarianttien massojen sarake, joka on otsikoitu nimellä M.

iMassat = kaksoismyonit.M
iMassat.head()
0    17.4922
1    11.5534
2     9.1636
3    12.4774
4    14.3159
Name: M, dtype: float64

Rivien valinta tapahtuu luomalla vastaavasti uusi muuttuja, jonka alkioiksi valitaan ne alkuperäisen datan alkiot, jotka täyttävät annetut ehdot. Tällöin esimerkiksi jonkin arvovälin valinta olisi

uusiNimi = nimi[(nimi.arvosarake >= alaraja) & (nimi.arvosarake <= ylaraja)]

Sinänsä jakoehto voi olla mikä tahansa muukin looginen elementti, kuten tasan tietty luku tai tekstinpätkä ei-numeerisessa datassa.

# Otetaan esimerkkinä hiukkasdatasta ne rivit, joissa molempien hiukkasten energia on 30 GeViä tai yli.

yli30 = kaksoismyonit[(kaksoismyonit.E1 >= 30) & (kaksoismyonit.E2 >= 30)]
yli30.head()
Run Event type1 E1 px1 py1 pz1 pt1 eta1 phi1 ... type2 E2 px2 py2 pz2 pt2 eta2 phi2 Q2 M
21 165617 74969122 G 59.9226 -46.1516 29.4115 -24.4070 54.7266 -0.4324 2.5742 ... G 52.4465 30.1097 -16.3989 -39.6876 34.2859 -0.9885 -0.4987 -1 89.9557
36 165617 75138253 G 97.1011 -23.6144 6.6660 -93.9497 24.5372 -2.0524 2.8665 ... G 30.5992 -11.6134 -25.9848 11.2347 28.4619 0.3851 -1.9911 1 88.6081
46 165617 75887636 G 152.9720 7.4657 -30.7098 -149.6710 31.6042 -2.2593 -1.3323 ... G 33.5835 -9.2878 28.7457 -14.6719 30.2089 -0.4684 1.8833 1 88.2438
78 165617 75833588 G 181.8770 44.1427 -14.9498 175.8040 46.6055 2.0379 -0.3265 ... G 170.0210 -34.6301 12.3248 166.0000 36.7579 2.2128 2.7997 -1 83.0943
110 165617 75779415 G 50.2440 37.8072 -12.2044 -30.7590 39.7283 -0.7124 -0.3122 ... G 49.2396 -47.6064 8.2338 -9.5061 48.3132 -0.1955 2.9703 -1 90.3544

5 rows × 21 columns

print (len(yli30))
print (len(kaksoismyonit))
6516
100000

Jos halutaan useampi kokonainen sarake erittelemättä rivejä, voi kirjoittaa

uusiMuuttuja = vanhaMuuttuja[['sarake1', 'sarake2', ...]]

Jos halutaan hakea tekstiä, voidaan kirjoittaa vaikkapa seuraavasti nimi.loc[ ]-funktion avulla.

uusiMuuttuja = vanhaMuuttuja.loc[vanhaMuuttuja['sarake'] == 'haluttu_asia']

Tällöin toki on tiedettävä tarkkaan mitä etsii. Jos haluaa kysyä datalta sokeammin kysymyksiä, contains-toiminto auttaa.

uusiNimi = vanhaNimi[vanhaNimi['sarake'].str.contains('haettu_pätkä')]

tekee uuden muuttujan, jossa on nyt haetun pätkän sisältäneet rivit. Oletusarvoisesti toiminto välittää kirjainkoosta, mutta sen voi laittaa pois päältä kirjoittamalla

uusiNimi = vanhaNimi[vanhaNimi['sarake'].str.contains('haettu_pätkä', case = False)]

Kannattaa huomata, että str.contains() itsessään on looginen operaatio, joten koodi palauttaa tiedon kyllä / ei kysytystä asiasta. Myös negaatio toimii, kuten alla suomalaisten alkoholivalmistajien listan selaaminen poistamalla kaikki Oy tai Oyj yritykset. Samalla toki voi mennä joku Oy-alkuinen firma, joten kannattaa olla tarkkana.

juomat = pd.read_csv('http://avoindata.valvira.fi/alkoholi/alkoholilupa_valmistus.csv', sep = ';', encoding = "ISO-8859-1", na_filter = False)
juomat.head()
Y-Tunnus Nimi Luvan alkupvm Olut Viini Hedelmäviini Tislatut alkoholijuomat Väkiviina Käsityöläisoluen vähittäismyyntilupa Tilaviinin vähittäismyyntilupa
0 0107011-5 Berner Oy 03.02.2014 X
1 0170318-9 Olvi Oyj 17.02.1995 X X X
2 0171079-2 Alahovin Viinitila Oy 25.02.1995 X X X X X
3 0171110-3 Oy Gust. Ranin Lignell & Piispanen 20.01.1995 X X
4 0193088-3 Kakslauttanen Arctic Resort Oy 25.02.2020 X
tiettyValmistaja = juomat[juomat['Nimi'].str.contains('Oy') == False]
print (len(juomat))
print (len(tiettyValmistaja))
204
35

Jos haluat lisätä tai poistaa sarakkeita datasta, siihen sopivat toiminnot nimi = nimi.assign(sarake = tiedot) ja nimi.drop([‘sarake1’,’sarake2’…], axis = 1). Dropissa axis on sikäli tärkeä parametri, että komento ymmärtää poimia nimenomaan sarakkeen.

# Sarakkeen poistaminen dropilla.
# Joskus .drop ei toimi suoraan, joten tallennetaan sen tulos aiemman muuttujan tilalle sekaannuksen välttämiseksi.

juomat = juomat.drop(['Nimi'], axis = 1)
juomat.head()
Y-Tunnus Luvan alkupvm Olut Viini Hedelmäviini Tislatut alkoholijuomat Väkiviina Käsityöläisoluen vähittäismyyntilupa Tilaviinin vähittäismyyntilupa
0 0107011-5 03.02.2014 X
1 0170318-9 17.02.1995 X X X
2 0171079-2 25.02.1995 X X X X X
3 0171110-3 20.01.1995 X X
4 0193088-3 25.02.2020 X
# Sarakkeen lisääminen assignilla.
# Laitetaan tässä äskeiseen mukaan sarake R, jossa on numeroita.
# Huolehdi, että sarake on oikean mittainen. Sisältö itsessään voi olla mitä vain, lukuja, tekstiä jne.

luvut = np.linspace(0, 100, len(juomat))
 
juomat = juomat.assign(R = luvut)
juomat.head()
Y-Tunnus Luvan alkupvm Olut Viini Hedelmäviini Tislatut alkoholijuomat Väkiviina Käsityöläisoluen vähittäismyyntilupa Tilaviinin vähittäismyyntilupa R
0 0107011-5 03.02.2014 X 0.000000
1 0170318-9 17.02.1995 X X X 0.492611
2 0171079-2 25.02.1995 X X X X X 0.985222
3 0171110-3 20.01.1995 X X 1.477833
4 0193088-3 25.02.2020 X 1.970443

4. Peruslaskutoimitukset ja syntaksilogiikka#

Kooste:

Itseisarvot \(\rightarrow\)

abs(x)  

Neliöjuuret \(\rightarrow\)

sqrt(x)  

Yhteenlasku \(\rightarrow\)

x + y 

Vähennyslasku \(\rightarrow\)

x - y  

Jakolasku \(\rightarrow\)

x/y  

Kertolasku \(\rightarrow\)

x*y  

Potenssit \(\rightarrow\)

x**y  

Suurin arvo \(\rightarrow\)

max(x)  

Pienin arvo \(\rightarrow\)

min(x)  

Oman funktion luominen \(\rightarrow\)

def nimi(syöte):
    toimitukset
    

Peruslaskutoimituksissa ei ole mitään erityisen ihmeellistä, ne kirjoitetaan siinä missä mihin tahansa muuhunkin tietokonelaskimeen. Jos haluaa ohjelman tulostavan useamman asian kerrallaan, kannattaa käyttää print()-komentoa jolloin muutakin kuin viimeisin vaihe tulee näkyviin. Tekstiä ja numeroita voi myös yhdistää, jolloin repr(lukuja) voi helpottaa. Tämä näppärä funktio palauttaa annetusta muuttujasta printattavan version. Tästä näppärästä listasta näkee mitä kaikkia sisäänrakennettuja funktioita Pythonissa on ilman pakettejakin, mm. pienimmän ja suurimman luvun etsimistä tiedostosta, lukujen pyöristämistä ja vastaavaa. Jos joku jäi ihmetyttämään, tässä vielä lisää samasta asiasta.

# Voit muokata 'luku'-muuttujan sisältöä eri laskuiksi.

luku = 14*2+5/2
teksti = 'Päivän tulos on: '
print (teksti + repr(luku))
Päivän tulos on: 30.5
# Max() hakee joukon suurimman alkion.

kasaLukuja = [3,6,12,67,578,2,5,12,-34]

print('Suurin luku on ' + repr(max(kasaLukuja)))
Suurin luku on 578

Kiinnostavampaa on, että Pythonissa voi luoda myös omia funktioitaan tiettyihin tarpeisiin. Tällöin määritellään funktion nimi ja vaikutus sisään laitettuun tietoon kirjoittamalla

def funktionNimi(syöte):  
    kuvaus sisennettynä
# Luodaan funktio, joka puolittaa sille syötetyn numeron.

def puolitus(a):
    print(a/2)
    
puolitus(6)
3.0
# Tehdään summauskone, joka kysyy käyttäjältä kokonaislukuja (toimii MyBinderissa). 

def summaus(x, y):
    summa = x + y
    lause = '{} + {} on yhteensä {}.'.format(x, y, summa)
    print(lause)

def vapaaValinta():
    a = int(input("Anna kokonaisluku: "))
    b = int(input("Anna toinenkin kokonaisluku: "))
    summaus(a, b)

vapaaValinta() 
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Input In [24], in <cell line: 13>()
     10     b = int(input("Anna toinenkin kokonaisluku: "))
     11     summaus(a, b)
---> 13 vapaaValinta()

Input In [24], in vapaaValinta()
      8 def vapaaValinta():
----> 9     a = int(input("Anna kokonaisluku: "))
     10     b = int(input("Anna toinenkin kokonaisluku: "))
     11     summaus(a, b)

File /opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/ipykernel/kernelbase.py:1174, in Kernel.raw_input(self, prompt)
   1167 """Forward raw_input to frontends
   1168 
   1169 Raises
   1170 ------
   1171 StdinNotImplementedError if active frontend doesn't support stdin.
   1172 """
   1173 if not self._allow_stdin:
-> 1174     raise StdinNotImplementedError(
   1175         "raw_input was called, but this frontend does not support input requests."
   1176     )
   1177 return self._input_request(
   1178     str(prompt),
   1179     self._parent_ident["shell"],
   1180     self.get_parent("shell"),
   1181     password=False,
   1182 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
# Luodaan funktio, joka palauttaa annetun radiaanilistan tulokset astelistana. While-silmukka käy läpi ensimmäisestä
# alkiosta haluttuun kohtaan asti listaa ja tekee alkioille jonkun toimituksen.

def kulmaus(a):
    i=0
    while i < len(a):
        a[i] = a[i]*360/(2*np.pi)
        i+=1
    return a;

radeja = [5,2,4,2,1,3]
kulmaus(radeja)
print(radeja)
[286.4788975654116, 114.59155902616465, 229.1831180523293, 114.59155902616465, 57.29577951308232, 171.88733853924697]
# Tai sama nätimmin for-silmukkana ihan samalla periaatteella:

def kulmain(a):
    for i in range(0,len(a)):
        a[i] = a[i]*360/(2*np.pi)
    return a;
    
rad = [1,2,3,5,6]
kulmain(rad)
print(rad)
[57.29577951308232, 114.59155902616465, 171.88733853924697, 286.4788975654116, 343.77467707849394]

5. Satunnaisdatan luominen#

Kooste:

Satunnainen kokonaisluku \(\rightarrow\)

rand.randint(alin,ylin)

Satunnainen luku 0 ja 1 välillä \(\rightarrow\)

rand.random()  

Mielivaltainen joukko mahdollisista \(\rightarrow\)

rand.choices(joukko, painotukset, k = määrä)  

Mielivaltainen joukko populaatiosta \(\rightarrow\)

rand.sample(joukko, k = määrä)  

Normaalijakauma \(\rightarrow\)

rand.normalvariate(odotusarvo, keskihajonta)  

Tasainen lukujono \(\rightarrow\)

np.linspace(alku, loppu, num = jakojen lukumäärä)  

Tasainen lukujono \(\rightarrow\)

np.arange(alku, loppu, askelkoko)

Joskus on kiinnostavaa luoda oikean datan kaveriksi simuloituja tai satunnaisia mittauksia. Siinä missä vaikkapa monimutkaisempien Monte Carlo -simulaatioiden tuottaminen meneekin tämän ohjeen tavoitteiden ulkopuolelle, voidaan silti vilkaista hieman erilaisia tapoja tuottaa satunnaisia numeroita. Tietokoneen tapauksessa on kuitenkin muistettava, että tavalliset satunnaisgeneraatiometodit ovat siinä mielessä pseudosatunnaisia deterministisiä prosesseja, ettei niitä kannata käyttää vaikkapa pankkisalaisuuksien tai turvalukujen kehittämiseen. Sitä varten on olemassa raskaampia ja monimutkaisempia keinoja.

# Luodaan satunnainen kokonaisluku halutulta väliltä.

lottoarpa = rand.randint(1,100)
teksti = 'Päivän voittoarpa on: '
print (teksti + repr(lottoarpa))
Päivän voittoarpa on: 20
# Luodaan satunnainen liukuluku 0 ja 1 väliltä kerrottuna viidellä.

luku = rand.random()*5
print (luku)
1.106541186311945
# Poimitaan satunnaisia jäseniä listalta, mutta tehdään tietyistä valinnoista todennäköisempiä.

nimiLapsille = ['Pekka','Jukka','Iida','Netta','Paula','Torsti']
todariPainot = [10,30,20,50,5,5]

# Määritetään montako nimeä halutaan, k = haluttu. Choices-komento voi ottaa saman tulokset useita kertoja.

nimet = rand.choices(nimiLapsille, weights = todariPainot, k = 3)
print(nimet)
['Jukka', 'Netta', 'Netta']
# Samanlainen valinta ilman toistoa.

oppilaat = ['Pekka','Jukka','Iida','Netta','Paula','Torsti']
kolmeVapaaehtoista = rand.sample(oppilaat, k = 3)
print (kolmeVapaaehtoista)
['Jukka', 'Iida', 'Torsti']
# Satunnaisluku annetusta normaalijakaumasta (odotusarvo, keskihajonta).

luku = rand.normalvariate(3, 0.1)
print (luku)
2.9905294642119853
# Luodaan tasaisesti jaettu lukusuora 1 ja 10 väliltä ja satunnaistetaan sitä hieman.

lukusuora = np.linspace(1, 10, 200)

def satunnaistin(a):
    b = a.copy()
    
    for i in range(0,len(b)):
        b[i] = b[i]*rand.uniform(0,b[i])
    return b;

tulos = satunnaistin(lukusuora)
# print(lukusuora)
# print(tulos)

fig = plt.figure(figsize=(15, 10))
plt.plot(tulos,'g*')
plt.show()
../_images/Ohjeita Pythonin käyttöön_59_0.png
# Toinen tapa tuottaa tasaisesti jaettu lukujoukko [a,b[ komennolla arange(a,b,c) missä c on askeleen koko.
# Toiminto voi joskus häröillä jos c ei ole kokonaisluku. Huomaa, että b ei kuulu joukkoon.

luvut = np.arange(1,10,1)
print(luvut)
[1 2 3 4 5 6 7 8 9]

6. Kuvaajien piirtäminen#

Kooste:

Peruskuvaaja \(\rightarrow\)

plt.plot(nimi, 'tyyli ja väri', muotoiluja)

Hajotuskuva eli scatterplot \(\rightarrow\)

plt.scatter(x-data, y-data, marker = 'merkkityyli', color = 'väri', muotoiluja)

Histogrammi \(\rightarrow\)

plt.hist(nimi, jakojenMäärä, range = (alku, loppu), muotoiluja)

Selite näkyviin (vaatii muotoiluparametrit label = ‘nimike’) \(\rightarrow\)

plt.legend(muotoiluja)

Kuvaajat nätisti näkyviin samaan kuvaan \(\rightarrow\)

plt.show()

Normaalijakauman sovitus dataan \(\rightarrow\)

(mu, sigma) = norm.fit(nimi)
...

Kuvaajan muotoiluja ja nimeämistä \(\rightarrow\)

plt.xlabel('vaakanimi')
plt.title('otsikko')
fig = plt.figure(figsize=(vaakaluku, pystyluku))

Virheiden piirtämisestä \(\rightarrow\)

plt.errorbar(arvo1, arvo2, xerr = virhe1, yerr = virhe2, fmt = 'none')

Kuvaajat ovat kenties tärkein syy käyttää ohjelmointia tiedeopetuksessa. Isoistakin aineistoista on nopeaa ja vaivatonta tuottaa selkeyttäviä visualisaatioita. Kuhunkin tapaukseen sopii omanlaisensa metodi, joista yleisimpiä tarkastellaan tässä.

Kuvaajien värejä ja merkkejä voi vaihtaa vapaasti. Täällä on lista ymmärretyistä grafiikoista.

# Peruskuva plot-funktiolla. Jos parametreina toimii vain yksi tietorivi, x-akseli otetaan järjestysnumeroista.

lukuja = [1,3,54,45,52,34,4,1,2,3,2,4,132,12,12,21,12,12,21,34,2,8]
plt.plot(lukuja, 'b*')

# plt.show() on hyvä laittaa piirtojen loppuun. Kuvaajan saa näkyviin ilmankin, mutta joskus näkyviin tulisi pelkkä
# tiedostopaikka tai laskennallista sotkua. Tämä selkeyttää asioita, käytä sitä.

plt.show()
../_images/Ohjeita Pythonin käyttöön_64_0.png
# Kuvaajille on kuitenkin hyvä antaa nimiä, jotta niiden lukijallekin on selvää mitä tapahtuu. 
# Tässä näet miten eri osia voi nimetä.

# Kaksi satunnaista datasettiä.

tulos1 = np.linspace(10, 20, 50)*rand.randint(2,5)
tulos2 = np.linspace(10, 20, 50)*rand.randint(2,5)

# Piirretään molemmat.

plt.plot(tulos1, 'r^', label = 'Mittaus 1')
plt.plot(tulos2, 'b*', label = 'Mittaus 2')

# Nimetään akselit ja otsikot. Fontsize-parametrilla saa tekstit haluttuun kokoon.

plt.xlabel('Aika (s)', fontsize = 15)
plt.ylabel('Nopeus (m/s)', fontsize = 15)
plt.title('Liikkeen mittauksia \n', fontsize = 15) # \n luo uuden rivin otsikon muotoilua varten

# Lisätään selite. Jos loc-parametria ei määritellä, selite laitetaan automaattisesti johonkin mihin se mahtuu.

plt.legend(loc='upper left', fontsize = 15)

# Näytetään kuvaaja.

plt.show()
../_images/Ohjeita Pythonin käyttöön_65_0.png
# Myös trigonometrisia funktioita voidaan piirtää nätisti.

# Tehdään x-akseliksi tasainen lukusuora.

x = np.linspace(0, 10, 100)

# Määritetään piirrettävät funktiot-

y1 = np.sin(x)
y2 = np.cos(x)

# Piirretään kuvaajat.

plt.plot(x, y1, color = 'b', label = 'sin(x)')
plt.plot(x, y2, color = 'g', label = 'cos(x)')

plt.legend()

plt.show()
../_images/Ohjeita Pythonin käyttöön_66_0.png
# Kuvaajien oletuskoko Jupyterissa on jokseenkin pieni. figsize-komento auttaa skaalaamaan kuvia haluttuihin mittoihin.
# Toistetaan äskeinen kuvaaja.

x = np.linspace(0, 10, 100)

y1 = np.sin(x)
y2 = np.cos(x)

# Tässä määrätään koko. Voit testata miten lukujen muokkaus vaikuttaa.

fig = plt.figure(figsize=(15, 10))

plt.plot(x, y1, color = 'b', label = 'sin(x)')
plt.plot(x, y2, color = 'g', label = 'cos(x)')

plt.legend()

plt.show()
../_images/Ohjeita Pythonin käyttöön_67_0.png

Toinen perinteinen kuvaaja on arvopareja piirtävä scatterplot, jossa molemmat akselit ovat muuttujia. Hyvin yleinen esimerkiksi fysiikan tutkimuksessa.

def satunnaistin(a):
    b = a.copy()
    for i in range(0,len(b)):
        b[i] = b[i]*rand.uniform(0,1)
    return b;

# Otetaan satunnaista dataa, jossa toinen arvo jakautuu välillä 0-5 ja toinen 0-20.

arvo1 = satunnaistin(np.linspace(3,5,100))
arvo2 = satunnaistin(np.linspace(10,20,100))

fig = plt.figure(figsize=(10,5))
plt.scatter(arvo1, arvo2, marker ='*', color = 'b')
plt.show()
../_images/Ohjeita Pythonin käyttöön_69_0.png
# Toinen scatter-esimerkki, jossa molemmat arvot
# hajoavat normaalijakauman mukaan odotetusta lineaarisesta riippuvuudesta.


def satunnaistaja(a):
    b = a.copy()
    for i in range(0,len(b)):
        b[i] = b[i]*rand.normalvariate(1, 0.1)
    return b;

arvo1 = satunnaistaja(np.linspace(3,5,100))
arvo2 = satunnaistaja(np.linspace(10,20,100))

fig = plt.figure(figsize=(10,5))
plt.scatter(arvo1, arvo2, marker ='*', color = 'b', label = 'Mittaukset')

# Lasketaan huvikseen mukaan myös suoran sovite pienimmällä neliösummalla.

slope, intercept, r_value, p_value, std_err = stats.linregress(arvo1, arvo2)
plt.plot(arvo1, intercept + slope*arvo1, 'r', label='Sovitus')

plt.legend(fontsize = 15)
plt.show()

# Jos haluaisi tietää suoran matemaattisia arvoja, voisi kirjoittaa esim. print (slope) jne.
../_images/Ohjeita Pythonin käyttöön_70_0.png

Yksi merkittävä kuvaajatyyppi on histogrammi, jolla voidaan kuvata eri tulosten suhteellista esiintyvyyttä datassa. Histogrammeja näkee hyvin monissa paikoissa, niin hiukkasfysiikassa, lääketieteessä kuin yhteiskunnallisissakin tiedoissa.

# Otetaan satunnainen ikäjakauma ja luodaan sitä kuvaava histogrammi.

def ikageneraattori(a):
    b = a.copy()
    for i in range(0, len(b)):
        b[i] = b[i]*rand.randint(1,100)
    return b;

iat = ikageneraattori(np.ones(1000))

fig = plt.figure(figsize = (10,5))
plt.hist(iat, bins = 100, range = (0,110))

plt.xlabel('Ikävuodet', fontsize = 15)
plt.ylabel('Määrä', fontsize = 15)
plt.title('Ikäjakauma %i ihmisen otoksessa \n' %(len(iat)), fontsize = 15 ) 

plt.show()
../_images/Ohjeita Pythonin käyttöön_72_0.png
# Otetaan hiukkasdataa törmäyksistä, joista havaittiin tulevan myonipareja.

kaksoismyonit = pd.read_csv('http://opendata.cern.ch/record/545/files/Dimuon_DoubleMu.csv')

# Piirretään koko roskan histogrammi invariantin massan, sarake M, suhteen.

fig = plt.figure(figsize = (10,5))
plt.hist(kaksoismyonit.M, bins = 300, range = (0,150))

plt.xlabel('Invariantti massa (GeV/$c^2$)', fontsize = 15)
plt.ylabel('Tapahtumien lukumäärä', fontsize = 15)
plt.title('Invariantin massan jakauma myoneista laskettuna \n', fontsize = 15 ) 

plt.show()
../_images/Ohjeita Pythonin käyttöön_73_0.png
# Tarkastellaan ylläolevasta vain 80 ja 100 välillä olevaa aluetta. Tähän kävisi myös range-parametrin siirto,
# mutta on kevyempää rajata pienempi pala dataa käsittelyyn.

huippu = kaksoismyonit[(kaksoismyonit.M >= 80) & (kaksoismyonit.M <= 100)]


fig = plt.figure(figsize = (10,5))
plt.hist(huippu.M, bins = 200, range = (80,100))

plt.xlabel('Invariantti massa (GeV/$c^2$)', fontsize = 15)
plt.ylabel('Tapahtumien lukumäärä', fontsize = 15)
plt.title('Invariantin massan jakauma myoneista laskettuna \n', fontsize = 15 ) 

plt.show()
../_images/Ohjeita Pythonin käyttöön_74_0.png

Keskimäärin epälineaaristen käyrien sovittaminen tuloksiin vaatii hieman enemmän koodaamista, mutta jakaumien tapauksessa Pythonista löytyy aika kattavasti valmiita komentoja. Ylläolevaan kuvaajaan saa esimerkiksi kivan normaalijakauman sovitteen:

# Tässä määritellään sovituksen rajat. Nämä on hyvä määritellä tässä, jotta itse koodiin ei tarvitse porautua
# jälkeenpäin ja etsiä jokaista viittauskohtaa erikseen.

alaraja = 87
ylaraja = 95

pala = kaksoismyonit[(kaksoismyonit.M > alaraja) & (kaksoismyonit.M < ylaraja)]

fig = plt.figure(figsize=(15, 10))

# Tässä määritellään miltä väliltä alkuperäistä dataa katsellaan. Sovitteen ei tarvitse seurata sitä vaan 
# se on suunniteltu pelkän huipun kohdalle.

esite_ala = 80
esite_yla = 100

alue = kaksoismyonit[(kaksoismyonit.M > esite_ala) & (kaksoismyonit.M < esite_yla)]

# Koska näytetyn histogrammin pinta-ala asetetaan ykköseen kertymäfunktiota varten, määritellään tässä kerroin
# joka huomioi tarkastellun alueen ja koko alueen suhteen sovitusta tehtäessä.

kerroin = len(pala)/len(alue)
(mu, sigma) = norm.fit(pala.M)

# Tässä piirretään itse histogrammi valituissa rajoissa koko alkuperäisestä datasta..

n, bins, patches = plt.hist(kaksoismyonit.M, 300, density = 1, facecolor = 'green', alpha=0.75, histtype='stepfilled',
                            range=(esite_ala,esite_yla))

# Tässä piirretään normaalijakauman funktio aiemman kertoimen kera.

y = kerroin*norm.pdf( bins, mu, sigma)
l = plt.plot(bins, y, 'r--', linewidth=2)

plt.xlabel('Massa [GeV/$c^2$]',fontsize=15)
plt.ylabel('Tapahtuman todennäköisyys', fontsize=15)

# Tämä otsikko näyttää hieman karulta, mutta se huomioi nyt datasta lasketut tilastolliset arvot.

plt.title(r'$\mathrm{Histogrammi\ invarianteista\ massoista\ normeerattuna\ ykköseen:}\ \mu=%.3f,\ \sigma=%.3f$'
          %(mu,sigma),fontsize=15)

# Tähän saa myös ruudukon, huraa!

plt.grid(True)

plt.show()
../_images/Ohjeita Pythonin käyttöön_76_0.png

Kuvaajan voi piirtää myös tiedostosta, jossa ei ole numeroita. Otetaan esimerkiksi tilastoja Lontoon liikenneonnettomuuksista.

# Nelisenkymmentätuhatta tapaturmaa erinäisin kulkupelein, samalla tapahtumalla on sama viitenumero AREFNO.

liikenne = pd.read_table('../Data/2016-gla-data-extract-vehicle.csv', sep = ",")
loukkaantuneet = pd.read_table('../Data/2016-gla-data-extract-casualty.csv', sep = ",")
liikenne.head()
AREFNO Borough Boro Easting Northing Vehicle Ref. Vehicle Type Vehicle Type (Banded) Vehicle Manoeuvres Vehicle Skidding ... Junction Location Object in C/W Veh. Leaving C/W Veh. off C/W Veh. Impact VJNYPURP DECODED Driver Sex Driver Age Driver Age (Banded)
0 1160001090 CITY OF LONDON 0 531350 181580 1 08 Taxi 4 Taxi 04 Slowing Or Stopping 0 No Skidding/Overturn ... 0 Not At Jct 00 None 0 Did Not Leave 00 None 4 N/S Hit First 1 Jny Part of Work 1 Male 46 35-64
1 1160002980 CITY OF LONDON 0 532780 180410 1 01 Pedal Cycle 1 Pedal cycle 13 Overtake Move Veh O/S 0 No Skidding/Overturn ... 0 Not At Jct 00 None 0 Did Not Leave 00 None 1 Front Hit First 5 Other/Not Known 3 Not Traced 0 Unknown
2 1160002980 CITY OF LONDON 0 532780 180410 2 01 Pedal Cycle 1 Pedal cycle 14 Overtake Stat Veh O/S 0 No Skidding/Overturn ... 0 Not At Jct 00 None 0 Did Not Leave 00 None 2 Back Hit First 5 Other/Not Known 1 Male 46 35-64
3 1160006347 CITY OF LONDON 0 531400 181570 1 09 Car 3 Car 09 Turning Right 0 No Skidding/Overturn ... 8 Jct Mid 00 None 0 Did Not Leave 00 None 1 Front Hit First 5 Other/Not Known 1 Male 22 17-24
4 1160006347 CITY OF LONDON 0 531400 181570 2 09 Car 3 Car 05 Moving Off 0 No Skidding/Overturn ... 8 Jct Mid 00 None 0 Did Not Leave 00 None 3 O/S Hit First 5 Other/Not Known 1 Male 69 65+

5 rows × 21 columns

loukkaantuneet.head()
AREFNO Borough Boro Easting Northing CREFNO Casualty Class Casualty Sex Casualty Age (Banded) Casualty Age No. of Casualties Casualty Severity Ped. Location Ped. Movement Mode of Travel
0 1160001090 CITY OF LONDON 0 531350 181580 1 3 Pedestrian 1 Male 25-59 54 1 3 Slight 09 In Road - Not Crossing 7 In Rd Facing Traffic 1 Pedestrian
1 1160002980 CITY OF LONDON 0 532780 180410 1 1 Driver/Rider 1 Male 25-59 46 1 2 Serious -2 Unknown -2 N/A 2 Pedal Cycle
2 1160006347 CITY OF LONDON 0 531400 181570 3 2 Passenger 2 Female 60+ 61 1 3 Slight -2 Unknown -2 N/A 4 Car
3 1160006347 CITY OF LONDON 0 531400 181570 2 1 Driver/Rider 1 Male 60+ 69 1 3 Slight -2 Unknown -2 N/A 4 Car
4 1160006347 CITY OF LONDON 0 531400 181570 1 1 Driver/Rider 1 Male 16-24 22 1 3 Slight -2 Unknown -2 N/A 4 Car
# Erotellaan datasta ikähaarukka, jota tarkastellaan.

alaraja = 16
ylaraja = 25
ikahaarukka = liikenne.loc[(liikenne['Driver Age'] <= ylaraja) & (liikenne['Driver Age'] >= alaraja)]
# Tehdään kuvaaja tämän ikäryhmän onnettomuuksista eri välineillä.

fig = plt.figure(figsize = (10,5))
plt.hist(ikahaarukka['Vehicle Type'], bins = 50)

# Määritetään tässä miten vaaka-akselin selitteet toimivat. Erityisesti miten ne kallistuvat.

plt.xticks(rotation = 40, ha = 'right')

plt.show()
../_images/Ohjeita Pythonin käyttöön_82_0.png

Kuvaajasta nähdään, että suurin osa liikenneonnettomuuksista on väemmän yllättäen autojen törmäilyä. Pieni yllätys voi kuitenkin tulla siitä, että listassa näkyy myös ratsuhevonen. Tätä voisi lähteä tarkastelemaan syvemminkin, esimerkiksi seuraavasti:

# Erotellaan hevostörmäykset liikennedatasta.

hepat = liikenne.loc[liikenne['Vehicle Type'] == '16 Ridden Horse']
hepat.head()
AREFNO Borough Boro Easting Northing Vehicle Ref. Vehicle Type Vehicle Type (Banded) Vehicle Manoeuvres Vehicle Skidding ... Junction Location Object in C/W Veh. Leaving C/W Veh. off C/W Veh. Impact VJNYPURP DECODED Driver Sex Driver Age Driver Age (Banded)
33056 0116TW60237 RICHMOND-UPON-THAMES 24 518450 173770 1 16 Ridden Horse 8 Other 18 Going Ahead Other 0 No Skidding/Overturn ... 2 Jct Cleared 00 None 0 Did Not Leave 00 None 1 Front Hit First 1 Jny Part of Work 1 Male 25 25-34
33057 0116TW60237 RICHMOND-UPON-THAMES 24 518450 173770 2 16 Ridden Horse 8 Other 18 Going Ahead Other 0 No Skidding/Overturn ... 2 Jct Cleared 00 None 0 Did Not Leave 00 None 2 Back Hit First 1 Jny Part of Work 1 Male 28 25-34

2 rows × 21 columns

# Jaha, sama AREFNO, eli hepat ajoivat toistensa perään. Mites vakavaa vammaa tuli?

heppaVammat = loukkaantuneet.loc[loukkaantuneet['AREFNO'] == '0116TW60237']
heppaVammat.head()

# Protip: jos haluaa olla helppo suuren datan kanssa, manuaalisen numeronsyötön sijaan tuohon laitettaisiin
# viite alkuperäisen taulukon heppaosasta kaivetun solun mukaan ja koko prosessi tapahtuisi automaattisesti. 
# Kannattaa kokeilla oman akateemisen mielenkiinnon vuoksi kokeilla.
AREFNO Borough Boro Easting Northing CREFNO Casualty Class Casualty Sex Casualty Age (Banded) Casualty Age No. of Casualties Casualty Severity Ped. Location Ped. Movement Mode of Travel
22096 0116TW60237 RICHMOND-UPON-THAMES 24 518450 173770 1 1 Driver/Rider 1 Male 25-59 28 1 3 Slight -2 Unknown -2 N/A 8 Other Vehicle

Kuvaajien piirtoon liittyen vielä sananen virheistä. Oikeissa mittauksissa on aina heiluntaa sen suhteen, mikä on tarkka tulos ja miten tarkasti jotain edes voidaan mitata. Nämä tarkkuusarvot voidaan kaivaa tilastollisilla prosesseilla tuloksiin tehdyistä sovitteista tai voi olla että ne ovat tiedossa jokaisen mittapisteen kohdalla erikseen, kuten vaikkapa koulussa tehtävien kokeiden kanssa voisi käydä (mikäli virheeseen ehditään paneutua). Tehdään esimerkki tällaisesta.

def satunnaistaja(a):
    b = a.copy()
    for i in range(0,len(b)):
        b[i] = b[i]*rand.normalvariate(1, 0.1)
    return b;

# Luodaan mittapisteet normaalijakautuneesti heiluen.

arvo1 = satunnaistaja(np.linspace(3,5,100))
arvo2 = satunnaistaja(np.linspace(10,20,100))

# Annetaan kullekin mittapisteelle normaalijakautunut satunnainen virhe.

virhe1 = (1/5)*satunnaistaja(np.ones(len(arvo1)))
virhe2 = satunnaistaja(np.ones(len(arvo2)))

fig = plt.figure(figsize=(10,5))
plt.scatter(arvo1, arvo2, marker ='*', color = 'b', label = 'Mittaukset')
plt.errorbar(arvo1, arvo2, xerr = virhe1, yerr = virhe2, fmt = 'none')

# Lasketaan huvikseen mukaan myös suoran sovite pienimmällä neliösummalla.

slope, intercept, r_value, p_value, std_err = stats.linregress(arvo1, arvo2)
plt.plot(arvo1, intercept + slope*arvo1, 'r', label='Sovitus')

plt.legend(fontsize = 15)
plt.show()

# Jos haluaisi tietää suoran matemaattisia arvoja, voisi kirjoittaa esim. print (slope) jne.
../_images/Ohjeita Pythonin käyttöön_87_0.png

7. Animaatioiden tekeminen#

Pythonilla voi tehdä animaatioita monella tavalla. Emme kuitenkaan suosittele plotly-paketin käyttöä Notebookien kanssa, sillä se on niin hidasta ettei mistään tule mitään. Tehdään tässä esimerkki, joka näyttää kivasti miksi enemmän dataa antaa parempia tuloksia.

data = pd.read_csv('http://opendata.cern.ch/record/545/files/Dimuon_DoubleMu.csv')

iMass = data.M
# Määritetään funktio, joka tuottaa kuvamme. Num kertoo monennessako askeleessa mennään ja funktio piirtää joka
# askeleelle uuden histogrammin.

def updt_hist(num, iMass):
    plt.cla()
    axes = plt.gca()
    axes.set_ylim(0,8000)
    axes.set_xlim(0,200)
    plt.hist(iMass[:num*480], bins = 120)

Huom: animaatioita sisältävät solut ovat hitaita. Mitä enemmän kuvia, sitä hitaampaa niiden tekeminen on. Tämä voi viedä useita minuutteja.

# Tuodaan animaatioita tuottava paketti.

import matplotlib.animation
%%capture

# (Toimii MyBinderissa) Capture-magic on tässä estämässä tätä solua tuottamasta suoraan näkyvää tulosta, jottei outputista puske
# omituisia tyhjiä kuvia tai muuta turhaa. Se siisteyttää elämää.

fig = plt.figure()

# fargs kertoo mitä muuttujia funktio ottaa sisäänsä ja tyhjä kohta kertoo ohjelmalle, että sen on käytettävä
# kahta muuttujaa. Toiseksi otetaan senhetkinen ruutu.

anim = matplotlib.animation.FuncAnimation(fig, updt_hist, frames = 200, fargs=(iMass, ) )

# anim.to_jshtml() muuttaa animaation (javascript)html:ksi, jolloin Notebook osaa näyttää sen.

from IPython.display import HTML
HTML(anim.to_jshtml())
# Tämän ajamalla animaatio saadaan sitten näkyväksi, kun se on ohjelman muistissa.

HTML(anim.to_jshtml())

8. Kartat ja lämpökartat#

Interaktiivisia karttoja joihin voi piirtää dataa Notebookeilla? Kyllä kiitos!

Tällaisten tekeminen on helpompaa kuin voisi kuvitella. Tässä esimerkissä nähdään, miten homma toimii kun saatavilla on jotain dataa, joka sisältää leveys- ja pituuspiirikoordinaatteja.

# Folium-paketista löydämme karttapohjia.

import folium

# Tiheyttä värein edustava heatmap-systeemi on myös näppärä, joten otetaan se käyttöön.

from folium.plugins import HeatMap
# Tässä datassa on jatkuvasti päivittyvät tiedot maanjäristyksistä eri puolella planeettaa viimeisen
# kuukauden ajalta. Siellä voi olla jo tämäkin päivä.

quakeData = pd.read_csv('https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv')
quakeData.head()
# Koska datamme on DataFrame-muodossa, muutamme sen ensin listoiksi, joita HeatMap ymmärtää.

# Tehdään tarpeeksi pitkä lista, johon voimme tallentaa haluamiamme arvoja.

dat = [0]*len(quakeData)

# Listaan tulee arvoja (tuples), jotka sisältävät leveyspiirin, pituuspiirin ja järistyksen voimakkuuden.
# Magnitudia ei sinänsä tarvittaisi, mutta se on mukava olla siellä vaikkapa siltä varalta, että haluaa tarkastella
# vain tietyn rajan ylittäviä tapauksia.

for i in range(0, len(quakeData)):
    dat[i] = [quakeData['latitude'][i], quakeData['longitude'][i], quakeData['mag'][i]]
# Siltä varalta, että tiedoissa on mukana sellaisia rivejä, joiden magnitudia ei tiedetä (listattu NaN:ina),
# poistetaan ne ettei listamme ala käyttäytymään hankalasti.

dat = [x for x in dat if ~np.isnan(x[2])]
# Erilaisia karttatyylejä: https://deparkes.co.uk/2016/06/10/folium-map-tiles/
# world_copy_jump = True mahdollistaa kartan rullauksen sivulle niin, että asiat näkyvät yhä.
# Jos haluat vain yhden karttalevityksen, lisää argumentti no_wrap = True sulkuihin.
# Argumentti control_scale lisää karttaan mittakaavan.

m = folium.Map([15., -75.], tiles='openstreetmap', zoom_start=3, world_copy_jump = True, control_scale = True)

HeatMap(dat, radius = 15).add_to(m)

m

9. Ongelmia? Katso tänne#

Kooste:

Yhyy, en osaa?
Solu jäi jumiin eikä piirrä kuvaajaani tai aja koodiani?
Virheilmoitus herjaa ‘nimi is not defined’ tai ‘nimi does not exist’?
Yritän tallentaa muuttujaan asioita, mutta print(nimi) kertookin None?
Datani ei suostu lataamaan?
Lataamassani datassa on jotain omituisia ‘NaN’-arvoja?
Yhdistin palasia datasta, mutta nyt en enää pysty tekemään asioita uudella muuttujallani?
Koodini ei toimi, vaikka se on ihan varmasti oikein kirjoitettu?
Datan päivämäärät sekoittavat toiminnan, miten korjaan?
Kopioin datan uuteen muuttujaan, jonka käsittelyn jälkeen huomaan alkuperäisen muuttujan arvojen vaihtuneen?

Yhyy, en osaa?#

Nou hätä, kukaan ei aloita mestarina. Tekemällä oppii ja virheet kuuluvat asiaan.

Pythonissa on kivana puolena sen laaja käyttäjäkunta: mitä ikinä keksitkään kysyä, et ole ensimmäinen ja vastaus luultavasti löytyy ensimmäisten googlaustulosten joukosta. Tässä on ohjeita muutamiin yleisimpiin ongelmatilanteisiin.

Solu jäi jumiin eikä piirrä kuvaajaani tai aja koodiani?#

Jos solun ajamisessa kuluu joitain sekunteja pidempään ilman, että sen pitäisi tehdä mitään erityisen raskasta, on mahdollista että koodissa on virheellisesti kirjoitettu jokin silmukka johon koneen lukija jää jumiin. Pysäytä kernelin toiminta ja tarkista koodisi mahdollisten kitkakohtien varalta. Jos et löydä ongelmaa, yksinkertaista syntaksia kunnes olet varma, että palaset etenevät loogisesti haluttuun suuntaan.

Yksi yleinen ongelma on, että syntaksivirhe ajaa koneen tekemään jotain väärää. Esimerkkitapaus: piirrät histogrammia yhdestä isomman datan sarakkeesta, mutta unohdat sarakkeen nimen koodista. Tällöin kone yrittää toteuttaa käskyäsi ja hämmentyy saamastaan koko datan sisältävästä taulukosta. Pysäytä kernelin toiminta ja korjaa muuttujien nimet oikein.

Virheilmoitus herjaa ‘nimi is not defined’ tai ‘nimi does not exist’?#

Muuttujaa, johon viittaat, ei ole olemassa. Olethan muistanut varmasti ajaa tässä istunnossa sen solun, jossa kyseinen muuttuja määritellään? Muista myös kirjainkoon tarkka merkitys.

Yritän tallentaa muuttujaan asioita, mutta print(nimi) kertookin None?#

Tallennuksessasi on jokin ongelma. Muista aina tallentaa tekemäsi operaatiot muuttuujaan, eli esimerkiksi

muuttuja = ... lataus datasta
muuttuja = muuttuja*2

eikä

muuttuja = ... lataus datasta
muuttuja*2

Jos muuttujan tarkasteleminen palauttaa None, tällöin muuttujasi on tyhjä. Tarkasta, että haluamasi operaatio oikeasti on tehtävissä oleva eikä hajoa vaikkapa jonkin sisäfunktion vääriin syötteisiin.

Datani ei suostu lataamaan?#

Tavallisia csv- ja vastaavia tekstitiedostoja voi tarkastella tekstinkäsittelyohjelmilla. Tällöin näet, millä merkillä arvot on eroteltu, miltä riviltä relevantti data alkaa tai onko tiedoston sisältö ylipäätään se mitä kuvittelit.

Jakomerkit, otsikkorivit ja vastaavat voit määrittää lukufunktion parametreina, esimerkiksi

pd.read_csv('linkki tiedostoon', sep = ';')

avaisi puolipisteellä erotetun csv:n. Lisää ylempänä dataa käsittelevässä kappaleessa 3.

pd.read_table() voi auttaa selvittämään datan luonnetta jos ei jaksa avata tekstieditoria.

Joskus ongelma johtuu merkistöjä ohjaavien koodien yhteensopimattomuudesta. Tällöin parametri encoding voi auttaa. Näistä löydät enemmän täältä. Jos oletusasetus ei toimi, usein jokin ISO-variantti kelpaa.

Lataamassani datassa on jotain omituisia ‘NaN’-arvoja?#

NaN, ‘Not a Number’, kertoo että tässä kohtaa dataa ei ole ymmärrettävää arvoa. Joko arvo on omituinen (kuten negatiivisen luvun neliöjuuri) tai sitä ei vain ole.

Monet funktiot eivät välitä NaN-arvoista tai niiden parametreihin voi laittaa asetuksen, jolla ne eivät huomioi niiden olemassaoloa. Tämä helpottaa etenkin isojen datojen läpikäyntiä, kun kaikkia mahdollisia virheitä ei huomioida eikä kone jumitu törmätessään matemaattiseen mahdottomuuteen.

Yhdistin palasia datasta, mutta nyt en enää pysty tekemään asioita uudella muuttujallani?#

Yhdistitkö eri tyyppisiä muuttujia? Kokonaisluvut (integer) ovat tietokoneelle erilaisia kuin desimaaleja ymmärtävät liukuluvut (float), jotka ovat erilaisia kuin teksti (string) ja niin edelleen. Joskus numerosarjakin voi olla kirjoitettu johonkin dataan stringinä, mikä on kovasti epämukavaa huomata jälkikäteen. Pythonissa on erilaisia vertailukomentoja, kuten isstring, joilla voi tarkistaa muuttujien tyypin.

Yhdistithän palaset oikeisiin suuntiin? Jos halusit sarakkeet vierekkäin, niitä ei kannata vahingossa liittää peräkkäin yhdeksi pitkäksi sarakkeeksi. Tiedoston tarkastelu vaikkapa len(nimi) ja nimi.head() komennoilla on usein hyödyllistä näissä tilanteissa.

Koodini ei toimi, vaikka se on ihan varmasti oikein kirjoitettu?#

Tarkista nyt kuitenkin, ettei ole piste väärässä kohdassa tai väärä kirjainkoko jossakin.

Jos koodi ei ihan oikeasti vaan toimi vaikka pitäisi, syy voi löytyä kerneliin jääneistä aiemmista tiedoista muistissa. Näissä tapauksissa voi välillä vain kokeilla Kernel-valikosta Restart & Clear Outputtia ja uudelleen ajamista vaikka parikin kertaa.

Datan päivämäärät sekoittavat toiminnan, miten korjaan?#

Päivämäärät saattavat tulla ilmoitettuina monessa muodossa. Jos oletusasetukset eivät saa dataa toimimaan nätisti, pd.read_csv():n dokumentaatiosta näkyy millä sen parametreilla voi vaikuttaa päivämäärien tulkintaan. Esim. dayfirst tai date_parser saattavat ratkaista ongelman. Pythonissa on myös paketti time, josta löytyy varmasti sopivat palat tilanteeseen.

Kopioin datan uuteen muuttujaan, jonka käsittelyn jälkeen huomaan alkuperäisen muuttujan arvojen vaihtuneen?#

Python operoi osoittimilla, joissa muuttujaan tallentuu oikeastaan tiedon itsensä sijaan paikkatieto siitä, mistä päin muistia kyseinen tieto löytyy. Kopioitaessa kokonaista datamuuttujaa toiseen muuttujaan yksittäisten rivien tai sarakkeiden sijaan saattaa käydä niin, että siinä ei oikeastaan tehdä muuta kuin viitataan vanhaan tietoon. Jos uudelle muuttujalle tehdään muutoksia, tällöin osoittimet vievät muutokset myös vanhaan dataan ja puf, sekin on muuttunut. Tätä sattuu joskus ja se on hitusen rasittavaa. Olemme huomanneet hyväksi kopioida alkuperäisen:

uusiNimi = vanhaNimi.copy()

jolloin oikeasti tehdään uutta tietoa muistiin niin, ettei viittaus vahingossakaan tapahdu alkuperäiseen muuttujaan. Kiinnostavaa kyllä tätä ongelmaa ei tunnu esiintyvän muussa kuin koko datan kerralla siirtämisen yhteydessä, eli yksittäisten rivien erottelu uusiin muuttujiin tapahtuu ilman mitään viittausta alkuperäistietoihin.