Domanda Python \\ Operazioni Dataframe \\ Scraped Data \\ Clusterizzazione Manuale

valo83

Utente Iron
26 Novembre 2021
30
8
1
14
Ultima modifica:
Ciao a tutti,

per una serie di dati scaricati dai social, vorrei clusterizzare i singoli tweet o post secondo delle parole chiave che ho scritto in alcuni file txt.
Il data Frame è:

1640958860503.png
1640958895628.png


Il codice che ho scritto è:

Codice:
for row in frame:
    words = []
    words = frame["Cleaned Text into list"].loc
    for word in p_calcio:
        for word1 in words: #words list in a dataframe
            if word1 == word:
                frame["Cluster"] = 'Calcio'

Vorrei evitare di tracciare le sottostringhe: ad esempio se metto tra le parole dell'univerità "poli" questa parola è una sotto-stringa di Napoli. E quindi clusterizzata in maniera errata.
La colonna
Codice:
 frame["Cleaned Text into list"]
è di una lista di stringhe. Il file .txt è un elenco ogni parola a capo, tutte minuscole.

L'errore è:

KeyError: 1241

Non ho trovato nessuna documentazione.

Qualcuno mi può aiutare?

Grazie mille
Messaggio unito automaticamente:

Questo codice per una singola riga, dovrebbe funzionare.

Codice:
if_contains = ['one', 'Timone', 'Tisix','Two eight nine']
save = []
words = ['one', 'five', 'Tisix', 'two eight nine']

for word in words:
    if word in if_contains:
        save.append(word)

print(save)

Ma probabilmente mi sto incartando con i cicli For annidati
Messaggio unito automaticamente:

Inoltre vorrei anche implementare per concetti formati da due parole:
1640961929831.png

Qui ad esempio mi piacerebbe che trovasse "università degli studi di torino" come stringa di 3 parole (stopwords a parte).

Grazie mille
 
Ciao di nuovo :)

Python:
words = frame["Cleaned Text into list"].loc

Questa linea di codice è incompleta e suppongo che si completi con .loc[1241]. Se ho ragione questa linea causa l'errore. Se vuoi cercare una specifica row attraverso un indice numerico, non devi usare loc ma iloc.

Python:
if_contains = ['one', 'Timone', 'Tisix','Two eight nine']
save = []
words = ['one', 'five', 'Tisix', 'two eight nine']

for word in words:
    if word in if_contains:
        save.append(word)

print(save)

Tutto questo codice può essere reso molto più semplice usando l'intersezione tra i sets. Eccoti un esempio:
Python:
# Inserisci la stringa su cui eseguire la ricerca e trasformala in set
stringa = "Ciao io sono giorgio"
set_ricerca = set(stringa.split(" "))

# Ipotizziamo tu voglia cercare queste due parole
set_chiavi = {"ciao", "Giorgio"}

# Nota come per via delle maiuscole e minuscole non concordanti il riscontro non troverebbe le parole inserite
# Quindi mettiamo tutte le parola in maiuscolo
set_ricerca = set(map(lambda x: x.upper(), set_ricerca))
set_chiavi = set(map(lambda x: x.upper(), set_chiavi))

# Calcola l'intersezione
match = set_ricerca & set_chiavi

print(match)
#OUTPUT -> {"GIORGIO", "CIAO"}
Il set da con le parole chiave ("set_chiavi") lo puoi anche estrapolare dal file in questione con un readlines. Inoltre ti consiglio di mettere in maiuscolo (o minuscolo) tutte le parole che su cui stai eseguendo la ricerca e le parole chiave, questo fa in modo che ci sia il riscontro anche quando le lettere maiuscole e minuscole non coincidono.

Questo metodo sopra non riesce a cercare parole che contengano più spazi, come "università degli studi di torino", ma è qualcosa che si può sistemare con qualche linea di codice ma pensa prima a far funzionare la versione più basilare.

Spero di averti dato almeno un punto su cui partire.
 
  • Mi piace
Reazioni: valo83
Ultima modifica:
Direi che questo codice è ok
ma non trovo / non sono in grado di fare una corretta sintassi di ciclo for per un dataset.
Questo è il problema principale....
Messaggio unito automaticamente:

Direi che questo codice è ok
ma non trovo / non sono in grado di fare una corretta sintassi di ciclo for per un dataset.
Questo è il problema principale....
Codice:
p_politica01=open('parole_politica.txt')
p_politica = [line.rstrip('\n') for line in p_politica01]
set_chiavi=set(p_politica)
for row in range(len(frame)):
    for word in frame['Cleaned Text']:
         for element in word:
            for word_1 in p_politica:
                set_ricerca={}
                set_chiave={}
                set_ricerca=set(element)
                set_chiave=word_1
                match = set_ricerca & set_chiavi
                if match is not None:
                    print (match)

Questo il codice che ho provato a scrivere, ma c'è qualcosa che non va.....
 
Ultima modifica:
Prima di tutto ti consiglio di usare un context manager quando lavori con i file in python (vedi un esempio nel codice sotto). Per quanto riguarda il ciclo for per le rows in un dataframe devi utilizzare apply. Ecco un codice esempio:
Python:
# Utilizzo di un context manager per leggere le parole chiave
with open("file.txt", "r") as f:
    # Leggi ogni riga del file e ritorna una lista
    raw_keys = f.readlines()
  
    # Rimuovi il caratte "\n" presente in ogni elemento della lista
    keys = set(map(lambda x: x.replace("\n", ""), raw_keys))
  
def find_match(stringa: str, set_chiavi=keys):
    """ Funzione che data una stringa trova una possibile corrispondenza tra le parole presenti in tale stringa e le parole chiave"""
  
    set_ricerca = set(stringa.split(" "))
  
    # Rendere tutto maiuscolo per evitare errori di match
    set_ricerca = set(map(lambda x: x.upper(), set_ricerca))
    set_chiavi = set(map(lambda x: x.upper(), set_chiavi))
  
    # Calcola l'intersezione
    match = set_ricerca & set_chiavi
  
    return match

# Tramite apply ogni riga della feature "Cleaned text" del dataframe "df"
# Viene passata alla funzione "find_match"
matches = df["Cleaned text"].apply(find_match)

# OUTPUT: matches sarà una series 1d che ad ogni riga riporta i matches trovati nella rispettiva colonna del df

Questo dovrebbe essere un codice esempio che dovrebbe fare al caso tuo...

Fammi sapere!
 
  • Mi piace
Reazioni: valo83
Prima di tutto ti consiglio di usare un context manager quando lavori con i file in python (vedi un esempio nel codice sotto). Per quanto riguarda il ciclo for per le rows in un dataframe devi utilizzare apply. Ecco un codice esempio:
Python:
# Utilizzo di un context manager per leggere le parole chiave
with open("file.txt", "r") as f:
    # Leggi ogni riga del file e ritorna una lista
    raw_keys = f.readlines()
   
    # Rimuovi il caratte "\n" presente in ogni elemento della lista
     keys = set(map(lambda x: x.replace("\n", ""), raw_keys))
   
def find_match(stringa: str, set_chiavi=keys):
    """ Funzione che data una stringa trova una possibile corrispondenza tra le parole presenti in tale stringa e le parole chiave"""
   
    set_ricerca = set(stringa.split(" "))
   
    # Rendere tutto maiuscolo per evitare errori di match
    set_ricerca = set(map(lambda x: x.upper(), set_ricerca))
    set_chiavi = set(map(lambda x: x.upper(), set_chiavi))
   
    # Calcola l'intersezione
    match = set_ricerca & set_chiavi
   
    return match

# Tramite apply ogni riga della feature "Cleaned text" del dataframe "df"
# Viene passata alla funzione "find_match"
matches = df["Cleaned text"].apply(find_match)

# OUTPUT: matches sarà una series 1d che ad ogni riga riporta i matches trovati nella rispettiva colonna del df

Questo dovrebbe essere un codice esempio che dovrebbe fare al caso tuo...

Fammi sapere!
L'output dovvrebbe essere scritto nella colonna frame["Clusters"] --> se parole dal file parole_calcio.txt "Calcio" e poi ci sono gli altri file e altre parole.
 
L'output dovvrebbe essere scritto nella colonna frame["Clusters"]
Se vuoi scrivere l'output in una specifica colonna del df puoi fare così
Python:
frame["Clusters"] = frame["Cleaned text"].apply(find_match)

poi ci sono gli altri file e altre parole.
Invece se hai diversi file da aprire, cosa che sinceramente io ti sconsiglio, puoi generalizzare l'estrazione delle parole chiave attraverso una funzione che passi ad ogni file che vuoi aprire
Python:
def extract_keys(filename):
    """ Dato un filename, estrai le parole chiave contenute in quel file e ritorna il set """
    
    with open(filename, "r") as f:
        # Leggi ogni riga del file e ritorna una lista
        raw_keys = f.readlines()
 
        # Rimuovi il caratte "\n" presente in ogni elemento della lista
        keys = set(map(lambda x: x.replace("\n", ""), raw_keys))
    
    return keys

# Estrai le chiavi contenute in ogni file
keys_calcio = extract_keys("calcio.txt")
keys_scuola = extract_keys("scuola.txt")
# etc, etc ...
Poi dovresti unire tutte le keys estratte e passarle come argomento della funzione. La cosa migliore da fare sarebbe lavorare con un file json, dove ci inserisci tutte le parole chiave raggruppate per argomento.

Aspetto risposta...
 
Faccio fatica con Python... ora mi manca questa parte e poi dovrei avere terminato con Twitter.
Cerco di risolvere così e poi in caso me lo studio (se hai un link, tutorial, ... ben venga).

Di fatto:

Codice:
# Utilizzo di un context manager per leggere le parole chiave
with open("parole_politica.txt", "r") as f:
    # Leggi ogni riga del file e ritorna una lista
    raw_keys = f.readlines() 
    # Rimuovi il caratte "\n" presente in ogni elemento della lista
    keys = set(map(lambda x: x.replace("\n", ""), raw_keys))
  
def find_match(stringa: str, set_chiavi=keys):
    """ Funzione che data una stringa trova una possibile corrispondenza tra le parole presenti in tale stringa e le parole chiave"""
  
    set_ricerca = set(stringa.split(" "))
  
    # Rendere tutto maiuscolo per evitare errori di match
    set_ricerca = set(map(lambda x: x.upper(), set_ricerca))
    set_chiavi = set(map(lambda x: x.upper(), set_chiavi))
  
    # Calcola l'intersezione
    match = set_ricerca & set_chiavi
    if match is not None :
        text = "Politca"
    return text

# Tramite apply ogni riga della feature "Cleaned text" del dataframe "df"
# Viene passata alla funzione "find_match"
frame["Clusters"] = frame["Cleaned Text"].apply(find_match)

# OUTPUT: matches sarà una series 1d che ad ogni riga riporta i matches trovati nella rispettiva colonna del df
frame['Clusters']

Errore: la Colonna "Cluster" è solo con la Parola politica.
Ti allego anche il file.
 

Allegati

  • CSV.zip
    205.4 KB · Visualizzazioni: 2
Il codice che uso invece è:
Codice:
import pandas as pd
import os
import glob
import re
import emoji
import string
from collections import Counter
import nltk
from stop_words import get_stop_words
from nltk.corpus import stopwords
from nltk.stem.porter import PorterStemmer
import textcleaner as tc
pd.options.display.max_rows=999
actual_dir = os.getcwd()
path = actual_dir
extension = 'csv'
os.chdir(path)
result = glob.glob('tweets_*.{}'.format(extension))
df_result=pd.DataFrame(result)
df_result.columns = ['Query']
li = []
for filename in result:
    df = pd.read_csv(filename, index_col=None, header=0)
    df["Unnamed: 0"]=filename
    li.append(df)
frame = pd.concat(li, axis=0, ignore_index=True)

frame = frame.rename(columns={'Unnamed: 0': 'Query'})

def puliziaQuery_str(text):
    text = text.replace("tweets_turin_","").replace(".csv","")
    return text
frame['Query'] = frame['Query'].apply(puliziaQuery_str)
frame['Media']=frame['Media'].apply(str)
frame['Media']=frame['Media'].apply(lambda s: re.sub('\[Photo\(.*\)\]', 'Photo', s))
frame['Media']=frame['Media'].apply(lambda s: re.sub('\[Gif\(.*\)\]', 'Gif', s))
frame['Media']=frame['Media'].apply(lambda s: re.sub('\[Video\(.*\)\]', 'Video', s))
frame=frame.drop(['boh'], axis = 1)
frame=frame.drop(['Label'], axis = 1)

def remove_emoji(text):
    return emoji.get_emoji_regexp().sub(u'', text)
frame['Cleaned Text'] = frame['Text'].apply(remove_emoji)

def hashtag(text):
    text = re.findall(r"#(\w+)",text)
    return text
frame['Hashtags'] = frame['Cleaned Text'].apply(hashtag)

def hashtag_str(text):
    text = "  ".join(text)
    return text
frame["Hashtags_str"]=frame["Hashtags"].apply(hashtag_str)


def eliminare_hashtag(text):
    text = re.sub(r"#(\w+)"," ",text)
    return text
frame['Cleaned Text'] = frame['Cleaned Text'].apply(eliminare_hashtag)

def a_capo(text):
    text = text.replace("\n"," ")
    return text
frame['Cleaned Text'] = frame['Cleaned Text'].apply(a_capo)

def trova_link_int (text) :
    text = re.findall(r'http\S+', text)
    return text
frame['All links in Text'] = frame['Cleaned Text'].apply(trova_link_int)

def link_out_from_text (text) :
    text=re.sub(r'http\S+', '', text)
    return text
frame['Cleaned Text'] = frame['Cleaned Text'].apply(link_out_from_text)

def tags (text):
    text=re.findall("@[a-zA-Z0-9]+\S*",text)
    text
    return text
frame['Tags'] = frame['Text'].apply(tags)
def tags_str(text):
    text = "".join(text)
    text = text.replace("@"," @")
    return text
frame["Tags_Str"]=frame["Tags"].apply(tags_str)

def tags_out (text):
    text=re.sub("@[a-zA-Z0-9]+\S*"," ",text)
    return text
frame["Cleaned Text"] = frame["Cleaned Text"].apply(tags_out)

def minuscolo_spazi (text):
    text = text.lower()
    text = ' '.join(text.split())
    return text
frame["Cleaned Text"]=frame["Cleaned Text"].apply(minuscolo_spazi)

def punteggiatura_out (text):
    text = re.sub(r'[^\w\s]',' ',text)
    return text
frame["Cleaned Text"]=frame["Cleaned Text"].apply(punteggiatura_out)

stop_words00 = list(get_stop_words('it'))         
nltk_words00 = list(stopwords.words('italian'))
stop_words00.extend(nltk_words00)

stop_words01 = list(get_stop_words('en'))         
nltk_words01 = list(stopwords.words('english'))
stop_words01.extend(nltk_words01)
stop_words_valo = ["oggi","de"]
frame["Cleaned Text"] = frame["Cleaned Text"].apply(lambda x: ' '.join([word for word in x.split() if word not in (stop_words_valo)]))

frame["Cleaned Text"] = frame["Cleaned Text"].apply(lambda x: ' '.join([word for word in x.split() if word not in (stop_words00)]))
frame["Cleaned Text"] = frame["Cleaned Text"].apply(lambda x: ' '.join([word for word in x.split() if word not in (stop_words01)]))

def string_lista (text):
    text = text.split()
    return text
frame["Cleaned Text into list"]=frame["Cleaned Text"].apply(string_lista)
frame['Cluster'] = ""
p_politica01=open('parole_politica.txt')
p_politica = [line.rstrip('\n') for line in p_politica01]
set_chiavi=set(p_politica)
for index, row in frame.iterrows():
     for word in frame["Cleaned Text into list"].iloc :
        for word_1 in p_politica :
            set_ricerca={}
            set_chiave={}
            set_ricerca=set(word)
            set_chiave=word_1
            match = set_ricerca & set_chiavi
            if match is not None:
                #frame["Cluster"].loc="Politica"
                frame.loc[frame["Cleaned Text into list"],"Cluster"]  = "Politica"
df_cluster = frame['Cluster'].value_counts()
df_cluster
 
Io ti consiglio di implementare il codice secondo il mio esempio poiché risulta essere più efficiente e in futuro ci puoi lavorare sopra più facilmente. Se hai altri dubbi chiedi pure senza problemi
 
Ultima modifica:
Yes ok.
Codice:
# Utilizzo di un context manager per leggere le parole chiave
with open("file.txt", "r") as f:
    # Leggi ogni riga del file e ritorna una lista
    raw_keys = f.readlines()
 
    # Rimuovi il caratte "\n" presente in ogni elemento della lista
    keys = set(map(lambda x: x.replace("\n", ""), raw_keys))
 
def find_match(stringa: str, set_chiavi=keys):
    """ Funzione che data una stringa trova una possibile corrispondenza tra le parole presenti in tale stringa e le parole chiave"""
 
    set_ricerca = set(stringa.split(" "))
 
    # Rendere tutto maiuscolo per evitare errori di match
    set_ricerca = set(map(lambda x: x.upper(), set_ricerca))
    set_chiavi = set(map(lambda x: x.upper(), set_chiavi))
 
    # Calcola l'intersezione
    match = set_ricerca & set_chiavi
 
    return match

# Tramite apply ogni riga della feature "Cleaned text" del dataframe "df"
# Viene passata alla funzione "find_match"
matches = df["Cleaned text"].apply(find_match)

# OUTPUT: matches sarà una series 1d che ad ogni riga riporta i matches trovati nella rispettiva colonna del df
Messaggio unito automaticamente:

Questo codice gira
ma devo aggiungere una condizione.
Codice:
    match = set_ricerca & set_chiavi
    if match is not None :
        cluster = "Politica"
    return cluster

Ma così su tutte le righe mi dà "Politica" nella colonna Clusters.

1641133866918.png
 
Ho appena capito cosa tu vuoi fare, errore mio. Tu vuoi raggruppare le stringhe per argomenti giusto? Quindi vuoi capire se nella stringa ci sono parole relative alla politica, e se ci sono allora inserisci nella colonna del df quell'argomento. Se ho capito bene si può modificare il codice
Python:
def extract_keys(filename):
    """ Dato un filename, estrai le parole chiave contenute in quel file e ritorna il set """
    with open(filename, "r") as f:
        # Leggi ogni riga del file e ritorna una lista
        raw_keys = f.readlines()
 
        # Rimuovi il caratte "\n" presente in ogni elemento della lista
        keys = set(map(lambda x: x.replace("\n", ""), raw_keys))
   
    return keys
 
def find_match(stringa, argomento, filename):
    """ Funzione che data una stringa trova una possibile corrispondenza tra le parole presenti in tale stringa e le parole chiave"""

    # Estrai le parole chiave e definisci il set su cui eseguire la ricerca
    set_chiavi = extract_keys(filename)
    set_ricerca = set(stringa.split(" "))
 
    # Rendere tutto maiuscolo per evitare errori di match
    set_ricerca = set(map(lambda x: x.upper(), set_ricerca))
    set_chiavi = set(map(lambda x: x.upper(), set_chiavi))
 
    # Calcola l'intersezione
    match = set_ricerca & set_chiavi

    # Se è stata trovata una corrispondenza allora ritorna l'argomento che si stava cercando
    # Se non vi è nessuna corrispondenza ritorna None
    if match:
        return argomento
    else:
        return

def make_clusters(stringa: str):
    """ Prende ogni argomento che si vuole analizzare e ritorna una lista contenente gli argomenti trattati a seconda delle parole """
   
    # Dizionario contenente: {argomento: filname parole chiave}
    argomenti = {"politica": "politica.txt",
                 "scuola": "scuola.txt",
                 "calcio": "calcio.txt"}
   
    # Ritorna una lista che contiene le parole relative ad un certo argomento
    # Prima ripulisci la lista dai None values
    raw_cluster = [find_match(stringa, chiave, argomenti[chiave]) for chiave in argomenti.keys()]
    cluster = [element for element in raw_cluster if element]
    
    return cluster


# Passare la funzione "make_clusters" ad ogni row del df
frame["Clusters"] = frame["Cleaned Text"].apply(make_clusters)

# OUTPUT: matches sarà una series 1d che ad ogni riga riporta i matches trovati nella rispettiva colonna del df
frame['Clusters']
Guarda se questo script fa più a caso tuo, chiaramente devi cambiare il dizionario "argomenti" con le tue parole chiave e i tuoi files (io ho messo solo degli esempi)

Puoi fare in modo anche di inserire quel certo argomento nel cluster solo se sono stati trovati più parole cercate (in questo caso almeno 3)
Python:
if len(match) > 2:
    return argomento
else: return
 
Ultima modifica:
Vuol dire che in quella stringa non c'era nessuna parola chiave che cercavi
Yes si, qui ci sono. :)
Sto cercando di popolare un po' i file in txt.
Mi son reso conto che una parola non la prende... boh...
Messaggio unito automaticamente:

1641143562746.png

1641143615076.png


Mi sai spiegare il motivo?