Del 2: Indeksering¶
Denne oppgaven er originalt "Nedbørsoppgaven del 1". Nedbørsoppgavene Del 1 til 5 er laget som støtte-oppgaver til oppgaven "Nedbør i Norge". Oppgaven "Nedbør i Norge" er en lengre oppgave som kommer innom mange forskjellige elementer i programering. Intensjonen er at hvis man først gjør deloppgavene 1 til 5 og så går gjennom eksempelet på starten av oppgaven "Nedbør i Norge" så vil alt det programeringstekniske i oppgaven "Nedbør i Norge" være repetisjon, slik at man kan fokusere på den naturvitenskaplige tolkningen av dataene. På denne måten får man både trening i programmering og erfaring med hvordan det kan brukes til å studere og forstå naturvitenskaplige prosesser.
I forrige oppgave, Del1_Intro_til_programmering_i_python.ipynb (kaller denne Del1_Intro fra nå av) begynte vi å se på både indeksering og ekte datasett. I denne oppgaven fortsetter jobber vi videre med dette. Indekseringen kan være forvirrende i starten men er noe vi trenger hele tiden, så det er lurt å bruke litt ekstra tid på dette i starten.
Tips: I filen Huskeregler.ipynb i mappen "Kom_i_gang" finner du generelle regler for indeksering i Python.
Vi bruker datasettet over årlig temperatur i Bergen til å gå gjennom disse punktene.
Last inn pakkene som trengs¶
import numpy as np # for matematikk m.m.
import matplotlib.pyplot as plt # for figurer
import sys
# Bytt ut stien under slik at den peker på hvor på din PC du har
# lagret mappen "Funksjoner".
sys.path.append(r"W:\Work\Documents\EkteData\EkteData\Funksjoner")
from EkteDataFunk import loadData # for filinnlasting
Del 1: Last inn et datasett¶
Last inn datasettet "TempBergenYearlyNonan.txt". I oppgavene "Level2 Nedbørsoppgaven/Del2_RyddDatasett" og "Konseptoppgaver/Funksjoner" er det detaljer på hvordan filinnlastingen fungerer (se https://github.com/irendundas/EkteData), men i denne oppgaven fokuserer vi på andre aspekter.
file='TempBergenYearlyNonan.txt'
data = loadData(file, rydd='N')
# Linjen under gir dimensjonene til datasettet. Man må skrive "print()"
# rundt fordi bare den siste linjen i en celle printes ut automatisk
print(data.shape)
data # Dette printer ut alle verdiene i "data"
Outputet av data.shape
er (160, 2). Det betyr at data
er en matrise med 160 rader og 2 kolonner. data
har altså to dimensjoner.
Del 2: Lag en figur av dataene¶
Det er alltid praktisk å starte med å lage en enkel figur for å visualisere hva du faktisk jobber med. Husk tittle og tekst på aksene slik vi gjorde i oppgaven Del1_Intro.
Linjen under plotter alle dataene i kolone 2 av arrayet data
som en funksjon av kolonne 1.
data[:,0]
betyr:
"alle radene", i kolonnen med indeks0
, altså kolonne nr 1.data[:,1]
betyr:
"alle radene", i kolonnen med indeks1
, altså kolonne nr 2.
plt.plot(data[:,0], data[:,1])
plt.title('Gjennomsnittlig årsnedbør Bergen')
plt.xlabel('År')
plt.ylabel('Temperatur [\u2103]') # \u2103 er kode for "grader celcius"
# Linjen under gjør bare at en tekst-linje man ikke trenger ikke
# blir printet ut. Prøv selv å kjøre denne cellen uten denne linjen
# (kommenter den ut med "#") om du vil.
plt.show()
Del 3: Lagre dataene til variabler¶
data.shape
viste oss at datasettet over temperatur har 160 rader og 2 kolonner. Hvis vi vil ha tak i alle årene og lagre dem i en vektor som vi kaller "tid" skriver vi tid=data[:,0]
, altså alle radene :
, men kun den første kolonnen 0
.
tid=data[:,0]
tid
array([1861., 1862., 1863., 1864., 1865., 1866., 1867., 1868., 1869., 1870., 1871., 1872., 1873., 1874., 1875., 1876., 1877., 1878., 1879., 1880., 1881., 1882., 1883., 1884., 1885., 1886., 1887., 1888., 1889., 1890., 1891., 1892., 1893., 1894., 1895., 1896., 1897., 1898., 1899., 1900., 1901., 1902., 1903., 1904., 1905., 1906., 1907., 1908., 1909., 1910., 1911., 1912., 1913., 1914., 1915., 1916., 1917., 1918., 1919., 1920., 1921., 1922., 1923., 1924., 1925., 1926., 1927., 1928., 1929., 1930., 1931., 1932., 1933., 1934., 1935., 1936., 1937., 1938., 1939., 1940., 1941., 1942., 1943., 1944., 1945., 1946., 1947., 1948., 1949., 1950., 1951., 1952., 1953., 1954., 1955., 1956., 1957., 1958., 1959., 1960., 1961., 1962., 1963., 1964., 1965., 1966., 1967., 1968., 1969., 1970., 1971., 1972., 1973., 1974., 1975., 1976., 1977., 1978., 1979., 1980., 1981., 1982., 1983., 1984., 1985., 1986., 1987., 1988., 1989., 1990., 1991., 1992., 1993., 1994., 1995., 1996., 1997., 1998., 1999., 2000., 2001., 2002., 2003., 2004., 2005., 2006., 2007., 2008., 2009., 2010., 2011., 2012., 2013., 2014., 2015., 2016., 2017., 2018., 2019., 2020.])
Oppgave 1:¶
Skriv ut
- de fem første radene til
data
og begge kolonnene - de to siste verdiene til
tid
- år nummer 5 til 10 i tidsserien
Oppgave 2:¶
Lag en variabel "temp" som inneholder den andre kolonnen med temperatur.
temp=data[:,1]
Del 4: Se på en utvalgt del av datasettet¶
Vi har 160 år med data. Det er ikke alltid man vil studere alle årene på en gang. I forrige oppgave Del1 Intro til programmering så vi på de 10 første og siste årene, de 50 siste årene etc. Men hva om vi har en mistanke om at det skjedde noe veldig spennende mellom 1873 - 1947? Da kan vi plukke ut de radene som tilsvarer disse årene og kun plotte disse. For å plukke ut de rette radene må vi finne indeksen til 1873 og 1947 i vektoren "tid". Det finnes alltid mange måter å gjøre slikt på, og vi skal se på to forskjellige måter.
Metode 1: Denne er kanskje mest intuitiv, men ikke så effektiv, og heller ikke så robust i kompliserte koder
Siden tidssteget i tid
er i hele år kan man finne antall år mellom det året man er ute etter og startåret i vektoren. Dette gir antall år i mellom, som da tilsvarer indeksen til året du er ute etter.
# Tanken bak variabelnavnene er "id=index", "t=tid", "1/2=start/end"
idt1=1873-tid[0]
idt2=1947-tid[0]
idt1, idt2
(12.0, 86.0)
idt1
er 12 og idt2
er 86. Da skal tid[12]=tid[idt1]=1873
og tid[86]=tid[idt2]=1947
. Vi prøver:
tid[idt1]
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-12-d6150d345268> in <module> ----> 1 tid[idt1] IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
Dette gir en feilmelding. Problemet er at idt1=12.0
som er en float-verdi, altså et desimaltall, mens indekser kun kan være "integers, slices (:
), [...] ". En float-verdi har desimaler, mens integer-verdier er heltall. Hvis vi konverterer idt1 og idt2 fra float til integer løser vi problemet. Dette gjør vi med den innebygde kommandoen int
.
idt1=int(idt1)
idt2=int(idt2)
idt1, idt2
(12, 86)
Nå kan vi bruke indeksene tid1 og tid2:
tid[idt1], tid[idt2]
(1873.0, 1947.0)
Lag en figur som viser temperatur fra 1873 til og med 1947
Husk at indeksering [x:y]
ikke inkluderer y. Så for å dekke alle indeksene "fra tid1 til og med tid2" må man skrive [idt1:idt2+1]
plt.plot(tid[idt1:idt2+1], temp[idt1:idt2+1])
plt.title('Gjennomsnittlig årsnedbør Bergen')
plt.xlabel('År')
plt.ylabel('Temperatur [\u2103]')
plt.show()
Metode 2: Denne metoden er mer effektiv, veldig anvennelig i store datasett, og robust i kompliserte koder.
Men vi må introdusere en ny funksjon: np.where()
.
Denne funksjonen lar deg finne verdier som oppfyller et kriterie, for eksempel at tid skal være 1873 eller større:
np.where(tid>=1873)
(array([ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159], dtype=int64),)
Eller 1947 eller mindre:
np.where(tid<=1947)
(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, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86], dtype=int64),)
Alle verdier i tid som oppfyller begge disse kriteriene er de indeksene vi vil ha.
idt=np.where((tid>=1873) & (tid<=1947))
idt
(array([12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86], dtype=int64),)
Disse indeksene kan vi så gi til tid.
tid[idt]
array([1873., 1874., 1875., 1876., 1877., 1878., 1879., 1880., 1881., 1882., 1883., 1884., 1885., 1886., 1887., 1888., 1889., 1890., 1891., 1892., 1893., 1894., 1895., 1896., 1897., 1898., 1899., 1900., 1901., 1902., 1903., 1904., 1905., 1906., 1907., 1908., 1909., 1910., 1911., 1912., 1913., 1914., 1915., 1916., 1917., 1918., 1919., 1920., 1921., 1922., 1923., 1924., 1925., 1926., 1927., 1928., 1929., 1930., 1931., 1932., 1933., 1934., 1935., 1936., 1937., 1938., 1939., 1940., 1941., 1942., 1943., 1944., 1945., 1946., 1947.])
Med denne metoden slipper vi også problemet med floats/integers.
Vi lager en figur for å vise at de to metodene for å finne indekser gir samme resultat. Det vises kun en oransje linje, selv om vi har plottet to. Det er fordi outputtet av begge indekseringsmetodene er det samme, slik at de to linjene ligger perfekt oppå hveradre.
# Indeksering basert på Metode 1
plt.plot(tid[idt1:idt2+1], temp[idt1:idt2+1])
# Indeksering basert på Metode 2
plt.plot(tid[idt], temp[idt])
plt.title('Gjennomsnittlig årsnedbør Bergen')
plt.xlabel('År')
plt.ylabel('Temperatur [\u2103]')
plt.show()
Man kan alså definere et intervall med indekser på to forskjellige måter:
x[2:8]
x[[2, 3, 4, 5, 6, 7]]
tid[2:8], tid[[2, 3, 4, 5, 6, 7]]
(array([1863., 1864., 1865., 1866., 1867., 1868.]), array([1863., 1864., 1865., 1866., 1867., 1868.]))
Oppgave 3¶
Skriv ut årene og temperatur for alle årene fra året du ble født frem til 2016. Bruk både metode 1 og 2.
# Metode 1
# Metode 2:
Oppgave 4¶
Skriv ut årene og temperatur for alle årene fra året du ble født frem til og med slutten av datasettet. Bruk den metoden du vil, men pass på at det siste året (2020) blir med.
Oppgave 5¶
Skriv ut temperatur på hele 1900-tallet. Skriv også ut årene.
Del 5: Velg ut datapunkt med et jevnt intervall¶
Iblant trenger man å hente ut data med jevne intervall, for eksempel hvis man har et datasett med daglig oppløsning og bare vil ha tak i onsdagene, eller man bare vil ha tak i bursdagen sin fra en tidsserie med daglig oppløsning som spenner over flere år. Eller kanskje man vil hente ut data fra kl 12 for å sjekke solstyrke midt på dagen.
Med indeksering kan man bruke to eller tre verdier. Bruker man to angir man kun start og stop: [start:stop]
. Bruker man tre angir man også intervallet av verdier man er interessert i: [start:stop:step]
. Skal man f.eks. ha annenhver verdi mellom indeks a
og b
skriver man [a:b:2]
. Husk at det fremdeles er a
til og ikke med b
. Skal man ha med hele tidsserien skriver man [::step]
. De to kolonene på rad viser at både start og slutt skal med. Hvis a=5
og b=20
blir dette:
tid[5:20:2]
array([1866., 1868., 1870., 1872., 1874., 1876., 1878., 1880.])
Oppgave 6¶
Finn temperaturen for hvert tiende år fra starten til slutten av tidsserien. Skriv ut årene i tillegg (for å sjekke at indekseringen du har satt stemmer).
Oppgave 7¶
Finn temperaturen for hvert tiende år siden 1870 frem til og med 2016. Skriv igjen ut årene i tillegg (for å sjekke at indekseringen du har satt stemmer).
Her kan du igjen benytte Metode 1 eller 2 for å finne årene mellom 1870 og 2016. Du kan enten finne indeksen til 1870 og indeksen il 2016 separat og så printe ut hvert tiende år/hver tiende temperatur mellom disse indeksene, ELLER, du kan finne indeksen til alle år mellom 1870 og 2016 og plugge hver tidende av disse indeksene inn i "tid".
Du kan komme til å støte på dette problemet: du får en output av idt som er idt=(array([9, 10, 11, ...]))
. For å velge bestemte indekser av dette outputtet må den ytterste parantesen vekk. For å få til dette skriver man idt=idt[0]
. Da går man "et nivå ned" i itd
og unngår den ytterste parantesen. Du kan nå indeksere idt.
Et notat til cellen over: man trenger ikke alltid lage variabler for alt. Man trenger for eksempel ikke "omdøpe" idt[0]
til idt
, man kan bruke idt[0]
direkte.
Har du for eksempel en vektor med indekser via Metode 2, men vil bare ha annenhver verdi kan du skrive
tid[idt[0][0::2]]
.Dette er det samme som
tid[idt[0][0:-1:2]]
.idt[0]
gjør at du unngår den ytterste parantesen du får når du bruker np.where.idt[0][0::2]
gjør at at du unngår den første parantesen, og velger ut annenhver indeks fra start til slutt.tid[idt[0][0::2]]
gjør at du ikke bare får indeksene men de faktiske tid-verdiene.
Ved å skrive alt dette i en linje slipper man mange mellomvariabler, som kan gjøre koden ryddig og oversiktlig. Det finnes selvfølgelig en grense - blir koden FOR kompakt og linjene FOR lange blir det fort uoversiktlig. Men dette er litt smak og behag, og så lenge man kommenterer godt i koden sin og forklarer hva som skjer blir det meste bra.
Oppgave 8¶
Lag en figur som viser hele temperaturtidsserien, men plot også hvert tiende år på samme figur. Synes du tidsserien blir godt representert ved å bare se på hvert tiende år? I en seinere oppgave, f.eks. en om for-løkker (NAT624/Del8 eller Nedbørsoppgaven/Del5), ser vi på hvordan man kan representere datasettet ved å ta gjennomsnittet over tiårsperioder. Hvordan tror du denne tidsserien representerer datasettet i forhold til den figuren vi har her?
Del 6: Del et datasett inn i et gitt antall like lange perioder¶
I stedet for å se på f.eks. hvert tiende, tyvende, 55te, etc. år kan det være man vil dele tidsserien inn i f.eks. fire like lange tidsperioder. Da må man ta utgangspunkt i lengden til tidsseerien, og så finne indeksene som deler tidsserien inn i antallet bolker man er ute etter. Siden det ikke er sikkert at lengden til tidsserien er perfekt delelig på antall tidsperioder du vil dele den inn i må man passe på at inndelingen man finner kan brukes som indekser, altså være heltall.
Vi tar utganspunkt i at vi vil dele tidsserien vår i tre like bolker.
Et notat til cellen under: Noen ganger er det praktisk å dele linjer opp. Det er standard å ta linjeskift rett etter første parantes, da hopper man automatisk fire mellomrom inn på neste linje. Så fordeler man innholdet i parantesen på linjer slik man synes er logisk. Rett før den avsluttende parantesen lager man et nytt linjeskift. Denne parantesen hopper automatisk tilbake til margen. På den måten er det lett å se hvor bolken avsluttes.
ind=[
0, # startindeks
len(tid)/3, # indeksen som markerer den første tredelen
2*len(tid)/3, # indeksen som markerer den andre tredelen
len(tid) # sluttindeksen
]
ind
[0, 53.333333333333336, 106.66666666666667, 160]
Disse veridene kan ikke brukes som indekser. De må både rundes av til heltall og konverteres fra float til integer.
ind=[
0,
int(np.round(len(tid)/3)),
2*int(np.round(len(tid)/3)),
len(tid)
]
ind
[0, 53, 106, 160]
Disse KAN brukes som indekser. Vil man f.eks plotte kun siste tredel av tidperioden kan man nå skrive:
plt.plot(tid[ind[2]:ind[-1]], temp[ind[2]:ind[-1]])
plt.title('Gjennomsnittlig årsnedbør Bergen')
plt.xlabel('År')
plt.ylabel('Temperatur [\u2103]')
plt.show()
Man kunne også skrevet det mer kompakt, slik som under, men i blant har man behov for å lagre indeksen til alle inndelingene, for eksempel hvis man vil plotte alle tidsperiodene i samme figur. Dette gir mening hvis man f.eks. vil plotte standardavviket til hver periode, trendlinjer til hver periode, eller andre variabler basert på tidsserien man har.
I tillegg blir det fort rotete og vanskelig å lese koden når man gjør alt på en gang i få linjer.
plt.plot(
tid[2*int(np.round(len(tid)/3)):],
temp[2*int(np.round(len(tid)/3)):]
)
plt.title('Gjennomsnittlig årsnedbør Bergen')
plt.xlabel('År')
plt.ylabel('Temperatur [\u2103]')
plt.show()
Oppgave 9¶
- Del tidsserien inn i fire like lange bolker.
- Plot de to midterste bolkene i samme figur.
- For å vise tydeligere at dette er de to midterste bolkene: plot hele tidsserien i bakgrunnen.
Last ned og prøv selv: