Oppslagstabeller og teksthåndtering Ole Christian Lingjærde, Dept of Informatics, UiO 4. oktober 2018
Dagens agenda Oppslagstabeller: verktøy for å lagre par av verdier Lese filer inn i oppslagstabeller En liten quiz Teksthåndtering
Hvordan implementere en matematisk funksjon i Python En matematisk funksjon f er et sett av regler som tilordner en entydig verdi f (x) til hver x. Vi kan implementere dette i Python med funksjoner eller med oppslagstabeller. Eksempel: Vi har reglene f (5) = 10 og f (11) = 15. Implementasjon med funksjon def f(x): if x == 5: return 10 elif x==11: return 15 print(f(5)) # Resultat: skriver ut 10 Implementasjon med oppslagstabell d = {5: 10, 11: 15} print(d[5]) # Resultat: skriver ut 10
Et eksempel til Oppgave: Implementer følgende regler i Python: 'Norway' --> 'Oslo' 'Sweden' --> 'Stockholm' 'France' --> 'Paris' Implementasjon med funksjon def f(x): if x == 'Norway': return 'Oslo' elif x == 'Sweden': return 'Stockholm' elif x == 'France': return 'Paris' Implementasjon med oppslagstabell d = {'Norway': 'Oslo', 'Sweden': 'Stockholm', 'France': 'Paris'}
Legge til flere regler Anta at vi ønsker å utvide funksjonen med en regel til: 'Norway' --> 'Oslo' 'Sweden' --> 'Stockholm' 'France' --> 'Paris' 'Nepal' --> 'Kathmandu' # NY REGEL Implementasjon med funksjon Vi må skrive en helt ny funksjon, denne gangen med fire regler. Implementasjon med oppslagstabell # Vi kan enkelt utvide oppslagstabellen vi allerede har laget: d['nepal'] = 'Kathmandu'
Lister vs. oppslagstabeller Lister Vi kan tenke på en liste i Python som en samling par: a = [6, 3, 7] # Parene (0,6), (1,3), (2,7) print(a[1]) # Skriv ut siste halvdel av paret (1,3) Indeksen er implisitt når vi definerer listen, men vi kan bruke den siden for å få tilgang til verdier i listen (slik som ovenfor). Oppslagstabeller En oppslagstabell er også en samling med par: d = {0:6, 1:3, 2:7} # Parene (0,6), (1,3), (2,7) print(d[1]) # Skriv ut siste halvdel av paret (1,3) Indeksen (= nøkkelen) gis eksplisitt når vi definerer oppslagstabellen, og kan også brukes siden for å få tilgang til verdier i oppslagstabellen.
Oppslagstabeller gir indeksfrihet! For en liste må indeksene alltid være 0, 1,..., N-1 (hvor N er lengden på listen). For en oppslagstabell kan indeksene (= nøklene) være hva som helst. Eksempler: # Parene (-2,6), (6,3), (3,7) d = {-2:6, 6:3, 3:7} # Parene ('Hamar','Norway'), ('Uppsala','Sweden') d = {'Hamar':'Norway', 'Uppsala':'Sweden'} # Parene ('pi', 3.14), ('e', 2.718), ('g', 9.81) d = {'pi':3.14, 'e':2.718, 'g':9.81} # Parene ((0,0), 'lowerleft') and ((1,1), 'upperright') d = {(0,0):'lowerleft', (1,1):'upperright'} Det er noen restriksjoner på nøklene: de må være entydige og være av en immutable data type (int, float, complex, string, tuple,...).
Nøkler må være immutable objekter Noen dataobjekter i Python kan endres etter at de er laget - slike objekter kalles mutable. Andre dataobjekter kan ikke endres og kalles immutable. Eksempler: Et listeobjekt kan endres. Vi kan f.eks. endre ite verdien med (a[i] = new_value) uten å lage en helt ny liste. Et tuppelobjekt kan ikke endres. Forsøk på å endre ite verdien gir feilmelding. Hvis vi må gjøre endringer, er vi nødt til å lage et helt nytt tuppel. Eksempler: d = {1: 34, 2: 67, 3: 0} # Nøkkelen er int d = {1:6424, 'X':64345} # Nøkkelen er int eller string d = {(0,0): 4, (1,-1): 5} # Nøkkelen er et tuppel d = {[0,0]: 4, [-1,1]: 5} # FEIL: nøkkelen kan ikke være liste
En oppslagstabell er en uordnet samling nøkkel/verdi par Nøkler og verdier Vi har sett at en oppslagstabell er en samling av par (a,b). Første element i hvert par kalles en nøkkel og andre element en verdi. I oppslagstabellen d = {'Oslo': 3} er Oslo nøkkel og 3 tilhørende verdi. To par kan ikke ha samme nøkkel, men de kan ha samme verdi. Oppslagstabeller er uordnete En oppslagstabell lagrer ikke parene i en bestemt rekkefølge. Man kan heller se for seg oppslagstabellen som en sekk med par (a,b) uten noen bestemt ordning.
Å lage oppslagstabeller For å lage en oppslagstabell initialiserer vi den og legger inn nye par etter behov. En typisk anvendelse er å lagre data som vi leser fra fil og hvor én kolonne er en entydig identifikator. Initialise en ny oppslagstabell # Lag en tom tabell: d = {} # Lag en tabell med to par: d = {'Oslo': 13, 'London': 15.4} # Lag en tabell med to par (alternativ metode): d = dict(oslo=13, London=15.4) Utvide en oppslagstabell # Anta at d er en oppslagstabell # Legg et nytt par til d: d['madrid'] = 26.0 # Legg en hel oppslagstabell d2 til d: d.update(d2)
Å fjerne par fra oppslagstabeller Vi kan fjerne et par (a,b) fra en oppslagstabell med del d[a]. Eksempler: In [1]: d = {'pi':3.14, 'e':2.718, 'g':9.81} In [2]: del d['e'] # Fjern paret ('e', 2.718) In [3]: del d['pi'] # Fjern paret ('pi', 3.14) In [4]: d = dict(yes=1, No=0) In [5]: del d['maybe'] ---------------------------------------------------------------------- KeyError Traceback (most recent call <ipython-input-37-759d96d71ff6> in <module>() ----> 1 del d['maybe'] KeyError: 'Maybe'
Er en bestemt nøkkel tilstede i tabellen? Vi kan teste om en nøkkel er tilstede med key in d. Vi kan hente ut verdien med d.get(key) Eksempel: d = {'Bergen': 'Norway', 'Cambridge': 'UK'} if 'Oxford' in d: print('oxford is a key in the dictionary') else: print('oxford is not a key in the dictionary') value = d.get('bergen', '') value = d.get('oxford', '') # Now value == 'Norway' # Now value = ''
Å løpe gjennom elementene i en oppslagstabell Løpe over elementene i vilkårlig rekkefølge: d = {-2:6, 6:3, 3:7} for key in d: print('key = %g and value = %g' % (key, d[key])) Løpe over elementene i sortert nøkkelrekkefølge: d = {-2:6, 6:3, 3:7} for key in sorted(d): print('key = %g and value = %g' % (key, d[key]))
Lage en liste over alle nøkler/verdier Anta at vi har definert tabellen d = {'Paris': 17.5, 'London': 15.4, 'Madrid': 26.0} >>> d.keys() <listiterator at 0x111b2a4d0> >>> list(d.keys()) ['Paris', 'London', 'Madrid'] >>> d.values() <listiterator at 0x111b2a710> >>> list(d.values()) [17.5, 15.4, 26.0]
Eksempel: lagre polynom med oppslagstabell Polynomet p(x) = 1 + x 2 + 3x 7 kan representeres som oppslagstabell med potenser som nøkler (int) og koeffisienter som verdier (float): p = {0: -1, 2: 1, 7: 3.5} Evaluér polynomet i I c ix i for gitt x: def f(p,x): val = 0.0 for power in p: val += p[power] * x ** power return val Kortversjon: def f2(p,x): return sum(p[power] * x ** power for power in p)
Polynomer kan også representeres med lister Listeindeksen svarer her til potensen. F.eks. kan polynomet 1 + x 2 + 3x 7 lagres som p = [-1, 0, 1, 0, 0, 0, 0, 3] Det generelle polynomet N i=0 c ix i lagres som: [c0, c1, c2,..., cn]. Evaluér polynomet N i=0 c ix i for gitt x: def f3(p, x): val = 0 for power in range(len(p)): val += p[power] * x ** power return val
Hva er best for polynomer: liste eller oppslagstabell? Oppslagstabellen lagrer bare koeffisienter ulik null. Sammenlikn tabell og liste for polynomet 1 x 200 : p = {0: 1, 200: -1} # len(p) er 2 p = [1, 0, 0, 0,..., 200] # len(p) er 201 Oppslagstabeller kan enkelt håndtere negative potenser, f.eks. 1 2 x 3 + 2x 4 p = {-3: 0.5, 4: 2} print f(p, x=4)
Eksempel: lese en fil med to kolonner Mange datafiler består av kolonner med data, slik som denne hvor første kolonne er en entydig pasient-id og andre kolonne er et mål på DNA-skade: MB.0000 0.00096 MB.0002 0.24787 MB.0005 0.2779 MB.0006 0.29428 MB.0010 0.61225...... Vi kan lese filen over inn i en oppslagstabell slik: damage = {} infile = open('dnadamage.txt', 'r') for line in infile: words = line.split() damage[words[0]] = float(words[1]) infile.close() # For å skrive ut DNA-skade for pasient MB.0005: print(damage['mb.0005']) # 0.00096
Eksempel: lese en fil med tre kolonner Datafil: Oslo 21.8 'Norway' Bergen 17.6 'Norway' London 18.1 'UK' Berlin 19 'Germany' Paris 23 'France' Rome 26 'Italy' Helsinki 17.8 'Finland' Program: infile = open('cityinfo.txt', 'r') data = {} for line in infile: words = line.split() data[words[0]] = [float(words[1]), words[2]] infile.close() # For å skrive ut informasjonen om Paris: print(data['paris']) # [23.0, "'France'"] print(data['paris'][0]) # 23.0 print(data['paris'][1]) # 'France'
En tabulær fil kan leses inn i en nestet oppslagstabell Datafilen table.dat: A B C D 1 11.7 0.035 2017 99.1 2 9.2 0.037 2019 101.2 3 12.2 no no 105.2 4 10.1 0.031 no 102.1 5 9.1 0.033 2009 103.3 6 8.7 0.036 2015 101.9 Vi lager først en tom oppslagstabell rows For hver rad Vi kan lage en oppslagstabell data[p][i] (dict of dict) for å holde måling no. i (1, 2, etc.) for egenskap p ('A', 'B', etc.)
Quiz 1 Hva skrives ut? Spørsmål A d = {-2:-1, -1:0, 0:1, 1:2, 2:-2} print(d[0]) Spørsmål B d = {-2:-1, -1:0, 0:1, 1:2, 2:-2} print(d[d[0]]) Spørsmål C d = {-2:-1, -1:0, 0:1, 1:2, 2:-2} print(d[-2]*d[2])
Svar på Quiz 1 Hva skrives ut? Spørsmål A d = {-2:-1, -1:0, 0:1, 1:2, 2:-2} print(d[0]) # 1 Spørsmål B d = {-2:-1, -1:0, 0:1, 1:2, 2:-2} print(d[d[0]]) # 2 Spørsmål C d = {-2:-1, -1:0, 0:1, 1:2, 2:-2} print(d[-2]*d[2]) # 2
Quiz 2 Hva skrives ut? Spørsmål A table = {'age':[35,20], 'name':['anna','peter']} for key in table: print('%s: %s' % (key,table[key])) Spørsmål B table = {'age':[35,20], 'name':['anna','peter']} vals = list(table.values()) print(vals) print(vals[0]) print(vals[0][0]) Spørsmål C table = {'age':[35,20], 'name':['anna','peter']} print(table['name'][1], table['age'][1])
Svar på Quiz 2 Spørsmål A table = {'age':[35,20], 'name':['anna','peter']} for key in table: print('%s: %s' % (key,table[key])) # age: [35, 20] # name: ['Anna', 'Peter'] Spørsmål B table = {'age':[35,20], 'name':['anna','peter']} vals = list(table.values()) print(vals) print(vals[0]) print(vals[0][0]) # [[35, 20], ['Anna', 'Peter']] # [35, 20] # 35 Spørsmål C table = {'age':[35,20], 'name':['anna','peter']} print(table['name'][1], table['age'][1]) # ('Peter', 20)
Quiz 3 Hva blir innholdet i oppslagstabellen d? Spørsmål A d = {3:5, 6:7} e = {4:6, 7:8} d.update(e) Spørsmål B d = {3:5, 6:7} e = {4:6, 7:8} d.update(e) d.update(e) Spørsmål C d = {6:100} e = {6:6, 7:8} d.update(e)
Svar på Quiz 3 Hva blir innholdet i oppslagstabellen d? Spørsmål A d = {3:5, 6:7} e = {4:6, 7:8} d.update(e) # {3: 5, 4: 6, 6: 7, 7: 8} Spørsmål B d = {3:5, 6:7} e = {4:6, 7:8} d.update(e) d.update(e) # {3: 5, 4: 6, 6: 7, 7: 8} Spørsmål C d = {6:100} e = {6:6, 7:8} d.update(e) # {6: 6, 7: 8}
Quiz 4 Filen teledata.txt inneholder info om mobilkunder: Age Income Gender Monthly calls ID 45 720k Female 46 A001 27 440k Male 3 A002 17 0 Male 52 A006 24 60k Female 18 A014............... Hvordan kan du lagre dataene som fem lister? Hvordan kan du lagre dataene som én liste? Hvordan kan du lagre dataene som en oppslagstabell?
Svar på Quiz 4 # Fem lister: # ['Age', 45, 27,...] # ['Income', '720k', '440k',...] # ['Gender', 'Female', 'Male',...] # ['Monthly calls', 46, 3,...] # ['ID', 'A001', 'A002',...] # En liste: # [['Age',45,27,...], ['Income','720k', '440k',...], osv] # En oppslagstabell: # {'Age':[45,27,17,...], 'Income':[720k,440k,0,...], osv}
Tekstbehandling Vi har sett at Python er velegnet for matematiske beregninger og visualiseringer. Python er også et effektivt verktøy for å håndtere tekststringer. Mange avanserte anvendelser av tekststreng-behandling (f.eks. nettsøk og DNA-analyser) involverer matematiske og statistiske beregninger.
Eksempel: internettsøk Google og andre nettsøkmetoder benytter avansert tekstbehandling. Crawlers går gjennom WWW for å finne filer og analysere innholdet deres.
Eksempel: DNA-analyser DNA-sekvenser kan representeres som veldig lange tekststrenger. Algoritmer for å finne og sammenlikne mønstre i slike tekststrenger er svært viktige i moderne biologi og medisin.
Tekstbehandling - kjapp repetisjon s = 'This is a string, ok?' # To split a string into individual words: s.split() # ['This', 'is', 'a', 'string,', 'ok?'] # To split a string with another delimiter s.split(',') # ['This is a string', ' ok?'] s.split('a string') # ['This is ', ', ok?'] # To find the location of a substring: s.index('is') # 2 # To check if a string contains a substring: 'This' in s # True 'this' in s # False # To select a particular character in a string: s[0] # 'T' s[1] # 'h' s[2] # 'i' s[3] # 's'
Ekstraher delstrenger s = 'This is a string, ok?' # Remove the first character s[1:] # 'his is a string, ok?' # Remove the first and the last character s[1:-1] # 'his is a string, ok' # Remove the two first and two last characters s[2:-2] # 'is is a string, o' # The characters with index 2,3,4 s[2:5] # 'is ' # Select everything starting from a substring s[s.index('is a'):] # 'is a string, ok?' # Remove trailing blanks s = ' A B C ' s.strip() # 'A B C' s.lstrip() # 'A B C ' s.rstrip() # ' A B C'
Konkatener (slå sammen) tekststrenger a = ['I', 'am', 'happy'] # Join list elements ''.join(a) # 'Iamhappy' # Join list elements with space between them ' '.join(a) # 'I am happy' # Join list elements with '%%' between them '%%'.join(a) # 'I%%am%%happy'
Substituer delstrenger s = 'This is a string, ok?' # Replace every blank by 'X' s.replace(' ', 'X') # 'ThisXisXaXstring,Xok?' # Replace one word by another s.replace('string', 'text') # 'This is a text, ok?' # Replace the text before the comma by 'Fine' s.replace(s[:s.index(',')], 'Fine') # 'Fine, ok?' # Replace the text from the comma by ' dummy' s.replace(s[s.index(','):], ' dummy') # 'This is a string dummy'
Lineavbrekk i tekststrenger Linjer separeres av ulike kontrolltegn på ulike plattformer. # Concatenate with Unix/Linux/Mac type line break s1 = '\n'.join(['line A', 'Line B', 'Line C']) # Concatenate with Windows type line break s2 = '\r\n'.join(['line A', 'Line B', 'Line C']) # Platform dependent line splitting: s1.split('\n') # Works: ['Line A', 'Line B', 'Line C'] s1.split('\r\n') # FAILS: ['Line A\nLine B\nLine C'] s2.split('\n') s2.split('\r\n') # FAILS: ['Line A\r', 'Line B\r', 'Line C'] # Works: ['Line A', 'Line B', 'Line C'] # Better line splitting (platform independent): s1.splitlines() # Works: ['Line A', 'Line B', 'Line C'] s2.splitlines() # Works: ['Line A', 'Line B', 'Line C']
Noen flere tekstfunksjoner # Check if a string only contains digits s = '314' s.isdigit() # True s = ' 314' s.isdigit() # False s = '3.14' s.isdigit() # False # Change to lower-case or upper-case s = 'ABC def' s.lower() # 'abc def' s.upper() # 'ABC DEF' # Starts with and ends with substring s = 'This is a string' s.startswith('this is') # True s.endswith('this is') # False
Eksempel Anta at vi ønsker å lese par med data (x,y) fra en fil. Eksempelfil: (1.3,0) (-1,2) (3,-1.5) (0,1) (1,0) (1,1) (0,-0.01) (10.5,-1) (2.5,-2.5) Algoritme: 1 Les en linje av gangen 2 For hver linje splittes linjen opp i ord 3 For hvert ord, fjern parenteres og splitte resten på komma
Hvordan gjøre det i Python infile = open('pairs.dat', 'r') pairs = [] # Create a list to hold all the pairs for line in infile: words = line.split() for w in words: w = w[1:-1] # Remove parentheses numbers = w.split(',') pair = (float(numbers[0]), float(numbers[1])) pairs.append(pair)
Listen av par [(1.3, 0.0), (-1.0, 2.0), (3.0, -1.5), (0.0, 1.0), (1.0, 0.0), (1.0, 1.0), (0.0, -0.01), (10.5, -1.0), (2.5, -2.5)]
Nettsider er bare tekstfiler Teksten er en blanding av HTML-kommandoer og teksten som vises i nettleseren: <html> <body bgcolor="orange"> <h1>a Very Simple Web Page</h1> <!-- headline --> Ordinary text is written as ordinary text, but when we need headlines, lists, <ul> <li><em>emphasized words</em>, or <li> <b>boldfaced words</b>, </ul> we need to embed the text inside HTML tags. We can also insert GIF or PNG images, taken from other Internet sites, if desired. <hr> <!-- horizontal line --> <img src="http://www.simula.no/simula_logo.gif"> </body> </html>
Programmer kan ekstrahere data fra nettsider Et program kan laste ned en nettside, som en HTML-fil, og ekstrahere data ved å isolere den relevante teksten i filen. Eksempel: climate data from the UK Vi laster ned oxforddata.txt til en lokal fil Oxford.txt: import urllib baseurl = 'http://www.metoffice.gov.uk/climate/uk/stationdata' filename = 'oxforddata.txt' url = baseurl + '/' + filename urllib.urlretrieve(url, filename='oxford.txt')
Strukturen til klimadatafilen Oxford.txt Oxford Location: 4509E 2072N, 63 metres amsl Estimated data is marked with a * after the value. Missing data (more than 2 days missing in month) is marked by ---. Sunshine data taken from an automatic... yyyy mm tmax tmin af rain sun degc degc days mm hours 1853 1 8.4 2.7 4 62.8 --- 1853 2 3.2-1.8 19 29.3 --- 1853 3 7.7-0.6 20 25.9 --- 1853 4 12.6 4.5 0 60.1 --- 1853 5 16.8 6.1 0 59.5 ---... 2010 5 17.6 7.3 0 28.6 207.4 2010 6 23.0 11.1 0 34.5 230.5 2010 7 23.3* 14.1* 0* 24.4* 184.4* Provisional 2010 10 14.6 7.4 2 43.5 128.8 Provisional
Lese klimadataene Algorithm: 1 Read the place and location in the file header 2 Skip the next 5 (for us uninteresting) lines 3 Read the column data and store in dictionary 4 Test for numbers with special annotation, "provisional" column, etc. Program, del 1: local_file = 'Oxford.txt' infile = open(local_file, 'r') data = {} data['place'] = infile.readline().strip() data['location'] = infile.readline().strip() # Skip the next 5 lines for i in range(5): infile.readline()
Lese klimadata - del 2 Program, part 2: data['data'] ={} for line in infile: columns = line.split() year = int(columns[0]) month = int(columns[1]) if columns[-1] == 'Provisional': del columns[-1] for i in range(2, len(columns)): if columns[i] == '---': columns[i] = None elif columns[i][-1] == '*' or columns[i][-1] == '#': # Strip off trailing character columns[i] = float(columns[i][:-1]) else: columns[i] = float(columns[i])
Lese klimadataene - del 3 Program, part 3 for line in infile:... tmax, tmin, air_frost, rain, sun = columns[2:] if not year in data['data']: data['data'][year] = {} data['data'][year][month] = {'tmax': tmax, 'tmin': tmin, 'air frost': air_frost, 'sun': sun}