Del 5: For-løkker¶
Nedbørsoppgavene Del 1 til 6 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 programerings-elementer. Intensjonen er at hvis man først gjør deloppgavene 1 til 6 og så går gjennom det nokså omfattende eksempelet på starten av oppgaven "Nedbør i Norge" så vil 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.
For-løkker er nyttige når man skal gjøre nesten det samme flere ganger på rad. Det gjør koden ryddig og oversiktlig, og det blir enklere å holde oversikten på hvilke beregninger som egentlig gjøres. I blant kan de samme operasjonene gjøres med både for-løkker og vektoroperasjoner.
Det kan bli forvirrende å holde kontroll på indekseringen til forløkker i starten, særlig hvis det er flere "nivåer" med for-løkker ("nested" loops). Dette ser vi på i Del 6: doble forløkker.
I denne oppgaven bruker vi data av årlig gjennomsnittstemperatur i Bergen fra 1861 til 2021 for å vise et eksempel på hvordan en for-løkke kan brukes.
import numpy as np # for regneoperasjoner
import matplotlib.pyplot as plt # for figurer
import matplotlib
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")
sys.path.append(r"C:\Users\iren_\Documents\EkteData\Funksjoner")
from EkteDataFunk import loadData # for filinnlasting
file='TempBergenYearlyNonan.txt'
temp,tid = loadData(file)
temp=np.squeeze(temp)
temp.shape, tid.shape
Tenk deg at du vil gjøre en beregning (for ekesmpel ta gjennomsnittet) over hvert tiår med data. Da må du 1) dele datasettet inn i tiårs-bolker, og 2) ta gjennomsnittet over hver bolk.
For å finne gjennomsnittstemperaturen for hver av de 16 tiårsperiodene kan man bruke en for-løkke.
1. Kjør en for-løkke¶
Vi går først gjennom fire små eksempler for å forklare hvordan en for-løkke fungerer.
# Print ut en og en verdi i listen [0, 1, 2, 3]
print('Example 1')
for i in [0,1,2,3]:
print(i)
# Print ut en og en verdi i range(4). range(4) betyr fire verdier fra og med 0
print('Example 2')
for i in range(4):
print(i)
# Print ut verdiene i temperaturvektoren med indekser 0, 1, 2, 3
print('Example 3')
for i in range(4):
print(temp[i])
# Print ut verdiene i tids-vektoren med indekser 30 til og ikke med 40
print('Example 4')
for i in temp[30:40]:
print(i)
Eksempel 3 og 4 illustrerer to viktige ting. I linjen for a in b:
kan b
være både en liste med indekser som i eksempel 3 og en liste med faktiske verdier som i eksempel 4. Legg merke til at i eksempel 3 printer vi ut temp[i]
mens i eksempel 4 printer vi i
direkte.
Oppgave 1.¶
Lag tre for-løkker.
- en som printer ut de første fem temperaturene i "temp"
- samme som over, men med en annerledes for-løkke
- en som printer ut både år og temperatur mellom år 1876 og 1883
2. Del datasettet inn i tiårs-bolker¶
Tilbake til hovedoppgaven: Vi lager en vektor ind10
(index 10, 10 fordi vi seinere skal gjøre det samme med 20-års bolker) med indeksene som skiller hver 10års bolk.
Vi bruker funksjonen np.linspace(start, stop, lengde)
til å lage vektoren med indekser som gir oss start og slutt på alle 10års-bolkene våre. lengde
beskriver hvor mange 10års-bolker vi har plass til i tidsserien vår.
start=0
stop=len(tid)
# "lengde" må være en integer, altså et heltall uten desimaler.
# Vi må legge til 1 for å få inndelingen rett: siden vi vil ha med
# endepunktet trenger vi en ekstra verdi.
lengde=int(stop/10)+1
# Husk: np.linspace(start, stop, antall verdier)
ind10=np.linspace(start, stop, lengde)
# ind10 består nå av start- og slutt-indeks til hver av de 16 tiårs-bolkene
# (0 er start-indeks og 10 er slutt-indeks til den første bolken etc)
ind10
# Husk fra Del 1 om indeksering: indekser MÅ være integers
ind10=ind10.astype(int)
ind10
Vi kunne fått det samme resultatet ved å bruke f.eks. np.arange
istedet. Da må vi bruke stop + 1
istedet for stop
for å definere at vi vil ha med endepunktet.
Notat: Tidsserien vi har på 160 år kan deles perfekt inn i 10års-bolker. I andre tilfeller måtte man tatt stilling til hva man skulle gjort med rest-verdier, f.eks. hvis datasettet var 167 tidssteg langt. Skal man la de 7 siste årene gjelde som en tiårspreiode, eller skal man kutte disse årene ut fra analysen?
np.arange(start,stop+1,10)
Oppgave 2:¶
Hva skjer dersom du ikke legger til 1 i beregningen av lengde
? Prøv!
# Kjør denne cellen for å resette feilen du introduserte i oppgave 2
start=0
stop=len(tid)
lengde=int(stop/10)+1
ind10=np.linspace(start, stop, lengde)
ind10=ind10.astype(int)
3. Bruk en for-løkke til å finne gjennomsnittstemperaturen for hver av de 16 tiårsperiodene¶
For-løkken i cellen under betyr: "for
hvert element i
i range(16)=[0,1,2,...,15]
, skriv ut gjennomsnittsverdien av temperatur i perioden med indeks ind[i]
til ind[i+1]
".
For f.eks. i=3
betyr dette: skriv ut gjennomsnittet av temperatur fra ind[3]
til ind[4]
. Dette er det samme som 30
til 40
, altså temp[30:40]
, som er det samme som temperaturen fra 1891-1900. Denne utregningen gjøres for alle i
mellom 0 og 15 (16 elementer i range(16)
). Hver gang utregningen gjøres heter en iterasjon. Siden utregningen her gjøres 16 ganger er det 16 iterasjoner i denne for-løkken.
Vi må ta gjennomsnitt med nanmean fordi det er NaN-verdier i datasettet.
for i in range(16):
print(np.nanmean(temp[ind10[i]:ind10[i+1]]))
Oppgave 3.¶
Skriv ut gjennomsnittstemperaturen for de 9 første tiårsperiodene ved å bruke en for-løkke. Skriv også ut året hver disse 9 tiårsperiodene starter.
4. Lagre verdiene fra en for-løkke¶
I cellene over skriver vi ut verdiene, men i blant trenger man å lagre verdiene for seinere bruk. Da må man skrive resultatet til en ny variabel etter hver iterasjon. For en effektiv kode er det lurt å lage en variabel på forhånd som du kan skrive resultatene inn i underveis. Dette funker dersom man vet dimensjonene til resultatet på forhånd, noe man ikke alltid gjør - da må man istedet la variablen bygge på dimensjonene sine underveis, og det er ofte tidkrevende. Vi trenger imidlertid ikke ta stilling til det her, fordi vi vet hvor stor resultat-matrisen vår skal være: (16, 1) for 16 tiårsperioder.
Vi ser først på tre metoder for å beregne gjennomsnittstemperatur for hver av de 9 første tiårs-bolkene ved hjelp av en for-løkke, og lagrer resultatet i test
.
test=np.zeros((9,1))
Metode 1:
I denne metoden er i
selve de 9 første elementene i ind=0,10,20,...,80
. Indeksen til test
blir da int(i/10)=0,1,2,...,8
.
for i in ind10[:9]:
# temp[i:i+10] er temperaturen fra startåret og ti år frem i tid.
# Er f.eks. i=40 blir dette temp[40:50]=temp fra 1901 til 1910.
test[int(i/10)]=np.nanmean(temp[i:i+10])
test.shape, test
Metode 2: Cellen under er en annen for-løkke som gir nøyaktig samme resultat. Denne metoden ligner mer på eksempelet i seksjon 3 der vi ikke skrev resultatene til en variabel.
for i in range(9): # range(9) = fra 0 til og IKKE med 9, i.e., 9 elementer.
# for e.g., i=0 blir dette temp[ind[0]:ind[1]]=temp[0:10]
test[i]=np.nanmean(temp[ind10[i]:ind10[i+1]])
test
Metode 3:¶
Istedet for å iterere gjennom enten indekser eller faktiske verdier (range(9)
vs ind10[:9]
) kan man gjøre begge deler på en gang med np.enumerate()
. Vi går gjennom et enkelt eksempel først:
for count, val in enumerate(ind10):
print('count=', count, ', val=', val, )
Her er val
selve verdien til ind10
, og count
teller gjennom antall iterasjoner.
Eksempelet med gjennomsnittet for hver av de første 9 10års-periodene blir da:
for count, val in enumerate(ind10[:9]):
test[count]=np.nanmean(temp[val:val+10])
test
Oppgave 4.¶
Lag en for-løkke som lagrer gjennomsnittstemperaturen av ikke bare de 9 første, men alle de 16 tiårsperiodene til variablen meanTemp10
.
I cellen under lager vi en variabel som du etterpå kan skrive gjennomsnittsverdiene til inni for-løkken. Først er de 16 plassene bare fylt med 0.
meanTemp10=np.zeros((16, 1))
meanTemp10.shape
# Forslag 1
for i in ind10[:16]:
meanTemp10[int(i/10)]=np.nanmean(temp[i:i+10])
print('forslag1:',meanTemp10)
# Forslag 2
5. Plot dataene¶
For å plotte dataene tenger vi en tidsvektor med midtpunktet av hver tiårs-bolk.
Vi starter på tid[4]
og slutter på tid[-5]
fordi dette er midtpunktene til den første og den siste tiårsperioden.
print(tid[4]), print(tid[-5])
T10=np.arange(tid[4],tid[-5],10)
# For å dobbeltsjekke hvordan np.arange funker, sjekk dokumentasjonen. Her
# finner du at inputene er (start,stop,step).
T10
plt.plot(T10,meanTemp10)
plt.title('Temperatur 1860-2020')
plt.xlabel('År')
plt.ylabel('Temperatur [\u2103]')
plt.xlim([1860,2020])
plt.ylim([7,9])
plt.show()
For å kontrollere at dette gjennomsnittet gjenspeiler de faktiske dataene på en god måte kan det være lurt å plotte begge linjene i samme figur.
plt.plot(tid, temp, label='årlig') # legg til de originale dataene i bakgrunnen.
plt.plot(T10, meanTemp10, label='hvert tiår')
plt.title('Temperatur 1860-2020')
plt.xlabel('År')
plt.ylabel('Temperatur [\u2103]')
plt.xlim([1860,2020])
plt.ylim([5.5,10])
plt.legend()
plt.show()
Oppgave 5:¶
Se tilbake på Del 3: Plotting av tidsserier der vi plottet hvert tiende datapunkt. Kommenter på forskjellene mellom denne figuren og figuren rett over.
Oppgave 6:¶
Beregn temperatur for tjueårs-bolker. Bruk den metoden som er mest intuitiv/logisk for deg. Om du gjør det på en annen måte enn i eksemplene over er det helt ok. For å sjekke at metoden din funker kan du gjøre det likt som over og se om du får samme resultat med begge fremgangsmåter.
Oppgave 7:¶
Lag en figur der du viser de originale dataene, gjennomsnittet over tiårsbolkene, og gjennomsnittet over tjueårs-bolkene i samme figur.
Last ned og prøv selv: