# %% RECAP import json # with open(filename, mode=..., encoding='utf8') as FILE: # lettura/scrittura di file JSON con la libreria json # json.load(FILE) # lettura di pagine web o file binari da web con la libreria requests # %%% analisi del testo e ricerca # 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) # 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') # --- FIXME: come usare meno memoria? lavorando riga per riga # %%% - quante volte appare una parola nel testo in percentuale # più rapido: scansione unica e costruzione incrementale del dizionario def conta_parole(parole): risultato = {} for parola in parole: if parola in risultato: risultato[parola] += 1 else: risultato[parola] = 1 N = len(parole) # divido ciascun valore per N per avere la percentuale di presenza della parola for chiave in risultato: risultato[chiave] /= N return risultato # rifaccio i conti sulle parole di Alice conteggi = conta_parole(parole) # le 50 parole più presenti sono: più_presenti = sorted(conteggi.items(), reverse=True, key=lambda X: X[1])[:50] # %% Importanza delle parole nei file # --- importanza = tf_i * idf # frequenza del termine nel file iesimo # tf_i = # presenze / # parole del file # inverso della frequenza del termine nei documenti # idf = log( # di file / # di file che contengono la parola ) # vedi https://en.wikipedia.org/wiki/Tf%E2%80%93idf # %%% Cosa ci serve # --- per ogni file la frequenza percentuale di una parola # --- per ogni parola il numero percentuale di file in cui appare # per ogni file costruiamo il dizionario delle frequenze% delle sua parole # costruiamo il dizionario delle parole del numero% di files in cui appaiono frequenze = {} # conteggio delle parole nei file: frequenze[name][parola] -> % di parole presenze = {} # conteggio dei file che contengono una parola: presenze[parola] -> % di file # riempio i due dizionari con i dati per ciascun file # le frequenze% delle parole di quel file # le presenze% delle parole in tutti i file for filename, encoding in files.items(): parole = estrai_parole(filename, encoding) freq = conta_parole(parole) frequenze[filename] = freq for p in freq: if p in presenze: presenze[p] += 1 else: presenze[p] = 1 # idf è il logaritmo dell'inverso della presenza% from math import log N = len(files) for p in presenze: presenze[p] = log(N/presenze[p]) # %%% per cercare i file che meglio corrispondono ad un gruppo di parole # per calcolare il peso di una parola in un file # tf lo ottengo da frequenze[file][parola] # idf lo ottengo da presenze[parola] #def peso_parola(parola, filename): # return (presenze[parola] * # (frequenze[filename][parola] if parola in frequenze[filename] else 0)) def peso_parola(parola, filename): # dict.get(key, default) torna il default se la key non è nel dict, altrimenti il valore associato return presenze[parola] * frequenze[filename].get(parola, 0) # il peso di una query è la somma dei pesi della query def peso_query(lista_parole, filename): return sum([ peso_parola(p, filename) for p in lista_parole ]) # per cercare i file che meglio corrispondono ad un gruppo di parole # per ogni file calcolo il peso di quelle parole per quel file # ordino i file per peso decrescente # ritorno la lista di file con i pesi def query(lista_parole): pesi = { filename: peso_query(lista_parole, filename) for filename in files } def criterio_di_ordinamento(coppia): return coppia[1] return sorted( pesi.items(), reverse=True, key=criterio_di_ordinamento ) #return sorted( pesi.items(), reverse=True, key=lambda file_peso: file_peso[1] ) ricerca = [ 'monster', 'blood' ] risultato = query(ricerca) # il migliore è Frankenstein, seguito da Sherlock Holmes r2 = [ 'hatter', 'mad'] ris2 = query(r2) # il migliore è alice in inglese seguito da Sherlock Holmes e Frankenstein ######################################################################################### # %% IMMAGINI come matrici di pixel import images # %%% matrici di pixel: liste di liste di pixel # %%% pixel: tuple di 3 interi R,G,B ovvero le luminosità di Red,Green,Blue # con valori tra 0 e 255 compresi (1 byte) # definiamo qualche colore black = 0,0,0 white = 255, 255, 255 red = 255, 0, 0 green = 0, 255, 0 blue = 0, 0, 255 cyan = 0, 255, 255 yellow= 255,255, 0 purple= 255, 0, 255 # %%% costruire una immagine WxH di un dato colore # --- modo sbagliato def crea_immagine_errata(larghezza, altezza, colore): return [[ colore ]*larghezza ] * altezza img2 = crea_immagine_errata(30, 40, blue) img2[5][7] = red # --- modo giusto def crea_immagine(larghezza, altezza, colore=black): return [ [ colore ]*larghezza for i in range(altezza) ] img = crea_immagine(30, 40, red) img[5][7] = blue # %%% caricare e salvare una immagine con la libreria images img3 = images.load('3cime.png') img3[40][3:7] = [red]*4 images.save(img3, '3cime-2.png') # %%% disegnare sulla immagine # %%% evitare di sbordare # --- controllando le posizioni def draw_pixel2(img, x, y, colore): altezza = len(img) larghezza = len(img[0]) if 0 <= x < larghezza and 0 <= y < altezza: img[y][x] = colore # --- usando try-except per catturare l'errore def draw_pixel(img, x, y, colore): try: img[y][x] = colore except IndexError: pass # --- BEWARE of negative indexes!!! (che non producono errori) # --- BEWARE of generic 'catch-all' except clauses!!!!! (che nascondono TROPPI errori) draw_pixel2(img3, 2000, 400, cyan) # %%% ruotare una immagine di 90° in senso anti/orario # X_destinazione = y_sorgente # Y_destinazione = larghezza_sorgente - 1 - x_sorgente def ruota_sx(img): altezza = len(img) larghezza = len(img[0]) img_destinazione = crea_immagine(altezza, larghezza) # per ogni pixel della immagine originale for x in range(larghezza): for y in range(altezza): # calcolo le coordinate della destinazione X = y Y = larghezza - 1 - x # e copio il pixel img_destinazione[Y][X] = img[y][x] return img_destinazione img_r = ruota_sx(img3) # --- PER CASA: rotazione destra # %%% Disegnare una retta orizzontale/verticale def draw_h_line(img, x, y, x2, colore): for X in range(x, x2+1): draw_pixel2(img, X, y, colore) def draw_v_line(img, x, y, y2, colore): larghezza = len(img[0]) y_inizio = min(max(y,0), larghezza-1) y_fine = max(min(y2, larghezza-1),0) for Y in range(y_inizio, y_fine): img[Y][x] = colore # --- e diagonale??? come??? # dipende dalla direzione def draw_slope(img, x1, y1, x2, y2, colore): dx = x2-x1 dy = y2-y1 # si varia lungo la direzione più lunga # se dx > dy ci calcola la x per ciascuna x if abs(dx) >= abs(dy): m = dy/dx for x in range(x1, x2+1): y = int(round(m*x + y1)) img[y][x] = colore # altrimenti per ciascuna y calcoliamo la x else: # TODO: scandiamo l'immagine per y crescenti # semplicemente scambiando x e y m1 = dy/dx for y in range(y1, y2+1): x = int(round(m1*y + x1)) img[y][x] = colore # FIXME: e se dx == 0 oppure dy == 0? disegnamo una retta verticale o orizzontale # FIXME: e se dx < 0 oppure dy < 0? scambiamo i due punti se: # dx < 0 e andiamo in orizzontale # dy < 0 e andiamo in verticale draw_slope(img_r, 10,20, 100, 90, green) # %%% Disegnare un rettangolo/quadrato pieno # questa è facile def draw_rectangle(img, x1,y1, x2,y2, colore): for x in range(x1, x2+1): for y in range(y1, y2+1): draw_pixel2(img, x,y, colore) draw_rectangle(img_r, 30,50, 80, 120, purple) # %%% disegnare una scacchiera # %%% disegnare una ellisse/cerchio pieno # --- con somma delle distanze dai fuochi <= R from math import sqrt def draw_ellisse(img, x1, y1, x2, y2, D, colore): larghezza = len(img[0]) altezza = len(img) for y in range(altezza): for x in range(larghezza): D1 = sqrt((x-x1)**2 + (y-y1)**2) # distanza dal primo fuoco D2 = sqrt((x-x2)**2 + (y-y2)**2) # distanza dal secondo fuoco if D1+D2 < D: img[y][x] = colore draw_ellisse(img_r, 50, 200, 90, 150, 100, cyan) # e per disegnare un cerchio? # "un cerchio è una ellisse che non ce l'ha fatta" # cerchio = ellisse con entrambi i fuochi nello stesso punto e somma delle distanze = 2 raggio def draw_circle(img, x, y, r, colore): draw_ellisse(img, x, y, x, y, 2*r, colore) # %%% copiare una immagine dentro un'altra # %%% ritagliare una immagine # %% GIOVEDI': filtri ed elaborazioni # %%% disegnare un albero senza turtle # gray # blur # contrast # pixellation # random noise # lens # edge # %% LUNEDI': Immagini e pixel come classi di oggetti