Introduzione
Spesso quando pensiamo al mondo dell’analisi pensiamo sempre a dei dati sperimentali numerici che poi dovranno essere in qualche modo elaborati e poi, in seguito, visualizzati su grafico.
In realtà l’analisi non ha pregiudizio per quanto riguarda la tipologia di “dato da analizzare”, questo oltre che il semplice numero, può essere anche un’immagine, un suono o anche un testo. Ed è proprio l’analisi dei testi, ed in particolare del linguaggio utilizzato, di cui parleremo in questo articolo.
Infatti, lo scopo di questo articolo è quello di introdurre la libreria NLTK, una libreria Python che permette il Language Processing e l’analisi dei testi in generale. Vedremo come installarlo sul nostro computer ed effettueremo i primi approcci per comprendere meglio come funziona e come ci potrà essere utile.
Installazione di NLTK
L’installazione della libreria all’interno di Python è molto semplice, dato che è disponibile come pacchetto PyPI.
Per installarla su ambienti Python 3.x
$ pip3 install nltk
Per installarla su ambienti Python 2.7.x
$ pip install nltk
Comunque per questo articolo e per tutti gli articoli che verranno pubblicati su questo sito, l’ambiente di riferimento sarà sempre Python 3.x.
Language Processing
Prima di cominciare introduciamo alcuni concetti chiave di cui bisogna tenere conto quando si ha a che fare con il Language Processing.
- milioni di parole come dati
- contesto
- vocabolario
- linguaggio
- evoluzione del linguaggio
Uno dei principali problemi quando si affronta l’analisi di un testo è la grande quantità di parole (dati) in esso contenuti, spesso si parla di analizzare e confrontare diverse opere con migliaia e migliaia di pagine (milioni di parole). Devono quindi essere disponibili strumenti che permettano di elaborare tale mole di dati, e a questo l’informatica ci viene davvero in aiuto.
A tal proposito, sono state sviluppate molte tecniche di analisi che si basano principalmente su strumenti di statistica e su algoritmi complessi di ricerca. Molte di queste tecniche e strumenti sono disponibili nella libreria NLTK.
Un altro aspetto importante nell’analisi dei testi è che le parole presenti in un testo non occupano una posizione casuale, ma si trovano in un particolare contesto. Il contesto (context) è un concetto molto importante perché comporta la relazione che una parola ha con le parole che la procedono e la seguono.
Infatti chi analizza i testi, sa benissimo l’importanza di come le parole vengono inserite all’interno di una frase, Spesso alcune parole vengono accoppiate con altre parole per modificarne il significato, per darne un contesto positivo o negativo, o per mutarne il significato. Da qui partono le basi per l’Analisi Emozionale del testo.
Inoltre tali modalità di accoppiamento delle parole, variano da autore ad autore, perché il vocabolario utilizzato da ogni persona è unico. Quindi una analisi del contesto all’interno di testo ci potrebbe confermare la paternità o meno di un testo da parte di un autore.
Un altro aspetto importante di cui tenere conto è il linguaggio con cui il testo è stato scritto. Nel mondo sono moltissime le lingue utilizzate ed ognuna si basa su un proprio vocabolario ed una diversa modalità di come strutturare le frasi. Una buona analisi del linguaggio dovrà tenerne conto quando dovrà essere applicata a linguaggi differenti.
Un altro campo di applicazione del Language Processing è l’evoluzione del linguaggio nel tempo. Infatti non solo l’uso di un linguaggio ed il suo vocabolario cambia da individuo ad individuo, ma si modifica ed evolve nel tempo. Alcuni vocaboli verranno abbandonati, sostituiti da altri nuovi, spesso spinti dalle evoluzioni sociali e da nuovi concetti introdotto. Ma la maggior parte dei vocaboli si melle paroleodifica nel tempo in maniera naturale, alcune consonanti o vocali vengono modificate, cambiando gradualmente il modo in cui queste vengono pronunciate.
Tante sono le applicazioni che il Language Processing ci offre, e moltissimi sono gli strumenti che ci vengono dalla libreria NLTK. Ecco perchè questa libreria rappresenta davvero un potente strumento per tutti quelli che operano nel settore dell’analisi del testo.
Iniziamo con NLTK
Adesso che abbiamo installato la libreria, possiamo cominciare a lavorare con alcuni dati già preparati, correlati a NLTK, che sono già disponibili per poter effettuare delle prove.
Si possono scaricare questi dati direttamente dall’interprete di Python.
import nltk nltk.download()
Dall’interprete si aprirà un menu a pannelli in cui si possono selezionare molti dati (per esempio libri) ed estensioni da utilizzare con la libreria NLTK.
Oppure se non è abilitata la parte grafica nel vostro sistema operativo, vi apparirà la versione testuale.
In questo caso scrivete solo:
d book
In entrambe i casi, effettuate il download della NLTK book collection. Vedrete un elenco di opere letterarie scaricate nel vostro sistema.
Per vedere l’elenco dei libri appena scaricati e disponibili per i nostri test è sufficiente scrivere:
from nltk.book import *
E in pochi secondi vi apparirà la lista completa.
Come potete vedere ciascun libro è identificato con una variabile text#.
Quindi conoscendo i testi disponibili la prossima volta si potrà importare direttamente il testo desiderato.
from nltk.book import text3
Il testo (book) appena importato (text3) è il libro della Genesi in inglese (The book of Genesis). Il testo è in un formato facilmente manipolabile da parte della libreria, cioè è un array, in cui ciascun elemento è una stringa di caratteri. Quindi può essere manipolato come tale.
Si può accedere ad un determinato elemento specificando il suo indice tra le parentesi quadre.
print(text3[143])
Oppure ricercare l’indice dell’elemento contenente una determinata parola.
print(text3.index('angels'))
Come in questo caso, se una determinata parola è contenuta più volte nel testo, l’indice corrispondente sarà quello della prima occorrenza, cioè l’elemento con l’indice più basso a contenere quella parola.
Come per tutti gli array, valgono le regole dello slicing.
Quindi se vogliamo stampare le prime 40 parole del testo, possiamo scrivere:
print(text3[1:41])
Abbiamo ottenuto tutti gli elementi contenuti nei primi 40 elementi dell’array. Se invece vogliamo avere l’array scritto in forma testuale è meglio questa opzione:
print((' ').join(text3[1:41]))
Otteniamo così una riga di testo più leggibile.
Il Dizionario
Nella parte introduttiva abbiamo visto che uno dei concetti fondamentali della analisi del linguaggio è il dizionario. Vediamo una serie di esempi con cui la libreria NLTK ci permette di lavorare concretamente con questo concetto.
Ogni testo è caratterizzato da un particolare set di parole, che identificheremo come dizionario (vocabulary). Questo spesso è legato alla particolare tematica del libro e al particolare autore che lo ha scritto (questo vale se il testo è in lingua originale).
Iniziamo con la cosa più semplice da fare, quella di misurare di quante parole è composto il testo in analisi.
print(len(text3))
Ottenendo.
Il numero che abbiamo ottenuto non è esattamente il numero delle parole presenti nel testo, bensì token. Questo è il termine tecnico per una sequenza di caratteri definiti agli estremi da due spazi. Quindi possono essere parole, ma anche simboli, punteggiatura, emoticos, e quant’altro. Per comprendere meglio questo, possiamo utilizzare la funzione set(). Questa funzione raggruppa in un insieme tutte i token presenti in un testo eliminando i duplicati. Se poi ordiniamo i token presenti all’interno in ordine alfabetico, quello che otterremo potremo considerarlo come il nostro dizionario di token.
print(sorted(set(text3)))
Otterremo un array con tutti i token presenti nel testo in ordine alfabetico.
Per conoscere le dimensioni del dizionario:
print(len(set(text3)))
Una metrica interessante che si può usare nell’analizzare un testo è la sua ricchezza lessicale, cioè quanti più termini diversi vengono utilizzati in un testo. Questa misura si può ottenere semplicemente calcolando il rapporto tra i token presenti nel dizionario e i token presenti nel testo (non le parole 😉 ).
print(len(set(text3)) / len(text3))
Questi valori sono relativi all’intero dizionario presente in un testo, ma è possibile effettuare un approccio anche sulle singole parole (token). Per esempio, vogliamo sapere quante volte una parola è presente in un testo. Per esempio la parola “angels” nel libro della Genesi (text3). Si può facilmente ottenere questo valore con il metodo count().
print(text3.count("angels"))
ottenendo 4
Frequency Distribution
Adesso che abbiamo ben chiaro il concetto di dizionario, una funzione utile per le nostre analisi è la distribuzione di frequenza delle parole. Cioè una funzione che ci dica quanto le parole del dizionario sono presenti all’interno del testo tramite un semplice conteggio su cui si potranno fare delle statistiche.
Per ottenere questo, la libreria NLTK ci fornisce una semplice funzione FreqDist() che applicata ad un testo crea un oggetto contenente tutte le informazioni sulla frequenza delle parole del testo. Esistono poi una serie di metodi per poi ottenere le informazioni al suo interno.
Per esempio vediamo quali sono le 12 parole più frequenti all’interno del libro della Genesi utilizzando il metodo most_common().
dist = nltk.FreqDist(text3) print(dist) print() print(dist.most_common(12))
Come potete vedere dal codice, la distribuzione è un oggetto. Inoltre come potevamo intuire facilmente, le 12 parole (o meglio token) più frequenti sono caratteri di punteggiatura, preposizioni e congiunzioni.
Le parole più lunghe
Un’altra tipologia di ricerca a cui si può pensare è quella in base alla lunghezza delle parole. Per esempio, potremmo essere interessati a conoscere tutte le parole più lunghe di 12 caratteri presenti in un testo. In questo caso si crea dapprima un set (dizionario array contenente tutti i vocaboli presenti all’interno di un testo). Poi si effettua la scansione elemento per elemento (by element) per selezionare solo quelle che rispondono al requisito di essere più lunghe di 12 caratteri. Ecco il codice dell’esempio:
S = set(text3) longest = [i for i in S if len(i) > 12] print(sorted(longest)
Eseguendolo si troveranno le parole presenti nel libro della Genesi più lunghe.
Il Contesto
Adesso che abbiamo visto come ottenere e lavorare su un dizionario, introduciamo un’altra serie di esempi che ci permettano di lavorare su un altro importantissimo concetto nell’analisi del linguaggio: il contesto delle parole.
Ricercare le occorrenze di una stringa in un testo
Una delle operazioni base che si esegue più spesso è la ricerca di una parola in un testo. In NLTK si può fare questa operazione mediante la funzione concordance().
Per esempio vogliamo vedere se è presente la parola “angels” nel libro della Genesi.
text3.concordance("angels")
Otterremo il seguente risultato:
Come possiamo vedere ci sono 4 occorrenze. Vengono quindi mostrate 4 diverse frasi. Come si può ben vedere, la parola “angels” compare al centro di ciascuna riga, e prima e dopo ci sono le parole che la precedono o la seguono nel testo. Questo viene chiamato contesto (context). Come abbiamo detto in precedenza, questo concetto è molto importante nell’analisi del linguaggio.
Quindi concordance() ci permette di vedere le parole ricercate nel loro contesto.
Da qui, con NLTK si può eseguire un passo ulteriore. Per esempio… dall’analisi del contesto si possono trovare parole simili a quella ricercata?
Proviamo ad utilizzare la funzione similar().
Riscriviamo tutto quello che abbiamo fatto programmaticamente (in modo che il tutto possa essere inserito in un codice di programma e non interattivamente con l’interprete).
import nltk nltk.download('book') from nltk.book import text3 text3.similar("angels")
Otterremo il seguente risultato
Questo è l’elenco delle parole che compaiono in un contesto simile alla parola “angels” e che quindi possono essere in qualche modo correlate ad essa e che svolgono nel testo una funzione simile (vediamo infatti la parola god).
Questo tipo di analisi non serve tanto a trovare dei sinonimi alla parola “angels”, quanto a caratterizzare l’autore ed il suo dizionario (che differisce da autore ad autore). Per esempio, nella genesi la parola “angels” viene utilizzata nel significato “sacro” associandola ad altre parole molto comuni nel senso biblico e religioso.
Se invece analizzeremo la stessa parola in un altro testo, per esempio Moby Dick (text1)
import nltk nltk.download('book') from nltk.book import text1 text1.concordance("angels") print(" ") text1.similar("angels")
Otterremo
In questo caso, rimarremo sorpresi nello scoprire che la parola “angels”, per il senso comune correlata al mondo del sacro e del religioso, è più presente nel libro di Moby Dick che nella Genesi. Dal contesto delle 9 occorrenze si vedono molte citazioni prese dalla bibbia e dal tema religioso in generale. Questo ci suggerisce che l’autore di Moby Dick usa frequentemente citazioni bibliche, che tiene molto in uso la parola “angels” anche se poi il contesto generale del libro riporta termini legati alla pesca e alla navigazione. Basta vedere i risultati di similar().
Collocation
Introduciamo ora altri due concetti inerenti alla tematica del contesto. Una collocation è una sequenza di parole che nel testo appaiono molto spesso insieme (questo anche nel gergo parlato). Per esempio “vino rosso” è un buon esempio di collocazione, mentre “vino blu” o “vino verde” no. Anzi questi due ultimi esempi ci fanno subito risaltare un senso di stranezza. Questo perchè le collocazioni sono molto radicate nel nostro senso comune di intendere il linguaggio. Potremmo spesso intendere le collocazioni come l’associazioni di più parole che spesso vengono considerate come specificativo di un concetto unico nella nostra mente. In altri linguaggi queste collocazioni sono più evidenti dato che vengono rese esplicite tramite accorpamento di più parole per crearne una nuova del dizionario. Il tedesco è un ottimo esempio in questo caso: Rotwein è un termine presente nel dizionario (Rot = rosso) e (Wein = vino). Nel Tedesco si tende molto spesso ad accorpare più parole per crearne di nuove.
text3.collocations()
Dispersion Plot
Un’altra operazione interessante è quella di vedere graficamente la distribuzione nel testo di alcune determinate parole. A volte può essere interessante sapere se determinate parole sono equamente distribuite nel testo, o si trovano esclusivamente in una determinata parte del testo.
Per fare questo si usa il dispertion plot di NLTK.
text2.dispertion_plot([“god”,”angels”,”genesis”,”Abraham”])