Nel text mining, un gruppo importante di metriche riguarda la frequenza delle parole (o di qualsiasi token) in un certo corpus di documenti testuali. Tuttavia, puoi usare anche un insieme aggiuntivo di metriche nei casi in cui ogni documento abbia un valore numerico associato che descrive un certo attributo del documento.
Alcuni esempi:
Tweet e il rispettivo numero di interazioni.
URL con relative pageview e frequenze di rimbalzo.
Titoli di film e incassi lordi.
Keyword con impression, clic e conversioni.
In questo tutorial,
per prima cosa realizzerai una semplice funzione che calcola e confronta lโoccorrenza assoluta e ponderata delle parole in un corpus di documenti. Questo a volte puรฒ far emergere tendenze e aggregazioni nascoste che non sono necessariamente chiare osservando solo la top ten circa. Spesso possono anche differire dalla frequenza assoluta delle parole.
Poi vedrai un set di dati reale (titoli di film e incasso lordo), con lโobiettivo di scoprire tendenze nascoste. Anticipo: in qualche modo salterร fuori lโamore!
Userai Python come linguaggio di programmazione e sfrutterai la struttura dati defaultdict del modulo collections per il grosso del lavoro, oltre ai DataFrame di pandas per gestire lโoutput finale.
Frequenza assoluta e ponderata delle parole: introduzione
Supponiamo che tu abbia due tweet e che il loro contenuto e il numero di impression (visualizzazioni) siano i seguenti:
Testo del tweet
Visualizzazioni
spain
800
france
200
ร semplice fare lโanalisi di base e scoprire che le parole sono divise 50:50 tra "france" e "spain". In molti casi questo รจ tutto ciรฒ che hai, e puoi solo misurare la frequenza assoluta delle parole e cercare di inferire certe relazioni. In questo caso, perรฒ, hai anche dei dati su ciascuno dei documenti.
La frequenza ponderata qui รจ chiaramente diversa, e la divisione รจ 80:20. In altre parole, sebbene "spain" e "france" compaiano una volta ciascuna nei tuoi tweet, dalla prospettiva dei lettori la prima รจ apparsa 800 volte, mentre la seconda 200 volte. Cโรจ una grande differenza!
Frequenza semplice delle parole usando defaultdict
Ora considera questo esempio leggermente piรน articolato per un set simile di documenti:
Documento
Visualizzazioni
france
200
spain
180
spain beaches
170
france beaches
160
spain best beaches
160
Ora cicli sui documenti, li dividi in parole e conti le occorrenze di ciascuna parola:
from collections import defaultdict
import pandas as pd
text_list =['france','spain','spain beaches','france beaches','spain best beaches']
word_freq = defaultdict(int)for text in text_list:for word in text.split():
word_freq[word]+=1
pd.DataFrame.from_dict(word_freq, orient='index') \
.sort_values(0, ascending=False) \
.rename(columns={0:'abs_freq'})Offerto da
ย
abs_freq
spain
3
beaches
3
france
2
best
1
Nel ciclo sopra, la prima riga itera su text_list uno alla volta. La seconda riga (allโinterno di ciascun documento) itera sulle parole di ogni elemento, divise per lo spazio (che avrebbe potuto essere anche un altro carattere ("-", ",", "_", ecc.)).
Quando provi ad assegnare un valore a word_freq[word] ci sono due possibili scenari:
La chiave word esiste: in tal caso lโassegnazione avviene (aggiungendo uno)
La chiave word non รจ in word_freq: in questo caso defaultdict chiama la funzione di default a cui รจ stato assegnato quando รจ stato definito, che in questo caso รจ int.
Quando int viene chiamato restituisce zero. Ora la chiave esiste, il suo valore รจ zero, ed รจ pronta a ricevere un +1.
Anche se nella prima tabella la parola in cima era "france", dopo aver contato tutte le parole allโinterno di ciascun documento vediamo che "spain" e "beaches" sono a pari merito al primo posto. Questo รจ importante per far emergere tendenze nascoste, soprattutto quando lโelenco di documenti con cui stai lavorando รจ dellโordine delle decine o centinaia di migliaia.
Frequenza ponderata delle parole
Ora che hai contato le occorrenze di ciascuna parola nel corpus di documenti, vuoi vedere la frequenza ponderata. Cioรจ, vuoi vedere quante volte le parole sono apparse ai tuoi lettori, rispetto a quante volte le hai usate.
Nella prima tabella, la frequenza assoluta delle parole era divisa equamente tra "spain" e "france", ma "spain" aveva chiaramente molto piรน peso, perchรฉ il suo valore era 800, contro i 200 di "france".
Ma qual รจ la frequenza ponderata delle parole per la seconda tabella, leggermente piรน complessa?
Scopriamolo!
Puoi riutilizzare parte del codice di sopra, con alcune aggiunte:
# default value is now a list with two ints
word_freq = defaultdict(lambda:[0,0])# the `views` column you had in the first DataFrame
num_list =[200,180,170,160,160]# looping is now over both the text and the numbersfor text, num inzip(text_list, num_list):for word in text.split():# same as before
word_freq[word][0]+=1# new line, incrementing the numeric value for each word
word_freq[word][1]+= num
columns ={0:'abs_freq',1:'wtd_freq'}
abs_wtd_df = pd.DataFrame.from_dict(word_freq, orient='index') \
.rename(columns=columns) \
.sort_values('wtd_freq', ascending=False) \
.assign(rel_value=lambda df: df['wtd_freq']/ df['abs_freq']) \
.round()
abs_wtd_df.style.background_gradient(low=0, high=.7, subset=['rel_value'])Offerto da
ย
abs_freq
wtd_freq
rel_value
spain
3
510
170
beaches
3
490
163
france
2
360
180
best
1
160
160
Alcune osservazioni:
Sebbene "france" fosse la frase piรน alta in assoluto, "spain" e "beaches" sembrano piรน prominenti quando consideri la frequenza ponderata.
rel_value รจ una semplice divisione per ottenere il valore per occorrenza di ciascuna parola.
Guardando rel_value, vedi anche che, pur essendo "france" piuttosto bassa nella metrica wtd_freq, sembra avere potenziale, perchรฉ il valore per occorrenza รจ alto. Questo potrebbe suggerire, per esempio, di aumentare la copertura di contenuti su "france".
Potresti anche voler aggiungere altre metriche che mostrino le percentuali e le percentuali cumulative di ciascun tipo di frequenza, cosรฌ da avere una prospettiva migliore su quante parole costituiscono la maggior parte del totale, se ce ne sono:
Altro si puรฒ analizzare, e con piรน dati in genere arrivano piรน sorprese.
Come potrebbe apparire tutto questo in un contesto reale con dati reali?
Esaminerai i titoli dei film, vedrai quali parole sono piรน usate nei titoli โ cioรจ la frequenza assoluta โ e quali parole sono associate al maggior incasso, ovvero la frequenza ponderata.
Boxoffice Mojo ha un elenco di oltre 15.000 film, insieme al relativo incasso lordo e posizioni in classifica. Inizia facendo scraping dei dati usando requests e BeautifulSoup โ Puoi giร esplorare Boxoffice Mojo qui se ti va:
ย
import requests
from bs4 import BeautifulSoupOfferto da
final_list =[]for i inrange(1,156):ifnot i%10:print(i)
page ='http://www.boxofficemojo.com/alltime/domestic.htm?page='+str(i)+'&p=.htm'
resp = requests.get(page)
soup = BeautifulSoup(resp.text,'lxml')# trial and error to get the exact positions
table_data =[x.text for x in soup.select('tr td')[11:511]]# put every 5 values in a row
temp_list =[table_data[i:i+5]for i inrange(0,len(table_data[:-4]),5)]for temp in temp_list:
final_list.append(temp)Offerto da
Vedrai che alcuni valori numerici hanno caratteri speciali ($, , e ^), e alcuni valori sono in realtร N/A. Quindi devi sistemarli:
na_year_idx =[i for i, x inenumerate(final_list)if x[4]=='n/a']# get the indexes of the 'n/a' values
new_years =[1998,1999,1960,1973]# got them by checking onlineprint(*[(i, x)for i, x inenumerate(final_list)if i in na_year_idx], sep='\n')print('new year values:', new_years)Offerto da
(8003,['8004','Warner Bros. 75th Anniversary Film Festival','WB','$741,855','n/a'])(8148,['8149','Hum Aapke Dil Mein Rahte Hain','Eros','$668,678','n/a'])(8197,['8198','Purple Moon (Re-issue)','Mira.','$640,945','n/a'])(10469,['10470','Amarcord','Jan.','$125,493','n/a'])newyearvalues:[1998,1999,1960,1973]Offerto da
for na_year, new_year inzip(na_year_idx, new_years):
final_list[na_year][4]= new_year
print(final_list[na_year], new_year)Offerto da
['8004','Warner Bros. 75th Anniversary Film Festival','WB','$741,855',1998]1998['8149','Hum Aapke Dil Mein Rahte Hain','Eros','$668,678',1999]1999['8198','Purple Moon (Re-issue)','Mira.','$640,945',1960]1960['10470','Amarcord','Jan.','$125,493',1973]1973Offerto da
Ora trasformi lโelenco in un DataFrame di pandas assegnando alle colonne i nomi appropriati e convertendo ai tipi di dato desiderati.
import re
regex ='|'.join(['\$',',','\^'])
columns =['rank','title','studio','lifetime_gross','year']
boxoffice_df = pd.DataFrame({'rank':[int(x[0])for x in final_list],# convert ranks to integers'title':[x[1]for x in final_list],# get titles as is'studio':[x[2]for x in final_list],# get studio names as is'lifetime_gross':[int(re.sub(regex,'', x[3]))for x in final_list],# remove special characters and convert to integer'year':[int(re.sub(regex,'',str(x[4])))for x in final_list],# remove special characters and convert to integer})print('rows:', boxoffice_df.shape[0])print('columns:', boxoffice_df.shape[1])print('\ndata types:')print(boxoffice_df.dtypes)
boxoffice_df.head(15)Offerto da
rows:15500columns:5
data types:
lifetime_gross int64
rank int64
studio object
title object
year int64
dtype: object
Offerto da
ย
lifetime_gross
rank
studio
title
year
0
936662225
1
BV
Star Wars: The Force Awakens
2015
1
760507625
2
Fox
Avatar
2009
2
681084109
3
BV
Black Panther
2018
3
659363944
4
Par.
Titanic
1997
4
652270625
5
Uni.
Jurassic World
2015
5
623357910
6
BV
Marvel's The Avengers
2012
6
620181382
7
BV
Star Wars: The Last Jedi
2017
7
534858444
8
WB
The Dark Knight
2008
8
532177324
9
BV
Rogue One: A Star Wars Story
2016
9
504014165
10
BV
Beauty and the Beast (2017)
2017
10
486295561
11
BV
Finding Dory
2016
11
474544677
12
Fox
Star Wars: Episode I - The Phantom Menace
1999
12
460998007
13
Fox
Star Wars
1977
13
459005868
14
BV
Avengers: Age of Ultron
2015
14
448139099
15
WB
The Dark Knight Rises
2012
La parola "star" รจ tra le prime, poichรฉ compare in cinque dei primi quindici film, e sai anche che la saga di Star Wars ha ancora piรน film, diversi dei quali in alto in classifica.
Ora utilizziamo il codice sviluppato e vediamo come funziona su questo set di dati. Non cโรจ nulla di nuovo nel codice seguente, semplicemente lo racchiudi tutto in una funzione:
defword_frequency(text_list, num_list, sep=None):
word_freq = defaultdict(lambda:[0,0])for text, num inzip(text_list, num_list):for word in text.split(sep=sep):
word_freq[word][0]+=1
word_freq[word][1]+= num
columns ={0:'abs_freq',1:'wtd_freq'}
abs_wtd_df =(pd.DataFrame.from_dict(word_freq, orient='index').rename(columns=columns ).sort_values('wtd_freq', ascending=False).assign(rel_value=lambda df: df['wtd_freq']/ df['abs_freq']).round())
abs_wtd_df.insert(1,'abs_perc', value=abs_wtd_df['abs_freq']/ abs_wtd_df['abs_freq'].sum())
abs_wtd_df.insert(2,'abs_perc_cum', abs_wtd_df['abs_perc'].cumsum())
abs_wtd_df.insert(4,'wtd_freq_perc', abs_wtd_df['wtd_freq']/ abs_wtd_df['wtd_freq'].sum())
abs_wtd_df.insert(5,'wtd_freq_perc_cum', abs_wtd_df['wtd_freq_perc'].cumsum())return abs_wtd_df
word_frequency(boxoffice_df['title'], boxoffice_df['lifetime_gross']).head()Offerto da
ย
abs_freq
abs_perc
abs_perc_cum
wtd_freq
wtd_freq_perc
wtd_freq_perc_cum
rel_value
The
3055
0.068747
0.068747
67518342498
0.081167
0.081167
22100930.0
the
1310
0.029479
0.098227
32973100860
0.039639
0.120806
25170306.0
of
1399
0.031482
0.129709
30180592467
0.036282
0.157087
21572975.0
and
545
0.012264
0.141973
12188113847
0.014652
0.171739
22363512.0
2
158
0.003556
0.145529
9032673058
0.010859
0.182598
57168817.0
Non sorprende che le "stop word" siano in cima, cosa piuttosto comune per la maggior parte delle raccolte di documenti. Inoltre sono duplicate, dove alcune sono in maiuscolo e altre no. Quindi hai due cose chiare da gestire:
Rimuovere tutte le stop word: puoi farlo aggiungendo un nuovo parametro alla funzione e fornendo la tua lista di stop word.
Gestire tutte le parole in minuscolo per rimuovere i duplicati
Ecco un semplice aggiornamento della funzione (nuovo parametro rm_words, e anche le righe 6, 7 e 8):
# words will be expandeddefword_frequency(text_list, num_list, sep=None, rm_words=('the','and','a')):
word_freq = defaultdict(lambda:[0,0])for text, num inzip(text_list, num_list):for word in text.split(sep=sep):# This should take care of ignoring the word if it's in the stop wordsif word.lower()in rm_words:continue# .lower() makes sure we are not duplicating words
word_freq[word.lower()][0]+=1
word_freq[word.lower()][1]+= num
columns ={0:'abs_freq',1:'wtd_freq'}
abs_wtd_df =(pd.DataFrame.from_dict(word_freq, orient='index').rename(columns=columns ).sort_values('wtd_freq', ascending=False).assign(rel_value=lambda df: df['wtd_freq']/ df['abs_freq']).round())
abs_wtd_df.insert(1,'abs_perc', value=abs_wtd_df['abs_freq']/ abs_wtd_df['abs_freq'].sum())
abs_wtd_df.insert(2,'abs_perc_cum', abs_wtd_df['abs_perc'].cumsum())
abs_wtd_df.insert(4,'wtd_freq_perc', abs_wtd_df['wtd_freq']/ abs_wtd_df['wtd_freq'].sum())
abs_wtd_df.insert(5,'wtd_freq_perc_cum', abs_wtd_df['wtd_freq_perc'].cumsum())
abs_wtd_df = abs_wtd_df.reset_index().rename(columns={'index':'word'})return abs_wtd_dfOfferto da
from collections import defaultdict
word_freq_df = word_frequency(boxoffice_df['title'],
boxoffice_df['lifetime_gross'],
rm_words=['of','in','to','and','a','the','for','on','&','is','at','it','from','with'])
word_freq_df.head(15).style.bar(['abs_freq','wtd_freq','rel_value'],
color='#60DDFF')# E6E9EBOfferto da
ย
word
abs_freq
abs_perc
abs_perc_cum
wtd_freq
wtd_freq_perc
wtd_freq_perc_cum
rel_value
0
2
158
0.00443945
0.00443945
9032673058
0.0137634
0.0137634
5.71688e+07
1
star
45
0.0012644
0.00570385
5374658819
0.00818953
0.0219529
1.19437e+08
2
man
195
0.00547907
0.0111829
3967037854
0.0060447
0.0279976
2.03438e+07
3
part
41
0.00115201
0.0123349
3262579777
0.00497129
0.0329689
7.95751e+07
4
movie
117
0.00328744
0.0156224
3216050557
0.00490039
0.0378693
2.74876e+07
Diamo unโocchiata allo stesso DataFrame ordinato in base a abs_freq:
(word_freq_df.sort_values('abs_freq', ascending=False).head(15).style.bar(['abs_freq','wtd_freq','rel_value'],
color='#60DDFF'))Offerto da
ย
word
abs_freq
abs_perc
abs_perc_cum
wtd_freq
wtd_freq_perc
wtd_freq_perc_cum
rel_value
26
love
211
0.00592863
0.069542
1604206885
0.00244438
0.111399
7.60288e+06
Ora visualizziamo per confrontare entrambe e vedere le tendenze nascoste:
import matplotlib.pyplot as plt
plt.figure(figsize=(20,8))
plt.subplot(1,2,1)
word_freq_df_abs = word_freq_df.sort_values('abs_freq', ascending=False).reset_index()
plt.barh(range(20),list(reversed(word_freq_df_abs['abs_freq'][:20])), color='#288FB7')for i, word inenumerate(word_freq_df_abs['word'][:20]):
plt.text(word_freq_df_abs['abs_freq'][i],20-i-1,
s=str(i+1)+'. '+ word +': '+str(word_freq_df_abs['abs_freq'][i]),
ha='right', va='center', fontsize=14, color='white', fontweight='bold')
plt.text(0.4,-1.1, s='Number of times the word was used in a movie title; out of 15500 movies.', fontsize=14)
plt.text(0.4,-1.8, s='Data: boxofficemojo.com Apr. 2018. Feedback: @eliasdabbas', fontsize=14)
plt.vlines(range(0,210,10),-1,20, colors='gray', alpha=0.1)
plt.hlines(range(0,20,2),0,210, colors='gray', alpha=0.1)
plt.yticks([])
plt.xticks([])
plt.title('Words Most Used in Movie Titles', fontsize=22, fontweight='bold')# =============
plt.subplot(1,2,2)# plt.axis('off')
plt.barh(range(20),list(reversed(word_freq_df['wtd_freq'][:20])), color='#288FB7')for i, word inenumerate(word_freq_df['word'][:20]):
plt.text(word_freq_df['wtd_freq'][i],20-i-1,
s=str(i+1)+'. '+ word +': '+'$'+str(round(word_freq_df['wtd_freq'][i]/1000_000_000,2))+'b',
ha='right', va='center', fontsize=14, color='white', fontweight='bold')
plt.text(0.4,-1.1, s='Alltime boxoffice revenue of all movies whos title contained the word. (Top word is "2") ', fontsize=14)
plt.text(0.4,-1.8, s='Data collection & methodology: http://bit.ly/word_freq', fontsize=14)
plt.vlines(range(0,9_500_000_000,500_000_000),-1,20, colors='gray', alpha=0.1)
plt.hlines(range(0,20,2),0,10_000_000_000, colors='gray', alpha=0.1)
plt.xlim((-70_000_000,9_500_000_000))
plt.yticks([])
plt.xticks([])
plt.title('Words Most Associated With Boxoffice Revenue', fontsize=22, fontweight='bold')
plt.tight_layout(pad=0.01)
plt.show()Offerto da
Sembra che, almeno nella mente di produttori e sceneggiatori, lโamore conquisti tutto! ร la parola piรน usata in tutti i titoli dei film. Non รจ perรฒ cosรฌ in alto quando si passa alla frequenza ponderata (incasso al botteghino).
In altre parole, se guardi tutti i titoli dei film, la parola "love" sarebbe quella che incontreresti piรน spesso. Ma stimando quale parola sia apparsa di piรน agli occhi degli spettatori (usando lโincasso lordo come metrica), allora "2", "star" e "man" sarebbero le piรน viste, o comunque associate al maggior incasso.
Per essere chiari: questi sono calcoli molto semplici. Quando dici che la frequenza ponderata della parola "love" รจ 1.604.106.767, significa semplicemente che la somma degli incassi lordi a vita di tutti i film il cui titolo includeva la parola "love" รจ stata quella cifra.
ร anche interessante che "2" sia la parola in cima. Ovviamente non รจ una parola, ma indica che i secondi capitoli delle serie cinematografiche ammontano a una somma molto grande. Lo stesso vale per "3", che รจ in quinta posizione. Nota che anche "part" e "ii" sono nella top ten, a conferma dello stesso fatto.
"American" e "movie" hanno un alto valore relativo.
Una nota rapida sulle stop word usate in questa funzione
Di solito forniresti un elenco piรน completo di stop word rispetto a quello qui usato, soprattutto se stai lavorando con articoli o post sui social. Ad esempio il pacchetto nltk fornisce elenchi di stop word in diverse lingue, che puoi scaricare e utilizzare.
Le parole qui sono state scelte dopo alcune verifiche sui film principali. Molte di queste sono solitamente considerate stop word, ma nel caso dei titoli di film, aveva senso mantenerne alcune perchรฉ potrebbero dare qualche indizio. Per esempio, le parole "I", "me", "you" potrebbero suggerire certe dinamiche sociali. Un altro motivo รจ che i titoli di film sono frasi molto brevi, e stiamo cercando di ricavarne il maggior significato possibile.
Puoi sicuramente provare con il tuo set di parole e ottenere risultati leggermente diversi.
Guardando alla lista originale dei titoli dei film, vediamo che alcune delle parole principali non compaiono nemmeno nella top ten, ed รจ proprio questo il tipo di insight che cerchiamo di far emergere con questo approccio.
Successivamente, ha senso esplorare meglio le parole principali piรน interessanti. Filtriamo i film che contengono "2" e vediamo:
(boxoffice_df[boxoffice_df['title'].str.contains('2 | 2',case=False)]# spaces used to exclude words like '2010'.head(10))Offerto da
ย
lifetime_gross
rank
studio
title
year
15
441226247
16
DW
Shrek 2
2004
30
389813101
31
BV
Guardians of the Galaxy Vol. 2
2017
31
381011219
32
WB
Harry Potter and the Deathly Hallows Part 2
2011
35
373585825
36
Sony
Spider-Man 2
2004
38
368061265
39
Uni.
Despicable Me 2
2013
64
312433331
65
Par.
Iron Man 2
2010
79
292324737
80
LG/S
The Twilight Saga: Breaking Dawn Part 2
2012
87
281723902
88
LGF
The Hunger Games: Mockingjay - Part 2
2015
117
245852179
118
BV
Toy Story 2
1999
146
226164286
147
NL
Rush Hour 2
2001
Diamo anche unโocchiata ai principali film con "star":
boxoffice_df[boxoffice_df['title'].str.contains('star | star',case=False)].head(10)Offerto da
ย
lifetime_gross
rank
studio
title
year
0
936662225
1
BV
Star Wars: The Force Awakens
2015
6
620181382
7
BV
Star Wars: The Last Jedi
2017
8
532177324
9
BV
Rogue One: A Star Wars Story
2016
11
474544677
12
Fox
Star Wars: Episode I - The Phantom Menace
1999
12
460998007
13
Fox
Star Wars
1977
33
380270577
34
Fox
Star Wars: Episode III - Revenge of the Sith
2005
65
310676740
66
Fox
Star Wars: Episode II - Attack of the Clones
2002
105
257730019
106
Par.
Star Trek
2009
140
228778661
141
Par.
Star Trek Into Darkness
2013
301
158848340
302
Par.
Star Trek Beyond
2016
E, infine, i principali film con "man":
boxoffice_df[boxoffice_df['title'].str.contains('man | man',case=False)].head(10)Offerto da
ย
lifetime_gross
rank
studio
title
year
18
423315812
19
BV
Pirates of the Caribbean: Dead Man's Chest
2006
22
409013994
23
BV
Iron Man 3
2013
35
373585825
36
Sony
Spider-Man 2
2004
48
336530303
49
Sony
Spider-Man 3
2007
53
330360194
54
WB
Batman v Superman: Dawn of Justice
2016
59
318412101
60
Par.
Iron Man
2008
64
312433331
65
Par.
Iron Man 2
2010
82
291045518
83
WB
Man of Steel
2013
176
206852432
177
WB
Batman Begins
2005
182
202853933
183
Sony
The Amazing Spider-Man 2
2014
Prossimi passi e miglioramenti
Come primo passo, potresti cercare di ottenere piรน parole: i titoli dei film sono estremamente brevi e spesso non veicolano il significato letterale delle parole. Per esempio, un padrino dovrebbe essere una persona che assiste al battesimo di un bambino e promette di prendersene cura (o magari un mafioso che uccide per piacere?!).
Un ulteriore esercizio potrebbe essere quello di ottenere descrizioni piรน dettagliate, oltre al titolo del film. Per esempio:
"A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers."
ci dice molto di piรน del film rispetto a "The Matrix".
In alternativa, potresti anche prendere una delle seguenti strade per arricchire la tua analisi:
Analisi statistica migliore: gestione di valori estremi/outlier, uso di altre metriche.
Text mining: raggruppare parole e argomenti simili ("happy", "happiness", "happily", ecc.).
Analisi granulare: eseguire la stessa funzione per anni/decenni diversi o per determinati studi di produzione.
Potresti essere interessato a esplorare direttamente questi dati e altri ancora:
Ok, ora hai esplorato il conteggio delle parole nei titoli dei film e visto la differenza tra frequenze assolute e ponderate, e come trascurarne una possa far perdere una parte importante del quadro. Hai anche visto i limiti di questo approccio e alcune proposte per migliorare la tua analisi.
Hai creato una funzione dedicata che puoi eseguire facilmente per analizzare qualsiasi set di dati testuali simili con numeri, e sai come questo puรฒ migliorare la comprensione di questo tipo di dati.
Provala analizzando le prestazioni dei tuoi tweet, gli URL del tuo sito, i tuoi post su Facebook o qualsiasi altro set di dati simile che potresti incontrare.
Potrebbe essere piรน semplice clonare il repository con il codice e provare tu stesso.
La funzione word_frequency fa parte del pacchetto advertools, che puoi scaricare e provare nel tuo lavoro/ricerca.
Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocitร dei workflow.
Impara le basi dellโanalisi dei dati con Python in sole 4 ore. Questo corso online ti introduce allโinterfaccia di Python e ai pacchetti piรน usati.
Nel text mining, un gruppo importante di metriche riguarda la frequenza delle parole (o di qualsiasi token) in un certo corpus di documenti testuali. Tuttavia, puoi usare anche un insieme aggiuntivo di metriche nei casi in cui ogni documento abbia un valore numerico associato che descrive un certo attributo del documento.
Alcuni esempi:
In questo tutorial,
defaultdictdel modulocollectionsper il grosso del lavoro, oltre aiDataFramedi pandas per gestire lโoutput finale.Frequenza assoluta e ponderata delle parole: introduzione
Supponiamo che tu abbia due tweet e che il loro contenuto e il numero di impression (visualizzazioni) siano i seguenti:
ร semplice fare lโanalisi di base e scoprire che le parole sono divise 50:50 tra "france" e "spain". In molti casi questo รจ tutto ciรฒ che hai, e puoi solo misurare la frequenza assoluta delle parole e cercare di inferire certe relazioni. In questo caso, perรฒ, hai anche dei dati su ciascuno dei documenti.
La frequenza ponderata qui รจ chiaramente diversa, e la divisione รจ 80:20. In altre parole, sebbene "spain" e "france" compaiano una volta ciascuna nei tuoi tweet, dalla prospettiva dei lettori la prima รจ apparsa 800 volte, mentre la seconda 200 volte. Cโรจ una grande differenza!
Frequenza semplice delle parole usando
defaultdictOra considera questo esempio leggermente piรน articolato per un set simile di documenti:
Ora cicli sui documenti, li dividi in parole e conti le occorrenze di ciascuna parola:
Nel ciclo sopra, la prima riga itera su
text_listuno alla volta. La seconda riga (allโinterno di ciascun documento) itera sulle parole di ogni elemento, divise per lo spazio (che avrebbe potuto essere anche un altro carattere ("-", ",", "_", ecc.)).Quando provi ad assegnare un valore a
word_freq[word]ci sono due possibili scenari:wordesiste: in tal caso lโassegnazione avviene (aggiungendo uno)wordnon รจ inword_freq: in questo casodefaultdictchiama la funzione di default a cui รจ stato assegnato quando รจ stato definito, che in questo caso รจint.Quando
intviene chiamato restituisce zero. Ora la chiave esiste, il suo valore รจ zero, ed รจ pronta a ricevere un +1.Anche se nella prima tabella la parola in cima era "france", dopo aver contato tutte le parole allโinterno di ciascun documento vediamo che "spain" e "beaches" sono a pari merito al primo posto. Questo รจ importante per far emergere tendenze nascoste, soprattutto quando lโelenco di documenti con cui stai lavorando รจ dellโordine delle decine o centinaia di migliaia.
Frequenza ponderata delle parole
Ora che hai contato le occorrenze di ciascuna parola nel corpus di documenti, vuoi vedere la frequenza ponderata. Cioรจ, vuoi vedere quante volte le parole sono apparse ai tuoi lettori, rispetto a quante volte le hai usate.
Nella prima tabella, la frequenza assoluta delle parole era divisa equamente tra "spain" e "france", ma "spain" aveva chiaramente molto piรน peso, perchรฉ il suo valore era 800, contro i 200 di "france".
Ma qual รจ la frequenza ponderata delle parole per la seconda tabella, leggermente piรน complessa?
Scopriamolo!
Puoi riutilizzare parte del codice di sopra, con alcune aggiunte:
Alcune osservazioni:
rel_valueรจ una semplice divisione per ottenere il valore per occorrenza di ciascuna parola.rel_value, vedi anche che, pur essendo "france" piuttosto bassa nella metricawtd_freq, sembra avere potenziale, perchรฉ il valore per occorrenza รจ alto. Questo potrebbe suggerire, per esempio, di aumentare la copertura di contenuti su "france".Potresti anche voler aggiungere altre metriche che mostrino le percentuali e le percentuali cumulative di ciascun tipo di frequenza, cosรฌ da avere una prospettiva migliore su quante parole costituiscono la maggior parte del totale, se ce ne sono:
Altro si puรฒ analizzare, e con piรน dati in genere arrivano piรน sorprese.
Come potrebbe apparire tutto questo in un contesto reale con dati reali?
Esaminerai i titoli dei film, vedrai quali parole sono piรน usate nei titoli โ cioรจ la frequenza assoluta โ e quali parole sono associate al maggior incasso, ovvero la frequenza ponderata.
Boxoffice Mojo ha un elenco di oltre 15.000 film, insieme al relativo incasso lordo e posizioni in classifica. Inizia facendo scraping dei dati usando
requestseBeautifulSoupโ Puoi giร esplorare Boxoffice Mojo qui se ti va:Vedrai che alcuni valori numerici hanno caratteri speciali (
$,,e^), e alcuni valori sono in realtรN/A. Quindi devi sistemarli:Ora trasformi lโelenco in un
DataFramedi pandas assegnando alle colonne i nomi appropriati e convertendo ai tipi di dato desiderati.La parola "star" รจ tra le prime, poichรฉ compare in cinque dei primi quindici film, e sai anche che la saga di Star Wars ha ancora piรน film, diversi dei quali in alto in classifica.
Ora utilizziamo il codice sviluppato e vediamo come funziona su questo set di dati. Non cโรจ nulla di nuovo nel codice seguente, semplicemente lo racchiudi tutto in una funzione:
Non sorprende che le "stop word" siano in cima, cosa piuttosto comune per la maggior parte delle raccolte di documenti. Inoltre sono duplicate, dove alcune sono in maiuscolo e altre no. Quindi hai due cose chiare da gestire:
Ecco un semplice aggiornamento della funzione (nuovo parametro
rm_words, e anche le righe 6, 7 e 8):Diamo unโocchiata allo stesso DataFrame ordinato in base a
abs_freq:Ora visualizziamo per confrontare entrambe e vedere le tendenze nascoste:
Sembra che, almeno nella mente di produttori e sceneggiatori, lโamore conquisti tutto! ร la parola piรน usata in tutti i titoli dei film. Non รจ perรฒ cosรฌ in alto quando si passa alla frequenza ponderata (incasso al botteghino).
In altre parole, se guardi tutti i titoli dei film, la parola "love" sarebbe quella che incontreresti piรน spesso. Ma stimando quale parola sia apparsa di piรน agli occhi degli spettatori (usando lโincasso lordo come metrica), allora "2", "star" e "man" sarebbero le piรน viste, o comunque associate al maggior incasso.
Per essere chiari: questi sono calcoli molto semplici. Quando dici che la frequenza ponderata della parola "love" รจ 1.604.106.767, significa semplicemente che la somma degli incassi lordi a vita di tutti i film il cui titolo includeva la parola "love" รจ stata quella cifra.
ร anche interessante che "2" sia la parola in cima. Ovviamente non รจ una parola, ma indica che i secondi capitoli delle serie cinematografiche ammontano a una somma molto grande. Lo stesso vale per "3", che รจ in quinta posizione. Nota che anche "part" e "ii" sono nella top ten, a conferma dello stesso fatto.
"American" e "movie" hanno un alto valore relativo.
Una nota rapida sulle stop word usate in questa funzione
Di solito forniresti un elenco piรน completo di stop word rispetto a quello qui usato, soprattutto se stai lavorando con articoli o post sui social. Ad esempio il pacchetto
nltkfornisce elenchi di stop word in diverse lingue, che puoi scaricare e utilizzare.Le parole qui sono state scelte dopo alcune verifiche sui film principali. Molte di queste sono solitamente considerate stop word, ma nel caso dei titoli di film, aveva senso mantenerne alcune perchรฉ potrebbero dare qualche indizio. Per esempio, le parole "I", "me", "you" potrebbero suggerire certe dinamiche sociali. Un altro motivo รจ che i titoli di film sono frasi molto brevi, e stiamo cercando di ricavarne il maggior significato possibile.
Puoi sicuramente provare con il tuo set di parole e ottenere risultati leggermente diversi.
Guardando alla lista originale dei titoli dei film, vediamo che alcune delle parole principali non compaiono nemmeno nella top ten, ed รจ proprio questo il tipo di insight che cerchiamo di far emergere con questo approccio.
Successivamente, ha senso esplorare meglio le parole principali piรน interessanti. Filtriamo i film che contengono "2" e vediamo:
Diamo anche unโocchiata ai principali film con "star":
E, infine, i principali film con "man":
Prossimi passi e miglioramenti
Come primo passo, potresti cercare di ottenere piรน parole: i titoli dei film sono estremamente brevi e spesso non veicolano il significato letterale delle parole. Per esempio, un padrino dovrebbe essere una persona che assiste al battesimo di un bambino e promette di prendersene cura (o magari un mafioso che uccide per piacere?!).
Un ulteriore esercizio potrebbe essere quello di ottenere descrizioni piรน dettagliate, oltre al titolo del film. Per esempio:
ci dice molto di piรน del film rispetto a "The Matrix".
In alternativa, potresti anche prendere una delle seguenti strade per arricchire la tua analisi:
Potresti essere interessato a esplorare direttamente questi dati e altri ancora:
Ok, ora hai esplorato il conteggio delle parole nei titoli dei film e visto la differenza tra frequenze assolute e ponderate, e come trascurarne una possa far perdere una parte importante del quadro. Hai anche visto i limiti di questo approccio e alcune proposte per migliorare la tua analisi.
Hai creato una funzione dedicata che puoi eseguire facilmente per analizzare qualsiasi set di dati testuali simili con numeri, e sai come questo puรฒ migliorare la comprensione di questo tipo di dati.
Provala analizzando le prestazioni dei tuoi tweet, gli URL del tuo sito, i tuoi post su Facebook o qualsiasi altro set di dati simile che potresti incontrare.
Potrebbe essere piรน semplice clonare il repository con il codice e provare tu stesso.
La funzione
word_frequencyfa parte del pacchetto advertools, che puoi scaricare e provare nel tuo lavoro/ricerca.Dai unโocchiata e fammi sapere! @eliasdabbas
Dai unโocchiata al Tutorial sui dizionari in Python di DataCamp per imparare a creare la frequenza delle parole, e al tutorial Web Scraping & NLP in Python per imparare a tracciare le distribuzioni di frequenza delle parole.