# --- # jupyter: # jupytext: # formats: ipynb,py:percent # text_representation: # extension: .py # format_name: percent # format_version: '1.3' # jupytext_version: 1.14.0 # kernelspec: # display_name: Python 3 (ipykernel) # language: python # name: python3 # --- # %% [markdown] toc=true slideshow={"slide_type": "notes"} #

Table of Contents

#
# %% [markdown] slideshow={"slide_type": "slide"} # # Fondamenti di Programmazione # # # **Andrea Sterbini** # # lezione 11 - 10 novembre 2022 # %% [markdown] slideshow={"slide_type": "subslide"} # # RECAP: Files # # - **`with open(, mode=, encoding='utf8') as F: # ...`** # - **`F.read()`** legge tutto il contenuto # - **`F.readline()`** legge UNA riga (compreso **`'\n'`**) # - **`F.readlines()`** legge TUTTE le righe (compresi **`'\n'`**) # - **`F.write()`** scrive un testo nel file (dovete aggiungere voi '\n') # - **`print( ..., file=F)`** stampa nel file (molto comodo) # - **`F.seek(0)`** torna alla posizione 0 (inizio) # %% [markdown] slideshow={"slide_type": "subslide"} # # Ricerca delle parole più importanti in un gruppo di documenti # - per ogni documento # - carichiamo le parole # - le contiamo # - ne calcoliamo la frequenza percentuale # %% [markdown] slideshow={"slide_type": "subslide"} # ## Ricerca del file più importante dato un gruppo di parole # Le parole più frequenti potrebbero essere troppo comuni e poco significative # (con poco contenuto semantico) # # Quindi una parola sarà "interessante" se **appare spesso in un file** ma **appare in pochi file** # # Vedi https://en.wikipedia.org/wiki/tf-idf # %% [markdown] slideshow={"slide_type": "subslide"} # ## Ci serve la frequenza di una parola in ciascun file (tf = term frequency) # - per ciascun file # - contiamo le parole # - dividiamo per il numero totale di parole # %%% analisi del testo e ricerca slideshow={"slide_type": "subslide"} # ho un elenco di files con il loro encoding, ottenuto unzippando files.zip files = { 'files/holmes.txt' : 'utf-8-sig', 'files/alice.txt' : 'utf-8-sig', 'files/frankenstein.txt' : 'utf-8-sig', 'files/alice_it.txt' : 'latin', 'files/prince.txt' : 'utf-8-sig' } # %%%% - estrazione delle parole (rimpiazzando i nonalpha con spazi) slideshow={"slide_type": "subslide"} # per estrarre le parole da un file che contiene anche segni di interpunzione def estrai_parole(filename, encoding): # apro il file e ne leggo il contenuto with open(filename, encoding=encoding) as FILE: text = FILE.read() # se voglio lo converto in lowercase (ma potrei preferire di no) text = text.lower() # calcolo l'insieme dei caratteri presenti nel testo caratteri = set(text) # ne estraggo solo i caratteri non-alpha nonalfa = [ c for c in caratteri if not c.isalpha() ] # li rimpiazzo con spazio for c in nonalfa: text = text.replace(c, ' ') # uso split sul testo in cui ho eliminato tutti i non-apha return text.split() # esempio: parole del libro Alice nel paese delle meraviglie parole = estrai_parole('files/alice_it.txt', 'latin') ### come usare meno memoria? lavorando riga per riga parole[:30] # %%%% - mi serve l'importanza di una parola in un testo slideshow={"slide_type": "subslide"} # per contare quante sono le parole def conta_parole(parole): # trovo le parole uniche usando un insieme parole_uniche = set(parole) # e costruisco il dizionario parola:conteggio usando count ### INEFFICIENTE!!! N = len(parole) return { parola: parole.count(parola)/N for parola in parole_uniche } # rifaccio i conti sulle parole di Alice conteggi = conta_parole(parole) #print(list(conteggi.items())[:20]) # le 50 parole più presenti in Alice sono: più_presenti = sorted(conteggi, reverse=True, key=lambda K: conteggi[K])[:50] list(più_presenti) # %%%% per ogni file costruiamo il dizionario delle frequenze delle sua parole slideshow={"slide_type": "subslide"} # frequenza delle parole nel file iesimo # - tf_i = # presenze / # parole del file # per tutti i file costruiamo il dizionario del numero di files in cui appaiono frequenze = {} # conteggio delle parole nei file: frequenze[name][parola] -> % di parole # conto le frequenze file per file for filename, encoding in files.items(): print(filename) parole = estrai_parole(filename, encoding) conteggio = conta_parole(parole) frequenze[filename] = conteggio # %% [markdown] slideshow={"slide_type": "subslide"} # ## e l'inverso della presenza della parola nei file (idf = inverse document frequency) # %%%% per ogni file costruiamo il dizionario delle frequenze delle sua parole slideshow={"slide_type": "subslide"} # conto i file che contengono una parola # presenze[parola] -> # di file che la contengono presenze = {} for filename in files: for parola in frequenze[filename]: presenze[parola] = presenze.get(parola, 0) + 1 # divido per il numero di file per avere la presenza % Nfile = len(files) for parola in presenze: presenze[parola] /= Nfile list(presenze.items())[:30] # %%%% - importanza delle parole nei idf file = tf_i * slideshow={"slide_type": "subslide"} from math import log # log dell'inverso della frequenza nei documenti # - idf = log( # di file / # di file che contengono la parola ) idf = { parola : log(1/quante) for parola,quante in presenze.items() } list(idf.items())[:30] # NOTA: ogni parola appare in almeno UN file quindi 1/N è sempre calcolabile # %%%% - importanza delle parole nei idf file = tf_i * slideshow={"slide_type": "subslide"} # e finalmente posso vedere quale file "pesa di più" rispetto alle parole della query # date le parole di una query # per ciascun file # ne conto la frequenza nel file # la moltiplico per idf # sommando ottendo il "peso" del file per quella query query = [ 'turtle', 'alice'] pesi = {} for file in frequenze: peso = 0 for p in query: # aggiungo il peso della parola (0 se non presente) peso += frequenze[file].get(p,0) * idf[p] pesi[file] = peso print(f"{file:30} {peso:00.3}") # ordino i file per peso decrescente sorted(pesi.items(), reverse=True, key=lambda coppia: coppia[1]) # %% LUNEDI': json, http requests e analisi del testo e ricerca [markdown] slideshow={"slide_type": "slide"} # # Altri tipi di file: [JSON](https://docs.python.org/3/library/json.html) # File di testo con sintassi semplificata per rappresentare: # - dizionari # - liste # - interi, stringhe, float, bool, None # # Molto usati nelle applicazioni WEB # %% slideshow={"slide_type": "subslide"} ## Esempio import json with open('api.github.com.json') as F: api = json.load(F) # !cat api.github.com.json # %% LUNEDI': json, http requests e analisi del testo e ricerca [markdown] slideshow={"slide_type": "subslide"} # ## Attenzione: # - NON si possono inserire commenti # - NO virgola aggiuntiva in fondo a lista o dizionario # - SOLO chiavi semplici (NO tuple, SI int, float, str, none, bool) # - SOLO doppi apici **`"`** (NO singoli apici) # %% [markdown] slideshow={"slide_type": "subslide"} # ## Abbastanza facili da usare in python # Conversione automatica per alcuni tipi di dati # - **`json.load() -> dict | list | tipo_base`** # - **`json.dump(, )`** # # Personalizzabile anche per altri tipi di oggetti (non ovvio) # %% slideshow={"slide_type": "subslide"} # posso anche leggere da una stringa in formato JSON XX = json.loads(''' [{"nome": "Minnie", "cognome": "Mouse", "telefono": "555-54321", "indirizzo": "via di M.me Curie 1", "città": "Topolinia"}, {"nome": "Pippo", "cognome": "de' Pippis", "telefono": "555-33333", "indirizzo": "via dei Pioppi 1", "città": "Topolinia"}] ''') print(XX) # oppure produrre la stringa in formato JSON da una struttura Python print(json.dumps(XX)) # %% slideshow={"slide_type": "subslide"} # o produrre una stringa json.dumps(XX) # %% slideshow={"slide_type": "subslide"} # Un file json può contenere valori semplici (int, float, str, True=true, False=false, None=null) print(json.dumps(None)) print(json.dumps([False, True])) ## NOTA: le stringhe DEVONO essere racchiuse da doppi apici print(json.dumps('Pape"rino')) # %%% file json slideshow={"slide_type": "subslide"} # Esempio: data una tabella come lista di dizionari agenda = [ {'nome': 'Paperino','cognome':'Paolino', 'telefono':'555-1313', 'indirizzo': 'via dei Peri 113', 'città': 'Paperopoli'}, {'nome': 'Gastone', 'cognome':'Paperone', 'telefono':'555-1717', 'indirizzo': 'via dei Baobab 42', 'città': 'Paperopoli'}, {'nome': 'Paperon', 'cognome':"de' Paperoni", 'telefono':'555-99999', 'indirizzo': 'colle Papero 1', 'città': 'Paperopoli'}, {'nome': 'Archimede','cognome':'Pitagorico', 'telefono':'555-11235', 'indirizzo': 'colle degli Inventori 1', 'città': 'Paperopoli'}, {'nome': 'Pietro', 'cognome':'Gambadilegno', 'telefono':'555-66666', 'indirizzo': 'via dei Ladri 13', 'città': 'Topolinia'}, {'nome': 'Trudy', 'cognome':'Gambadilegno', 'telefono':'555-66666', 'indirizzo': 'via dei Ladri 13', 'città': 'Topolinia'}, {'nome': 'Topolino','cognome':'Mouse', 'telefono':'555-12345', 'indirizzo': 'via degli Investigatori 1', 'città': 'Topolinia'}, {'nome': 'Minnie', 'cognome':'Mouse', 'telefono':'555-54321', 'indirizzo': 'via di M.me Curie 1', 'città': 'Topolinia'}, {'nome': 'Pippo', 'cognome':"de' Pippis", 'telefono':'555-33333', 'indirizzo': 'via dei Pioppi 1', 'città': 'Topolinia'}, ] # %%% file json slideshow={"slide_type": "subslide"} import json # prima la salvo nel file with open('agenda.json', encoding='utf8', mode='w') as F: json.dump(agenda, F) # !cat agenda.json # %%% file json slideshow={"slide_type": "subslide"} # poi la ricarico with open('agenda.json', encoding='utf8') as F: L1 = json.load(F) L1[:2] # e ne mostro i primi 2 elementi # %% [markdown] slideshow={"slide_type": "subslide"} # # File [YAML](https://pyyaml.org/wiki/PyYAMLDocumentation) (un altro modo di rappresentare dati annidati in testo semplice) # - dizionari (una **chiave `:` valore** per ciascuna riga) # - liste ( valori su righe diverse, preceduti da **`-`**) # - dati semplici (str senza apici se possibile, True, False, null=None, interi, float) # - documenti multipli # - generici oggetti Python (advanced) # # Per annidare le strutture si usa l'**indentazione** # %% slideshow={"slide_type": "subslide"} import yaml with open('agenda.yaml', mode='w', encoding='utf8') as F: yaml.dump(agenda[:2], F) # !cat agenda.yaml # %% [markdown] slideshow={"slide_type": "subslide"} # ## leggere/scrivere file YAML # - **`yaml.dump(, FILE)`** salva l'oggetto nel file aperto con open # - **`yaml.safe_load(FILE) -> `** legge l'oggetto dal file aperto con open # %% slideshow={"slide_type": "subslide"} ## posso decodificare direttamente del testo testo = """ none: [~, null] bool: [true, false, on, off] int: 42 float: 3.14159 list: - LITE - RES_ACID - SUS_DEXT dict: hp: 13 sp: 5 """ yaml.safe_load(testo) # %% slideshow={"slide_type": "subslide"} with open('esempio.yaml', mode='w', encoding='utf8') as F: print(testo, file=F) # !cat esempio.yaml # oppure leggerlo direttamente da file with open('esempio.yaml') as F: EX = yaml.safe_load(F) EX # %% [markdown] slideshow={"slide_type": "subslide"} # # Leggere pagine o file da Internet # Ci sono molte librerie, la più comune è **[requests](https://requests.readthedocs.io/en/latest/user/quickstart/)** # - permette di eseguire sia **GET** che **POST** # - con parametri # - torna un codice di errore/OK # - e decodifica il testo della pagina html o json # # Molto potente, permette anche streaming, sessioni, ... # %%% HTTP requests slideshow={"slide_type": "subslide"} # importo la libreria import requests # leggo la pagina di python.org pagina = requests.get('https://python.org') # lo status code ci dice se tutto è andato bene status = pagina.status_code # se è un testo nell'attributo text trovo il testo decodificato contenuto = pagina.text status, pagina.encoding, contenuto[:100] # %% [markdown] slideshow={"slide_type": "subslide"} # ## le pagine JSON vengono automaticamente decodificate # %%% HTTP requests slideshow={"slide_type": "fragment"} # leggo da internet una pagina JSON pagina_json = requests.get('https://api.github.com') # la decodifica avviene automaticamente col metodo json() risultato = pagina_json.json() risultato # %% [markdown] slideshow={"slide_type": "slide"} # ## Scaricare file binari # - il contenuto "raw" è nell'attributo **`content`** della risposta # %%%% leggo una immagine da https://www.python.org/static/img/python-logo@2x.png run_control={"marked": false} slideshow={"slide_type": "fragment"} ## possiamo anche scaricare file binari logo = requests.get('https://www.python.org/static/img/python-logo@2x.png') # posso estrarre dagli headers le dimensioni e il tipo del file () print(logo.headers['Content-Type'], logo.headers['Content-Length']) # se si tratta di un file di dati (immagine/audio/film ...) # il contenuto lo trovo nell'attributo content dati = logo.content # per salvarlo devo aprire il file in scrittura ('w') e in modo binario ('b') with open('logo.png', mode='wb') as F: F.write(dati) # %% [markdown] slideshow={"slide_type": "fragment"} # ![](logo.png) # %% [markdown] slideshow={"slide_type": "subslide"} # ## Passare parametri ad una GET # Si usa l'argomento **`params`** # - **`parametri={ chiave: valore, ...}`** # - **`requests.get( , params=parametri )`** # %% slideshow={"slide_type": "subslide"} # Search GitHub's repositories for requests response = requests.get( #'https://api.github.com/search/repositories', 'https://google.com', params={'q': 'requests+language:python'}, ) # response.json() response.text[:1000] # %% [markdown] slideshow={"slide_type": "subslide"} # ## Passare parametri ad altri metodi HTTP # Si usa l'argomento **`data=`** # ```python # requests.post( 'https://httpbin.org/post', # data={'key':'value'}) # requests.put( 'https://httpbin.org/put', # data={'key':'value'}) # requests.delete( 'https://httpbin.org/delete') # requests.head( 'https://httpbin.org/get') # requests.patch( 'https://httpbin.org/patch', # data={'key':'value'}) # requests.options('https://httpbin.org/get') # ```