Nedbørsoppgaven Del 4: Manipulering av datasett

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 programerings-elementer. Intensjonen er at hvis man først gjør deloppgavene 1 til 5 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.


I del 1 og 3 har vi gått gjennom

  • indeksering og hvordan man kan dele inn en tidsserie ved helg av indeksering,
  • hvordan man plotter tidsserier ved hjelp av pakken matplotlib.

Nå skal vi bruke dette til å se på hvordan man kan velge ut spesifikke indekser fra et datasett og sette dette sammen til et nytt datasett. Ved hjelp av Del 3 kan vi fremstille resultatet på en oversiktlig måte.

Last inn pakkene som tengs

In [ ]:
import numpy as np # for regneoperasjoner
import matplotlib.pyplot as plt # for figurer
from EkteDataFunk import loadData # for filinnlasting

1. Last inn et datasett

For denne delen bruker vi et større datasett enn i Del 1 til 3 slik at vi har litt mer å ta utgangspunkt i. Dette datasettet har like mange rader, men 13 kolonner: en for tid, og tolv for hver av månedene.

In [ ]:
file='TempBergen.txt'
temp,tid = loadData(file)
In [ ]:
print(tid)
print(temp)
print('temp shape=',temp.shape)

"temp" er for stor til å vise hele - matrisen har 161 rader og 12 kolonner. Derfor er det "..." for å vise at ikke hele datasettet vises.

2. Del datasettet inn i sesonger

Siden vi nå har verdier for alle månedene, og ikke bare års-gjennomsnitt, kan vi sammenligne månedene mot hverandre, men vi kan også se på sesongene i forhold til hverandre. Det er lettere å sammenligne fire tidsserier enn 12 tidsserier, og det er ikke sikkert man alltid trenger alle detaljene fra hver måned selv om årsoppløsningen er for lav. Det er dette vi skal gjøre i denne delen: sette månedene sammen til sesonger, og se på endringen i sesongene over tid.

In [ ]:
# Spring: alle rader utenom den siste (kun NaN) og kolonne 2 til og ikke med 5,
# altså mars, april og mai
spring=temp[:-1,2:5] 

# Summer: alle rader utenom den siste (kun NaN) og kolonnene for juni, juli 
# og august
summer=temp[:-1,5:8]

# Fall: alle rader utenom den siste (kun NaN) og kolonnenen for september, 
# oktober og november
fall=temp[:-1,8:11]
In [ ]:
spring.shape, summer.shape, fall.shape

3. Spesialbehandling for vinter

Vinter er litt tricky: januar og februar, og desember samme år hører ikke til samme vinter. Vi deler derfor vinter inn i Des (desember) og JanFeb (januar og februar), og setter dem sammen igjen til en matrise etterpå. Vinter 1862 blir den første vinter-verdien vi har, og består av demeber 1861 og januar og februar 1862. Vinter 2021 er den siste vinteren (demeber 2020 og januar og februar 2021). Vi har altså like mange vinter-verdiene som vår, sommer og høst-verdier, men vinter-verdiene er forskjøvet med ett år. Ta en titt på hvordan datasettet ser ut her, så blir det tydeligere hvorfor vinterverdiene er forskjøvet: https://github.com/irendundas/EkteData/blob/main/TempBergen.txt

In [ ]:
Des=temp[:-1,11] 
# Vi må bruke :-1 fordi vi vil ha 1861 til og med 2020: vi vil ikke ha med den 
# aller siste desember-verdien

JanFeb=temp[1:,0:2] # Jan og Feb fra og med 1862, kolonne 1 og 2. 
In [ ]:
Des.shape, JanFeb.shape

Nå er "JanFeb" alle januar og februar-månedene fra 1862 til 2021, mens "Des" er desember fra 1861 til 2020. Vi lar Januar og Februar "bestemme" hvilket år vinteren hører til, så vi har altså vinter-data fra 1862-2021.

Vi vil sette disse to arrayene sammen til ett array slik at det får likt oppsett som de andre sesongene. Det finnes mange måter å gjøre dette på, og vi går gjennom tre metoder her

Metode 1: Lag et tomt array med rette dimensjoner og plugg arrayene du vil sette sammen inn i det tomme arrayet. Dette forutsetter at du vet de endelige dimensjonene på forhånd.

In [ ]:
winter=np.zeros((160,3)) # 160 år, 3 måneder
winter[:,0]=Des
winter[:,1:]=JanFeb
winter

Metode 2: Bruk en ny funksjon: np.concatenate. Denne funksjonen tar et sett med matriser eller vektorer og setter dem sammen langs den aksen du spesifiserer. Vektorene/matrisene må være like lange langs en av aksene, i dette tilfellet tidsaksen. Matrisen kan bygges på i den retningen som har ulik dimensjon.

Se for deg at du skal stable tallerkner. Alle tallerknene må ha lik diameter. Dette er den dimensjonen som må være lik for å stable. Men du kan ha en tallerken og stable oppå tre nye samtidig. Dette er den dimensjonen som ikke trenger å være lik. I vårt tilfelle er tidsaksen lik, mens antall måneder kan være ulik. axis=1 betyr altså at vi stabler måneder.

In [ ]:
winter=np.concatenate((Des,JanFeb),axis=1)

Her får vi en feilmelding. Se på ValueError: axis 1 is out of bounds for array of dimension 1. Probelmet er altså noe med dimensjonene. Se tilbake på Des.shape og JanFeb.shape som var (160,) og (160, 2). Vi prøver altså å bygge på langs axis=1, men Des har kun en dimensjon.

For at dimensjonene skal stemme må vi altså legge til en dimensjon for "Des". Dette kan vi gjøre med funksjonen np.newaxis. Des[:,np.newaxis].shape = (160,1), altså to dimensjoner, så da fungerer np.concatenate.

Vi prøver på nytt:

In [ ]:
winter=np.concatenate((Des[:,np.newaxis],JanFeb),axis=1)
winter

Oppgave 1:

Prøv å lage winter-vektoren ved å sette sammen Des og JanFeb i motsatt rekkefølge, altså winter=np.concatenate((JanFeb, Des),axis=1). Da får du en ny litt annerledes feilmelding. Prøv å finne ut av hvilken informasjon meldingen gir det, og finn en løsning til probelemet.

In [ ]:
 

Oppgave 2:

Alternativt kunne man definert Des=temp[0:len(temp)-1,11:] eller Des=temp[:-1,11:] dersom man liker kort notasjon best (disse to alternativene git deg akkurat det samme). Da får Des automatisk dimensjonene (160,1). "11:" gjør at Des kommer på tabell-format (array) slik som JanFeb og ikke en liste. Lag winter-vektoren på denne måten.

In [ ]:
 

Oppgave 3: Gjennomsnitt av hver sesong

Finn gjennomsnittet av hver årstid. Siden det finnes NaN-verdier i datasettet må vi bruke np.nanmean

In [ ]:
winter=
spring=
summer=
fall=

Oppgave 4: Lag en figur

Vi kan nå plotte alle sesongene i samme figur, se tilbake til Nedbørsoppgaven Del 3 for tips. Husk legend og tekst på aksene.

For denne figuren må du også passe på at du plotter de ulike årstidene for riktige år. Hvilket år begynner vinter på? Hvilken år begynner vår, sommer og høst på?

In [ ]:
 

4. Månedlig oppløsning med np.reshape

En annen nyttig ting kan være å plotte hele tidsserien med månedlig oppløsning. Men da må radene med måneder settes etter hverandre til en lang tidsserie.

En enkel måte å gjøre dette på er med np.reshape. Denne funksjonen bevarer alle verdiene men endrer dimensjonene. "temp" har nå dimensjoner (161, 12). Med np.reshape kan man gi "temp" dimensjonene (161x12,1) istedet.

In [ ]:
temp_mo=np.reshape(temp,(161*12,1))

fig=plt.figure(figsize=(10, 6)) # Lag akser å plotte i
ax = fig.add_subplot(111)

# Plot tidsserien
ax.plot(temp_mo)

ax.set(
    title='Temperatur 1860-2020',
    xlabel='År',
    ylabel='Temperatur [\u2103]', # \u2103 er koden for grader celcius. 
    xlim=(0,len(temp_mo)) # sett grense for x-aksen
) 

plt.show()

Oppgave 5:

Dette ble veldig rotete. Gi en forklaring på hvorfor, utover at det er mange flere datapunkt.

5. Månedlig oppløsning og årlig oppløsning i samme figur

Det skjer mye på midten av plottet, mens toppen og bunnen virker mer organisert. Vi vil plotte tidsserien sammen med sommer- og vinter-gjennomsnittene for å se om det finnes en sammenheng.

Lag en tidsvektor manuelt. Vi har imidlertid ingen tidvektor for den månedlige tidsserien. De må vi lage selv. Det finnes funksjoner for slikt i Python, men siden vi opererer med måneder og ikke trenger å ta stilling til skuddår og slikt kan det gjøres manuelt. Vi tar utgangspunkt i tidsvektoren over år. Hver måned skal være 1/12 år lengre enn forrige måned.

Vi bruker denne fremgangsmåten:

  • Lager igjen en matrise med kun nuller, som da vi satte sammen vinter-månedene i Metode 1, men denne ganger med temp.shape=(161,12)
In [ ]:
time_mo=np.zeros(temp.shape) # 161 år, 12 måneder
  • Januar er året uten noen desimaler, altså året + 1/12*0. Sett derfor kolonne 0 = tid
In [ ]:
time_mo[:,0]=tid
  • Februar skal være året + 1/12*1, mars er året + 1/12*2 etc
In [ ]:
# Dette kunne man gjort mye mer elegant med hjelp av en for-løkke, det kommer 
# vi til i neste del, Del 5. 

time_mo[:,1]=tid+1/12*1
time_mo[:,2]=tid+1/12*2
time_mo[:,3]=tid+1/12*3
time_mo[:,4]=tid+1/12*4
time_mo[:,5]=tid+1/12*5
time_mo[:,6]=tid+1/12*6
time_mo[:,7]=tid+1/12*7
time_mo[:,8]=tid+1/12*8
time_mo[:,9]=tid+1/12*9
time_mo[:,10]=tid+1/12*10
time_mo[:,11]=tid+1/12*11
In [ ]:
time_mo

Oppgave 6:

Bruk np.reshape akkurat som for temperatur for å lage en lang vektor heller enn en matrise

In [ ]:
time_mo=

Oppgave 7:

Lag en figur som viser både den månedlige tidsserien og vinter og sommer sammen.

In [ ]:
 

Diskuter igjen forklaring på hvorfor det skjer så mye på midten av plottet, og kommenter på sommer- og vinter-gjennomsnittene i forhold til månedsgjennomsnittene.

Oppgave 8:

Lag en tilsvarende figur som den over, men plot januar og juli istedet for sommer og vinter. Inkluder en legend i figuren.

Diskuter forskjellene.

In [ ]:
 
In [ ]:
 

Last ned og prøv selv:

Åpne med Google Colab
Åpne i github