Lab7

Innlevering på CodeGrade og autotester er dessverre litt forsinket. Vi jobber med saken.

Grunnleggende oppgaver (E):

Nivå A-D:

Viktig: studer og forstå godt kursnotatene om filer og CSV. I tillegg er det lurt å skumme gjennom kursnotatene om å håndtere krasj med try/except.


Nivå E
Sum med strenger

I filen string_sum.py skal du skrive en enkel funksjon kalt add_together(s). Anta at parameteren s er en streng hvor noen av «ordene» representerer tall (ulike ord i strengen er skilt av mellomrom). Funksjonen skal returnere summen av de ordene i strengen som er heltall. Hvis ingen av strengene inneholder heltall, skal funksjonen returnere 0.

Test koden din ved å legge til disse linjene nederst i filen:

print('Tester add_together... ', end='')
assert 6 == add_together('4 2')
assert 9 == add_together('5 -1 3 2')
assert 0 == add_together('foo bar qux')
assert 42 == add_together('foo 42 qux')
assert 42 == add_together('foo2 42 2qux 3x1')
assert 0 == add_together('-9- 3-2')
print('OK')

Split-metoden klipper opp en streng ved et gitt symbol, og returnerer en liste av bitene som er igjen.

# Eksempler på bruk av .split()
s = 'foo bar qux'
parts = s.split(' ') # parts er nå en liste ['foo', 'bar', 'qux']
print(parts[1]) # bar

s = '3;9;42'
parts = s.split(';') # parts er nå en liste ['3', '9', '42']
print(parts[0] + parts[2]) # 342

  • Begynn med å benytte .split() -metoden for å omgjøre strengen til en liste av ord.
  • Bruk en løkke for å gå igjennom alle elementene i listen.
  • Før løkken starter, opprett en variabel som holder den løpende totalsummen initiert med verdien 0. For hver iterasjon av løkken kan det være vi legger til et nytt tall til denne variabelen.
  • Benytt try-except når du forsøker å konvertere strengen til heltall. Dersom omgjøringen feiler, hopp over dette elementet og fortsett videre med neste element i listen.
  • Returner totalsummen når løkken er ferdig.

Nivå E
Høye temperaturer

I high_temperatures.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, og
  • threshold_temp, et flyttall som representerer en temperatur.

La funksjonen åpne filen path_input, gå gjennom linjene i filen og lage 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.

Test koden din ved å laste ned temperatures.txt og legg til disse linjene nederst i filen:

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.strip() == actual_result.strip()
print('OK')

  • Les innholdet i filen path_input og lagre det i en variabel.
  • Opprett en liste for linjene vi senere skal skrive til filen path_output. Initier den som en tom liste.
  • Gå gjennom hver linje i innholdet (se f. eks. splitlines-metoden).
    • For hver linje: benytt split -metoden for å dele opp linjen i tre deler: månedens navn, minimumstemperaturen og maksimumstemperaturen.
    • Omgjør maksimumstemperaturen til et flyttall og sammenlign med threshold. Hvis temperaturen er tilstrekkelig høy, legg til linjen i listen vi opprettet tidligere.
  • Åpne filen path_output i skrivemodus. Skriv ut linjene i listen til filen. Husk linjeskift.

Nivå E
Kraftige jordskjelv

Denne oppgaven består av tre deler. Skriv funksjoner til alle deloppgaver i én felles fil, high_impact.py.

Eksempelet under er en forkortet og forenklet oversikt over registrerte jordskjelv i CSV format hentet fra CORGIS.

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

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 (en streng) som input, og som returnerer impact-kolonnen i den linjen som et flyttall. Dersom det er noe feil med input som gjør at det ikke er mulig å se hvilken styrke jordskjelvet har, skal funksjonen returnere None, men ikke krasje.

Test koden din ved å legge til disse linjene nederst i filen:

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")
assert None is get_impact("us20006i0y;Burma;not_a_number;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 og linjer hvor det ikke er gyldig impact-verdi ikke er inkludert.

Test koden din ved å legge til følgende linjer nederst i filen:

print("Tester filter_earthquakes... ", end="")
input_string = """\
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
nc72666892;California;not_a_number;2016-08-23 03:21:18
"""
# Test 1
expected_value = """\
id;location;impact;time
nc72666881;California;1.43;2016-07-27 00:19:43
us20006i0y;Burma;4.9;2016-07-27 00:20:28
"""
actual_value = filter_earthquakes(input_string, 1.1)
assert expected_value.strip() == actual_value.strip()

# Test 2
expected_value = """\
id;location;impact;time
us20006i0y;Burma;4.9;2016-07-27 00:20:28
""" 
actual_value = filter_earthquakes(input_string, 3.0)
assert expected_value.strip() == actual_value.strip()

# Test 3
expected_value = """\
id;location;impact;time
"""
actual_value = filter_earthquakes(input_string, 5.0)
assert expected_value.strip() == actual_value.strip()
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. Legg deretter til koden under nederst i filen:

print('Tester filter_earthquakes_file... ', end='')

def read_file(path):
    with open(path, 'rt', encoding='utf-8') 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
'''
actual_value = read_file('earthquakes_above_7.csv')
assert expected_value.strip() == actual_value.strip()
print('OK')

# Manuell test: Finn earthquakes_above_7.csv, åpne og se at innholdet stemmer
Nivå D
Første bokstav siste ord

Denne oppgaven består av to deler. Skriv funksjonen i filen first_of_last.py.

Del A

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. Kjør programmet ditt på filen askeladden.txt.

Test koden din ved å legge til disse linjene nederst i filen:

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')

Del B

Her gjør vi det samme som i forrige del oppgave, men funksjonen skal ikke krasje hvis vi nevner en fil som ikke finnes, dvs når en FileNotFoundError oppstår.

Skriv en funksjon som heter first_letters(filepath) som bruker first_letter_last_word(filepath) fra Del A. 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 en tom streng ''.

Test koden din ved å legge til disse linjene nederst i filen:

print('Tester first_letters... ', end='')
assert('sti' == first_letters('askeladden.txt'))
assert('' == first_letters('de_tre_bukkene_bruse.txt'))
print('OK')
Nivå C
God stil

Denne oppgaven består av to deler. Skriv funksjoner til begge deloppgaver (A og B) i én felles fil, nice_style.py.

Del A

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.

Test koden din ved å legge til disse linjene nederst i filen:

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.

Del B

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. Kjør koden din på filene test_file1.py, test_file2.py og test_file3.py.

Test koden din ved å legge til disse linjene nederst i filen:

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")
Nivå B
Mulige ord

I denne oppgave skal vi ta utgangspunktet fra oppgaven ordspill fra lab5. Det er lurt å løse den oppgaven først.

I filen possible_words.py skriv en funksjon possible_words_from_file(path, letters) som tar inn en filsti path til en fil som inneholder en ordliste med ett lovlig ord på hver linje, samt en bokstavsamling letters. La funksjonen returnere en liste med alle ord man kan lage av de gitte bokstavene.

For å teste funksjonen kan du laste ned den offisielle ordlisten fra Norsk Scrabbleforbund (nsf2022.txt) og legge den i samme mappe possible_words.py kjøres fra.

Husk at hvilken mappe du kjører fra ikke alltid er den samme mappen hvor programmet ligger; men dersom du åpner VSCode i samme mappe som skriptet ditt, vil dette også være den mappen du kjører programmet fra.

Test koden din ved å legge til disse linjene nederst i filen:

print("Tester possible_words_from_file... ", end="")
assert(['du', 'dun', 'hu', 'hud', 'hun', 'hund', 'nu', 'uh']
        == possible_words_from_file("nsf2022.txt", "hund"))

# Ekstra test for varianten hvor det er wildcard i bokstavene
# assert(['a', 'cd', 'cv', 'e', 'i', 'pc', 'wc', 'æ', 'å']
#         == possible_words_from_file("nsf2022.txt", "c*"))
print("OK")
Nivå A
Bestått

For veldig lenge siden i en galakse langt, langt borte var det en gang en emneansvarlig som skulle avgjøre hvem som skulle få ta eksamen. Data om studentene var samlet i en semikolon-separert csv-fil du kan laste ned her: course_data.csv. I denne filen står B for bestått, mens alle andre verdier betyr ikke bestått.

Reglene for å få ta eksamen var som følger:

  • Alle quizer måtte være bestått
  • Minst 6 av 11 lab’er måtte være bestått
  • Minst 3 av de 5 siste lab’ene måtte være bestått
  • Dersom man har bestått kartleggingsprøven telles det som å ha bestått de 6 første labene

Emneansvarlig ønsker å skrive en funksjon som leser csv-filen og returnerer en liste med id til dem som fikk bestått. Kunne du hjulpet ham?

I filen passed.py skriv en funksjon students_who_passed(path) som leser csv-filen over og returnerer en liste med id’er for de studentene som har bestått arbeidskravene i kurset.

Test koden din ved å legge til disse linjene nederst i filen:

print("Tester students_who_passed... ", end="")
assert(['abc101', 'abc103', 'abc105', 'abc109', 'abc111', 'abc113'] 
        == students_who_passed("course_data.csv"))
print("OK")