Oppgaver med tall fra virkeligheten

Nedbørsoppgaven Del 4: Omorganisering av datasett


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 [ ]:
tid, temp, 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]

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 vinter 2021 den siste. 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[0:len(temp)-1,11] 
# Vi må bruke 0:len(temp)-1 fordi vi ikke vil ha med den aller siste 
# desember-verdien

JanFeb=temp[1:,1:3] # Jan og Feb fra og med 1862, kolonne 1 og 2. 

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 [ ]:
JanFeb.shape, Des.shape
In [ ]:
winter=np.zeros((160,3)) # 160 år, 3 måneder
winter[:,0:2]=JanFeb
winter[:,2]=Des
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. For at dimensjonene skal stemme må vi også legge til en dimensjon for "Des". Opprinnelig har denne vektoren Des.shape = (160,), men for å settes sammen med JanFeb som har JanFeb.shape = (160,2) må Des også ha to dimensjoner. Des[:,np.newaxis].shape = (160,1), altså to dimensjoner, så da fungerer np.concatenate.

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

Alternativt kunne man definert Des=temp[0:len(temp)-1,11:]. 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.

In [ ]:
Des=temp[0:len(temp)-1,11:]
winter=np.concatenate((JanFeb,Des),axis=1)
winter

4. Gjennomsnitt av hver sesong

Vi kan nå ta gjennomsnittet av hver årstid. Siden det finnes NaN-verdier i datasettet må vi bruke np.nanmean

In [ ]:
winter=np.nanmean(winter,axis=1) 
spring=np.nanmean(spring,axis=1)
summer=np.nanmean(summer,axis=1)
fall=np.nanmean(fall,axis=1) 

5. Lag en figur

Vi kan nå plotte alle sesongene i samme figur

In [ ]:
string=('Vinter','Vår','Sommer','Høst')

ax=plt.axes() # Lag akser å plotte i

ax.plot(tid[1:],winter, label=string[0]) # tid[1:] fordi vi ikke har vinter-verdier for 1861
ax.plot(tid[:-1],spring, label=string[1])
ax.plot(tid[:-1],summer, label=string[2])
ax.plot(tid[:-1],fall, label=string[3])
ax.legend(loc='lower right', ncol=len(string))

ax.set(
    title='Temperatur i Bergen (1861-2020)',
    xlabel='År',
    ylabel='Temperatur [\u2103]', # \u2103 er koden for grader celcius. 
    xlim=[1860,2020]
)# Sett grense for x-aksen

plt.grid()

6. 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å de 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()

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

7. 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 shape.temp=(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
  • Bruk np.reshape akkurat som for temperatur for å lage en lang vektor heller enn en matrise
In [ ]:
time_mo=np.reshape(time_mo,(161*12,1))
time_mo
In [ ]:
fig=plt.figure(figsize=(10, 6)) # Lag akser å plotte i
ax = fig.add_subplot(111)

# Plot tidsserien
ax.plot(time_mo,temp_mo)
ax.plot(tid[1:],winter)
ax.plot(tid[:-1],summer)

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

plt.show()

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.

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

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *