Nedbørsoppgaven 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.

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.

In [ ]:
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
In [ ]:
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.

In [ ]:
# Print ut en og en verdi i listen [0, 1, 2, 3]
print('Example 1')
for i in [0,1,2,3]:
    print(i)    
In [ ]:
# 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)    
In [ ]:
# Print ut verdiene i temperaturvektoren med indekser 0, 1, 2, 3 
print('Example 3')
for i in range(4):
    print(temp[i])
In [ ]:
# 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"
In [ ]:
 
  • samme som over, men med en annerledes for-løkke
In [ ]:
 
  • en som printer ut både år og temperatur mellom år 1876 og 1883
In [ ]:
 

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.

In [ ]:
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 
In [ ]:
# Husk fra Del 1 om indeksering: indekser MÅ være integers
ind10=ind10.astype(int) 
In [ ]:
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?

In [ ]:
np.arange(start,stop+1,10)

Oppgave 2:

Hva skjer dersom du ikke legger til 1 i beregningen av lengde? Prøv!

In [ ]:
 
In [ ]:
# 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.

In [ ]:
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.

In [ ]:
 

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.

In [ ]:
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.

In [ ]:
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]) 
In [ ]:
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.

In [ ]:
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]]) 
In [ ]:
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:

In [ ]:
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:

In [ ]:
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.

In [ ]:
meanTemp10=np.zeros((16, 1)) 
meanTemp10.shape
In [ ]:
# Forslag 1
for i in ind10[:16]: 
    meanTemp10[int(i/10)]=np.nanmean(temp[i:i+10]) 
   
print('forslag1:',meanTemp10)
In [ ]:
# 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.

In [ ]:
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
In [ ]:
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.

In [ ]:
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å Nedbørsoppgaven Del 3 der vi plottet hvert tiende datapunkt. Kommenter på forskjellene mellom denne figuren og figuren rett over.

In [ ]:
 

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.

In [ ]:
 

Oppgave 7:

Lag en figur der du viser de originale dataene, gjennomsnittet over tiårsbolkene, og gjennomsnittet over tjueårs-bolkene i samme figur.

In [ ]:
 
In [ ]:
 

Last ned og prøv selv:

Åpne med Google Colab
Åpne i github