Uke 9
Innlevering, Krav for å bestå og viktigheten av bonusoppgaver
- Du kan levere så mange ganger du vil. Siste innlevering teller.
- For å få bestått, må du få 100% riktig av CodeGrade på alle oppgaver som ikke er merket som bonus.
- Oppgavene som er merket som bonus er frivillige. Imidlertid er det verdt å bruke tid på disse oppgavene da de kan gi deg verdifull erfaring med å løse liknende problemer som kan dukke opp på eksamen.
Generelt tips: les alltid gjennom kursnotatene før du begynner på lab’en!
Oppgave 1
I filen uke_09_oppg_1.py, skriv en funksjon som heter filter_high_temperatures
med parametre:
path_input
, en filsti til en eksisterende fil som inneholder temperaturdata for hver måned. Hver linje består av månedens navn etterfulgt av minimums- og maksimumstemperaturen, adskilt med mellomrom. For eksempel, temperatures.txt.path_output
, en filsti til en fil som skal opprettes, ogthreshold_temp
, et flyttall som representerer en temperatur.
La funksjonen åpne filen path_input
, gå gjennom linjene i filen og lager en ny fil path_output
hvor kun de linjene der maksimumstemperaturen er minst threshold_temp
er inkludert. Om ingen måneder har en maksimum temperatur som er minst threshold_temp
så skal path_output
være en tom fil.
print("Tester filter_high_temperatures... ", end="")
filter_high_temperatures("temperatures.txt", "max_temps.txt", 15)
expected_result = """\
Mars 3 20
April 6 20
Mai 9 25
Juni 12 30
Juli 15 28
August 15 20
September 12 15
"""
with open("max_temps.txt", "rt", encoding='utf-8') as f:
actual_result = f.read()
assert(expected_result == actual_result)
print("OK")
Oppgave 2
Vi skal nok engang jobbe med k-mers. I oppgave 2 i uke 8,lagde vi en funksjon som returnerer k-mer-frekvensen i en DNA-sekvens. I denne oppgaven vil vi gjøre det samme, men denne gangen vil DNA-sekvensen være lagret i en tekstfil.
I filen uke_09_oppg_2.py skal du skrive en funksjon kmercount(filepath, k)
. Funksjonen skal ta inn en filsti til en tekstfil som inneholder en DNA-sekvens, og den skal returnere et dictionary som inneholder alle k
-mere i sekvensen og deres frekvens.
Formatet vi skal bruke til DNA-sekvensfilene heter FASTA. Hver sekvens starter med en header på én linje som starter med
>
Denne første linjen inneholder informasjon om DNA-sekvensen. Når du implementerer din funksjon for å lese sekvensen, kan du ignorere denne linjen.
Alle linjene etter
>
-linjen i en FASTA-fil tilhører den samme sekvensen og må leses som én enkel streng uten mellomrom eller linjeskift. De kan enkelt fjernes med f.eksstring.replace()
Testkjøring med KmerCountingInput.txt:
print("Tester kmercount... ", end="")
mer2 = {'AC': 41, 'CC': 50, 'CT': 31, 'TA': 55, 'AA': 80, 'TC': 26,
'AT': 35, 'TT': 31, 'CA': 35, 'AG': 25, 'GA': 10, 'GT': 24,
'GC': 7, 'TG': 9, 'GG': 16, 'CG': 8}
mer3 = {'ACC': 17, 'CCT': 18, 'CTA': 19, 'TAA': 27, 'AAC': 17, 'CTC': 4,
'TCC': 9, 'AAA': 36, 'AAT': 17, 'ATC': 9, 'CCC': 22, 'TAT': 8, 'ATT': 10,
'TTC': 9, 'TCA': 9, 'CAG': 7, 'AGA': 4, 'GAC': 2, 'ACA': 14, 'CAA': 13,
'TTA': 15, 'ATA': 12, 'TAC': 13, 'CCA': 9, 'AGT': 10, 'GTC': 4, 'AAG': 10,
'GTA': 9, 'TAG': 7, 'AGC': 4, 'GCA': 3, 'CAC': 8, 'CAT': 7, 'TCT': 7,
'CTT': 6, 'CTG': 2, 'TGG': 4, 'GGC': 2, 'ATG': 4, 'GGT': 6, 'TGT': 4,
'GTG': 2, 'GTT': 9, 'GAT': 3, 'AGG': 6, 'GGG': 5, 'GGA': 3, 'GAG': 1,
'TTT': 6, 'TTG': 1, 'GAA': 4, 'ACT': 5, 'GCG': 1, 'CGT': 4, 'GCT': 1,
'TGC': 1, 'GCC': 2, 'ACG': 5, 'CGA': 3, 'CGG': 1, 'TCG': 1, 'CCG': 1}
assert mer3 == kmercount("KmerCountingInput.txt", 3)
assert mer2 == kmercount("KmerCountingInput.txt", 2)
print("OK")
Oppgave 3
I filen uke_09_oppg_3.py, skriv en funksjon som heter first_letter_last_word(filepath)
som åpner en fil med den gitte filstien, og returnerer en streng som består av den første bokstaven til det siste ordet i hver linje. Tomme linjer ignoreres.
Testkjøring med askeladden.txt:
print("Tester first_letter_last_word... ", end="")
assert("sti" == first_letter_last_word("askeladden.txt"))
# Forklaring:
# Siste ord i første linje er 'sønner.' Første bokstav i dette ordet er 's'
# Siste ord i andre linje er 'til.' Første bokstav i dette ordet er 't'
# Siste ord i tredje linje er 'i.' Første bokstav i dette ordet er 'i'
print("OK")
Oppgave 4
Her gjør vi det samme som i oppgave 3, men funksjonen skal ikke krasje hvis vi nevner en fil som ikke finnes, dvs når en FileNotFoundError oppstår.
I filen uke_09_oppg_4.py, skriv en funksjon som heter first_letters(filepath)
som bruker first_letter_last_word(filepath)
fra Oppgave 3. Funksjonen skal ta et filsti som argument og returnere en streng med den første bokstaven i det siste ordet for hver linja av filen. Hvis filen ikke finnes skal den returnere ""
.
print("Tester first_letters... ", end="")
assert("sti" == first_letters("askeladden.txt"))
assert("" == first_letters("de_tre_bukkene_bruse.txt"))
print("OK")
Oppgave 5
I filen uke_09_oppg_5.py skal du skrive en enkel funksjon kalt add_together(a, b, c, d)
. Funksjonen skal ta fire argumenter og returnere summen av disse argumentene (a + b + c + d).
Dette er en enkel funksjon, men kan krasje hvis argumentene som sendes inn har forskjellige datatyper.
Så, oppgaven din er å implementere denne funksjonen på en måte som håndterer eventuelle feil som kan oppstå når du legger sammen argumentene. Hvis noe går galt, skal funksjonen skrive ut Failed with error: og så feilmeldingen, og retunere None
. Det kan gjøres med except ... as ...:
-konstruksjonen.
print("Tester add_together... ", end="")
assert(8 == add_together(1,2,3,2)) # ingen printout
assert(7 == add_together(0,2,3,2)) # ingen printout
assert("abcd" == add_together("a","b","c","d")) # ingen printout
assert([1,2,3,4] ==add_together([1],[2],[3],[4])) # ingen printout
assert(None== add_together(None,2,3,2))
# Her blir følgende skrevet ut:
#Failed with error: unsupported operand type(s) for +: 'NoneType' and 'int'
assert(None == add_together(1,"two",3,2))
# Her blir følgende skrevet ut:
#Failed with error: unsupported operand type(s) for +: 'int' and 'str'
assert(None == add_together(1,2,3,[2]))
#Her blir følgende skrevet ut:
#'Failed with error: unsupported operand type(s) for +:'int'and 'list''
print("OK")
Bonus 1
Tradisjonelt regnes 80 tegn for å være den maksimale lengden på en linje for å ha en god kodestil. Selv om moderne skjermer er i stand til å vise flere tegn på en linje, regnes grensen på 80 fremdeles for å være en god tommelfingerregel, og er for eksempel en del av Python sin offisielle stil-guide PEP 8.
Skriv funksjonen good_style(source_code)
som returnerer True
hvis alle linjene i strengen source_code
er mindre enn eller lik 80 tegn, False
ellers.
Merk at grensen på 80 tegn inkluderer selve linjeskift-symbolet på slutten av linjen, slik at det i praksis blir maksimalt 79 tegn på hver linje.
print("Tester good_style... ", end="")
assert(good_style("""\
def distance(x0, y0, x1, y1):
return ((x0 - x1)**2 + (y0 - y1)**2)**0.5
"""))
assert(good_style((("x" * 79) + "\n") * 20))
assert(not good_style("x" * 80))
assert(not good_style((("x" * 79) + "\n") * 5 +
(("x" * 80) + "\n") +
(("x" * 79) + "\n") * 5))
print("OK")
Benytt en løkke som itererer over alle linjene i en streng. string.splitlines()
kan hjelpe her. Pass på å telle med linjeskiftene.
Dersom vi blir ferdige med løkken uten å finne en eneste linje som er for lang, er svaret True
.
Bonus 2
Skriv funksjonen good_style_from_file(filename)
som leser inneholdet i filen filename
og returerer True
hvis inneholdet har god kodestil (alle linjene har mindre enn eller lik 80 tegn), False
hvis ikke.
For å teste koden kan du først opprette en fil test_file1.py i samme mappen som du kjører Bonus 2 fra, med følgende innehold:
def distance(x0, y0, x1, y1):
return ((x0 - x1)**2 + (y0 - y1)**2)**0.5
Opprett test_file2.py med følgende innehold:
def point_in_rectangle(x0, y0, x1, y1, x2, y2):
return (min(x0, x1) <= x2 <= max(x0, x1)) and (min(y0, y1) <= y2 <= max(y0, y1))
Og filen test_file3.py med følgende innehold:
def point_in_rectangle(x0, y0, x1, y1, x2, y2):
return (min(x0, x1) <= x2 <= max(x0, x1)) and (min(y0, y1) <= y2 <= max(y0, y1))
def distance(x0, y0, x1, y1):
return ((x0 - x1)**2 + (y0 - y1)**2)**0.5
print("Tester good_style_from_file... ", end="")
assert(good_style_from_file("test_file1.py"))
assert(not good_style_from_file("test_file2.py"))
assert(not good_style_from_file("test_file3.py"))
print("OK")
Bonus 3
En CSV-fil er en ren tekst-fil hvor innholdet er organisert i rader og kolonner. Hver rad er skilt med linjeskift, mens hver kolonne er skilt med et spesielt skillesymbol, for eksempel semikolon. Et eksempel på en CSV-fil er:
id;location;impact;time
nc72666881;California;1.43;2016-07-27 00:19:43
us20006i0y;Burma;4.9;2016-07-27 00:20:28
nc72666891;California;0.06;2016-07-27 00:31:37
Eksempelet er en forkortet og forenklet oversikt over registrerte jordskjelv hentet fra CORGIS.
I dennne oppgaven skal vi skrive et program som leser inn en CSV-fil med formatet over, og produserer en ny fil som inneholder alle de rekkene i den første filen hvor impact er større enn en gitt verdi.
Det finnes biblioteker som gjør håndtering av CSV-filer enklere, og det skal vi se mer på senere i kurset. I denne oppgaven skal vi derimot ikke importere biblioteker for CSV-håndtering, men kun bruke standard streng-metoder og kode vi har skrevet selv.
Del A
Skriv en funksjon get_impact(line)
som får én enkelt linje som input, og som returnerer impact-kolonnen i den linjen som et flyttall.
print("Tester get_impact... ", end="")
assert(1.43 == get_impact("nc72666881;California;1.43;2016-07-27 00:19:43"))
assert(4.9 == get_impact("us20006i0y;Burma;4.9;2016-07-27 00:20:28"))
print("OK")
Del B
Skriv en funksjon filter_earthquakes(earthquake_csv_string, threshold)
som tar inn en streng earthquake_csv_string
med CSV-data på formatet vist over, samt et flyttall threshold
. Funksjonen skal returnere en streng på samme format, men hvor linjer med impact strengt lavere enn threshold
-verdien ikke er inkludert.
Test koden din ved å legge til følgende linjer nederst i filen:
print("Tester filter_earthquakes... ", end="")
assert("""\
id;location;impact;time
nc72666881;California;1.43;2016-07-27 00:19:43
us20006i0y;Burma;4.9;2016-07-27 00:20:28
""" == filter_earthquakes("""\
id;location;impact;time
nc72666881;California;1.43;2016-07-27 00:19:43
us20006i0y;Burma;4.9;2016-07-27 00:20:28
nc72666891;California;0.06;2016-07-27 00:31:37
""", 1.1))
assert("""\
id;location;impact;time
us20006i0y;Burma;4.9;2016-07-27 00:20:28
""" == filter_earthquakes("""\
id;location;impact;time
nc72666881;California;1.43;2016-07-27 00:19:43
us20006i0y;Burma;4.9;2016-07-27 00:20:28
nc72666891;California;0.06;2016-07-27 00:31:37
""", 3.0))
assert("""\
id;location;impact;time
""" == filter_earthquakes("""\
id;location;impact;time
nc72666881;California;1.43;2016-07-27 00:19:43
us20006i0y;Burma;4.9;2016-07-27 00:20:28
nc72666891;California;0.06;2016-07-27 00:31:37
""", 5.0))
print("OK")
- Benytt en for-løkke og .splitlines -metoden for å gå igjennom alle linjene med data. Husk at den første linjen må behandles annerledes
- Bruk funksjonene fra forrige deloppgave for å finne ut om en gitt linje skal legges til i resultatet eller ikke.
- Husk å legge til linjeskift.
Del C
Skriv en funksjon filter_earthquakes_file(source_filename, target_filename, threshold)
som tar inn navnet på to filer, samt en grenseverdi. Funksjonen skal gjøre det samme som i forrige deloppgave, men leser inn data fra source_filename
, og skriver ut data til target_filename
.
For å teste funksjonen, last ned earthquakes_simple.csv og legg den i mappen hvor du kjører programmet fra (typisk den mappen du har åpnet VSCode i). Legg deretter til koden under nederst i filen:
print("Tester filter_earthquakes_file... ", end="")
def read_file(path):
with open(path, "rt") as f:
return f.read()
filter_earthquakes_file("earthquakes_simple.csv",
"earthquakes_above_7.csv", 7.0)
expected_value = """\
id;location;impact;time
us100068jg;Northern Mariana Islands;7.7;2016-07-29 17:18:26
us10006d5h;New Caledonia;7.2;2016-08-11 21:26:35
us10006exl;South Georgia Island region;7.4;2016-08-19 03:32:22
"""
assert(expected_value == read_file("earthquakes_above_7.csv"))
print("OK")
# Manuell test: Finn earthquakes_above_7.csv, åpne og se at innholdet stemmer