Il 0% ha trovato utile questo documento (0 voti)
2 visualizzazioni

Python

Caricato da

giorgia.pigoni
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
2 visualizzazioni

Python

Caricato da

giorgia.pigoni
Copyright
© © All Rights Reserved
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 26

PYTHON

Rilasciato nel 1991 dal suo creatore Guido van Rossum programmatore olandese, attualmente a Dropbox.
Deriva il suo nome dalla commedia Monty Python's Flying Circus dei celebri Monty Python, in onda sulla BBC nel corso degli anni
70.
- Linguaggio semplice, sintassi pulita e snella.
- Curva di apprendimento rapida.
- Linguaggio ad alto livello, astrazione dei dettagli di funzionamento di un computer.
- Linguaggio dinamico, tipizzazione dinamica.
- Linguaggio pseudocompilato, un interprete analizza il codice e lo esegue se corretto (linguaggio interpretato).
- Free software, licenza open - source: non solo il download dell'interprete per la propria piattaforma, così come l'uso di
Python nelle proprie applicazioni, è completamente gratuito; ma oltre a questo Python può essere liberamente
modificato e così ridistribuito, secondo le regole di una licenza pienamente open-source.
- Archivio di moduli introduttivi vasto e stabile.
Computer: macchina in grado di eseguire delle azioni elementari (passaggi), esegue le azioni su oggetti (dati di input) e produce
altri oggetti (dati di output/risultati). Le azioni vengono eseguite attraverso fasi scritte in un qualche linguaggio (istruzioni).
Linguaggio di programmazione: strumento attraverso il quale si riesce a dire al calcolatore cosa fare (fargli eseguire un
algoritmo).
Comporto da:
- sintassi (simboli e parole, regole grammaticali),
- semantica (significato dei simboli e delle parole).
Programma: testo scritto secondo la sintassi e la semantica di un linguaggio di programmazione.
<direttive di importazione moduli esterni> importa un modulo Python
<sequenza di statement> sequenza di comandi

SINTASSI DI BASE
print(contenuto): visualizzare i risultati di output, fornire informazioni intermedie e per mostrare i valori delle variabili.
Dichiarazione di una variabile: l’operazione per dare il nome ad una variabile senza assegnare un valore. Istruzione che
introduce un nuovo identificatore tramite il nome [ e il tipo ].
a = None
Definizione di una variabile: caso particolare di dichiarazione. Operazione che assegna un valore specifico alla variabile.
a=5
Nome di una variabile:
- iniziare con una lettera (a-z, A-Z) e un underscore (_)
- può contenere lettere, numeri e underscores
- è sensibile alle maiuscole e minuscole (a != A)
Assegnamento di una variabile: assegnare alla variabile un valore. Il valore ha associato un tipo.
Tipi di dato di base:
- intero (int), numeri interi
- decimale (float), numeri decimali
- stringa (str), sequenze di caratteri
- booleano (bool), valori logici True o False.
Operazioni di base:
- operazioni aritmetiche
- addizione (+)
- sottrazione (-)
- moltiplicazione (*)
- divisione (/)
- operazioni logiche
- and, or, not
- operazioni di confronto
- uguaglianza (==)
- diverso (!=)
- maggiore (>) e maggiore uguale (>=)
- minore (<) e minore uguale (<=)

TIPI BASE IN PYTHON


Classe: costrutto che rappresenta un tipo di dato astratto. La classe definisce le caratteristiche e il comportamento degli oggetti
che daranno creati basandosi su di essa.
Oggetto: istanza di una classe. Quando una classe viene istanziata, si crea un oggetto che ha accesso gli attributi e ai metodi
definiti nella classe.
Class nomeclasse:
def __init__(self, parametri):
self.parametro1 = valore1
self.parametro2 = valore2
def metodo1(self):
#comportamento classe
Python è un linguaggio completamente orientato agli oggetti. Ogni variabile (che ha un tipo) è un oggetto. Anche i tipi che si
potrebbero definire primitivi (es. int, float, ecc...) sono classi con i loro metodi e i loro attributi.
Tipi built - in, godono di una sintassi agevolata:
- numerics (int, float, complex)
- sequence (str, lit, tuple)
- set (set, frozenset)
- dict, dizionari di coppie key - value.
NUMERICS
- numeri interi, lunghezza binaria
- numeri float, separatore parte intera/parte decimale “.”
- numeri complessi, numero reale + numero reale con suffisso (z = 10 + 20j)
- z.real parte reale
- z.imag parte immaginaria
- booleani, sottotipo degli interi.
Operazioni possibili
- operazioni aritmetiche standard: +, -, *, /
- divisione / e divisione intera //
- resto divisione tra interi: %
- valore assoluto: abs() #funzione built - int
- numero complesso coniugato: conjugate()
- elevamento a potenza: **, pow() #funzione built - int
- arrotondamento: round() #funzione built - int
SEQUENCE E STRINGHE
Le stringhe sono racchiuse tra apici ‘’ oppure “”.
Le liste sono elementi di qualsiasi tipo anche non coerenti racchiusi tra [].
Le tuple sono elementi di qualsiasi tipo anche non coerenti racchiusi tra (). Sono immutabili.
Accesso:
- ad un elemento a[1]
- a sottoliste/sottostringhe a[1:3]
- partendo dal fondo a[-1]
Concatenazione:
- ‘a’ + ‘b’ = ‘ab’
- [1, 2] + [3, 4] = [1, 2, 3, 4]
Ripetizione:
- ‘a’ * 4 = ‘aaaa’
- [1] * 4 = [1, 1, 1, 1]
Lunghezza della sequenza/lista: len(a) = 4 #funzione built - in
STRINGHE

- s.lower(), s.upper() ritornano una copia della stringa s con lettere minuscole o maiuscole;

- s.count(substr) ritorna il numero di occorrenze della sottostringa substr in s;

- s.find(substr) ritorna l’indice della prima occorrenza della sottostringa substr in s;

- s.replace(sub1, sub2) rimpiazza le occorrenze della sottostringa sub1 con sub2 in s;

- ‘;’.join([1, 2, 3]) = “1; 2; 3” join concatena diversi elementi aggiungendo un separatore;

- ‘1 + 2 + 3 + 4 + 5’.split(‘+’) = [‘1’, ‘2’, ‘3’, split divide una stringa in elementi considerando un separatore.
‘4’, ‘5’]

LISTE
a = [] lista vuota
a = [1] lista di un elemento
a = [1, ‘1’, “A”, “boh”, 32, 3.8]
a = [1, 2, [1, 2, 3, 4], 3, 4] lista di lista
Ciascun elemento di una lista è una variabile contenente un dato del tipo corrispondente. Gli indici di una lista partono da 0.
Accesso:
- ad un elemento a[1]
- a sottoliste/sottostringhe a[1:3]
- partendo dal fondo a[-1]
Si possono definire liste multidimensionali come liste di liste. Non necessariamente di pari lunghezza. Si usano più indici fra
parentesi quadre per l'accesso agli elementi.
a = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9,10,11]]
print(a[0][0]) = ‘0’
Operazioni:
- lista.append(oggetto) appende l'oggetto in fondo alla lista;
- lista.insert(indice, oggetto) inserisce l'oggetto nella posizione indicata dall'indice;
- lista.pop(indice) estrae l'oggetto in posizione indice dalla lista;
- lista.pop() estrae l'ultimo elemento della lista
- lista.sort() ordina gli oggetti contenuti - modifica lista in-place;
- sorted(lista) non modifica la lista originale;
- len(lista) ritorna il numero di elementi contenuti in una lista;
- operatore in: ricerca elemento in una lista - 6 in lista → True.
Operazioni rimozione:
- lista.pop(ind1) rimuove l’elemento di ind1 e lo ritorna;
- lista.remove(elem1) rimuove l’elemento elem1 e non lo ritorna;
- del lista[ind1] statement che rimuove l’elemento di indice ind1.
Operazioni slicing:
- wt_slice = wt[start:stop]
- wt_slice = wt[start:stop:incremento]
Operazione di copia: si usa il riferimento. Il riferimento in programmazione indica un modo per accedere o identificare un’area di
memoria. Il riferimento è quindi il modo in cui il nome della variabile si riferisce a quella posizione di memoria.
CODA
Una coda è una struttura dati che segue il principio FIFO (First in, First out): il primo elemento inserito in una struttura dato è il
primo ad essere rimosso. Gli elementi vengono aggiunti alla parte posteriore della cosa e rimossi dalla parte anteriore.
queue = [] #coda vuota
queue.append(‘persona1’)
queue.append(‘persona2’)
queue.append(‘persona3’)
Coda dopo aggiunte: [‘persona1’, ‘persona2’, ‘persona3’]
prima_persona = queue.pop(0)
Persona1
seconda_persona = queue.pop(0)
Persona2
print(queue)
Persona3
È utilizzato in situazioni in cui è importante mantenere l'ordine di arrivo, come nella gestione delle richieste di servizio in una
rete, nella gestione delle code di stampa o nell'elaborazione delle richieste in un sistema distribuito.
- lista.append(oggetto) appende l’oggetto in fondo alla lista
- lista.pop(indice) estrae l’oggetto in posizione indice della lista.
STACK
LIFO (Last in, First out): principio che descrive l’ordine in cui gli elementi sono acceduti in una struttura dati. L’ultimo elemento
inserito in una struttura dati è il primo ad essere rimosso. Gli elementi vengono aggiunti o rimosso dalla cima dello stack.
stack = [] #stack vuoto
stack.append(‘libro1’)
stack.append(‘libro2’)
stack.append(‘libro3)
Stack dopo le aggiunte: [‘libro1’, ‘libro2’, ‘libro3’]
ultimo_libro = stack.pop()
Libro3
penultimo_libro = stack.pop()
Libro2
print(stack)
Libro1
È utilizzato in situazioni in cui l'ordine delle operazioni è critico, come nella gestione delle chiamate di funzioni in una pila di
esecuzione o nella gestione di operazioni "undo" in un'applicazione.
- lista.append(oggetto) appende l’oggetto in fondo alla lista
- lista.pop(indice) estrae l’oggetto in posizione indice della lista.
TUPLE
Le tuple sono immutabili (come le stringhe). Si possono usare tutti gli operatori delle liste tranne quelli di modifica.
SET
Il set, in Python, è un insieme non ordinato di oggetti non replicati:
- non ordinato, non posso accedere tramite indice
- non replicati, lo stesso oggetto sarà presente al massimo una volta
Creato con la funzione set() o {elem, elem}.
a = set() #insieme vuoto
b = set([lista]) #insieme creato dalla lista
c = {1,2} #insieme con dentro 1 e 2
I set sono comodi per certe operazioni: eliminare i duplicati (per esempio partendo da una lista), test di appartenenza più
efficiente che scorrere una lista, usando l’operatore in come per le liste.
Operazioni:
- s.add(elemento) aggiungere elementi a un set, non da errore se l’elemento è già presente;
- s.remove(elemento) rimuovere elementi da un set, da errore se si tenta di rimuovere un elemento non presente;
- len(s) cardinalità del set s;
- x in s, x not in s appartenenza all'insieme;
- s1.isdisjoint(s2) disgiunzione;
- s1.union(s2) unione anche con simbolo “|”;
- s1.intersection(s2) intersezione anche con simbolo “&”;
- s1.difference(s2) differenza anche con simbolo “-”.
DICTIONARY
Il dictionary è un’associazione chiave - parola di più elementi.
Creato con {} o dict().
a = {} #dictionary vuoto
a = dict() #dictionary vuoto
b = {‘chiave1’: valore1, ‘chiave2’: valore2}
I valori possono essere qualsiasi oggetto (interi, stringhe, ecc), le chiavi solo oggetti immutabili (numerics, stringhe, tuple). Le
chiavi sono uniche nel dizionario.
Operazioni:
- accesso ai valori, usando la chiave con la notazione [chiave];
- .keys() restituisce una lista con tutte le chiavi contenute nel dictionary;
- .values() restituisce una lista con tutti i valori contenuti nel dictionary;
- .items() restituisce una lista di tuple (chiave, valore);
- del a [chiave] elimina la chiave e il valore dal dizionario;
- .pop() elimina e restituisce il valore
- operatore in per vedere se una chiave è presente nel dictionary;
- .has_key(chiave) true se la chiave è presente nel dizionario.

TIPO DI DATO
Un programma manipola i dati attraverso le istruzioni. Le istruzioni indicano le operazioni da compiere sui dati. Il tipo indica
cosa rappresenta il dato e definisce quali operazioni sono ammesse.
Il tipo di dato è fondamentale per determinare il risultato di un’istruzione.
In Python non dobbiamo specificare il tipo delle variabili, usiamo solo il nome. Il programmatore però deve conoscere il tipo della
variabile per non indicare operazioni sbagliate e illecite tra variabili.
In un programma, ad ogni entità (variabile) è associato:
- nome, identificatore unico nel programma,
- tipo indicatore del tipo di dato associato all’entità,
- valore, valore attuale dell’entità,
- indirizzo, riferimento alla memoria che la contiene,
- tempo di vita, durata dell’esistenza dell’entità nel programma, intervallo di tempo in cui l’oggetto è mantenuto in
memoria,
- scope (campo di visibilità), indicatore della visibilità del nome all’interno del programma, parte del programma in cui
l’identificatore può essere usato.
Ogni linguaggio possiede specifici tipi di dato. I tipi di dato possono essere classificati:
- semplici o atomici,
- complessi o derivati,
- predefiniti o definiti dall’utente.
Il tipo di dato indica l’insieme di valori che una variabile, o il risultato di un'espressione, possono assumere, e le operazioni che
su tali valori si possono effettuare.
TIPIZZAZIONE
Processo in cui il compilatore o l'interprete classifica i dati in base al tipo, cioè se sono numeri, stringhe, oggetti, ecc. Questo
processo aiuta a determinare come i dati possono essere utilizzati nel programma e previene errori, come ad esempio cercare di
dividere una stringa per un numero.
Compiti principali:
- evita operazioni sbagliate e impedisce operazioni incompatibili,
- definisce la dimensione in memoria ovvero determina quanto spazio in memoria serve per memorizzare i dati,
- definisce lo scope e tempo di vita.
Obiettivi:
- safety, riduce i rischi di errori durante l’esecuzione permettendo di rilevare problemi,
3 / “Hi World” #l’operatore divisione non prevede le stringhe, il compilatore emette un semantic error
- ottimizzazioni, permette al compilatore di eseguire alcune operazioni più velocemente conoscendo il tipo di dato,
- astrazione, permette al programmatore di non doversi preoccupare di come i dati sono rappresentati internamente.

Statica Dinamica

- il tipo di una variabile viene dichiarato e - il tipo di una variabile viene determinato
assegnato durante la compilazione, una volta durante l’esecuzione del programma e può
assegnato il tipo non cambia. cambiare mentre il programma è in
esecuzione.

Forte Debole

- regole rigide sulla compatibilità dei tipi, - operazioni fatte anche tra tipi diversi e il
operazioni consentite solo tra tipi compatibili, linguaggio permette conversioni implicite tra
conversioni esplicite. tipi.

Safe Unsafe

- impedisce che operazioni di conversione - non impedisce che un’operazione di


errate possano causare un crash del conversione sbagliata causi un errore o crash.
programma.
Tipizzazione in Python
Python è un linguaggio orientato agli oggetti. Ogni variabile (che ha un tipo) è un oggetto. Anche i tipi che si potrebbero definire
primitivi (es. int, float, ecc...) sono classi con i loro metodi e i loro attributi. Nei linguaggi ad oggetti, ogni oggetto ha a
disposizione:
- i metodi e gli attributi della sua classe,
- i metodi e gli attributi ereditati.
Ereditarietà: una sottoclasse può ereditare metodi e attributi dalla classe base (superclasse). Questo permette di riutilizzare
codice e creare gerarchie di classi. Una classe base (o superclasse) è la classe da cui altre classi derivano. Una sottoclasse (o classe
derivata) è una classe che eredita da un'altra classe.
class Animale:
def __init__(self, nome):
self.nome = nome
def parla(self):
raise NotImplementedError("Questo metodo deve essere sovrascritto dalle sottoclassi")
class Cane(Animale):
def parla(self):
return f"{self.nome} dice: Bau!"
class Gatto(Animale):
def parla(self):
return f"{self.nome} dice: Miao!"
cane = Cane("Fido")
gatto = Gatto("Whiskers")
print(cane.parla()) # Output: Fido dice: Bau!
print(gatto.parla()) # Output: Whiskers dice: Miao!
L’overriding: permette alle sottoclassi di cambiare il comportamento di un metodo ereditato dalla classe base. Questo rende il
codice più flessibile e specifico.
class Uccello(Animale):
def parla(self):
return f"{self.nome} dice: Cinguettio!"
uccello = Uccello("Tweety")
print(uccello.parla()) # Output: Tweety dice: Cinguettio!
Polimorfismo: consente a oggetti di classi diverse di rispondere allo stesso metodo in modo diverso, senza modificare il codice
che li utilizza. L'interfaccia comune permette di trattarli uniformemente,
Duck typing: permette di utilizzare oggetti basati sui metodi e proprietà che essi implementano piuttosto che sul tipo
dell'oggetto stesso. L'espressione "If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck"
riassume bene l'idea: se un oggetto si comporta come un certo tipo, può essere trattato come tale.
Definiamo due classi Duck e Person, entrambe con un metodo quack, e una funzione in_the_forest che invoca il metodo quack su
un oggetto passato come parametro. La funzione non esegue alcun type checking, ma si basa solo sull'esistenza del metodo
quack.
class Duck:
def quack(self):
print("Quaaaaaack!")
class Person:
def quack(self):
print("The person imitates a duck.")
def in_the_forest(duck):
duck.quack()
# Creazione degli oggetti
duck = Duck()
person = Person()
# Chiamata della funzione con un oggetto Duck
in_the_forest(duck) # Output: Quaaaaaack!
# Chiamata della funzione con un oggetto Person
in_the_forest(person) # Output: The person imitates a duck.
Per implementare la funzione calcola in Python che riceve parametri che supportano i metodi + e *, possiamo definire la funzione
in modo che accetti tre argomenti e applichi le operazioni + e * su di essi. Questa funzione deve essere generica, in modo da poter
lavorare con numeri, liste e stringhe, rispettando il concetto di duck typing.
def calcola(a, b, c):
return (a + b) * c
# Esempi di utilizzo
e1 = calcola(1, 2, 3)
e2 = calcola([1, 2, 3], [4, 5, 6], 2)
e3 = calcola('mele ', 'e arance', 3)
print(e1) # Output: 9
print(e2) # Output: [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
print(e3) # Output: 'mele e arancemele e arancemele e arance'
Quasi tutti i linguaggi dinamici adottano un meccanismo di gestione degli attributi e dei metodi degli oggetti basato su duck
typing; Python lo adotta. Permette di ottenere polimorfismo senza dover per forza incorrere nell'overhead dovuto
all'ereditarietà. Ovviamente si aumenta la possibilità di errori a run time.

BLOCCO DI CODICE
In Python un blocco di codice ha le seguenti caratteristiche:
- inizia con ‘:’ (seguito dal ritorno a capo),
- è indentato dall’inizio alla fine,
- la fine dell'indentazione indica la fine del blocco,
- in altri linguaggi si usano le {}.
COSTRUTTO CONDIZIONALE
Un costrutto che esegue un codice se è verificata la condizione, e uno che mi faccia eseguire l’altro codice se non è verificata la
condizione. Nei vari passi di un algoritmo ci si trova spesso nella necessità di esprimere la scelta tra due o più sequenze di
istruzioni, in base al verificarsi o meno di una certa condizione durante l’esecuzione dello stesso algoritmo.
If - then - else
if <condizione>:
<codice>
else:
<codice>
Nella condizione si possono utilizzare:
- operatori logici and, or, not,
- operatori di confronto ==, !=, <, >, <=, >=,
- a in lista, una variabile, un dato.
If - elif - else
if <condizione1>:
<codice>
elif <condizione2>:
<codice>
else:
<codice>
Costrutti condizionali nidificati
Un’istruzione condizionale può contenere al suo interno istruzioni qualsiasi, e quindi anche altre istruzioni condizionali. Si parla
in questo caso di istruzioni nidificate (o annidate). L’uso di istruzioni condizionali nidificate consente di esprimere la scelta tra
più di due sequenze di istruzioni alternative.
COSTRUTTO ITERATIVO
Costrutto che esegua un codice in maniera iterativa, svolgendo determinate operazioni fintanto che una condizione è verificata.
In molti algoritmi è necessario ripetere una stessa sequenza di operazioni per una o più volte. Finché una data condizione è vera,
esegui una certa sequenza di istruzioni
Ciclo while
while <condizione>:
<codice>
fintanto che la condizione è vera il ciclo esegue il codice dentro al blocco.
b=0
while b < 10:
b=b+1
while <condizione>:
<codice>
else:
<codice>
quando esce dal ciclo esegue l’else.
b=0
while b < 10:
b=b+1
else:
b = 100
Ciclo for
Tipicamente consente di esprimere un solo tipo di iterazione che consiste nell’accedere a tutti gli elementi di una sequenza. È più
conciso rispetto al while.
for <condizione>:
<codice>
È utilizzato per iterare su una sequenza (come una lista, una tupla, una stringa o un range).
for <condizione>:
<codice>
else:
<codice>
quando esce esegue l’esle.
for e in lista:
print(e)
else:
print(“elementi finiti”)

ciclo while ciclo for

id = 0 for e in lista:
while (id < len(lista)): print(“l’elemento è: ”, e)
print(“l’elemento è: ”, lista[id])
id += 1 #id = id + 1
Il ciclo for può iterare su qualsiasi elenco di oggetti (stringhe, tuple, liste ecc..). In base al tipo di elenco gli elementi su cui itera
saranno differenti.
La funzione range() è comunemente utilizzata con il ciclo for per iterare su una sequenza di numeri in modo efficace.
for i in range(5):
print(i) # 0 1 2 3 4
Ciclo for su stringhe: ad ogni iterazione i è una copia ad un carattere.
a = ‘ab’
for i in a:
print(i) # a b
Ciclo for su liste: ad ogni iterazione i è una copia ad un oggetto della lista.
a = [“a”, “b”]
for i in a:
print(i) # a b
Ciclo for su tuple: come per le liste.
Ciclo for su set: ad ogni iterazione i è una copia ad un elemento dell’insieme.
a = set([‘a’, ‘b’])
for i in a:
print(i) # a b (l’ordine non è garantito)
Ciclo for su dictionary: ad ogni iterazione è una copia ad una chiave del dictionary; ma .items() restituisce sia la chiave che il
valore.
a = {‘k1’:‘a’, ‘k2’:‘b’}
for i in a:
print(i) #k1 k2 (l’ordine non è garantito)
a = {‘k1’:‘a’, ‘k2’:‘b’}
for k,v in a.items():
print(k,v) #k1 a, k2 a (l’ordine non è garantito)
enumerate(): è molto utile quando si lavora con cicli for e si ha bisogno di un contatore per tenere traccia dell'indice di ciascun
elemento durante l'iterazione su una sequenza (come una lista, una tupla o una stringa). Restituisce sia l'indice che l'elemento
stesso, rendendo il codice più leggibile e pulito. Enumera la sequenza su cui si itera, restituisce una tupla (indice, oggetto).
a = “abc”
for i in enumerate(a):
print(i) #(0 ‘a’, 1 ‘b’, 2 ‘c’)
In Python, per poter iterare su un oggetto con un ciclo for, l'oggetto deve essere iterabile. Un oggetto è considerato iterabile se
implementa il metodo speciale __iter__(), che deve restituire un iteratore. Un iteratore, a sua volta, è un oggetto che
implementa il metodo speciale __next__(), che restituisce l'elemento successivo della sequenza o solleva un'eccezione
StopIteration per indicare la fine della sequenza.
In pratica lo statement for chiama prima __iter__() e poi __next__() sull'oggetto ritornato.
Funzioni iter() e next()
- iter(obj), chiama il metodo __iter__() sull'oggetto obj e restituisce un iteratore;
- next(iterator), chiama il metodo __next__() sull'oggetto iterator e restituisce il prossimo elemento. Se non ci sono più
elementi, solleva l'eccezione StopIteration.
a = ['a', 'b']
# Utilizzo delle funzioni iter() e next()
i = iter(a) # Ottiene l'iteratore
print(next(i)) # Output: 'a'
print(next(i)) # Output: 'b'
try:
print(next(i)) # Raises StopIteration
except StopIteration:
print("Fine dell'iterazione") # Output: Fine dell'iterazione
COMANDI DI SALTO
In Python, i comandi di salto (break, continue, e pass) vengono utilizzati per controllare il flusso di esecuzione dei cicli e delle
strutture condizionali. Ecco una panoramica di ciascuno di questi comandi con esempi di utilizzo.
- break
Il comando break interrompe il ciclo in cui si trova, terminando immediatamente l'esecuzione del ciclo, ciclo for/while.
for i in range(10):
if i == 5:
break
print(i) #0 1 2 3 4
- continue
Il comando continue salta l'iterazione corrente del ciclo e passa direttamente alla successiva.
for i in range(10):
if i % 2 == 0:
continue
print(i) #1 3 5 7 9

FUNZIONI
Una funzione è un blocco di codice riutilizzabile che esegue un'azione specifica quando viene chiamato.
Tutti i linguaggi di programmazione mettono a disposizione un insieme di programmi che eseguono operazioni di utilità
generale. Tali programmi sono detti funzioni per analogia con il concetto di funzione matematica; sono in grado di elaborare un
determinato insieme di valori di ingresso e di produrre un risultato.
Caratteristiche
- Nome della funzione: ad ogni funzione è associato un nome simbolico (== per le variabili).
- Argomenti della funzione: ogni funzione può elaborare un determinato numero di valori di ingresso, detti
argomenti-analogia con le funzioni matematiche.
- Valore di ritorno: ogni funzione può restituire un valore, come risultato dell’elaborazione svolta sugli argomenti.
L’esecuzione di una funzione si ottiene attraverso una specifica espressione detta chiamata di funzione.
Sintassi nome_funzione (<arg1>, <arg2>, …, <argn>)
FUNZIONI PREDEFINITE
Le funzioni disponibili in un linguaggio di programmazione sono dette predefinite, o built-in. L’insieme di tali funzioni viene
detto libreria.
La disponibilità di funzioni predefinite evita agli utenti di dover scrivere propri programmi per realizzare operazioni di utilità
generale. Il linguaggio Python comprende un vasto insieme di funzioni di libreria. Alcune funzioni sono direttamente accessibili,
altre sono suddivise in diverse categorie, dette a loro volta librerie.
print(): stampa nella shell i valori di una o più espressioni. È una funzione particolare perché non restituisce nessun valore, il suo
unico scopo è stampare valori nella shell.
input(): permette l’acquisizione di dati mediante la tastiera; l’utente durante l’esecuzione di un programma può immettere una
qualsiasi sequenza di caratteri nella shell. Alla pressione del tasto invio la sequenza inserita sarà restituita dall’interprete sotto
forma di stringa.
- variabile = input(<espressione>)
L’interprete stampa l’espressione (se presente), di norma è una stringa. Resta in attesa che l’utente inserisca una sequenza di
caratteri e termini con invio. La sequenza viene assegnata a variabile sotto forma di una stringa. Durante l’esecuzione della
funzione input(), l’esecuzione del programma si sospende fino a che il tasto INVIO non viene premuto.
eval(): restituisce il valore di espressione, se espressione contiene una qualsiasi espressione valida. Può essere usata per
“convertire” in valori numerici i dati acquisiti mediante la funzione di input.
- variabile = eval(espressione)
pow(): radice quadrata.
int(): converte un valore in numero intero.
len(): lunghezza.
float(): converte un valore in numero decimale.
dict(): dizionari.
min(): trova il valore minimo
enumerate(): utilizzata per ottenere sia l'indice che il valore di ciascun elemento di un iterabile durante l'iterazione.
abs(x): valore assoluto.
bool([x]): converte un valore in booleano; se x è falso o omesso restituisce False, altrimenti restituisce True.
cmp(x, y): confronta i due oggetti x e y e restituisce un intero a seconda del risultato. Il valore d'uscita è negativo se x < y, zero se
x == y e quindi positivo se x > y.
dir([object]): senza argomenti, restituisce l'elenco dei nomi presenti nella locale tavola dei simboli corrente. Con un argomento,
cerca di restituire un elenco di attributi validi per quell'oggetto.
filter(function, list): costruisce una lista dagli elementi di list per i quali la funzione function restituisce vero.
help([object]): invoca l'aiuto di sistema built-in.
FUNZIONI DEFINITE DALL’UTENTE
Tutti i linguaggi di programmazione consentono inoltre ai programmatori di definire nuove funzioni: tali funzioni sono dette
definite dall’utente (user-defined).
La possibilità di definire nuove funzioni presenta diversi vantaggi:
- semplificare la scrittura di programmi complessi suddividendoli in più parti (funzioni),
- sottoproblemi logicamente indipendenti,
- riutilizzo di codice, l’esecuzione di una funzione può essere richiesta in diversi punti di uno stesso programma, senza
dover scrivere più volte le stesse istruzioni.
Definizione della funzione
def nome_funzione(<argomento_i>, <argomento_i+1>)
<codice>

return <valore_di_ritorno> #statement per restituire un valore
E’ possibile definire funzioni con parametri opzionali che assumono un valore di default se non passati durante l’invocazione. I
parametri opzionali devono essere gli ultimi.
def nome_funzione(a_i = 1, a_j = None)
E’ possibile ‘spacchettare’ sequenze (es. liste) e passare ogni elemento come positional argument. Si usa l’operatore * anteposto
alla lista. Il numero di parametri deve essere lo stesso di quelli definiti nella funzione.
a = [1, 2, 3, 4, 5]
funzione(*a) == funzione(1, 2, 3, 4, 5)
E’ possibile ‘spacchettare’ dizionari e passare ogni elemento come positional argument. Si usa l’operatore ** anteposto al
dizionario.
a = {a:1, b:2, c:3}
funzione(**a) == funzione(a = 1, b = 2, c = 3)
Parametri formali con * e **: gli operatori * e ** possono anche essere utilizzati nella definizione della funzione.
def funzione(*args, **kwargs)
In questo caso tutti i parametri posizionali passati alla funzione vengono condensati nella tupla args. Tutti i keyword arguments
vengono condensati in un dizionario kwargs. Utile se non si sa a priori il numero di parametri necessari.
Chiamata di una funzione
L’esecuzione di una funzione si ottiene attraverso una specifica espressione detta chiamata di funzione.
ret = nome_funzione(a, ‘c’) #si usa la notazione posizionale
E’ possibile anche specificare a quale parametro ci si riferisce (keyword arguments). Specificando il nome dei parametri si può
cambiare anche l’ordine degli argomenti.
ret = nome_funzione(a_j = ‘c’, a_i = 10)
È possibile anche mescolare le tecniche ma i positional argument vanno prima dei keyword argument.
METODI - FUNZIONI DI CLASSE
Le funzioni a differenza dei metodi non sono innestate in una classe e vengono chiamate in modo diverso.

CLOSURE
Una chiusura (closure) in programmazione è un concetto che si riferisce alla capacità di una funzione di catturare e mantenere lo
stato di variabili locali al di fuori del suo ambito di definizione. Una closure permette a una funzione di accedere a variabili non
locali che sono state definite nell'ambito di una funzione esterna, anche dopo che la funzione esterna ha terminato la sua
esecuzione.
Le funzioni innestate possono accedere alle variabili delle funzioni madri. Tali variabili vengono incapsulate nelle funzioni figlie
in modo che possano essere utilizzate anche quando la funzione madre termina.
Per creare una closure, è necessario definire una funzione all'interno di un'altra funzione e fare in modo che la funzione interna
utilizzi variabili definite nella funzione esterna. La funzione esterna deve poi restituire la funzione interna.
def esterna(x):
def interna(y):
return x + y
return interna
# Creiamo una closure
chiusura = esterna(10)
# Utilizziamo la closure
print(chiusura(5)) # Output: 15
print(chiusura(20)) # Output: 30
NAMESPACE
Insieme dei simboli visibili in un certo punto del programma. Obiettivo: evitare confusione ed equivoci nel caso di entità con nomi
uguali. Si può pensare a un namespace come a un dizionario in cui le chiavi sono i nomi degli oggetti e i valori sono gli oggetti
stessi. Ogni coppia chiave-valore mappa un nome all'oggetto corrispondente.
dir(): funzione built - in di Python per esplorare il namespace. Se invocata senza argomenti, dir() stampa i simboli visibili
dall'interprete in quel punto del codice, dove “simboli” sono:
- funzioni (built-in e definite da utente),
- variabili (built-in e definite da utente),
- moduli (precedentemente importati).
SCOPE
Parte del programma in cui l'identificatore può essere usato. Quando si parla di scope, cioè di contesto, si intende un’area di
codice dove ci si trova in un certo momento che determina quale namespace deve essere utilizzato da Python per la risoluzione
dei nomi di oggetti.
Tipi di scope
- Built - in, entità sempre disponibili.
# Utilizzo di una funzione built-in
print("Hello, World!") # print è una funzione built-in
- Global, entità definite nel codice (non dentro a funzioni);
# Variabile globale
x = 10
def funzione():
print(x) # Accede alla variabile globale x
funzione() # Output: 10
- Enclosing, entità incapsulate per effetto della closure;
def esterna():
y = 5 # Variabile nell'enclosing scope
def interna():
print(y) # Accede alla variabile y della funzione esterna interna()
esterna() # Output: 5
- Local, entità definite dentro alla funzione in cui si è.
def funzione():
z = 3 # Variabile locale
print(z)
funzione() # Output: 3
# print(z) # Genera un errore, z non è definita nel contesto globale
Regole di scope
Il modello di scoping del Python è unico e statico (determinato a tempo di compilazione).
print('ciao') #scope built - in
a = 1 #scope globale
def encaps():
b = 2 #scope enclosed
def local():
c = 3 + b # scope locale
def another():
d = 0 #non si vede in quanto in questo punto non è in nessuno scope
Il modello di scoping del Python è unico e statico (determinato a tempo di compilazione). Ordine di risoluzione:
- local,
- enclosed,
- global,
- built - in.
Si possono chiamare le variabili con lo stesso nome ‘nascondendo’ quelle degli scope più esterni. Le variabili locali
“sovrascrivono” temporaneamente le variabili omonime globali.
print(‘ciao’)
x=1
def encaps():
x=2
def local():
x=3
def another():
x=0
Si può forzare l'utilizzo di un’entità dello scope globale con la parola chiave global.
print(‘ciao’)
x=1
def encaps():
x=2
def local():
global x
x=3
def another():
x=0
Argomenti di funzione
Anche i parametri di una funzione entrano nel local scope.
def fun(x):
print(x)
x = ‘ciao’
print(x)
x=5
fun(x)
print(x)
All’interno della funzione viene cambiata la x locale. Al di fuori rimane la x di prima.

GESTIONE DELLE ECCEZIONI


Come in altri linguaggi, Python gestisce gli errori attraverso il concetto di Eccezione. Nell'ambiente di runtime (RE), viene
definita una classe madre che rappresenta eccezioni software generiche. Ogni anomalia a runtime è associata a una sottoclasse
specifica della classe madre.
Le eccezioni possono essere sollevate in due modi:
- automaticamente, queste eccezioni si verificano in seguito ad anomalie a runtime causate da istruzioni,
- manualmente, i programmatori possono sollevare manualmente eccezioni utilizzando istruzioni come “raise” o
“throw”.
All’interno di una funzione (o blocco) si sollevano le eccezioni con il comando raise:
raise NameException(arg1,arg2)
In caso di eccezione, viene eseguito del codice di gestione. Se non è presente un gestore specifico, verrà utilizzato un gestore
predefinito che stamperà lo stack delle chiamate ed uscirà dal programma.
try:
<codice> #prova ad eseguire questo codice
except:
<codice> #viene eseguito questo codice se viene sollevata un’eccezione
else:
<codice> #se il codice del try finisce regolarmente allora esegue questo codice
finally:
<codice> #in ogni caso esegue anche questo

CLASSI - METODI DELLA CLASSE


Vantaggi
- astrazione, capacità di ridurre il numero di dettagli implementativi,
- incapsulamento, capacità di proteggere lo stato interno dai moduli (protezione),
- ereditarietà, capacità di ereditare il comportamento di una classe (riuso del codice),
- polimorfismo, capacità di adattare il comportamento in funzione dei parametri.
class ClassName:
<codice>
Tipicamente si utilizza la seguente convenzione:
- NomiMaiuscoli per i nomi delle classi
- nomiminuscoli per i nomi di variabili, funzioni e metodi.
Attributi di classe: gli attributi di classe appartengono direttamente alla classe e non agli oggetti istanziati di quella classe.-Sono
definiti direttamente nel blocco di codice della classe.
class ClassName:
a = ‘ciao’
i=0
Gli attributi di classe possono essere acceduti direttamente tramite la classe:
- ClassName.i
Ma anche utilizzando gli oggetti istanziati:
- istanza = NomeClasse()
- istanza.i
Metodi
I metodi sono le funzioni della classe. Si definiscono con la parola chiave def e hanno come primo argomento obbligatorio il
riferimento all’oggetto stesso self. Self permette di accedere ai metodi/attributi dell’oggetto.
class ClassName:
def metodo1(self, arg1, arg2):
<codice>
I metodi si possono chiamare attraverso l’oggetto istanziato.
- istanza = NomeClasse()
- istanza.metodo1(“ciao”, 1).
Quando si chiama il metodo su oggetto non si passa il self. L’interprete è a conoscenza che self sarà riferito a quell’oggetto
(istanza.metodo1)
Costruttore
Il costruttore è un metodo speciale che inizializza l’oggetto istanziato. Deve avere come nome __init__. Ogni volta che
istanziamo un oggetto della classe, stiamo di nascosto invocando il costruttore.
class ClassName:
def __int__(self, arg1, arg2):
<codice>
Attributi dell’oggetto: appartengono all’oggetto istanziato. Vengono inizializzati all’interno del costruttore. Lo si fa attraverso
self.
class ClassName:
def __init__(self, arg1, arg2):
self.b = 0
Namespace e Scope
La definizione di una classe crea un nuovo namespace (scope locale della classe). Classi diverse possono avere variabili e funzioni
con lo stesso nome.
Ordine di risoluzione:
- Local,
- Enclosed,
- Global,
- Built-in.

MODULI INTRODUTTIVI
Abbiamo visto:
- tipi di dato (numerics, stringhe, liste, dizionari, …),
- costrutti condizionali e iterativi (if-else, while, for),
- funzioni (built-it, definite dall’utente),
- classi (metodi e attributi).
Sappiamo che:
- con i costrutti iterativi (while/for) possiamo iterare più volte su alcune istruzioni (il blocco di codice del while/for),
- con le funzioni possiamo scrivere codice logicamente indipendente e chiamare le funzioni più volte nel codice,
- con le classi possiamo definire nuovi tipi e grazie all’ereditarietà riutilizzare il codice.
Il modulo è l'unità di base per salvare codice che potrà essere riutilizzato. Un modulo è un file contenente definizioni di variabili,
funzioni e classi. Le definizioni possono essere importate e usate da altri moduli
Due tipi di modulo:
- modulo che contiene nostro codice, il nome del file è lo stesso del modulo con estensione .py;
- modulo che contiene codice scritto da altri.
In Python è possibile importare moduli esterni (librerie) per poterle utilizzare all’interno del proprio codice.
import <name>
import <name> as <nk> #nickname, si rinomina il modulo per comodità
from <name> import <elenco>
from <name> import *

DATA SCIENCE
La data science è l'applicazione di tecniche computazionali e statistiche per affrontare o ottenere intuizioni [manageriali o
scientifiche] su qualche problema nel mondo reale.
Data Science:
- ampia, necessariamente più grande di una singola disciplina;
- interdisciplinare, statistica, informatica, ricerca operativa, apprendimento automatico e statistico, data warehousing,
visualizzazione, matematica, scienza dell'informazione, ecc;
- centrata sull'analisi, fondata sul desiderio di trovare informazioni nei dati e sfruttarle per guidare la presa di decisioni.
La capacità di prendere i dati - di essere in grado di comprenderli, processarli, estrarne valore, visualizzarli, comunicarli - sarà
una competenza estremamente importante nei prossimi decenni, non solo a livello professionale ma anche a livello educativo per
i bambini delle scuole elementari, per i ragazzi delle scuole superiori e per gli studenti universitari.
La Data Science riguarda lo studio dei dati per estrarre il valore dei dati stessi.

Data collection - acquisizione dei dati: questo processo riguarda la raccolta di dati da varie fonti, che possono essere interne
all'azienda (come database aziendali) o esterne (come i social media o tramite web scraping, ovvero l'estrazione di dati da pagine
web). La raccolta dei dati è fondamentale per alimentare qualsiasi analisi successiva. Indipendentemente dalla fonte, è cruciale
garantire che i dati raccolti siano accurati, completi e affidabili, poiché la qualità dei risultati analitici dipende direttamente dalla
qualità dei dati di partenza.
Data processing - processamento dei dati: dopo la raccolta dei dati, è necessario processarli per renderli adatti all'analisi.
Questo processo include la pulizia dei dati, ovvero la rimozione di dati duplicati, incompleti o errati, e la loro trasformazione in
un formato standardizzato e utilizzabile. Inoltre, potrebbe essere necessario aggregare o combinare dati da diverse fonti.
L'obiettivo finale del processamento dei dati è preparare i dati per l'analisi successiva, un processo noto come "data wrangling".
Exploratory analysis and data viz - esplorazione iniziale dei dati: dopo il processamento iniziale, è utile condurre un'analisi
esplorativa dei dati per ottenere una comprensione iniziale del dataset. Ciò può includere calcoli statistici di base come media,
varianza, deviazione standard, nonché la creazione di visualizzazioni dei dati per identificare modelli o tendenze. Questa fase è
cruciale per identificare eventuali anomalie o pattern interessanti nei dati che possono guidare l'analisi successiva.
Analysis, hypothesis testing and ML - analisi esaustiva: una volta completata l'esplorazione iniziale, è possibile passare a
un'analisi più approfondita. Questo può includere la formulazione di ipotesi specifiche da testare utilizzando metodi statistici
appropriati, nonché l'applicazione di tecniche avanzate di apprendimento automatico per estrarre informazioni più complesse
dai dati. Questa fase può aiutare a trarre conclusioni significative e a prendere decisioni informate basate sui risultati dell'analisi.
Python è estremamente diffuso in ambito scientifico per lo sviluppo e la rapida prototipazione di applicazioni di calcolo. Oltre alle
sue caratteristiche di semplicità, integrazione ed estendibilità, lo si deve alla presenza di numerosi moduli dedicati.
Moduli introduttivi
- Numpy (numeric python): array e matrici multi - dimensionali.
- Scipy (scientific python): algoritmi, modelli, statistica, integrazione, filtraggio, algebra lineare, ottimizzazione.
- Matplotlib: funzionalità di plotting 2D e 3D.
- Pandas: strutture dati e tool per analisi dati molto semplici da usare (ispirato a R).
- Scikit - learn: machine learning, dataset e metriche.

DATA COLLECTION
Modalità di acquisizione:
- scaricamento diretto e caricamento da archiviazione locale (download dataset),
- generazione locale tramite codice scaricato (dati di simulazione),
- interrogazione di un database per estrarre i dati desiderati (db privati o pubblici),
- interrogazione con un’API (application programing interface) da internet,
- web scraping, script automatizzati per estrarre dati dalle pagine web.
Dati strutturati e Dati non strutturati
Dati strutturati:
- dati organizzati in una forma predefinita,
- formato rigido e regolare,
- solitamente facilmente accessibili e elaborati da algoritmi,
(dati tabellari, record di log strutturati).
Dati non strutturati:
- dati non organizzati, senza schema,
- formato libero,
- richiedono alcune accortezze per essere usati,
- più difficili da analizzare e interpretare,
(immagini, video, testo, dati sensoriali).
Dati strutturati
Dati strutturati generali:
- file e stringhe con valori separati da virgola (CSV),
- file e stringhe Notazione JavascriptObject (JSON),
- file e stringhe HTML, XHTML, XML.
Dati strutturati specifici del dominio:
- shapefiles, dati vettoriali geospaziali (OpenStreetMap),
- file RVT, progettazione architettonica (Autodesk Revit).
Python offre diversi parser per i formati standard! (diversi moduli).
EXPLORATORY ANALYSIS
Formato CSV
Un file Comma Separated value(CSV) memorizza dati tabellari come semplice testo.
Che cos'è una tabella?
- Una tabella è un insieme di righe e colonne.
- Ogni riga (record di dati) ha un indice.
- Ogni colonna ha un nome.
- Una cella è identificata da una coppia (indice, nome).
- Una cella può avere o meno un valore.
Ogni riga del file è un record di dati. Le celle del record sono separate da virgole, punto e virgola, o da qualsiasi altro separatore
(TSV).
FILE I/O
Operazioni su file
Sulle unità di memorizzazione (dischi/chiavette etc) i dati sono registrati in blocchi contenenti sequenze di lunghezza fissa
composte da byte.Il file è una componente del Sistema Operativo che fornisce una “visione” comoda per accedere ai dati. Visione
a file, contenuti nelle directory (le cartelle).
Ogni file ha un nome (o piu' precisamente almeno un nome). Per individuare un file nel file system occorre un pathname (nome
del percorso). A partire dalla radice si possono nominare tutte le directory da attraversare per “raggiungere” il file. Per evitare di
specificare sempre il percorso a partire dalla radice ogni processo ha una directory di lavoro. Di norma un processo eredita la
stessa directory di lavoro del processo che l'ha attivato.
Apertura file
Per aprire un file si usa l'istruzione open(), per chiuderlo si usa il metodo close.
open() ha due parametri:
- il primo e' il pathname del file,
- il secondo e' il modo di apertura.
Modalità di apertura del file:
- 'r' per leggere
- 'w' per scrivere (azzera il contenuto del file)
- ‘a’ per aggiungere ad un file già presente (append)
- ‘b’ (da aggiungere ad uno dei precedenti) per aprire il file in modalità binaria.
Lettura file
Diversi modi per leggere il contenuto di un file.
- Si può leggere l'intero file in una stringa:
filecontents=fd.read()
- Si può leggere una porzione del file in una stringa, specificando il quantitativo di byte da leggere (size). L'argomento size
è opzionale. Se size è omesso, verrà letto l’intero contenuto del file fino al carattere EOF. Se il primo carattere letto è EOF,
restituisce una stringa vuota:
fd.read(size)
- Si può leggere una riga e poi le successive con identico metodo:
fileline = fd.readline()
- Si può leggere tutto il file ritornando una lista di stringhe, una per ogni linea:
filelines = fd.readlines()
- Si può fare un ciclo usando il file come sequenza, a ogni iterazione “line” sarà istanziato ad una riga del file
for line in fd:
<operazioni su line>
Chiusura file
Una volta finito di lavorare con il file bisogna chiuderlo. Chiusura del file e rilascio delle risorse associate: fd.close().
With consente una sintassi alternativa:
with open(“file”, “r”) as fd:

Nel blocco dipendente dall'istruzione with, fd è il descrittore del file aperto. Se avviene un errore il blocco non viene eseguito e
comunque il file viene chiuso alla fine del blocco.
Scrittura file
Analogamente alla lettura, ci sono molti modi di scrivere su un file aperto in scrittura:
- fd.write(string), scrive il contenuto di string nel file fd presso la posizione corrente del cursore, e restituisce None;
Nota: per scrivere qualcosa che non sia una stringa su un file di testo occorre prima convertirlo in stringa.
- fd.writelines(sequence), consente di scrivere sul file una sequenza di stringhe (valore di ritorno None), non aggiunge
separatori, il parametro sequence può essere qualsiasi oggetto iterabile che produca stringhe (tipicamente liste, tuple o
set di stringhe);
- si può usare il parametro “file=” della print() (solo in Python3) print(“hello world”, file=fd).
NUMPY
È lo standard universale per lavorare con dati numerici in Python. Contiene strutture dati di array multidimensionali e matrici: gli
array. A differenza delle liste:
- tutti gli elementi in un array NumPy dovrebbero essere omogenei,
- NumPy utilizza molto meno memoria per memorizzare i dati.
import numpy as np
a = np.array([1, 2, 3])
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
Esempio:

import numpy as np importazione numpy

>>> np.zeros(2) crea array di dimensione 2 pieno di 0


array([0., 0.]) np.zeros(shape, dtype, order = ‘c/f’)
- dtype, tipo di dato dell’array
- order, ordine degli elementi nella memoria
(‘c’ per row-major, ‘f’ per column - major)

>>> np.ones(2) crea array di dimensione 2 pieno di 1


array([1., 1.]) np.ones(shape, dtype, order)

np.arange(4) crea array con valori sequenziali da 0 a 3


array([0, 1, 2, 3]) np.arange([start, ] stop, [step, ] dtype)

np.linspace(0, 10, num=5) cre array di 5 numeri equidistanti tra 0 e 10


array([ 0. , 2.5, 5. , 7.5, 10. ]) np.linspace(start, stop, num)
- num, numeri di valori da generare

>>> x = np.ones(2, dtype=np.int64) crea array di 2 unità con tipo di dato ‘int64’
array([1, 1])

>>> a = np.array([1, 2, 3, 4]) concatena i due array ‘a’ e ‘b’ lungo l’asse esistente (asse
>>> b = np.array([5, 6, 7, 8]) 0 di default)
>>> np.concatenate((a, b)) np.concatenate((a1, a2, …), axis)
- axis, asse lungo il quale concatenare
array([1, 2, 3, 4, 5, 6, 7, 8])

>>> x = np.array([[1, 2], [3, 4]]) concatena i due array bidimensionali ‘x’ e ‘y’ lungo l’asse
>>> y = np.array([[5, 6]]) 0 (verticale)
>>> np.concatenate((x, y), axis=0)
array([[1, 2], [3, 4], [5, 6]])

PICKLE
Pickle è un modulo che permette di salvare oggetti arbitrari su file.- Serializza il dato. La serializzazione è molto comune per
trasmettere e salvare dati. Serializzazione: processo di conversione di un oggetto o di una struttura dati in un formato che può
essere facilmente memorizzato. Per salvare dati su disco.
Sintassi:
- pickle.dump(obj, file), salva l’oggetto su file
- pickle.dumps(obj), restituisce la serializzazione come stringa
- pickle.load(file), legge il file e restituisce l’oggetto
- pickle.loads(data), legge data (stringa) e restituisce l’oggetto.
Cosa può essere salvato:
- built-in;
- integers, floating - point numbers, complex numbers;
- strings, bytes, bytearrays;
- tuples, lists, sets, and dictionaries contenenti oggetti salvabili;
- funzioni ( no lambda);
- classi istanze che abbiano il metodo __getstate__ salvabile (di default ritorna il __dict__) (utile per salvare solo
alcuni campi).
Notare che funzioni e classi vengono salvate utilizzando solo il loro nome (non il codice per motivi di sicurezza). Questo implica
che quando vengono deserializzate è necessario importarle preventivamente.
PANDAS
Le principali strutture dati offerte da Pandas sono:
- Series, è un'entità unidimensionale simile ad un array che può contenere dati di qualsiasi tipo (int, str, float, …). Ogni
elemento della Serie è associato a un'etichetta, chiamata indice, che consente di accedere e manipolare i dati in modo
efficiente. Le Serie possono essere create partendo da liste, dizionari, array NumPy, ecc.
- DataFrame, è una struttura dati bidimensionale simile a una tabella, in cui ogni colonna rappresenta una variabile e ogni
riga rappresenta un'osservazione. I DataFrame consentono di organizzare e manipolare i dati in modo flessibile e
intuitivo. Possono essere pensati come una collezione di Serie che condividono lo stesso indice.
import pandas as pd
>>> s = pd.Series( [1, 3, 5, np.nan, 6, 8])
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
>>> df2 = pd.DataFrame({"A": 1.0,
"B": pd.Timestamp("20130102"),
"C": pd.Series(1, index=list(range(4)), dtype="float32"),
"D": np.array([3] * 4, dtype="int32"),
"E": pd.Categorical(["test", "train", "test", "train"]),
"F": "foo" })

A B C D E F

0 1.0 2013-01-02 1.0 3 test foo

1 1.0 2013-01-02 1.0 3 train foo

2 1.0 2013-01-02 1.0 3 test foo

3 1.0 2013-01-02 1.0 3 train foo


EXPLORATORY DATA ANALYSIS
Si vuole ottenere una comprensione iniziale dei dati. Statistiche di base (media, varianza, std). Visualizzazione preliminar
L'EDA (Exploratory Data Analysis) è l'ultimo passo prima delle statistiche di alto livello e delle tecniche di machine learning:
- si desidera ottenere rapidamente una panoramica dei dati attraverso statistiche riassuntive, visualizzazioni, ecc.
- individua sfumature come la distribuzione asimmetrica, la distribuzione dei dati, le tendenze, l'interazione tra coppie di
variabili, i problemi,
- suggerisce quali assunzioni fare sulle statistiche e sui modelli di machine learning e quali approcci adottare.
Gli obiettivi dell'EDA includono:
- comprensione dei dati, struttura dei dati, caratteristiche delle variabili e la loro distribuzione;
- individuazione di pattern e tendenze correlazioni tra variabili, comportamenti anomali, pattern ricorrenti;
- validazione delle ipotesi verificare o confutare ipotesi preliminari. Se è verificata una relazione tra due variabili;
- preparazione dei dati trattare o trasformare i dati, affrontare eventuali problemi di qualità dei dati, come valori mancanti
o outlier,
- comunicazione dei risultati tabelle, istogrammi, scatter plot, box plot, ecc.
Comprensione dei dati e Individuazione di pattern e tendenze:
- Statistiche descrittive, estrae dai dati o da serie di dati le informazioni contenute;
- statistiche inferenziali (o induttive), estrae da un campione dei dati le informazioni del campione, trasferendole poi sui
dati globali.
Statistiche descrittive univariate:
- misure di posizione media, mediana, moda,
- misure di dispersione varianza, deviazione standard.
Statistiche descrittive bivariate:
- misura di correlazione,
- grafici a dispersione e coefficienti di regressione.
Misure di posizione
- Moda: il valore che compare più frequentemente in un insieme di dati. (due o più mode se ugualmente frequenti).
1
- Media: Sample data y = [ y1, y2, y3, y4, …, yN ] → 𝑦 = 𝑁
Σ𝑦𝑖
- Media di medie (Averaging Averages): una media su una serie di medie, può portare a risultati distorti o inaffidabili. Non
tiene conto della distribuzione dei dati sottostanti o delle loro variazioni.
- Media ponderata (Weighted Average): ciascun valore contribuisce al risultato finale in modo proporzionale al suo peso
Σ 𝑤𝑖𝑦𝑖
relativo y = [ y1, y2, y3, y4, …, yN ] w = [ w1, w2, w3, w4, …, wN ] → 𝑦 = Σ 𝑤𝑖
- Mediana: il valore al centro della serie dopo aver ordinato la serie stessa.
Misure di dispersione
2 1 2
- Varianza: 𝑆𝑥 = 𝑛
Σ(𝑥𝑖 − 𝑥)
1 2
- Deviazione standard: espresso con la stessa unità di misura dei valori osservati → 𝑆𝑥 = 𝑛
Σ(𝑥𝑖 − 𝑥)
Misure di correlazione
La Correlazione indica il grado di relazione tra due variabili. Rappresenta quanto le variazioni in una variabile sono associate alle
variazioni nell'altra variabile. Movimento simultaneo attraverso una relazione statistica. Variazione simultanea "indotta" dalla
variazione di un terzo effetto comune. Le correlazioni sono utili perché possono indicare una relazione predittiva che può essere
sfruttata in pratica.
La relazione tra due variabili può essere rappresentata graficamente su un asse x e y (scatter plot).
Correlazione e Causalità: due cose che vanno insieme non implicano necessariamente una relazione di causa. Una variabile può
essere fortemente correlata a un'altra, senza per questo causarla. La correlazione non implica necessariamente causalità. Quando
c'è una correlazione tra X e Y. X causa Y o Y causa X, o entrambi? O c'è una terza variabile Z che causa sia X che Y, e quindi, X e Y
sono correlati?
Coefficiente di correlazione di Pearson - coefficiente di correlazione lineare
𝑐𝑜𝑣(𝑥,𝑦)
ρ(𝑥, 𝑦) = σ𝑥σ𝑦
1
𝑐𝑜𝑣(𝑥, 𝑦) = 𝑛 Σ(𝑥𝑖 − 𝑥)(𝑦𝑖 − 𝑦)
[+1, -1] dove
- +1 perfetta correlazione lineare positiva
- 0 assenza di correlazione lineare
- -1 perfetta correlazione lineare negativa

Una correlazione di Pearson pari a zero indica che i valori non sono correlati linearmente. Tuttavia, è possibile che ci sia
comunque una correlazione.
Coefficiente di correlazione si Spearman
Non presuppone una relazione lineare tra le variabili o che le variabili siano normalmente distribuite. Confrontiamo i ranghi dei
valori delle due variabili da confrontare.
𝑐𝑜𝑣(𝑅(𝑥), 𝑅(𝑦))
𝑟 𝑠
= ρ𝑅(𝑥), 𝑅(𝑦) = σ𝑅(𝑥)σ𝑅(𝑦)

Coefficiente di correlazione di Kendall


È una misura non parametrica di correlazione.Misura l'associazione ordinale tra due quantità misurate. concordanti: se (xi < xj)
and (yi < yj) o viceversa.
(𝑛𝑢𝑚𝑒𝑟𝑜 𝑐𝑜𝑝𝑝𝑖𝑒 𝑐𝑜𝑛𝑐𝑜𝑟𝑑𝑎𝑛𝑡𝑖) − (𝑛𝑢𝑚𝑒𝑟𝑜 𝑐𝑜𝑝𝑝𝑖𝑒 𝑑𝑖𝑠𝑐𝑜𝑟𝑑𝑎𝑛𝑡𝑖) 2(𝑛𝑢𝑚𝑒𝑟𝑜 𝑐𝑜𝑝𝑝𝑖𝑒 𝑑𝑖𝑠𝑐𝑜𝑟𝑑𝑎𝑛𝑡𝑖)
τ = (𝑛𝑢𝑚𝑒𝑟𝑜 𝑑𝑖 𝑐𝑜𝑝𝑝𝑖𝑒)
= 1 − 𝑛
( )
2

La correlazione può ingannare se non si dispone delle informazioni complete su ciascuna delle variabili. Una correlazione tra due
variabili è più piccola se l'intervallo di una o entrambe le variabili è troncato. Poiché non è disponibile l'intera variazione di una
variabile, non ci sono informazioni sufficienti per vedere le due variabili covariate insieme. Gli outlier sono data point che si
discostano così nettamente dal resto dei dati da poter influenzare il coefficiente di correlazione.
P - value livello di significatività
La significatività della relazione è espressa in livelli di probabilità: p (ad esempio, significativo con p = 0,05). Questo indica
quanto sia improbabile che un dato coefficiente di correlazione, si verifichi in assenza di una relazione nella popolazione (ipotesi
nulla).
- Più piccolo è il livello di p, più significativa è la relazione.
- Più grande è il coefficiente di correlazione, più forte è la relazione.
Il coefficiente p dovrebbe essere inferiore a 0,05.
OUTLIERS
Outlier: un data point che si discosta significativamente dalle altre osservazioni, dovuto alla variabilità del dataset o può indicare
un errore nell'unità di misura, errore di misurazione.
Standard Deviation Method per rilevare gli outliers: se sappiamo che la distribuzione dei valori nel campione è gaussiana o simile
a una distribuzione gaussiana, possiamo utilizzare la deviazione standard del campione come limite per identificare gli outlier.
- 3x deviazioni standard dalla media è un limite comune nella pratica per identificare gli outlier in una distribuzione
gaussiana o simile a una distribuzione gaussiana. [m - 3std, m + 3std].
- Per campioni piccoli di dati - 2x std (95%) - per campioni grandi di dati - 4x std (99.9%).

MATPLOTLIB
Matplotlib è una libreria completa per creare visualizzazioni statiche, animate e interattive in Python. Ha una vasta gamma di
funzionalità per creare grafici di alta qualità per la comunicazione e l'analisi dei dati.
Factor and component analysis
Dobbiamo "ridurre" il problema a un insieme più piccolo di fattori per costruire analisi dei dati più efficace nello spazio
dimensionale ridotto ma senza perdere molte informazioni. Le combinazioni di variabili osservate possono essere basi più
efficaci per le intuizioni, anche se il significato fisico è oscuro.
Dove troviamo la migliore discriminazione tra gli elementi e dove osserviamo i fenomeni chiave sottostanti? (Aree di varianza).
Aree di maggior “segnale”: indicano dove c'è una forte presenza di informazione nei dati.
Se due elementi o dimensioni sono altamente correlati o dipendenti, è probabile che rappresentino fenomeni correlati.
(dipendenza e correlazione).
Se due elementi o dimensioni ci informano sulla stessa varianza sottostante nei dati, combinarle per formare una singola misura
è ragionevole:
- parsimonia,
- riduzione dell'errore (riduce la variabilità).
Obiettivo: concentrarci sulle variabili non correlate o indipendenti, quelle con alta varianza e ottenere un insieme più piccolo di
variabili che spieghi la maggior parte della varianza nei dati.
Principal Component Analysis
La PCA è la forma più comune di analisi dei fattori. Le nuove variabili/dimensioni:
- sono combinazioni lineari di quelle originali,
- sono non correlate tra loro,
- ortogonali nello spazio dimensionale originale,
- catturano il più possibile la varianza originale nei dati.
La PCA (Principal Component Analysis) è la forma più comune di analisi dei fattori. Si tratta di un metodo di proiezione lineare
utilizzato per ridurre il numero di parametri trasferendo un insieme di variabili correlate in un nuovo insieme di variabili non
correlate, aumentando così l'espressività dei dati. Questo processo consente di mappare i dati in uno spazio di dimensionalità
inferiore (k << d), semplificando il problema. La PCA è una forma di apprendimento non supervisionato, poiché non richiede
l'immissione di conoscenza esterna.
Algoritmo PCA:
1. si crea la matrice X dei dati (N x d) - ogni riga xn è un data point, un’osservazione;
2. si standardizza X - da ciascun vettore di riga xn togliamo il vettore delle medie e dividiamo per la deviazione standard;
3. si calcola Σ, la matrice di covarianza di X - descrive le relazioni tra le variabili;
4. si trovano gli autovettori e gli autovalori di Σ;
5. si prendono i k autovettori con i più grandi autovalori.
6. queste sono le componenti principali.
Esempio:

Ogni punto rappresenta un’osservazione (il data point xn). x1 e x2 sono le due colonne. Creiamo la nostra tabella X(N, M).

Calcoliamo la media e la deviazione standard per ogni colonna (x1 e x2). Sottraiamo la media e dividiamo per la deviazione
standard ogni componente nella matrice dei dati X. Otteniamo un cambio di sistema di coordinate.

Calcoliamo la matrice di covarianza di X che rappresenta le relazioni tra le diverse variabili nel dataset.
X: dati (N, M)
cov(X): matrice di covarianza (M, M) chiamata anche Σ

Troviamo gli autovettori e gli autovalori della matrice di covarianza Σ. Obiettivo: cerchiamo le direzioni di massima varianza.
Possiamo vederlo con un problema di massimizzazione con vincoli (vogliamo massimizzare la varianza). Vogliamo trovare la
combinazione lineare
ta = X v a
tale che Var(ta) sia massimo,
dove Var(ta) = (X va)T(X va) = vaTXTX va = vaTΣva
vincoliamo vaTva = 1 per soluzione univoca
possiamo risolverlo usando i moltiplicatori di Lagrange e la soluzione è data dagli autovettori ortogonali di Σ.
Σ va = λa va

Prendiamo i k autovettori con più grandi autovalori. Abbiamo trovato le k componenti principali, in questo caso 2.

PC1 è la prima componente (con la maggior varianza).


PC2 è la seconda componente (la seconda con maggior varianza).
Lo scopo di PCA è essenzialmente decomporre la matrice dei dati X in:
- una parte che si riferisce alle variabili (loadings V),
- una parte che si riferisce ai campioni (scores T),
- la parte non modellata: i residui E.

PCA - Punteggi - Scores


I punteggi (scores) e i carichi (loadings) sono concetti fondamentali nella PCA che aiutano a comprendere la relazione tra le
variabili originali e le componenti principali.
Punteggi (Scores):
- rappresentano le coordinate dei campioni (osservazioni) nel nuovo sistema di coordinate definito dalle componenti
principali,
- ogni campione viene proiettato nello spazio delle componenti principali e le sue coordinate in questo spazio sono i
punteggi.
Es: un punteggio elevato su una determinata componente principale indica che il campione ha una grande influenza su quella
componente.
PCA - Carichi - Loadings
I punteggi (scores) e i carichi (loadings) sono concetti fondamentali nella PCA che aiutano a comprendere la relazione tra le
variabili originali e le componenti principali.
Carichi (Loadings):
- rappresentano le relazioni tra le variabili originali e le componenti principali,
- ogni carico mostra quanto una variabile originale contribuisce a una particolare componente principale,
- i carichi possono essere interpretati come i pesi delle variabili originali nel calcolo delle componenti principali,
- il segno del carico indica la direzione della relazione, carico positivo → una correlazione positiva, carico negativo → una
correlazione negativa.
Problemi e limitazioni
Cosa succede se i dati sono di dimensioni molto grandi? Es: d = 563 (d > 100).
Problema: la matrice di covarianza Σ è di dimensioni (d2). Soluzione:
- decomposizione del valore singolare (SVD),
- sono disponibili algoritmi efficienti,
- alcune implementazioni trovano solo i primi N autovettori.
Singular Value Decomposition (SVD)
Ogni matrice (di qualsiasi dimensione) può essere decomposta.
XN,M = UN,MSM,MVTM,M
UTU = I (autovettori sinistra)
VTV = I (autovettori destra)
Sono ortonormali, S è diagonale (autovalori singolari).
Scelta di k
La scelta di k determina quanto sarà la riduzione dimensionale che andrò a calcolare. A volte la scelta di k è obbligatoria.
Altre volte invece l'obiettivo è eliminare informazioni inutili e preservare gran parte del contenuto informativo.
Come:
- fissiamo t la percentuale del contenuto informativo che si desidera conservare (es t = 80%),
- scegliamo il valore minimo di k per il quale la somma dei k maggiori autovalori, rispetto alla somma di tutti gli
autovalori, è maggiore o uguale a t.
Ricordiamo che gli autovalori indicano la varianza lungo gli assi diversi.

Scree Plot:
- mostra la varianza spiegata da ciascuna delle componenti principali
- asse x, le componenti principali in ordine decrescente di importanza (dalla più importante alla meno importante)
- asse y, la varianza spiegata da ciascuna componente.
Lo Scree Plot è utile per determinare il "gomito" della curva, ovvero il punto in cui la varianza spiegata comincia a diminuire
notevolmente. Di solito, si scelgono le prime componenti principali prima del "gomito", in quanto contengono la maggior parte
delle informazioni utili nei dati.
SCIKIT - LEARN
Cosa offre:
- strumenti semplici ed efficienti per l'analisi predittiva dei dati-Strumenti accessibili a tutti e riutilizzabili in vari
contesti,
- basato su NumPy, SciPy e matplotlib,
- open source, utilizzabile anche a fini commerciali - licenza BSD
Task:
- classification,
- regression,
- clustering,
- dimensionality reduction,
- model selection,
- preprocessing.
Presentazione dei risultati
Plot Scores:
- vengono visualizzati i punteggi delle osservazioni rispetto alle prime due o tre componenti principali,
- permette di visualizzare la distribuzione dei dati nel nuovo spazio delle componenti principali,
- è utile per identificare cluster, pattern o anomalie nei dati.
Plot Loadings:
- mostra i carichi (loadings) delle variabili rispetto alle prime due o tre componenti principali,
- visualizza come le variabili originali contribuiscono alla definizione di ciascuna componente principale,
- identifica quali variabili sono fortemente correlate con ciascuna componente e quindi quali caratteristiche dei dati sono
più importanti nell'analisi.
CLUSTERING
Cluster (o gruppo): raggruppamento di osservazioni (data point) che sono simili tra loro rispetto a determinate caratteristiche o
attributi.
- Oggetti intra-cluster: Simili rispetto a ciascun oggetto nello stesso cluster.
- Oggetti inter-cluster: Dissimili rispetto agli oggetti in altri cluster.
Analisi di cluster:
- analisi per individuare gruppi di oggetti simili nella tabella di dati da analizzare,
- tecnica di unsupervised learning,
- organizzare i dati in classi in modo tale che vi sia alta similarità intra-classe e bassa similarità inter-classe.
Non gerarchico, ogni istanza viene collocata in esattamente in uno dei K cluster non sovrapposti. Poiché viene prodotto un solo
insieme di cluster, l'utente deve specificare il numero di cluster K desiderato.
Obiettivi:
- individuare gruppi di oggetti simili,
- raggruppare oggetti simili.
Nell’ Analisi di Cluster quindi bisogna definire un criterio di similarità (somiglianza). Questo determina una prima
“classificazione” dei diversi metodi di clustering:

Criterio di Similarità: un cluster è un gruppo di oggetti più simili tra loro (vicini in senso Euclideo) rispetto agli altri.
Criterio di Densità: un cluster è una regione (nello spazio delle variabili dove sono rappresentati i campioni) la cui densità (n°
campioni) eccede un dato valore di soglia threshold)
Similarità
Le più comuni misure di similarità sono:
- distanza euclidea (oggetti più vicini, più simili)

- distanza di Mahalanobis (tiene conto che ci sono variabili correlate)

- coefficiente di correlazione (coefficiente di Pearson)

- distanza di Hamming D = c/(a + b + c)


- similarità di Tanimoto D = a + b - 2c
Hierarchical: creano una decomposizione gerarchica dell'insieme di oggetti utilizzando un certo criterio.
Consideriamo i nostri dati in una struttura ad albero:
- ogni sample è una foglia,
- ogni nodo è un possibile cluster.
Dendrogramma: rappresentazione gerarchica dei dati Num. di dendrogrammi con n foglie = (2n -3)!/[(2(n -2)) (n -2)!].
Poiché non possiamo testare tutti gli alberi possibili, dovremo fare una ricerca euristica di tutti gli alberi possibili.
Bottom-Up (agglomerativo):
- scegliere la misura di similarità,
- si parte con ogni elemento è un cluster,
- trovare la migliore coppia da unire in un nuovo cluster,
- ripetere finché tutti i cluster non sono fusi insieme.
Serve un criterio per decidere come aggregare campioni/cluster.
- Single linkage (vicino più prossimo): in questo metodo la distanza tra due cluster è determinata dalla distanza dei due
oggetti più vicini (vicini più prossimi) nei diversi cluster.
- Complete linkage (più lontano): in questo metodo, le distanze tra i cluster sono determinate dalla distanza maggiore tra
due oggetti nei diversi cluster (ossia, dai "vicini più lontani").
- Group average linkage: in questo metodo, la distanza tra due cluster è calcolata come la distanza media tra tutte le coppie
di oggetti nei due diversi cluster.
- Wards Linkage: in questo metodo, cerchiamo di minimizzare la varianza dei cluster uniti.
Non c'è bisogno di specificare il numero di cluster in anticipo. La natura gerarchica si adatta bene all'intuizione umana per alcuni
domini. Non si adattano bene alla scalabilità: complessità temporale di almeno O(n2), dove n è il numero totale di oggetti. Come
tutti gli algoritmi di ricerca euristica, i massimi locali sono un problema. L'interpretazione dei risultati è (molto) soggettiva.
Top-Down (divisivo):
- si parte con un solo cluster con tutti gli elementi,
- considerare ogni modo per dividere il cluster in 2,
- scegliere la divisione migliore,
- ripetere ricorsivamente su entrambi i lati.
Partitioning: costruiscono varie partizioni e poi le valutano secondo un certo criterio.
K - means:
- scegli K = numero di cluster desiderato,
- dividi gli oggetti in K clusters in modo casuale,
- calcola il centroide (punto medio) di ciascun cluster,
- calcola la distanza di ogni oggetto da ciascun centroide,
- riassegna l’oggetto al cluster definito dal centroide a cui è più vicino,
- se almeno un oggetto viene cambiato di cluster torna al punto 3, altrimenti esci.
Step 0 - 1
Dato un insieme di campioni di cui potremmo non conoscere il numero di cluster e potremmo non conoscere la distribuzione,
definiamo K il numero di cluster.
Step 2
Dividi gli oggetti in K cluster, anche in modo casuale.
Step 3
Calcola il centroide (punto medio) di ciascun cluster. L’assegnamento a cluster era stato fatto in modo casuale. Quindi i centroidi
dei tre cluster combaceranno.
Step 4 - 5 - 6
Calcola la distanza di ogni oggetto da ciascun centroide. Riassegna l’oggetto al cluster definito dal centroide a cui è più vicino.
Almeno un oggetto è stato cambiato di cluster?
Step 3
Ricalcola il centroide (punto medio) di ciascun cluster. I centroidi ora sono più indicativi dei cluster.
Step 4 - 5 - 6
Ricalcola la distanza di ogni oggetto da ciascun centroide. Riassegna l’oggetto al cluster definito dal centroide a cui è più vicino.
Almeno un oggetto è stato cambiato di cluster? Ormai i cambiamenti sono minimi ma ci sono.
Step 3
Ricalcola il centroide (punto medio) di ciascun cluster. I centroidi ora sono più indicativi dei cluster.
Step 4 - 5 - 6
Ricalcola la distanza di ogni oggetto da ciascun centroide. Riassegna l’oggetto al cluster definito dal centroide a cui è più vicino.
Almeno un oggetto è stato cambiato di cluster? No in questo caso nessun punto è stato riassegnato ad un altro cluster e termina
l’esecuzione.
Partitioning Clustering
Pro:
- relativamente efficiente: O(tkn),
- dove n è #oggetti, k è #cluster e t è #iterazioni,
- normalmente: k, t << n,
- spesso termina in un ottimo locale,
- l'ottimo globale può essere trovato utilizzando tecniche quali: deterministic annealing e algoritmi genetici.
Debolezza:
- applicabile solo quando la media è definita - dati categorici?
- necessità di specificare in anticipo k (numero di cluster),
- incapaci di gestire dati rumorosi e outlier,
- non adatto a scoprire cluster con forme non convesse.
Clustering spettrale
Algoritmi di clustering basati sullo spettro di una matrice. Questi metodi utilizzano le proprietà degli autovalori e degli
autovettori di una matrice associata ai dati per effettuare il clustering. Utilizzati per identificare cluster in dati non lineari o con
forme complesse. Utilizzano concetti provenienti dalla teoria dei grafi e dall'analisi degli autovalori per identificare la struttura
intrinseca dei dati e suddividerli in cluster coerenti.
- Algoritmi che raggruppano i punti utilizzando gli autovettori delle matrici derivate dai dati.
- Ottenere una rappresentazione dei dati nello spazio a bassa dimensione che può essere facilmente clusterizzato.
- Varietà di metodi che utilizzano gli autovettori in modo diverso.
- Difficile da comprendere....
Spectral clustering:
1. Costruzione della matrice di similarità (W)
Si calcola una matrice che rappresenta la similarità tra i punti nel dataset. Rappresenta una matrice di adiacenza di un grafo
pesato. Le formule per calcolare possono variare a seconda del contesto e della natura dei dati.

Funzione di similarità del kernel gaussiano


2. Riduzione dimensionale (D)
Una volta ottenuta la matrice di similarità (W) si calcola la matrice diagonale (D) contenente le somme delle righe di (W).
3. Calcolo della Laplaciana normalizzata (L)
L può essere definita come la differenza tra la matrice diagonale dei gradi dei nodi (D) e la matrice di similarità (W).
4. Calcolo degli autovalori e autovettori (λ, v)
Calcola gli autovalori e gli autovettori della Laplaciana normalizzata (L). Gli autovalori (λ) e gli autovettori (v) soddisfano
l’equazione: Lv = λv. Gli autovettori corrispondenti agli autovalori più piccoli rappresentano le dimensioni principali del
sottospazio in cui i dati possono essere proiettati.
5. Clustering usando gli autovettori
Raggruppare i dati utilizzando gli autovettori corrispondenti agli autovalori più piccoli. È particolarmente efficace nel rilevare
cluster di forma arbitraria o non lineare, e può gestire dataset con distribuzioni diverse o non uniformi.
Teoria dei grafi
Un grafo G = (V,E)
- V insieme di vertici,
- E insieme di archi.
Se G è un directed graph, ogni arco è una coppia ordinata di vertici. Se G è un bipartite graph, i nodi divisi in due gruppi, gli archi
solo tra vertici di gruppi diversi. Se G è un weighted graph:
- Vertex weights ai > 0 per i ∈ V
- Edge weights wi,j > 0 per i,j ∈ E
Matrice di similarità
Similarity graph G = (V,E,W)
- V insieme di vertici (data point),
- E insieme di archi (se similarità > 0),
- W insieme degli edge weights (similarità)
Min - cut: partizione del grafo in due insiemi A e B tali che il peso degli spigoli che collegano i vertici in A ai vertici in B sia
minimo.

REAL USE - CASE


File samsungData.csv (circa 46 MB)
Si tratta di: misurazioni prese dall’accelerometro e dal giroscopio embedded negli smartphone Samsung di diversi soggetti che
stanno compiendo diverse attività.
Il giroscopio e l’accelerometro: servono per esempio in Google Maps, servono per ruotare l’interfaccia in base alla posizione dello
smartphone.
Data collection
Dati rilevati su 30 volontari tra 19 e 48 anni. Ognuno ha compiuto le 6 attività specificate portando lo smartphone attaccato alla
cintura.
Catturati i dati sull’accelerazione lineare su 3 assi X, Y e Z. I dati nel file sono il risultato di un sampling sui dati originali (1 dato
ogni 3 secondi).
Attività:
- walk
- walk down (walkdown)
- walk upstairs (walkup)
- standing
- sitting
- laying.
Exploratory analysis
Il file è in formato CSV (comma separated values, ‘valori separati da virgola’) sono file di testo semplici che contengono elenchi di
dati.
Il file contiene 7352 righe e 563 colonne.
Ogni riga rappresenta una serie di misurazioni su un soggetto durante una certa attività. Ogni colonna rappresenta i valori di una
determinata misurazione.
La penultima colonna rappresenta il soggetto (identificativo numerico). L'ultima colonna rappresenta l'attività svolta. La prima
colonna non ha nome e rappresenta un indice numerico dei record.
Obiettivo dello studio
1. Verificare se sia possibile, a partire dai dati rilevati dall'accelerometro e dal giroscopio dello smartphone, capire in modo
automatico quale sia l'attività umana in corso.
2. Perché? Filone di ricerca: Inferire il contesto da dati provenienti da sensori / dispositivi (es. accelerometro) per adattare il
comportamento del dispositivo intelligente al contesto.
3. Determinare quali attività possono essere automaticamente identificate dall'analisi dei dati e quali dati permettono di
farlo (facilitano il riconoscimento).
4. L'identificazione dei dati migliori per la classificazione delle attività può avvalersi di tecniche statistiche avanzate (PCA).
5. Verificare la scelta dei dati applicando tecniche di visualizzazione o di clustering automatico.
Ci aspettiamo che le attività che non implicano movimento non siano distinguibili le une dalle altre (laying, sitting, standing), le
attività di WALK, WALKUP e WALKDOWN possano essere distinte sfruttando i valori dell'accelerazione del corpo sui tre assi.
Apertura file
Navigate nella directory in cui avete salvato il file. Tasto destro, proprietà. Dovreste trovare il percorso del file.
Una volta individuato il file ed esserci assicurati che esista e che esista il path al file, dobbiamo aprire il file. Un file può essere
aperto in diverse modalità. Lo apriamo sia quando vogliamo leggere che quando vogliamo scrivere
Partendo dal nostro file samsungData.csv proviamo ad aprirlo in lettura e a popolare una nostra struttura dati. Popolare: riempire
cioè una variabile con il contenuto del file.
Lettura file
Siamo riusciti ad aprire in lettura e a leggere il contenuto di un file. Purtroppo quello che abbiamo ottenuto è una lista di stringhe.
Sappiamo che i nostri dati sono in formato tabellare, e i valori all’interno sono numeri.
Partendo dalla lista di stringhe ottenuta precedentemente. Memorizziamo i dati in un array e iniziamo la nostra prima analisi.
Calcoliamo la media la deviazione standard per ogni sensore e la media e la deviazione standard per l’intera matrice.
Forse l’aspetto più complicato è stato la gestione dei dati letti dal file (lista di stringhe) e la conversione/riempimento di un array
numpy con i numeri corrispondenti. Sappiamo che i nostri dati sono in formato tabellare, e i valori all’interno sono numeri.
Pickle è molto potente e utile, ma in questo caso non c’è qualcosa di più semplice? Siamo riusciti ad aprire in lettura e a leggere il
contenuto di un file come lista di stringhe. Come sfruttare il fatto di avere un file .csv?
Partendo dal DataFrame dell’esercizio precedente. Ricalcoliamo la media la deviazione standard per ogni sensore e la media e la
deviazione standard per l’intera matrice dei nostri dati.
Ora che abbiamo capito alcune cose in più, calcoliamo:
- misure di posizione media, mediana, moda
- misure di dispersione varianza e deviazione standard
- correlazione.
Non tutte, quelle che crediamo possano servire alla nostra analisi (Ricordare le ipotesi e gli obiettivi).
Factor and component analysis
Partiamo dal nostro spazio a d dimensioni (d = 563). Sappiamo cosa rappresentano:
- tBodyAcc-mean()-X,
- tBodyAcc-mean()-Y,
- tBodyAcc-mean()-Z.
Lo riduciamo a k dimensioni:
- variabili correlate le aggreghiamo,
- manteniamo le variabili indipendenti.
Cosa rappresentano le nuove k variabili? Come facciamo a ridurre la dimensione a k?
Se è andato tutto a buon fine siamo riusciti a calcolare e implementare la nostra prima PCA. Abbiamo però una PCA a 561
componenti. Avevamo invece detto che k << d! Guardando la documentazione vediamo che possiamo specificare un parametro
relativo al numero di componenti: n_component.
Torniamo al nostro notebook. Proviamo a identificare il numero di componenti sufficienti che vogliamo utilizzare. Proviamo a
trovare k che preservi il contenuto informativo. Proviamo a visualizzare lo Scree Plot.

Potrebbero piacerti anche