Del 7: forløkker og feilsøking¶
Ønsketimen: feilsøking, dokumentasjon, for-løkker og scatterplot¶
Denne oppgaven er dedikert til ting NAT624-studentene ønsket å se på den siste dagen av kurset: feilsøking, bruk av dokumentsjon, løkker og en (nesten) ny type figur, scatterplot.
Vi bruker tid spesifikt på feilsøking og å bruke dokumentasjon fordi dette er en av de siste kursdagene vi har, og dette er to ting det er veldig nyttig å være litt trygg på når dere skal bruke programmering i Python på egenhånd seinere. I denne biten er det flere deloppgaver med feil i som dere skal finne. I andre deloppgaver i denne biten må dere google dere frem for å løse en oppgave.
Løkker beskriver mange forskjellige operasjoner. Vi skal først og fremst se på for-løkker. Det heter for-løkker fordi syntaksen er "for disse scenarioene, gjør dette". Dette er praktisk når man skal gjøre nesten det samme mange ganger, f.eks. hvis vi skal finne trendlinjen til hver 20-årsperiode til en tidsserie. Eller plotte alle disse trendlinjene. Eller beregne gjennomsnittet til perioden. Vi ser også så vidt på if-løkker: "hvis dette er tilfellet, gjør dette", f.eks., "hvis verdien er større enn 1000, skriv ut verdien".
Scatterplot bruker vi tid på fordi dette er en enkel og veldig beskrivende metode for å se etter sammenhenger mellom to eller tre variabler. Flere av NAT624-studentene sammenlignet hverandres datasett og værmeldingen sine data i innleveringene sine. Noen av sammenlignet også med datasett over andre variabler enn temperatur, lastet ned fra f.eks. seklima.met.no. Å sammenligne to tidsserier kan gi mye informasjon, men scatterplot konsenterer sammenhenger på en litt annen måte som kan være nyttig.
Del 1: Feilsøking og bruk av dokumentasjon¶
import numpy as np # for regneoperasjoner
import matplotlib.pyplot as plt # for figurer
from matplotlib import cm # for å lage fargeskalaer
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
SMALL_SIZE = 10
MEDIUM_SIZE = 12
BIGGER_SIZE = 14
plt.rc('font', size=SMALL_SIZE) # controls default text sizes
plt.rc('axes', titlesize=MEDIUM_SIZE) # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
file='TempBergenYearlyNonan.txt'
temp,tid = loadData(file)
tid.shape, np.squeeze(temp).shape
((160,), (160,))
temp=np.squeeze(temp)
Plottingen av figuren under ser litt annerledes ut enn slik vi har plottet figurer tidligere, men forskjellene er egentlig veldig små. Hovedforskjellen er at vi først definerer aksene vi plotter i og kaller dem ax
. Dette er lignende noen av figurene vi lagde i Del 5. Dette er praktisk hvis man f.eks. vil ha to y-akser som i Del 5, eller flere panel, men også som i figuren under: i stedet for å skrive fem linjer som starter plt.title, plt.xlabel, plt.(...)
kan man skrive ax.set()
og definere alt av tittel, aksenavn etc inni samme parantes. Det ser ryddigere ut. Men resultatet er det samme.
ax=plt.axes() # Lag akser å plotte i
# Plot tidsserien
ax.plot(tid, temp)
# Definer grid, tekst etc.
ax.set(
title='Temperatur 1860-2020',
xlabel='År',
ylabel='Temperatur [\u2103]',
xlim=[1860,2020], # Sett grense for x-aksen
ylim=[5.5,10.5] # Sett grense for y-aksen
) # Sett grense for y-aksen
ax.grid() # Legg til rutenett (grid) i bakgrunnen
plt.show()
Del 1, oppgave 1¶
Finn gjennomsnittstemperaturen til hele perioden
# tips: np.mean()
Finn standardavviket til hele perioden
# tips: google "numpy standard deviation"
Finn indeksen til år 1900. Sjekk at du har riktig indeks ved å skrive ut verdien på denne indeksen i tidsvektoren.
# tips1: husk funksjonen np.where().
# tips2: husk at vi har brukt denne funksjonen før. Du kan altså enten bruke
# google til å finne dokumentasjonen på hvordan denne funksjonen brukes ELLER
# du kan finne filen der du jobbet med dette tidligere og se hva du gjorde da.
Lag en figur som viser temperatur fra år 1900 til slutten av tidsperioden. Bruk dokumentasjonen til matplotlib.pyplot til å velge linjefarge, markør og markørfarger.
# tips: Nedbørsoppgaven_del3_plotting_av_tidsserier
I figuren under har jeg lagt til "minor" grid-linjer og spesifisert farge, linjestil og gjennomsiktighet. Gjør de små grid-linjene oransje og gjør hoved-grid-linjene tykkere.
ax=plt.axes()
ax.plot(tid, temp)
ax.set(
title='Temperatur 1860-2020',
xlabel='År',
ylabel='Temperatur [\u2103]',
xlim=[1860,2020],
ylim=[5.5,10.5]
)
ax.minorticks_on()
ax.grid(which='minor', color=[.5, .6, .3], alpha=.3, linestyle='-')
ax.grid(which='major', linestyle='-')
plt.show()
Hva er feil med kodecellen under? Svaret finnes i feilmeldingen under. Tips: husk at den mest nyttige informasjonen i en feilmelding sjelden finnes midt i feilmeldingen.
ax=plt.axes()
ax.plot(tid, temp)
ax.set(
title='Temperatur 1860-2020',
xlabel='År',
ylabel='Temperatur [\u2103]',
xlim=[1860,2020],
ylim=[5.5,10.5]
)
ax.minorticks_on()
ax.grid(which='minor', color=[.4, .5, 2], alpha=.3, linestyle=':')
ax.grid(which='major', linestyle='-')
plt.show()
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) ~\Anaconda3\envs\EkteData\lib\site-packages\IPython\core\formatters.py in __call__(self, obj) 339 pass 340 else: --> 341 return printer(obj) 342 # Finally look for special method names 343 method = get_real_method(obj, self.print_method) ~\Anaconda3\envs\EkteData\lib\site-packages\IPython\core\pylabtools.py in <lambda>(fig) 246 247 if 'png' in formats: --> 248 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs)) 249 if 'retina' in formats or 'png2x' in formats: 250 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs)) ~\Anaconda3\envs\EkteData\lib\site-packages\IPython\core\pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs) 130 FigureCanvasBase(fig) 131 --> 132 fig.canvas.print_figure(bytes_io, **kw) 133 data = bytes_io.getvalue() 134 if fmt == 'svg': ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs) 2191 else suppress()) 2192 with ctx: -> 2193 self.figure.draw(renderer) 2194 2195 bbox_inches = self.figure.get_tightbbox( ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 39 renderer.start_filter() 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: 43 if artist.get_agg_filter() is not None: ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\figure.py in draw(self, renderer) 1861 1862 self.patch.draw(renderer) -> 1863 mimage._draw_list_compositing_images( 1864 renderer, self, artists, self.suppressComposite) 1865 ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 129 if not_composite or not has_images: 130 for a in artists: --> 131 a.draw(renderer) 132 else: 133 # Composite any adjacent images together ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 39 renderer.start_filter() 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: 43 if artist.get_agg_filter() is not None: ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\cbook\deprecation.py in wrapper(*inner_args, **inner_kwargs) 409 else deprecation_addendum, 410 **kwargs) --> 411 return func(*inner_args, **inner_kwargs) 412 413 return wrapper ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\axes\_base.py in draw(self, renderer, inframe) 2745 renderer.stop_rasterizing() 2746 -> 2747 mimage._draw_list_compositing_images(renderer, self, artists) 2748 2749 renderer.close_group('axes') ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 129 if not_composite or not has_images: 130 for a in artists: --> 131 a.draw(renderer) 132 else: 133 # Composite any adjacent images together ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 39 renderer.start_filter() 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: 43 if artist.get_agg_filter() is not None: ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\axis.py in draw(self, renderer, *args, **kwargs) 1167 1168 for tick in ticks_to_draw: -> 1169 tick.draw(renderer) 1170 1171 # scale up the axis label box to also find the neighbors, not ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 39 renderer.start_filter() 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: 43 if artist.get_agg_filter() is not None: ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\axis.py in draw(self, renderer) 289 for artist in [self.gridline, self.tick1line, self.tick2line, 290 self.label1, self.label2]: --> 291 artist.draw(renderer) 292 renderer.close_group(self.__name__) 293 self.stale = False ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 39 renderer.start_filter() 40 ---> 41 return draw(artist, renderer, *args, **kwargs) 42 finally: 43 if artist.get_agg_filter() is not None: ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\lines.py in draw(self, renderer) 765 gc.set_url(self.get_url()) 766 --> 767 lc_rgba = mcolors.to_rgba(self._color, self._alpha) 768 gc.set_foreground(lc_rgba, isRGBA=True) 769 ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\colors.py in to_rgba(c, alpha) 187 rgba = None 188 if rgba is None: # Suppress exception chaining of cache lookup failure. --> 189 rgba = _to_rgba_no_colorcycle(c, alpha) 190 try: 191 _colors_full_map.cache[c, alpha] = rgba ~\Anaconda3\envs\EkteData\lib\site-packages\matplotlib\colors.py in _to_rgba_no_colorcycle(c, alpha) 275 c = c[:3] + (alpha,) 276 if any(elem < 0 or elem > 1 for elem in c): --> 277 raise ValueError("RGBA values should be within 0-1 range") 278 return c 279 ValueError: RGBA values should be within 0-1 range
<Figure size 432x288 with 1 Axes>
Under finner jeg indeksene som deler tidsperioden inn i fire like deler. Men det blir feil når jeg prøver å bruke resultatet til å indeksere tid (for å sjekke at det funket). Hvorfor blir det feil?
indeks=[
0,
len(tid)*1/4,
len(tid)*2/4,
len(tid)*3/4,
len(tid)
]
tid[indeks]
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-13-561914ae2bd6> in <module> 7 ] 8 ----> 9 tid[indeks] IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
int(indeks[3])
120
# Hva om jeg vil konvertere alle elementene i "indeks" til integers?
# Dette går ikke
int(indeks)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-15-41273d50e908> in <module> 1 # Hva om jeg vil konvertere alle elementene i "indeks" til integers? 2 # Dette går ikke ----> 3 int(indeks) TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
# En verdi av ganger funker
indeks[3]=int(indeks[3])
indeks
[0, 40.0, 80.0, 120, 160]
# Dette går, men det er ganske tungvindt.
# Hva om "indeks" var 20 elementer lang?
indeks[1]=int(indeks[1])
indeks[2]=int(indeks[2])
indeks[3]=int(indeks[3])
indeks[4]=int(indeks[4])
indeks
[0, 40, 80, 120, 160]
Del 2: For-løkker¶
Vi tar utgangspunkt i problemet over (å finne indeksene som deler tidsperioden vår inn i fire like deler) til å introdusere for-løkker.
Den letteste måten å endre alle elementene i vektoren indeks
til integers er ved hjelp av en for-løkke. Dette er nyttig når man skal gjøre samme operasjon flere ganger.
Det finnes en hel oppgave om for-løkker i Ekte Data-filene: Level 2: Nedbørsoppgaven Del5 forløkke
Aller først et par enkle eksempler:
for ind in range(6): # range(6) er alle heltall fra 0 til 5 (6 heltall)
print(ind)
0 1 2 3 4 5
list(range(6))
[0, 1, 2, 3, 4, 5]
Prøv med ekte data:
file='TempBergenYearlyNonan.txt'
temp,tid = loadData(file)
temp=np.squeeze(temp)
# Skriv ut de 6 første verdiene i "temp"
for i in range(6):
print(temp[i])
8.336363636363636 7.716666666666666 7.758333333333334 6.866666666666667 7.2250000000000005 7.416666666666668
# Sjekk at det stemmer:
temp[:6]
array([8.33636364, 7.71666667, 7.75833333, 6.86666667, 7.225 , 7.41666667])
# for hver indeks [0,1,2,3] print ut temperaturen pluss temperaturen på
# neste indeks
for k in range(4):
print(temp[k]+temp[k+1])
16.0530303030303 15.475 14.625 14.091666666666669
# Sjekk at det stemmer:
print(temp[0]+temp[0+1])
print(temp[1]+temp[1+1])
print(temp[2]+temp[2+1])
print(temp[3]+temp[3+1])
16.0530303030303 15.475 14.625 14.091666666666669
Del 2, oppgave 2¶
Bruk en for-løkke til å printe ut temperatur + gjennomsnittstemperatur til de 10 første temperatur-verdiene
Tilbake til problemstillingen med indeksene:
Vi lager "indeks" på nytt og konverterer alle elementene til integers ved hjelp av en for-løkke
indeks=[
0,
len(tid)*1/4,
len(tid)*2/4,
len(tid)*3/4,
len(tid)
]
indeks
[0, 40.0, 80.0, 120.0, 160]
int(indeks[2])
80
for-løkker gjør en operasjon mange ganger etter hverandre. Hvor mange ganger bestemmes i den første linjen: for i in range(5)
betyr feks at operasjonen skal gjøres fem ganger. range(5)
iterer fra 0 til og med 4, altså 5 iterasjoner. i
blir da 0
i første iterasjon, 1
i neste iterasjon osv.
for i in range(len(indeks)):
indeks[i]=int(indeks[i])
print('i=',i,', indeks[i]=',indeks[i])
indeks
i= 0 , indeks[i]= 0 i= 1 , indeks[i]= 40 i= 2 , indeks[i]= 80 i= 3 , indeks[i]= 120 i= 4 , indeks[i]= 160
[0, 40, 80, 120, 160]
Nå kan vi f.eks. sammelingne de fire 40-årsperiodene ved å beregne gjennomsnittstemperaturen i hver periode.
for i in range(4):
mean=np.mean(temp[indeks[i]:indeks[i+1]])
print('Gj.snitt temp for periode nr.', i, '=', np.round(mean,2))
Gj.snitt temp for periode nr. 0 = 7.26 Gj.snitt temp for periode nr. 1 = 7.37 Gj.snitt temp for periode nr. 2 = 7.65 Gj.snitt temp for periode nr. 3 = 8.23
Gjennomsnittstemperaturen for den siste 40-årsperioden er en hel grad varmere enn gjennomsnittstemperaturen for den første 40-årsperioden.
Vi kan også sammenligne de fire 40-årsperiodene ved å se på en figur:
plt.plot(temp[indeks[0]:indeks[1]], label='1')
plt.plot(temp[indeks[1]:indeks[2]], label='2')
plt.plot(temp[indeks[2]:indeks[3]], label='3')
plt.plot(temp[indeks[3]:indeks[4]], label='4')
plt.legend(ncol=4)
plt.ylabel('Temperatur')
plt.xlabel('Indeks')
plt.show()
Dette kan vi også gjøre med en for-løkke, så slipper vi så mange nesten like kodelinjer:
for i in range(4):
plt.plot(temp[indeks[i]:indeks[i+1]], label=str(i+1))
plt.legend(ncol=4)
plt.ylabel('Temperatur')
plt.xlabel('Indeks')
plt.show()
Fordelen med for-løkker er at man slipper å skrive så mange nesten helt like kode-linjer.
I eksemplene over har vi brukt for in in range(x)
. Da er i
indeksen til hver iterasjon. Men man kan også la i
være faktiske verdier slik som under:
for val in indeks:
print(val)
# Sjekk at det er det samme:
indeks
0 40 80 120 160
[0, 40, 80, 120, 160]
Ved å bruke dette kan man gjøre for-løkken for plotting en del penere:
diff=int(len(tid)/4) # Fordi vi vet at vi har delt tidsserien inn i fire like deler
diff
40
for ind in indeks[:-1]:
plt.plot(temp[ind:ind+diff], label=str(ind))
plt.legend(ncol=4)
plt.ylabel('Temperatur')
plt.xlabel('Indeks')
plt.show()
Del 2, oppgave 4¶
- Bruk en forløkke til å dele tidsserien inn i 5 like lange deler.
- Bruk er for-løkke til å plotte alle de 5 periodene i samme figur.
I blant trenger man både en faktisk verdi og indeksen. Da kan man bruke enumerate
istedet for range
:
for count, val in enumerate(indeks):
print('count:', count, 'val:', val)
count: 0 val: 0 count: 1 val: 40 count: 2 val: 80 count: 3 val: 120 count: 4 val: 160
Ved å bruke dette kan man gjøre begge de to siste stegene vi har gjort over (1: konvertere til integers og 2: plotte) inne i samme løkke.
indeks=[
0,
len(tid)*1/4,
len(tid)*2/4,
len(tid)*3/4,
len(tid)
]
indeks
[0, 40.0, 80.0, 120.0, 160]
for count, val in enumerate(indeks[:-1]):
ind=int(val) # konverter indeksen til integer
plt.plot(temp[ind:ind+diff], label=str(ind))
plt.legend(ncol=4)
plt.ylabel('Temperatur')
plt.xlabel('Indeks')
plt.show()
For å gjøre poenget med for-løkker enda tydeligere: Hva om vi vil beregne gjennomsnitt og standardavvik for ti-årsperiode, og så lage en figur over alle disse punktene?
# lag ny indekseringsvektor:
diff=10 # Fordi vi vet at vi vil se på tiårsperioder
# Vi bruker np.arange fordi vi vet tidssteget vi vil ha
# syntaksen er: np.arange(start, stop, step)
indeks=np.arange(0, len(tid), diff)
indeks
array([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150])
for count, val in enumerate(indeks):
# Når f.eks. val=30 blir linjen under: np.mean(temp[30:30+10]), som er
# tiårsperiode nr 4.
mean=np.mean(temp[val:val+diff])
std=np.std(temp[val:val+diff])
# Vi vil plotte punktene midt i perioden de representerer. Derfor finner vi
# verdien til "tid" ikke ved f.eks. 30 eller 40, men på indeks nr 35.
tid_mean=tid[int(val+diff/2)]
plt.plot(tid_mean, mean, 'rp')
plt.plot(tid_mean, mean+std, 'cp')
plt.plot(tid_mean, mean-std, 'cp')
# TIPS: Hvis vi satte label-navn i plottelinjene over ville legend blitt
# veldig fordi hvert eneste punkt ville fått en linje i legend. I slike
# tilfeller kan man plotte NaN-punkt og angi markørstil som matcher de
# faktiske punktene man vil ha i legend.
plt.plot(np.nan, np.nan, 'rp', label='mean')
plt.plot(np.nan, np.nan, 'cp', label='mean +\- std')
plt.legend()
plt.ylabel('Temperatur')
plt.xlabel('År')
plt.show()
Det ville vært lettere å se på denne figuren om det var linjer mellom punktene. For å få det til må vi lagre verdiene til mean
og std
i egne vektorer slik at vi kan plotte dem i sin helhet etter at vi har beregnet alle.
# Lag to vektorer som kan fylles med verdier:
# En for gjennomsnitt og en for std.
mean=np.zeros(len(indeks))
std=np.zeros(len(indeks))
tid_mean=np.zeros(len(indeks))
for count, val in enumerate(indeks):
mean[count]=np.mean(temp[val:val+diff])
std[count]=np.std(temp[val:val+diff])
tid_mean[count]=tid[int(val+diff/2)]
# plot linjene og punktene
plt.plot(tid_mean, mean, 'rp', linestyle='-', label='mean')
plt.plot(tid_mean, mean+std, 'cp', linestyle='-', label='mean +\- std')
plt.plot(tid_mean, mean-std, 'cp', linestyle='-')
plt.ylabel('Temperatur')
plt.legend()
plt.xlabel('År')
plt.show()
Del 2, oppgave 5¶
Bruk den metoden du vil til å dele tidsperioden inn i 16 like lange perioder.
Lag en figur som tilsvarer den over
Gjør det samme igjen, men del perioden inn i 5 like lange perioder. Hva slags informasjon får du fra de forskjellige figurene? Hva er tydligs i den siste figuren vs den første, og motsatt?
Andre løkker¶
Det finnes flere løkker enn for-løkker, f.eks. if-løkker
. Denne oppgaven fokusere ikke på dette, men her er et kort eksempel for å vise hvordan det fungerer:
diff=3
if diff==3:
print(diff)
3
if diff==4:
print(diff)
elif diff > 4:
print(diff)
else:
print('diff is not equal or larger than 4')
diff is not equal or larger than 4
Vi kan bruke en if-løkke dersom vi f.eks. vil plotte en linje med punkter som skal være røde om det er varmere enn gjennomsnittet, og blå om det er kaldere enn gjennom snittet:
mean=np.mean(temp)
plt.plot(tid, temp, label='Temperatur')
for count, T in enumerate(temp):
if T > mean:
plt.plot(tid[count], T, 'rp')
elif T < mean:
plt.plot(tid[count], T, 'bp')
plt.plot(np.nan, np.nan, 'rp', label="warmer than mean")
plt.plot(np.nan, np.nan, 'bp', label='colder than mean')
plt.legend()
plt.hlines(mean, 1860, 2020, 'k', alpha=.3)
plt.ylabel('Temperatur')
plt.xlabel('År')
plt.show()
Her kunne man imidlertid også brukt indeksering:
plt.plot(tid, temp, label='Temperatur')
plt.plot(tid[temp>mean], temp[temp>mean], 'rp')
plt.plot(tid[temp<mean], temp[temp<mean], 'bp')
plt.plot(np.nan, np.nan, 'rp', label="warmer than mean")
plt.plot(np.nan, np.nan, 'bp', label='colder than mean')
plt.legend()
plt.hlines(mean, 1860, 2020, 'k', alpha=.3)
plt.ylabel('Temperatur')
plt.xlabel('År')
plt.show()
Slike metoder kan være veldig gode for å vise endringer eller spesielle perioder. Figuren over viser for eksempel tydelig at det har blitt varmere mot slutten av tidsperioden.
Del 3: Scatterplot¶
Tidsserier er ikke den eneste måten å presentere data på! Vi så litt på scatterplots allerede i Del5: sol, temp, og nedbør, og her ser vi videre på hvordan det kan være hjelpsomt for å undersøke sammenhenger mellom variabler.
Vi laster inn et nytt datasett: Nedbørsdata.
file='PrecipVestlandYearlyNonan.txt'
precip, tid_p = loadData(file)
file='TempBergenYearlyNonan.txt'
temp,tid_t = loadData(file)
temp=np.squeeze(temp)
precip=np.squeeze(precip)
# undersøk start og slutt til de to tidsseriene
print(tid_p[0])
print(tid_p[-1])
print(tid_t[0])
print(tid_t[-1])
1899.0 2020.0 1861.0 2020.0
# Finn indeksen der tid_t er lik det første året i tid_p
start=np.where(tid_t==tid_p[0])
# [0][0] må med for å få tak i kun tallet og ikke alle parantesene rundt
start=start[0][0]
# Slett starten av temperatur-tidsserien, slik at både temp og nedbør starter
# samme år
temp=temp[start:]
tid_t=tid_t[start:]
tid_t[0], tid_p[0]
(1899.0, 1899.0)
Lag et scatterplot
ax=plt.axes()
ax.scatter(temp,precip)
ax.set(
title='Temperatur og nedbør 1899-2020',
ylabel='Nedbør [mm]',
xlabel='Temperatur [\u2103]', # \u2103 er koden for grader celcius.
)
plt.show()
Som i oppgaven Del5: sol, temp, og nedbør fargekoder vi prikkene.
# cmap står for "colormap". Definer hvilken fargeskala du vil bruke.
cmap=cm.get_cmap('plasma', len(temp))
# Definer at fargeskalaen skal basere seg på tidsvektoren din
colors = tid_t
ax=plt.axes()
# Plot nedbør som en funksjon av temperatur. Fargeskalaen settes til "colors"
# (definert rett over), fargekartet settes til cmap som definert over,
# og du setter min og maks grenser til fargeksalaen.
sc=ax.scatter(
temp, precip,
c=colors, cmap=cmap,
vmin=min(colors), vmax=max(colors),
)
ax.set(
title='Temperatur og nedbør 1899-2020',
ylabel='Nedbør [mm]',
xlabel='Temperatur [\u2103]', # \u2103 er koden for grader celcius.
xlim=(5.5, 10),
ylim=(60,225)
)
# Plot fargeskalaen
plt.colorbar(sc, label='År')
plt.show()
Del 3, oppgave 6¶
Hvordan har forholdet mellom temperatur og nedbør endret seg over tid?
Bytt om få skriptet over slik at tid er langs x-aksen, temperatur langs y-aksen, og nedbør er fargekodet. Bruk et annet fargekart enn i figuren over. Tips: redefiner xlim og ylim
Hvilken figur synes du viser forholdet mellom nedbør og temperatur best?
Hvis man hovedsakelig er interessert i for eksempel endring i temperatur med tid, men gjerne vil vise at dette henger sammen med nedbør kan man inkludere tidsserien i scatterplottet. Dette drar oppmerksomheten mot tidsserien, men man kan likevel se at det var tørrere i starten av tidsperioden.
I linjen sc=ax.scatter(...)
i cellen under definerer vi et par nye ting:
s=50
gir størrelsen til prikkene (s: size)zorder=2.5
gjør at prikkene kommer oppå linjen. Default er at linjer kommer oppå prikker: ulike element har bestemte plasser i er plotte-hierarki i matplotlib. Men denne rekkefølgen kan endres ved å oppgizorder
(z-order: rekkefølge i z-retning, som tilsvarer lag på figuren).
cmap=cm.get_cmap('YlGnBu',len(temp))
colors = precip
# definer størrelsen til figuren, som i oppgaven Del5_sol_temp_nedbør
fig, ax = plt.subplots(figsize=(10,4))
# plot tidsserien
plt.plot(tid_t, temp, linewidth=1, color=[.8, .8, .8])
sc=ax.scatter(
tid_t, temp,
s=50,
c=colors, cmap=cmap,
vmin=min(precip), vmax=max(precip),
zorder=2.5
)
ax.set(
title='Temperatur og nedbør 1899-2020',
ylabel='Temperatur [\u2103]',
xlabel='År', # \u2103 er koden for grader celcius.
xlim=(tid_t[0], tid_t[-1])
)
plt.colorbar(sc, label='Nedbør [mm]') # denne linjen gjør at fargeskalaen vises til høyre.
plt.show()
Det kan også være nyttig å inkludere linjer for gjennomsnitt og standardavvik. Dette gjør det er tydelig å hva som er mer enn og mindre enn gjennomsnittet.
# cmap står for "colormap". Definer hvilken fargeskala du vil bruke.
cmap=cm.get_cmap('plasma', len(temp))
# Definer at fargeskalaen skal basere seg på tidsvektoren din
colors = tid_t
ax=plt.axes()
# Plot nedbør som en funksjon av temperatur. Fargeskalaen settes til "colors"
# (definert rett over), fargekartet settes til cmap som definert over,
# og du setter min og maks grenser til fargeksalaen.
sc=ax.scatter(
temp, precip,
c=colors, cmap=cmap,
vmin=min(colors), vmax=max(colors),
zorder=2.5
)
ax.set(
title='Temperatur og nedbør 1899-2020',
ylabel='Nedbør [mm]',
xlabel='Temperatur [\u2103]', # \u2103 er koden for grader celcius.
xlim=(5.5, 10),
ylim=(60,225),
)
# Plot fargeskalaen
plt.colorbar(sc, label='År')
# Inkluder linjer for gjennomsnitt og standardavvik.
plt.vlines(np.mean(temp), 60, 225, color=[.6, .6, .6])
plt.vlines(np.mean(temp)+np.std(temp), 60, 225, color=[.6, .8, .8])
plt.vlines(np.mean(temp)-np.std(temp), 60, 225, color=[.6, .8, .8])
plt.hlines(np.mean(precip), 5.5, 10, color=[.6, .6, .6])
plt.hlines(np.mean(precip)+np.std(precip), 5.5, 10, color=[.6, .8, .8])
plt.hlines(np.mean(precip)-np.std(precip), 5.5, 10, color=[.6, .8, .8])
plt.show()
Del 3, oppgave 7¶
La temperatur definere fargeskalane og lag et nytt scatterplot. Prøv deg frem for å vise tydlig en sammenheng du vil fokusere på.
- Vil du inkludere tidslinjen?
- Hvilket fargekart vil du bruker?
- Størrelse på prikkene?
- Rekkefølge på elementene i figuren?
- Hvilke variabler langs x- og y-aksene?
- Linjer for gjennomsnitt/standardavvik?
Last ned og prøv selv: