Lab7
Kursnotater for tema som er nye i denne laben:
Denne laben er litt annerledes enn tidligere laber siden noen av deloppgavene blir rettet manuelt. I tillegg er poengene fordelt slik at du får litt poeng for hver oppgave du gjennomfører. Det er maksimalt mulig å oppnå 25 poeng.
Oppgavene er gruppert etter hvor mange poeng de gir hver. Merk at oppgaven som gir 10 poeng rettes manuelt, og vil rettes som bestått eller ikke bestått, hvor bestått gir 10 poeng og ikke bestått gir 0 poeng. Programmet ditt må altså vise et riktig bilde (som matcher bildet som vises til slutt i oppgaveteksten) når det kjøres for å få bestått.
For oppgavene som blir rettet manuelt, vil du få poengscore og skriftlig tilbakemelding etter at vi er ferdige med å rette; men det er alltid mulig å oppsøke tilbakemelding fra gruppeleder underveis også.
- Vi vil trekke poeng manuelt dersom vi oppdager at oppgaven åpenbart ikke er løst, eller man forsøker å trikse seg gjennom de automatiske testene.
- Hver lab kan leveres så mange ganger du vil; det er siste innlevering som teller. Du kan umiddelbart se resultatene på de automatiske testene og hvor mange poeng du har fått etter hver innlevering.
Hver lab utgjør 2.5% av den endelige karakteren din i emnet. Du må få til sammen 100 poeng eller mer på labene for å kunne ta eksamen.
Det er veldig viktig at du siterer alle kilder og eventuelle samarbeid. Se mitt.uib for mer informasjon om vår policy på plagiat og samarbeid.
Det er lurt å levere inn mange ganger underveis. Du får umiddelbart en automatisk tilbakemelding på oppgavene, og du kan prøve igjen hvis noe ikke virker.
Innlevering av lab’er foregår via verktøyet Codegrade som du kommer til via mitt.uib.
- Gå til «Oppgåver» i menyen til venstre
- Klikk deg videre til CodeGrade ved å klikke på «Last lab i eit nytt vindauge»
- Last opp filene du har laget for lab’en og trykk på «Submit.»
Fremdeles problemer? Se (merk at videoen er litt gammel, så det kan være noen små forskjeller i hvordan det ser ut nå.)
Ser du ikke knappen du skal trykke på i mitt.uib?
- Dobbeltsjekk at du er innlogget på mitt.uib.
- Prøv å laste siden i nettleseren på nytt.
- Prøv en «force refresh» (F5 eller Ctrl+F5 eller Ctrl+Shift+F5 eller Ctrl+Shift+R eller Command+R eller Command+Shift+R avhengig av nettleser og operativsystem).
- Prøv å tøm cache i nettleseren din (se refreshyourcache.com for hvordan du gjør dette) og prøv på nytt.
- Prøv en annen nettleser.
Skotår
Tips: før du løyser denne oppgåva bør du lese kursnotata om moduler, særleg avsnittet om hovudfiler og modular.
I denne oppgåva skal du lage ein modul i fila leapyear.py. Den skal ha ein funksjon som fortel om eit år er eit skotår eller ikkje. Modulen du skriv skal både kunne køyrast som eit eige program der brukaren blir bede om å oppgi kva år hen vil vite meir om, i tillegg til at ein skal kunne importere funksjonen is_leap_year til bruk i andre modular.
Hugsar du oppgåva om skotår frå lab2? Her er ein repetisjon av reglane:
- Vanlegvis er eit år som er deleleg med 4 eit skotår (til dømes 1996 var eit skotår);
- bortsett frå år som også er delelege med 100 (til dømes 1900 er ikkje skotår);
- men viss året som er delelege med 100 også er deleleg med 400, då er det eit skotår likevel (til dømes er 2000 eit skotår).
Del A
- I fila leapyear.py, lag ein funksjon is_leap_year som tek inn eit årstal
year
og returnerarTrue
viss året er eit skotår, ogFalse
elles. Ta utgangspunkt i følgjande kode du kan kopiere inn i fila di:
def is_leap_year(year):
# TODO: skriv koden din for del A her
# (har du allereie gjorde oppgåva i lab 2, kan du kopiere derfrå)
...
def main():
# TODO: skriv koden din for del B her
...
if __name__ == "__main__":
# Vi kallar main berre viss *denne* fila blir køyrd som hovudprogrammet
main()
Du kan allereie no sjekke at det fungerer å bruke programmet ditt som ein modul. Last ned scriptet leapyear_test_A.py, legg det i same mappe som leapyear.py, og køyr det.
Del B
I denne delen av oppgåva skal vi gjere det mogleg å køyre leapyear.py som eit program som ein sluttbrukar kan nytte. Programmet skal:
- be brukaren om å skrive inn eit årstal,
- lese inn kva brukaren skriv (inkludert å konvertere til heiltal),
- kalle på funksjonen frå del A for å avgjere om årstalet er eit skotår eller ikkje, og til slutt
- skrive ut om året er eit skotår eller ikkje til skjermen. Sjå døme under for nøyaktig ordlyd.
Døme på interaksjonar med programmet:
Skriv inn eit årstal:
1996
1996 er eit skotår
Skriv inn eit årstal:
1800
1800 er ikkje eit skotår
Pass på at leapyear_test_A.py framleis fungerer!
Relaterte refleksjonsspørsmål vi forventar at du kan svare på under ein eventuell eksamen (diskuter gjerne med ein venn/ein gruppeleiar/på discord om du er usikker):
- Kva er formålet med
if __name__ == "__main__":
?- Kva er forskjellen viss vi fjernar
if __name__ == "__main__":
og rett og slett berre kallar påmain()
uansett?
Hundefakta
I denne oppgaven skal vi bli kjent med modulen json fra standardbiblioteket og det eksterne biblioteket requests. Men aller først, gå til denne nettsiden:
Prøv å trykk refresh et par ganger, og observer at du får en ny streng hver gang. I denne oppgaven skal vi skrive et program slik at programmet ditt printer tre tilfeldige fakta om hunder hver gang du kjører programmet. Faktaene skal du laste ned på nytt fra adressen over hver gang du kjører programmet.
Del A: JSON
I filen dog_facts.py, skriv en funksjon get_dog_facts med en parameter json_string. Funksjonen vil få som argument en JSON-formatert streng på formatet du finner på nettsiden over og skal returnere en liste med de delene av strengen som inneholder selve faktaene. Du kan anta at det alltid er tre fakta (men kode som fungerer uansett antall fakta er selvfølgelig enda bedre enn kode som er hardkodet for akkurat tre).
Test deg selv:
def test_get_dog_facts()
arg = '''\
{
"data": [
{
"id": "574c4504-250f-4743-9ff2-124e5c61e79e",
"type": "fact",
"attributes": {
"body": "Dogs live an average of 15 years."
}
},
{
"id": "cd131ea1-3327-4c2c-93e6-488582a3e2e7",
"type": "fact",
"attributes": {
"body": "Dogs have a wet nose to help them absorb scents."
}
},
{
"id": "58d82e49-0b2f-4911-bdde-0d686e092b72",
"type": "fact",
"attributes": {
"body": "Dogs can see in color, but not as vividly as humans."
}
}
]
}
'''
expected = [
'Dogs live an average of 15 years.',
'Dogs have a wet nose to help them absorb scents.',
'Dogs can see in color, but not as vividly as humans.',
]
actual = get_dog_facts(arg)
assert expected == actual
Legg merke til at JSON-formatet minner svært mye om Python sin syntaks for å opprette nye oppslagsverk og lister. Faktisk er de så tett beslektet at det i mange tilfeller er helt identisk. For å konvertere fra en JSON-streng til de tilsvarende objektene (oppslagsverk/lister) i Python, skal du bruke loads -funksjonen fra JSON-modulen. Se eksempel i notatene om json.
Etter at du har konvertert strengen til et oppslagsverk, gjenstår det å indeksere seg frem til riktig verdi og legge disse til i en liste du så returnerer.
Første hundefaktum finner du ved å indeksere oppslagsverket med
['data'][0]['attributes']['body']
Del B: requests
I samme fil, skriv en funksjon download_dog_facts uten parametre som laster tre fakta om hunder fra nettsiden oppgitt i begnnelsen av oppgaven og returnerer den json-formaterte strengen du får. Benytt den eksterne modulen requests for å laste ned nettsiden. Benytt utf-8 når du dekoder responsen til en streng.
Requests er en ekstern modul og må installeres før bruk. Men sannsynligvis har du den allerede installert, siden det er en av modulene som blir installert sammen med uib-inf100-graphics.
Siden denne funksjonen vil returnere en ny «tilfeldig» streng hver gang, har vi ikke tester for dette utover manuell testing. Du kan legge dette til nederst i filen.
if __name__ == '__main__':
json_string = download_dog_facts()
facts = get_dog_facts(json_string)
print('\n'.join(facts))
Når du kjører programmet skal det skrives ut tre fakta om hunder. Nøyaktig hvilke fakta vil være forskjellig fra gang til gang. For eksempel:
Dogs live an average of 15 years.
Dogs have a wet nose to help them absorb scents.
Dogs can see in color, but not as vividly as humans.
Polynom
Forberedelser: Gjør tutorial for pyplot og les gjennom notatene om det eksterne biblioteket matplotlib
I denne oppgaven skal du lage en funksjon som bruker matplotlib til å visualisere en andregradsfunksjon. I filen function_visualiser.py lag en funksjon plot_polynomial som har parametre for koeffisientene a
, b
og c
til en andregradsfunksjon, og en samling med tall xs
som angir x-verdiene som skal plottes. Funksjonen skal plotte funksjonen for disse x-verdiene med matplotlib. Vi skal også sette navn på aksene og gi plottet en tittel. Aksenavnene skal være «x» og «f(x)», og tittelen skal være «f(x) = ax^2 + bx + c» hvor du setter inn riktige verdier for a, b og c (se også eksempel under).
Husk at en andregradsfunksjon er gitt ved formelen: $$f(x) = ax^2 + bx + c$$
For å teste funksjonen, legg til dette på slutten av filen (gitt at du har importert matplotlib ved import matplotlib.pyplot as plt
):
import matplotlib.pyplot as plt
def plot_polynomial(a, b, c, xs):
... # din kode her
if __name__ == "__main__":
plot_polynomial(1, -5, 100, [-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10])
plt.show() # ha gjerne plt.show() utenfor plot_polynomial
Du skal få et vindu som ser omtrent slik ut hvis du har gjort det riktig:
Siden det ser litt rart ut å ha «1x^2» i tittelen når a = 1, eller « + -3x» når b = -3, kan du lage en funksjon som setter riktig fortegn foran koeffisientene a
, b
og c
i tittelen. Her er forslag til en måte å gjøre det på som blir litt finere, selv om heller ikke denne tar hensyn til alt:
def coeff_string(coefficient):
if coefficient == 1:
return " + "
elif coefficient == 0:
return None
elif coefficient >= 0:
return f" + {coefficient}"
elif coefficient < 0:
return f" - {abs(coefficient)}"
Du kan da bruke f-strengen
f'f(x) ={coeff_string(a)}x^2{coeff_string(b)}x{coeff_string(c)}'
som tittel på plottet.
Bonusoppgave (helt frivillig): lag en funksjon get_polynomial_string med en parameter coefficients som får gitt en liste med koeffisienter som argument. Funksjonen returnerer en pen strengrepresentasjon av funksjonsuttrykket.
def test_get_polynomial_string():
assert '2x^2 + 4x + 5' == get_polynomial_string([2, 4, 5])
assert '3x^3 + 2x^2 + 4x + 5' == get_polynomial_string([3, 2, 4, 5])
assert '3x^3 - 2x^2 + 4x + 5' == get_polynomial_string([3, -2, 4, 5])
assert '- 3x^3 + 2x^2 + 4x + 5' == get_polynomial_string([-3, 2, 4, 5])
assert '3x^3 + 4x - 5' == get_polynomial_string([3, 0, 4, -5])
assert '2x^2' == get_polynomial_string([2, 0, 0])
assert '- 3x' == get_polynomial_string([0, -3, 0])
assert '0' == get_polynomial_string([0, 0, 0])
assert '5' == get_polynomial_string([5])
assert '0' == get_polynomial_string([])
I funksjonen plot_function kan du gjøre følgende:
- Opprett en liste som inneholder \(f(x)\) for alle verdiene \(x\) i
xs
. Dette blir altså en liste over y-verdiene. - Bruk
plt.plot(xs, ys)
for å plotte funksjonen hvorys
er y-verdiene du regnet ut i steg 1. - Sett navn på aksene med
plt.xlabel
ogplt.ylabel
-funksjonene. - Bruk
plt.title
og en f-streng for å sette tittel. På grunn av fortegnene kan det bli litt kluss når du får negative koeffisienter, så se gjerne på hintet over for å få det til.
Studentregister
Du har et register med studentdata i en JSON-fil. I filen student_registry.py skal du lage en funksjon convert_students_to_csv som tar inn en sti til en JSON-fil med studentdata og en stil til en CSV-fil, og som så skriver dataen til CSV-filen med CSV-format. Bruk semikolon som skillesymbol i CSV-filen du oppretter.
Eksempler på studentdata i en JSON-fil:
{
"students": [
{
"id": 1,
"name": "Alice",
"area": "Biology",
"year": "1"
},
{
"id": 2,
"name": "Bob",
"area": "Chemistry",
"year": "2"
},
{
"id": 3,
"name": "Charlie",
"area": "Physics",
"year": "4"
}
]
}
JSON-filen representerer et oppslagsverk med én nøkkel, «students», der tilhørende verdi er en liste med oppslagsverk. Hvert oppslagsverk i denne listen representerer en student og har nøklene «id», «name», «area» og «year».
Når du har konvertert dataen fra students.json til en csv-filen, skal innholdet den se slik ut:
id;name;area;year
1;Alice;Biology;1
2;Bob;Chemistry;2
3;Charlie;Physics;4
Du kan laste ned eksemplene students.json og students2.json, programmet ditt skal fungere for begge disse samt andre JSON-filer som følger samme struktur.
-
Lag en funksjon convert_students_to_csv som tar inn en filsti til en JSON-fil med studentdata og en filsti til en CSV-fil.
-
Les innholdet fra json-filen til en streng og konverter strengen til et oppslagsverk
data
med loads -funksjonen fra den innbygde json modulen. -
Legg merke til at for hver student skal du til syvende og sist opprette én linje i den endelige strengen som skal bli innholdet i csv-filen. I tillegg kommer den øverste linjen i CSV-filen med overskriftene.
-
Gå gjennom
data['students']
med en for-løkke. I hver iterasjon av løkken vil iteranden referere til et nytt oppslagsverk som representerer en student.
For å teste funksjonen din, legg til den følgende koden på slutten av filen din og sjekk at csv-filen blir skrevet til disk slik som vist over.
if __name__ == "__main__":
convert_students_to_csv("students.json", "students.csv")
convert_students_to_csv("students2.json", "students2.csv")
Genetisk overlapp
Du jobber som forsker ved et biologisk institutt som studerer genetisk mangfold blant ulike arter. Instituttet har samlet DNA-prøver fra et bredt spekter av organismer, fra mikrober til planter, insekter og større dyr, inkludert mennesker. Prøvene har blitt analysert og lagret i filer som inneholder DNA-sekvenser, som er korte biter av DNA som kan brukes til å identifisere en spesifikk egenskap eller markør i organismen.
Din oppgave er å utvikle en funksjon som kan sammenligne to slike filer for å finne ut hvor mye genetikk de har til felles. Dette vil gi innsikt i det genetiske slektskapet mellom de to organismene, og kan hjelpe forskere med å forstå evolusjonære forhold og artenes opprinnelse.
I en fil genetics.py, skriv en funksjon count_overlap som tar inn to filstier path1
og path2
som begge viser til filer med genetisk data for hver sin organisme. Hver linje i filene inneholder en DNA-sekvens, og ingen sekvens forekommer mer enn én gang i samme fil. Funksjonen count_overlap skal returnere antall sekvenser som er inkludert i begge datasettene. Husk at filene kan ha newline på slutten, og denne telles ikke som en DNA-sekvens.
For eksempel, hvis innholdet i filen path1 er
TATT
GAGA
GAGG
og innholdet i filen path2 er
AGGA
CATT
GAGA
TATT
da ser vi at filene har to linjer som er like (TATT og GAGA). Svaret skal da være 2.
For å teste koden din kan du laste ned sample1.txt, sample2.txt, id1.txt og id2.txt. Legge alle filene inn i arbeidsmappen din, og så kan du teste funksjonen din med denne funksjonen:
def test_count_overlap_sample():
print('Tester count_overlap... ', end='')
assert 2 == count_overlap('sample1.txt', 'sample2.txt')
# Tester effektivitet (testen tar laaang tid ved feil løsning):
assert 100001 == count_overlap('id1.txt', 'id2.txt')
print('OK')
Merk at selv den riktige løsningen kjører ofte på mer enn 1 sekund avhengig av maskinen den kjøres på, men om løsningen din tar mer enn 10 sekunder er det sannsynligvis for treigt.
-
Les inn filene til lister med dna-sekvenser.
-
Gjør den første listen om til en mengde (dette er ikke nødvendig for å få et korrekt program, men det vil gjøre programmet mye mer effektivt – fordi operasjonen «sjekk om et element er til stede i samlingen» er mye raskere for en mengde enn for en liste).
-
Gå gjennom den andre listen og tell opp hvor mange av sekvensene som også er i den første.
-
Returner antall sekvenser som er i begge listene.
Jordskjelv
Du har blitt ansatt av et geologisk firma som skal finne ut de minst trygge stedene i verden å bygge boliger. Oppgaven din er å identifisere steder som har hatt store jordskjelv den siste tiden, slik at informasjonen kan selges til ulike boliginvesteringsfirmaer for at de ikke skal bygge boliger der.
I denne oppgaven skal vi kombinere bruken av minst fem moduler. Tre fra standardbiblioteket (datetime, csv, og json) og to eksterne moduler (requests og matplotlib).
Vi skal i denne oppgaven bruke requests til å laste ned data fra en nettside, og så konvertere dataen til en liste av oppslagsverk med csv-biblioteket. Deretter skal vi plotte den med pyplot fra matplotlib. Så skal vi kombinere dette med data fra en annen fil, og plotte begge deler sammen.
Skriv programmet ditt i filen earthquakes.py
.
Del A: last ned data om nylige jordskjelv med requests og datetime
Nettsiden til U.S. Geological Survey har åpent tilgjengelig og oppdaterte jordskjelvdata for hele verden. Ta for eksempel denne URL’en: https://earthquake.usgs.gov/fdsnws/event/1/query?format=csv&starttime=2024-03-11T17%3A14%3A55%2B0000&endtime=2024-03-21T17%3A14%3A55%2B0000&minmagnitude=5.8&orderby=magnitude&limit=5000
Om du får spørsmål om å lagre en fil når du trykker på linken over, bør du lagre den som en .csv-fil; for det er nemlig det du får. Om du åpner filen i en teksteditor (f. eks. VSCode) vil du se at den inneholder jordskjelvdata på .csv -format:
time,latitude,longitude,depth,mag,magType,nst,gap,dmin,rms,net,id,updated,place,type,horizontalError,depthError,magError,magNst,status,locationSource,magSource
2024-03-14T21:10:24.786Z,29.7971,-42.6608,10,6.2,mww,189,54,16.297,0.59,us,us6000miy6,2024-05-18T21:22:58.040Z,"northern Mid-Atlantic Ridge",earthquake,9.43,1.776,0.037,71,reviewed,us,us
2024-03-13T15:13:22.779Z,-5.831,150.6843,44,6,mww,142,29,4.981,0.96,us,us6000milg,2024-05-18T21:22:41.040Z,"68 km ESE of Kimbe, Papua New Guinea",earthquake,8.19,1.917,0.041,56,reviewed,us,us
2024-03-16T00:14:51.989Z,-58.935,158.3538,10,5.9,mww,57,52,4.46,0.5,us,us6000mj77,2024-05-18T21:23:15.040Z,"Macquarie Island region",earthquake,11.01,1.827,0.06,27,reviewed,us,us
Vi fikk altså en liste med tre jordskjelv. Men hvilke jordskjelv er det egentlig vi får? For å undersøke det, kan vi undersøke URL’en vi brukte for å laste ned dataen. Vi kan dele opp URL’en i flere deler:
https://earthquake.usgs.gov/fdsnws/event/1/query
er basen for URL’en. Dette kaller vi for et «API-endepunkt».?
betyr at resten av URL’en angir «query parametere» og tilhørende argumenter. Symbolet&
brukes for å skille mellom ulike parametere.format=csv
spesifiserer at vi ønsker dataen i .csv-format.starttime=2024-03-11T17%3A14%3A55%2B0000
spesifiserer at vi ønsker jordskjelv som finner sted fra og med 11. mars 2024 kl. 17:14:55 UTC. Dette er en måte å skrive tidspunkt på som kalles ISO 8601, men hvor noen symboler har blitt kodet slik at de tryggere kan brukes i en URL (%3A
er en kode for symbolet:
og%2B
er en kode for symbolet+
, så egentlig kan du tolke denne delen somstarttime=2024-03-11T17:14:55+0000
).endtime=2024-03-21T17%3A14%3A55%2B0000
spesifiserer at vi ønsker jordskjelv som finner sted til og med til 21. mars 2024 kl. 17:14:55 UTC. Strengen kan tolkes somendtime=2024-03-21T17:14:55+0000
minmagnitude=5.8
er en parameter som spesifiserer at vi ønsker jordskjelv med en styrke på 5.8 eller mer på Richters skala.orderby=magnitude
er en parameter som spesifiserer at vi ønsker jordskjelv sortert etter styrke.limit=5000
er en parameter som spesifiserer at vi ønsker maksimalt 5000 jordskjelv.
Det var altså fire jordskjelv som skjedde mellom 11. mars og 21. mars 2024 med en styrke på 5.8 eller høyere, og det er dem vi har fått informasjon om i .csv-filen som vi lastet ned.
For å laste ned data fra internett til et Python-program, bruker vi biblioteket requests.
- Installer
requests
(men du har sannsynligvis allerede installert det, siden det installeres automatisk sammen med uib-inf100-graphics) - Sjekk at du klarer å laste ned dataen fra URL’en over og skrive den ut i konsollen. Du kan bruke programmet under (men husk å bytte til ditt eget uib-brukernavn):
# Rask sjekk for at vi har klart å installere requests -biblioteket
import requests
url = 'https://earthquake.usgs.gov/fdsnws/event/1/query?format=csv&starttime=2024-03-11T17%3A14%3A55%2B0000&endtime=2024-03-21T17%3A14%3A55%2B0000&minmagnitude=5.8&orderby=magnitude&limit=5000'
headers = {'User-Agent': 'no.uib.ii.inf100.h24.lab7.mitt_uib_brukernavn'}
response = requests.get(url, headers=headers)
content = response.content.decode('utf-8')
print(content)
# Sjekk at du ser csv-dataen fra over i terminalen
Når du har fått dette til, er det på tide å gjøre det litt mer dynamisk; for eksempel ønsker vi å kunne hente ut jordskjelvdata basert på datoen når programmet kjøres. Umiddelbart ser det ut som vi må gjøre en seriøs streng-manipulasjon for å konstruere URL’en vi trenger; men heldigvis har requests-modulen gjort det enkelt å generere URL’er med parametere. For eksempel vil koden under gi nøyaktig samme resultat som koden over. Sjekk at du også får samme resultat! (PS: husk å endre mit_uib_brukernavn til ditt brukernavn eller bruk egentlig en hvilken som helst streng som kan identfisiere at programmet er akkurat ditt program)
import requests
baseurl = 'https://earthquake.usgs.gov/fdsnws/event/1/query'
headers = {'User-Agent': 'no.uib.ii.inf100.h24.lab7.mitt_uib_brukernavn'}
params = {
'format': 'csv',
'starttime': '2024-03-11T17:14:55+0000', # ISO 8601 format
'endtime': '2024-03-21T17:14:55+0000', # ISO 8601 format
'minmagnitude': 5.8,
'orderby': 'magnitude',
'limit': 5000,
}
response = requests.get(baseurl, params=params, headers=headers)
content = response.content.decode('utf-8')
print(content)
Nå virker det forhåpentligvis litt mer overkommelig å generere URL’er slik vi selv ønsker. Det gjenstår bare å kunne lage en ISO 8601-formatert streng for tidspunktet vi ønsker å hente ut jordskjelvdata for. Til dette formålet bruker vi datetime-modulen.
- Importer datetime, timezone og timedelta fra den innebygde datetime-modulen.
- Opprett et datetime-objekt for sluttidspunktet med tidspunktet «nå» i UTC tid (
end_time = datetime.now(timezone.utc)
). - Opprett et datetime-objekt for starttidspunktet med tidspunktet 365 dager før sluttidspunktet (
start_time = end_time - timedelta(days=365)
). - Konverter begge tidspunkter til en ISO 8601-formatert streng:
my_iso8601_string = my_datetime_object.strftime('%Y-%m-%dT%H:%M:%S%z')
- Bruk disse strengene i request’en din for å hente ut jordskjelvdata for det siste året.
Test at det fungerer. Helt til slutt skal vi flytte programmet vårt inn i en funksjon og gjøre det enda mer dynamisk:
- I filen earthquakes.py, skriv en funksjon
get_earthquakes_csv_string
med to parametre:n
et antall dager ogmagnitude
et flyttall. Funksjonen skal returnere en CSV-formatert streng med informasjon hentet fra U.S. Geological Survey om alle jordskjelv som har skjedd de sisten
dagene som hadde en styrke påmagnitude
eller mer. Dersom det er flere enn 5000 slike jordskjelv, skal kun de 5000 sterkeste av dem inkluderes.
# earthquakes.py
import requests
from datetime import datetime, timezone, timedelta
def get_earthquakes_csv_string(n, magnitude):
# Din kode her
...
if __name__ == "__main__":
s = get_earthquakes_csv_string(30, 5.8)
print(s) # vi fjerner print senere, men vi ser nå at vi er på rett vei
Del B: hent ut relevante jordskjelvdata
Vi har så langt fått tak i jordskjelvene som en csv-formatert streng. Nå er tidspunktet for å konvertere fra en slik streng til en liste med oppslagsverk (eller en 2D-liste, om du foretrekker det). Vi kunne kanskje gjort det selv med split
og lignende; men legg merke til at CSV-filen kan inneholde komma også i selve stedsnavnene. Da er det ikke så lett å splitte på komma som vi kanskje skulle ønske. Heldigvis har Python en innbygd modul for å lese CSV-filer som håndterer dette for oss: csv -modulen (det kan være spesielt aktuelt å lese avsnittet «DictReader med CSV-formatterte strenger»).
- Bruk csv-modulen for å konvertere csv-strengen til en liste med oppslagsverk. Strengen benytter komma (
,
) som skilletegn («delimiter») og hermetegn ("
) som grupperingssymbol («quotechar»). Begge disse er standard, så det er egentlig ikke behov for å spesifisere dem i vårt tilfelle.
Det vi egentlig ønsker å hente ut fra datasettet er en liste med tupler (lengdegrad, breddegrad, styrke)
, slik at vi kan plotte punktene.
- I earthquakes.py, lag en funksjon
get_earthquake_list
som tar inn en csv-formatert strengcsv_string
og returnerer en liste med jordskjelv. Hvert jordskjelv skal være en tuple med breddegrad, lengdegrad og styrke som flyttall.
For å teste funksjonen kan du laste ned og kjøre test_get_earthquake_list.py. Hint: pass på at testene passerer før du fortsetter, hvis du gjør feil her kan du få merkelige feil senere.
Del C: plot jordskjelvdata
Nå som vi har fått tak i dataen vi ønsker, skal vi plotte den med pyplot fra det eksterne biblioteket matplotlib.
I earthquakes.py, lag en funksjon plot_earthquakes som tar inn en liste med koordinater data_points
og plotter dem med plt.scatter
som blå prikker. Merk at data_points
er en liste med tupler (lengdegrad, breddegrad, magnitude)
, mens plt.scatter
tar inn to lister xs
og ys
som henholdsvis inneholder x- og y-verdiene til punktene. Du kan bruke en for-løkke for å hente ut x- og y-verdiene fra data_points
og legge dem i hver sin liste.
For å få et bedre inntrykk av hvor jordskjelvene har skjedd, ønsker vi å skalere størrelsen på prikkene etter magnituden på jordskjelvet. Dette kan gjøres ved å bruke parameteren s
ved kallet til scatter
-funksjonen. For å få en synlig størrelsesforskjell må vi skalere styrken eksponentielt med magnituden. Dette kan gjøres ved å lage en liste sizes
som inneholder <(3 opphøyd i magnituden) / 10> for hvert jordskjelv, og gi denne listen som argument til s
i scatter
-funksjonen.
Vi kan også sette alpha=0.2
for å gjøre prikkene litt gjennomsiktige, slik at vi lettere kan se hvor det er flest jordskjelv.
For å teste at alt henger sammen, går vi til if __name__ == '__main__':
-blokken som skal være på slutten av filen:
- her kaller du først get_earthquakes_string med styrke 4 og 50 dager som argument,
- deretter kaller du parse_earthquakes med resultatet av get_earthquakes_string som argument, og
- så gir du det resultatet til plot_earthquakes. Så kaller du
plt.show()
for å vise plottet.
Når du er ferdig skal programmet produsere noe som ser noenlunde slik ut (dette ble kjørt 16. oktober 2024):
Men dette er jo bare en haug med prikker? Ja, det er jo allerede litt kult, og om vi legger godviljen til kan vi skimte konturene av noen kontinenter og land. Vi ønsker likevel å gi prikkene litt mer mening og kontekst, og dette skal vi gjøre videre.
I funksjonen plot_earthquakes
:
- Gå gjennom
data_points
og hent ut x-verdi, y-verdi og magnitude for hvert punkt. Legg disse verdiene i hver sin liste (opprette som tomme lister før løkken begynner), slik at du til slutt ender opp med en liste med x-verdier, en liste med y-verdier, og en liste med størrelser. Pass på at størrelsene blir skalert eksponentielt med magnitude (size = 3**mag / 10
). - Gi disse verdiene som argumenter til
scatter
. Pass på at du må brukes=
for å sette listen av styrker inn som størrelser på prikkene. - Sett
alpha=0.2
.
Husk at dersom plottet ser feil ut kan det være feil fra Del B eller Del C. Pass på at du gir xs
og ys
til plt.scatter
i riktig rekkefølge, og at du har hentet ut koordinatene riktig i Del B. Husk: breddegrad (latitude) er som en y-verdi og sier noe om vertikal plassering, mens lengdegrad (longitude) er som en x-verdi og sier noe om horisontal plassering.
Del D: last inn kystlinjer
Ditt neste oppdrag vil være å finne fram et datasett av verdens kystlinjer, og så hente ut dataen vi har behov for slik vi gjorde for jordskjelvene. Først går du til Martyn Afford sitt prosjekt natural-earth-geojson, og laster ned ne_110m_coastline.json fra mappen /110m/physical
. Du må klikke på «download raw file» oppe til høyre når du får opp kartet.
Det du har lastet ned nå, er en tekst-fil som inneholder koordinater for hele verdens kystlinjer. Dette er en JSON-fil, som er en måte å lagre data på som er veldig likt Python sine oppslagsverk. En forenklet versjon av filen ser slik ut, hvis vi legger til litt ekstra mellomrom og linjeskift for å gjøre det lettere å lese:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"scalerank": 1,
"featurecla": "Coastline",
"min_zooom": 1.0
},
"geometry": {
"type": "LineString",
"coordinates": [
[ -163.71289567772871, -78.595667413241543 ],
[ -159.482404548154477, -79.046337579258974 ],
[ -163.027407803377002, -78.928773695794959 ]
]
}
},
{
"type": "Feature",
"properties": {
"scalerank": 0,
"featurecla": "Coastline",
"min_zooom": 0.0
},
"geometry": {
"type": "LineString",
"coordinates": [
[ -6.197884894220991, 53.867565009163364 ],
[ -9.977085740590269, 51.820454820353078 ],
[ -7.572167934591064, 55.131622219454869 ]
]
}
}
]
}
Om vi tolker data’en som om det var Python-kode, ser vi at den er organisert som et stort oppslagsverk som inneholder lister og andre oppslagsverk. Det er en nøkkel "features"
hvor tilhørende verdi er en liste; hvert element i denne listen er én øy (eller verdensdel). Hver slik øy er igjen representert som et oppslagsverk, og har en nøkkel "geometry"
hvor verdien igjen er et oppslagsverk som inneholder en nøkkel "coordinates"
som inneholder en liste med koordinater. Dette er en liste med punkter som danner en kystlinje.
Anta at
data
er en variabel som inneholder oppslagsverket vist over. Da kan vi hente ut de første koordinatene til den første øyen slik:>>> data['features'][0]['geometry']['coordinates'][0] [-163.71289567772871, -78.595667413241543]
Vi kan tolke oppslaget som at vi først åpner «features», så velger vi det første øyen i listen, så åpner vi «geometry» for nevnte øy, så «coordinates», og så henter vi ut det første punktet i listen.
json-modulen fra standardbiblioteket kan brukes for å konvertere en json-streng til et oppslagsverk.
I en funksjon load_coastlines, skal du
- lese inn dataen fra ne_110m_coastline.json som en streng, og
- bruke json-modulen fra standardbiblioteket for å konvertere strengen til et oppslagsverk, og
- returnere en liste med alle øyer. Hver øy skal være representert kun som en liste med koordinater.
Hvis den forenklede JSON-filen over var hele filen, ville altså funksjonen returnert en liste som så slik ut:
[
[ # Første øy
[ -163.71289567772871, -78.595667413241543 ],
[ -159.482404548154477, -79.046337579258974 ],
[ -163.027407803377002, -78.928773695794959 ]
],
[ # Andre øy
[ -6.197884894220991, 53.867565009163364 ],
[ -9.977085740590269, 51.820454820353078 ],
[ -7.572167934591064, 55.131622219454869 ]
]
]
Test deg selv: test_load_coastlines.py
- Les filen og konverter den til et oppslagsverk med json.
- Opprett en tom liste
lines
som du skal legge linjene (listene av koordinater) i. - Den øverste nøkkelen i oppslagsverket er
"features"
. Dette er en liste med kystlinjer. - Iterer over kystlinjene (bruk en for-løkke
for island in data['features']
) og hent ut kystlinjene somisland['geometry']['coordinates']
. Legg disse linjene til ilines
.
Del E: plot kystlinjer
Nå som vi har hentet ut kystlinjene, skal vi plotte dem sammen med jordskjelvdataen. I earthquakes.py, lag en ny funksjon plot_coastlines som tar inn en liste med verdensdeler/øyer islands
og plotter dem som linjer med pyplot sin plot
-funksjon. Bruk en løkke over øyene for å plotte hver øy en etter en. For å få det til å se ut som eksempelet, må du huske å sette color='grey'
som argument til plot
-funksjonen. Fargen er ikke et krav, og du får en kul overraskelse om du fjerner color
-argumentet.
For å teste funksjonen, kan du legge følgende til i if __name__ == '__main__'
-blokken, før kallet til plt.show:
- Et kall til load_coastlines for å laste inn kystlinjene.
- Et kall til plot_coastlines med resultatet fra foregående kall som argument for å plotte kystlinjene.
Når du er ferdig skal programmet produsere noe som ser noenlunde slik ut (hvis du kommenterer bort kallet til plot_earthquakes
):
I funksjonen plot_coastlines
kan du iterere over lines
og hente ut x- og y-verdier for hver linje på samme måte som vi gjorde i del C. Plot linjene med plt.plot
for å lage et linjeplot. Bruk color='grey'
for å få linjene grå, eller fjern argumentet for en kul overraskelse.
Husk at dersom plottet ser feil ut kan det være feil fra Del D eller Del E. Pass på at du gir xs
og ys
til plt.plot
i riktig rekkefølge, og at du har hentet ut koordinatene riktig i Del D.
Del F: plot jordskjelvdata og kystlinjer sammen
Nå har vi laget alt vi trenger for å plotte jordskjelv og kystlinjer sammen. Vi skal gjøre dette i if __name__ == '__main__'
-biten som vi har brukt så langt. Kommenter inn igjen plot_earthquakes
-kallet. Før du har kalt på funksjonene som plotter kystlinjer og jordskjelv, bruk plt.figure(figsize=(12,8))
for å lage en ny figur med størrelse 12x8. Dette er for å få et bedre perspektiv på jordskjelvene i forhold til kystlinjene. Legg også til et rutenett. Husk at plt.show()
skal kalles etter alt annet for å vise plottet.
Når programmet ditt produserer noe tilsvarende dette, så er du i mål!
- Opprett en ny figur med
plt.figure
og størrelsefigsize=(10,6)
. - Bruk funksjonene fra del A/B og D til å hente ut jordskjelvdata og kystlinjer.
- Plot jordskjelvdata og kystlinjer ved å bruke funksjonene fra del B og D med dataen over.
- Sett tittel på plottet med
plt.title
. - Legg til et rutenett med
plt.grid(True)
. - Kall
plt.show()
for å vise plottet.
Tilfeldig bursdag
På forhånd bør du lese om:
Det er også nyttig å søke opp eksempelkode for å se hvordan disse bibliotekene brukes.
I denne oppgaven skal du lage en funksjon som finner en tilfeldig bursdag i et år. I filen random_birthday.py, lag en funksjon random_birthday som tar inn et årstall year
og returnerer en tilfeldig dato i det året som en streng. Du kan bruke datetime
og random
for å lage en dato og utføre addisjon slik at du får en tilfeldig dato i det gitte året. For at testene skal fungere, må du bruke random slik at du genererer en dag i et år basert på dager fra 1.januar. For eksempel vil datoen bli 30.januar hvis du legger til 29 dager, eller 1. januar hvis du legger til 0 dager. Du må ta hensyn til skuddår.
Vi ønsker at datoen skal returneres som en string på formatet dd.mm.yyyy
, og til dette kan vi bruke strftime
fra datetime. Denne funksjonen gjør et datetime
-objekt om til en string, og argumentet spesifiserer formatet. %d
gir dag, %m
gir måned og %Y
gir år.
Siden vi skal vite hvor mange dager det er i året, må vi ta hensyn til skuddår. Hvis du har gjort oppvarmingsoppgaven Skuddår-modul, kan du importere funksjonen is_leap_year
fra der. Hvis ikke, se hintet under.
def is_leap_year(year):
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
I funksjonen random_birthday kan du gjøre følgende:
- Bruk datetime til å lage et
datetime
-objekt som representerer 1. januar iyear
. - Bruk
timedelta
og random til å legge til et tilfeldig antall dager til dette objektet. - Bruk
strftime
for å returnere datoen som en string.
For å teste funksjonen, legg til dette på slutten av filen (gitt at du har importert datetime og random) og sjekk at du får noen tilfeldige datoer i 2022, 2023 og 1912:
if __name__ == "__main__":
print(random_birthday(2022))
print(random_birthday(2023))
print(random_birthday(1912))
Fredag 13.
I filen friday_13th.py skriv en funksjon first_friday_13th_after(date)
som har som parameter et datetime
-objekt. Funksjonenen returnerer et nytt datetime
-objekt, som befinner seg på første fredag den 13. etter den gitte datoen. Dersom input-datoen selv er på en fredag den 13., er det neste datoen som skal returneres.
Denne oppgaven skal løses ved hjelp av datetime
-modulen fra Python sitt standardbibliotek.
Test koden din:
def test_first_friday_13th_after():
print('Tester first_friday_13th_after... ', end='')
# Test 1
result = first_friday_13th_after(datetime(2022, 10, 24))
assert (2023, 1, 13) == (result.year, result.month, result.day)
# Test 2
result = first_friday_13th_after(datetime(2023, 1, 13))
assert (2023, 10, 13) == (result.year, result.month, result.day)
# Test 3
result = first_friday_13th_after(datetime(1950, 1, 1))
assert (1950, 1, 13) == (result.year, result.month, result.day)
print('OK')
-
Se eksempel på bruk av datetime på den offisielle dokumentasjonen for datetime.
-
Kan være lurt å ha en hjelpefunksjon som sjekker om et gitt tidspunkt er på en fredag den trettende.
-
Forsøk å øke datoen med én dag helt til du treffer en dag som er på en fredag den trettende.
Kombinere CSV-filer
I denne oppgaven skal vi bruke CSV- biblioteket til å håndtere CSV-filer. Derfor bør du lese om dette i kursnotatene om csv
I denne oppgaven skal vi kombinere flere CSV-filer som ligger i en mappe til én stor CSV-fil. Hver av CSV-filene har det samme formatet, som også er det formatet den kombinerte CSV-filen skal ha:
uibid,karakter,kommentar
abc101,A,"Veldig bra, fra start til slutt"
abc102,B,"Denne kandidaten kan sin INF100, men bommer litt i oppgave 2 og 3"
abc103,C,"Denne kandidaten valgte å kun svare på partallsoppgavene"
I csv_combiner.py skriv en funksjon combine_csv_in_dir(dirpath, result_path)
som har to parametre:
dirpath
er en sti til en mappe som inneholder en rekke CSV-filer som skal kombineres. Det er kun filene som slutter på .csv i mappen som skal inkluderes, andre filtyper skal vi overse.result_path
er en sti til en CSV-fil som skal opprettes, som inneholder de kombinerte dataene fra alle CSV-filene.
For å teste funksjonen kan du laste ned samples.zip og pakke ut innholdet i samme mappe hvor du også finner csv_combiner.py. Innholdet skal ligge i en mappe som heter samples. Du kan så teste funksjonen din med denne koden:
print("Tester combine_csv_in_dir... ", end="")
# Mappen samples må ligge i samme mappe som denne filen
dirpath = os.path.join(os.path.dirname(__file__), "samples")
combine_csv_in_dir(dirpath, "combined_table.csv")
with open("combined_table.csv", "rt", encoding='utf-8') as f:
content = f.read()
assert("""\
uibid,karakter,kommentar
abc104,C,hei
abc105,D,"med komma, her er jeg"
abc106,E,tittit
abc101,A,Her er min kommentar
abc102,B,"Jeg er glad, men her er komma"
abc103,C,Katching
""" == content or """\
uibid,karakter,kommentar
abc101,A,Her er min kommentar
abc102,B,"Jeg er glad, men her er komma"
abc103,C,Katching
abc104,C,hei
abc105,D,"med komma, her er jeg"
abc106,E,tittit
""" == content)
print("OK")
Merk: csv
-biblioteket sin standard oppførsel når du lagrer 2D-lister som CSV er at det kun benyttes hermetegn dersom det er nødvendig. Det er nødvendig med hermetegn dersom en celle inneholder skilletegn (komma), linjeskift eller hermetegn. Dersom cellen ikke inneholder noen av de tre tegnene, vil det ikke inkluderes hermetegn i filen. Denne oppførselen kan endres ved å kalle på write_csv_file
-funksjonen i kursnotatene med argumentene quoting=csv.QUOTE_NONNUMERIC
eller quoting=csv.QUOTE_ALL
(se også offisiell dokumentasjon).
Dette betyr at selv om det kanskje er hermetegn i input-filen, vil disse ikke nødvendigvis bli med i resultat-filen. Assert-setningene over viser resultatet slik det blir med standard-innstillingene til csv-biblioteket.
-
CSV-filene i dette eksempelet er best lest med csv-biblioteket. Det blir fort komplisert å tolke dem selv, siden det kan være komma-tegn i kommentar-feltet.
-
Les om
os
-modulen, og legg spesielt merke tilos.walk
-funksjonen. -
Bruk gjerne en hjelpefunksjon
merge_table_into(master_table, new_table)
som tar som input en 2D-listemaster_table
som skal muteres, og en 2D-listenew_table
som inneholder det nye innholdet som skal legges til. For hver rad i new_table (bortsett fra første rad), kopier raden inn i master_table.
-
Opprett først en 2D-liste for resultat-tabellen vår, som initielt inneholder én rad (overskriftene). På slutten skal vi konvertere denne listen til CSV.
-
Bruk
os.walk
elleros.listdir
for å gå igjennom alle filene i mappen gitt veddirpath
(os.walk vil også gå inn i undermapper, og du trenger en nøstet løkke inne i os.walk for å gå gjennom listen med filer; ellers fungerer de nokså likt). For hver fil som ender på .csv (bruk f. eks..endswith
-metoden på strenger), åpne filen og les innholdet. -
Husk å bruke
os.path.join
-funksjonen for å omgjøre filnavn til filstier. -
For hver .csv -fil du finner, omgjør den til en 2D-liste, og legg til radene i resultat-tabellen (bruk hjelpefunksjonen beskrevet over).
Kvittering
I filen receipt.py skal du bruke modulen decimal. til å skrive et program som produserer en kvittering fra en butikk.
Koden din vil sannsynligvis ikke fungere hvis du ikke bruker decimal-modulen for å lagre tall, siden avrundingsfeil er veldig viktig å unngå, samtidig som vi skal bruke desimal-tall.
Ta utgangspunt i koden nedenfor:
def receipt_content(prices_filename, cash_register_filename):
"""Construct contents of a receipt of the cash register events,
given the store prices."""
# din kode her
def receipt(prices_filename, cash_register_filename):
"""Construct a receipt of the cash register events,
given the store prices."""
# get receipt content from receipt_content()
purchases_returns, total, vat, payment, change = receipt_content(
prices_filename, cash_register_filename
)
# the formatted lines of the receipt
receipt_lines = [f"{'Nr.':>4} {'Item':18} {'Price':>10}"]
for nr, item, price in purchases_returns:
receipt_lines.append(f"{nr:4d} {item:18} {price:10.2f}")
receipt_lines.append(f"Total: {total}")
receipt_lines.append(f"Of which VAT: {vat:.2f}")
receipt_lines.append(f"Payment: {payment}")
receipt_lines.append(f"Change {change}")
# add some dividers
max_len = max(len(line) for line in receipt_lines)
divider = "-" * max_len
receipt_lines.insert(1, divider)
receipt_lines.insert(-4, divider)
receipt_lines.insert(-2, divider)
return "\n".join(receipt_lines)
Du skal skrive koden til funksjonen receipt_content()
som beregner inneholdet i kvitteringen utifra en fil med butikkens priser og en fil med hendelsene ved kassen. Argumentet prices_filename
er en streng med navnet til filen som inneholder prisene. Argumentet cash_register_filename
er en streng med navnet til filen som inneholder hendelsene ved kassen.
Funksjonen receipt_content()
skal returnere en tupel som inneholder følgende (i denne rekkefølgen):
- En liste med tupler som inneholder (i følgende rekkefølge) antall, produkt og total pris, først for alle ting som har blitt kjøpt, i alfabetisk orden, og så for alle ting som har blitt returnert, i alfabetisk orden (se eksempelkvittering nedenfor). For de returnerte produktene blir det negative tall.
- Den totale prisen.
- Hvor mye av den totale prisen som er mva.
- Hvor mye som har blitt betalt.
- Hvor mye som blir betalt tilbake (her blir det ikke-positive tall).
Filen med butikkens priser er formattert slik som eksempelfilen prices.txt (som du kan bruke til å teste programmet ditt). Først står produkt, så semikolon (;
), og så prisen. Dette er egentlig en CSV-fil som bruker semikolon som skilletegn.
Filen med hendelsene ved kassen er formattert slik som eksempelfilen cash_register.txt (som du kan bruke til å teste programmet ditt). Først står hva som skjer (kjøp, retur eller betaling), så semikolon (;), og så produkten/verdien. Dette er også egentlig en CSV-fil som bruker semikolon som skilletegn.
Funksjonen receipt()
skal du ikke endre. Den bruker receipt_content()
til å beregne inneholdet i kvitteringen og så produserer receipt()
en pen kvittering utifra det innholdet. Du skal bare skrive kode til receipt_content()
.
Om din kode til receipt_content()
er riktig så skal denne testen passere:
print("Tester receipt... ", end="")
expected_value = """\
Nr. Item Price
------------------------------------
2 apple 10.00
1 chips 24.30
1 dish soap 26.20
1 frozen pizza 54.40
1 peanuts 18.50
1 toilet paper 34.00
3 tomato 30.00
-1 pocket book -149.00
-1 toothpaste -13.70
------------------------------------
Total: 34.70
Of which VAT: 6.94
------------------------------------
Payment: 100.00
Change -65.30"""
assert(expected_value == receipt("prices.txt", "cash_register.txt"))
print("OK")
-
Du må bruke biblioteket decimal for at beregningene skal bli riktige (prøv hvordan kvitteringen blir om du bruker float).
-
Decimal aksepterer både float og string for å lage decimals. Prøv hvordan kvitteringen blir om du gir decimals en float. Hvorfor skjer dette?
-
Det er mulig at betaling skjer flere ganger underveis ved kassen.
-
Du kan anta at alle betalinger ved kassen har positiv verdi.
-
Du kan anta at det ikke er noen feil i filene som inneholder prisene og hendelsene ved kassen.
-
Det er mulig at den totale prisen blir negativt (om kunden for eksempel bare returnerer ting).
-
Gitt en pris inklusive mva så multipliserer du prisen med 0.2 for å finne ut hvor mye av prisen som er mva.
-
Det kan være lurt å bruke dictionaries til prislisten og det som blitt kjøpt og det som blitt returnert.
-
Du må ikke skrive all kode innen funksjonen
receipt_content()
. Du kan dele opp koden i flere funksjoner på en måte du synes føles naturlig.
Værmelding
I filen forecast.py skriv en funksjon weather_in_bergen_next_hour()
uten parametre som returnerer en streng som sammenfatter været i Bergen den neste timen. Funksjonen skal hente vær-data fra meterologisk instutt, og bruke informasjon fra feltet «symbol_code» om den nærmeste timen.
Eksempelkjøring:
print("Været i Bergen neste time:", weather_in_bergen_next_hour())
Været i Bergen neste time: cloudy
For å løse denne oppgaven, skal vi
- Finne ut hvilken url vi kan bruke for å få informasjon om været fra meterologisk institutt
- Bruke requests -pakken for å laste ned informasjonen fra meterlogisk institutt inn i python-programmet
- Bruke json -pakken for å konvertere den nedlastede informasjonen til et oppslagsverk, og til slutt
- Slå opp på riktig sted i oppslagsverket og returnere denne informasjonen.
Steg A: nettadresse fra meterologisk institutt
Meterologisk instutt tilbyr massevis av vær-informasjon gratis (under lisensen CC-BY 4.0) fra sine nettsider. Slå opp på nettsiden https://api.met.no/weatherapi/locationforecast/2.0/ og finn OPENAPI UI fannen (Øverst i siden ved siden av DOCUMENTATION). Les om GET /compact under data.
- Du vil under «parameters» se to felter lat og lon hvor du kan fylle ut henholdsvis breddegrad og lengdegrad for hvilken posisjon du ønsker.
For å finne breddegrad og lengdegrad for et gitt sted, kan du på til https://maps.google.com og trykke på kartet. Da vil koordinatene til det gitte stedet vises.
- Når du har fylt ut breddegrad og lengdegrad, trykk på «Try it out!»
- Kopier med deg internettadressen under «Request URL». Dette er adressen til siden vi skal laste ned i programmet vårt (prøv den direkte i nettleseren også og se hva du får).
- Undersøk innholdet under «Response Body». Dette er den strengen vi får når vi laster ned innholdet fra den overnevnte nettadressen. Hvor ligger informasjonen vi er ute etter? Husk, vi ser etter verdier knyttet til nøkkelen «symbol_code».
Steg B: bruk requests for å laste ned informasjon
Bruk nettadressen vi fant i forrige steg som url. Merk at vilkårene til Meterologisk institutt sier at vi må identifisere hvem vi er i User-Agent
-feltet i «header». Det er tilstrekkelig å bruke "inf100.ii.uib.no <ditt uib-brukernavn>"
som verdi for dette feltet.
Steg C: bruk JSON for å konvertere til oppslagsverk
Du kan bruke json.loads()
for å konvertere en streng til et oppslagsverk. Her er et eksempel på hvordan du kan gjøre det:
import json
s = '{"a": 1, "b": 2}'
d = json.loads(s)
print(d["a"]) # 1
Her blir d et oppslagsverk med nøklene “a” og “b” og verdiene 1 og 2.
Steg D: returner verdien knyttet til nøkkelen «symbol code» i oppslagsverket
Det gjelder å holde tungen rett i munnen når du skal slå opp i oppslagsverket. En strategi kan være å begynne med å returnere d["properties"]
og så spesifisere videre ved å returnere d["properties"]["timeseries"]
og så videre, helt til riktig verdi blir returnert.
Det er ikke nødvendig å finne nøyaktig riktig time for å få oppgaven godkjent, det holder å returnere den første timen du får informasjon om (selv om den var i fortiden)
Steg E (frivillig): finn nøyaktig riktig time å rapportere for
For å rapportere for nøyaktig riktig time, må du matche den timen du får fra meterologisk instiutt med tiden akkurat nå. Les deg opp på datetime -modulen og se om du kan bruke informasjonen fra meterologisk institutt for å finne den nærmeste timen å rapportere fra.
Steg F (frivillig): mer omfattende værmelding
Kan du finne mer informasjon å rapportere? Nedbørsmengder? Vind? Sannsynlighet for nedbør (nå må du over på GET /complete hos meterologisk institutt)?