Comparando Discursos Políticos con Python y NLP

Este código compara dos corpus de texto pertenecientes a dos políticos, identifica las palabras más significativas en cada corpus utilizando el vectorizador TF-IDF y calcula una métrica de similitud entre los corpus preprocesados. Finalmente, muestra las palabras significativas y la métrica de similitud en la salida.

De esta manera se pueden ver las diferencias de foco que pone cada uno en la comunicación a sus electorados y sobre qué, en forma diferenciada, ponen atención en sus campañas o mensajes.

Donald Trump — Mauricio Macri

El código proporcionado realiza las siguientes tareas:

  • Leer dos archivos de texto que contienen los corpus de los políticos.
  • A continuación, se explica cada parte del código:

    También te puede interesarComparando Discursos Políticos con Python y NLP

    Importación de bibliotecas:

    Se importan las bibliotecas necesarias para procesar el texto, calcular el vectorizador TF-IDF y la métrica de similitud.

    import spacy
    import numpy as np
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity

    Función read_file:

    Esta función recibe la ruta de un archivo de texto y devuelve su contenido como una cadena de texto.

    También te puede interesarPasado, presente y futuro de la inteligencia artificial: un estudio de caso de la evolución de la IA
    def read_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
    return file.read()

    Función preprocess:

    Esta función recibe un texto y realiza las siguientes tareas de preprocesamiento:

  • Carga el modelo de lenguaje es_core_news_sm de spaCy para procesar texto en español.
  • def preprocess(text):
    nlp = spacy.load("es_core_news_sm")
    doc = nlp(text)
    tokens = [token.lemma_ for token in doc if not token.is_stop and not token.is_punct]
    return " ".join(tokens)

    Función get_significant_words:

    Esta función recibe dos corpus y realiza lo siguiente:

    También te puede interesarPandas Cheat Sheet: Functions for Data Analysis
  • Crea un vectorizador TF-IDF.
  • def get_significant_words(corpus1, corpus2):
    vectorizer = TfidfVectorizer()
    corpus = [corpus1, corpus2]
    X = vectorizer.fit_transform(corpus)
    feature_names = vectorizer.get_feature_names_out()

    top_n = 10
    significant_words = []
    for i in range(len(corpus)):
    row = np.squeeze(X[i].toarray())
    top_indices = row.argsort()[-top_n:]
    words = [feature_names[idx] for idx in top_indices]
    significant_words.append(words)

    return significant_words

    Función get_similarity_metric:

    Esta función recibe dos corpus y calcula la métrica de similitud (cosine similarity) entre ellos. Devuelve la métrica de similitud como un valor entre 0 y 1.

    También te puede interesarCómo clasificar documentos para una consulta usando NLP
    def get_similarity_metric(corpus1, corpus2):
    vectorizer = TfidfVectorizer()
    X = vectorizer.fit_transform([corpus1, corpus2])
    similarity_matrix = cosine_similarity(X)
    similarity_score = similarity_matrix[0, 1]
    return similarity_score

    Ejecución del código:

  • Se definen las rutas de los archivos de texto que contienen los corpus de los políticos y se leen usando la función read_file.
  • file_path1 = "politico1.txt"
    file_path2 = "politico2.txt"

    corpus_politico1 = read_file(file_path1)
    corpus_politico2 = read_file(file_path2)

    Se preprocesan los corpus utilizando la función preprocess para eliminar palabras vacías, signos de puntuación y aplicar la lematización.

    corpus1_preprocessed = preprocess(corpus_politico1)
    corpus2_preprocessed = preprocess(corpus_politico2)

    Se obtienen las palabras significativas de cada corpus utilizando la función get_significant_words.

    significant_words_list = get_significant_words(corpus1_preprocessed, corpus2_preprocessed)

    Se calcula la métrica de similitud (cosine similarity) entre los corpus preprocesados utilizando la función get_similarity_metric.

    similarity_score = get_similarity_metric(corpus1_preprocessed, corpus2_preprocessed)

    Se imprimen las palabras significativas y la métrica de similitud en la salida.

    print(f"Palabras significativas del político 1 ({file_path1}):")
    for word in significant_words_list[0]:
    print(f"- {word}")

    print(f"nPalabras significativas del político 2 ({file_path2}):")
    for word in significant_words_list[1]:
    print(f"- {word}")

    print(f"nMétrica de similitud entre los dos corpus: {similarity_score:.4f}")

    Código completo:

    # Importa las bibliotecas necesarias
    import spacy
    import numpy as np
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity

    # Función para leer archivos de texto
    def read_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
    return file.read()

    # Función para preprocesar el texto: lematización y eliminación de palabras vacías y signos de puntuación
    def preprocess(text):
    nlp = spacy.load("es_core_news_sm")
    doc = nlp(text)
    tokens = [token.lemma_ for token in doc if not token.is_stop and not token.is_punct]
    return " ".join(tokens)

    # Función para obtener las palabras significativas de dos corpus utilizando el vectorizador TF-IDF
    def get_significant_words(corpus1, corpus2):
    vectorizer = TfidfVectorizer()
    corpus = [corpus1, corpus2]
    X = vectorizer.fit_transform(corpus)
    feature_names = vectorizer.get_feature_names_out()

    top_n = 10
    significant_words = []
    for i in range(len(corpus)):
    row = np.squeeze(X[i].toarray())
    top_indices = row.argsort()[-top_n:]
    words = [feature_names[idx] for idx in top_indices]
    significant_words.append(words)

    return significant_words

    # Función para calcular la métrica de similitud (cosine similarity) entre dos corpus
    def get_similarity_metric(corpus1, corpus2):
    vectorizer = TfidfVectorizer()
    X = vectorizer.fit_transform([corpus1, corpus2])
    similarity_matrix = cosine_similarity(X)
    similarity_score = similarity_matrix[0, 1]
    return similarity_score

    # Rutas de los archivos de texto que contienen los corpus de los políticos
    file_path1 = "politico1.txt"
    file_path2 = "politico2.txt"

    # Lee los corpus de los archivos
    corpus_politico1 = read_file(file_path1)
    corpus_politico2 = read_file(file_path2)

    # Preprocesa los corpus
    corpus1_preprocessed = preprocess(corpus_politico1)
    corpus2_preprocessed = preprocess(corpus_politico2)

    # Obtiene las palabras significativas de cada corpus
    significant_words_list = get_significant_words(corpus1_preprocessed, corpus2_preprocessed)

    # Calcula la métrica de similitud entre los corpus
    similarity_score = get_similarity_metric(corpus1_preprocessed, corpus2_preprocessed)

    # Imprime las palabras significativas y la métrica de similitud
    print(f"Palabras significativas del político 1 ({file_path1}):")
    for word in significant_words_list[0]:
    print(f"- {word}")

    print(f"nPalabras significativas del político 2 ({file_path2}):")
    for word in significant_words_list[1]:
    print(f"- {word}")

    print(f"nMétrica de similitud entre los dos corpus: {similarity_score:.4f}")

    En caso de querer ver la representación en un diagrama de Venn:

    #pip install matplotlib-venn

    import spacy
    import numpy as np
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity
    import matplotlib.pyplot as plt
    from matplotlib_venn import venn2

    # ... (Las funciones y el código anterior se mantienen igual)

    # Visualización de palabras significativas con un diagrama de Venn
    politico1_words = set(significant_words_list[0])
    politico2_words = set(significant_words_list[1])

    plt.figure()
    venn2([politico1_words, politico2_words], set_labels=('Político 1', 'Político 2'))
    plt.title("Palabras significativas de los políticos")
    plt.show()

    La razón de probabilidades logarítmicas con prior informativo de Dirichlet (LODP, por sus siglas en inglés) es un método poderoso y robusto para comparar el uso de palabras en dos corpus, desarrollado por Monroe et al. en su artículo de 2009 “Fightin’ Words: Lexical Feature Selection and Evaluation for Identifying the Content of Political Conflict”. El método es particularmente útil para identificar palabras características que distinguen el lenguaje utilizado en dos textos o conjuntos de textos diferentes.

    En resumen, el LODP calcula la razón de probabilidades logarítmicas de las frecuencias de términos entre dos corpus, incorporando una distribución previa de las frecuencias de palabras para tener en cuenta posibles sesgos y suavizar las estimaciones. Esta distribución previa generalmente se genera a partir de un conjunto de datos más grande y representativo, lo cual es útil cuando los corpus que se comparan son pequeños o contienen datos escasos.

    LODP se basa en el concepto de un marco bayesiano que utiliza un prior de Dirichlet, que es una generalización multivariante de la distribución Beta. Aborda el problema del sobreajuste en el método tradicional de razón de probabilidades logarítmicas al incorporar conocimientos previos de un conjunto de datos más grande. Esto garantiza que las palabras raras o las palabras con razones de probabilidades logarítmicas extremas se regularicen, proporcionando una estimación más estable de la importancia de las palabras.

    Los pasos clave en el método LODP son los siguientes:

  • Preprocesar los textos dividiendo en tokens, convirtiendo a minúsculas y eliminando las palabras vacías y signos de puntuación.
  • El método LODP ofrece varias ventajas sobre los métodos tradicionales basados en frecuencias, como el TF-IDF, al incorporar información previa y al proporcionar una medida de significancia (z-scores) para cada palabra. Esto permite una comparación más confiable de la importancia y el uso de palabras en dos corpus, convirtiéndolo en una herramienta valiosa para el análisis comparativo de textos en el campo del procesamiento del lenguaje natural.

    Construyendo sobre la base proporcionada en la explicación anterior, el método LODP también destaca en su capacidad para manejar corpus desequilibrados, donde un corpus puede ser significativamente más grande que el otro. Este desequilibrio a menudo conduce a resultados poco confiables en otras técnicas, pero el LODP puede mantener su solidez gracias a la incorporación del prior informativo de Dirichlet.

    Una de las principales fortalezas de LODP es su adaptabilidad a diversas aplicaciones en el procesamiento del lenguaje natural, como:

  • Análisis comparativo de discursos políticos o manifiestos de partidos.
  • El método LODP también se puede extender para manejar más de dos corpus, realizando comparaciones por pares y agregando los resultados. Además, LODP se puede utilizar junto con otras técnicas de NLP, como el modelado de temas o el análisis de sentimientos, para proporcionar una comprensión más completa de las diferencias y similitudes entre los corpus.

    La razón de probabilidades logarítmicas con prior informativo de Dirichlet es un método altamente efectivo y versátil para comparar el uso de palabras en diferentes textos o conjuntos de textos. Su solidez frente a las variaciones en el tamaño del corpus, la incorporación de información previa y la medida de significancia (z-scores) lo convierten en una herramienta invaluable para investigadores y profesionales en el campo del procesamiento del lenguaje natural. La capacidad del método LODP para identificar palabras y patrones distintivos contribuye a una comprensión más profunda del lenguaje y la comunicación, lo que permite análisis más informados de una amplia gama de datos basados en texto.

    import numpy as np
    import spacy
    from collections import Counter
    from scipy.sparse import csr_matrix
    from scipy.special import psi
    import matplotlib.pyplot as plt

    def read_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
    return file.read()

    def preprocess(text):
    nlp = spacy.load("es_core_news_sm")
    doc = nlp(text)
    tokens = [token.lemma_ for token in doc if not token.is_stop and not token.is_punct]
    return tokens

    def log_odds_ratio_informative_dirichlet_prior(corpus1, corpus2, prior_corpus, alpha=0.01):
    term_count_corpus1 = Counter(corpus1)
    term_count_corpus2 = Counter(corpus2)
    term_count_prior = Counter(prior_corpus)

    vocab = set(term_count_corpus1) | set(term_count_corpus2) | set(term_count_prior)
    n_terms = len(vocab)

    term_indices = {term: i for i, term in enumerate(vocab)}

    counts1 = np.zeros(n_terms)
    counts2 = np.zeros(n_terms)
    prior_counts = np.zeros(n_terms)

    for term, count in term_count_corpus1.items():
    counts1[term_indices[term]] = count

    for term, count in term_count_corpus2.items():
    counts2[term_indices[term]] = count

    for term, count in term_count_prior.items():
    prior_counts[term_indices[term]] = count

    n1 = counts1.sum()
    n2 = counts2.sum()
    nprior = prior_counts.sum()

    counts1 += alpha * prior_counts
    counts2 += alpha * prior_counts

    log_odds = np.log(counts1) - np.log(counts1.sum()) - np.log(counts2) + np.log(counts2.sum())
    variance = 1 / counts1 + 1 / counts2

    z_scores = log_odds / np.sqrt(variance)

    return {term: z_scores[i] for term, i in term_indices.items()}

    def plot_top_words(z_scores, n=10):
    sorted_terms = sorted(z_scores, key=z_scores.get)
    top_positive_terms = sorted_terms[-n:]
    top_negative_terms = sorted_terms[:n]

    top_positive_scores = [z_scores[term] for term in top_positive_terms]
    top_negative_scores = [z_scores[term] for term in top_negative_terms]

    fig, ax = plt.subplots()

    y_pos = np.arange(n)

    ax.barh(y_pos, top_positive_scores, align='center', color='blue', label='Corpus 1')
    ax.barh(-y_pos - 1, top_negative_scores, align='center', color='red', label='Corpus 2')

    ax.set_yticks(np.hstack((y_pos, -y_pos - 1)))
    ax.set_yticklabels(top_positive_terms + top_negative_terms)
    ax.invert_yaxis()

    ax.set_xlabel('Z-Scores')
    ax.set_title('Top Words by Log Odds Ratio with Informative Dirichlet Prior')
    ax.legend()

    plt.show()

    file_path1 = "politico1.txt"
    file_path2 = "politico2.txt"

    corpus_politician1 = read_file(file_path1)
    corpus_politician2 = read_file(file_path2)

    corpus1_preprocessed = preprocess(corpus_politician1)
    corpus2_preprocessed = preprocess(corpus_politician2)

    # Combine both corpora to create a prior corpus
    prior_corpus = corpus1_preprocessed + corpus2_preprocessed

    z_scores = log_odds_ratio_informative_dirichlet_prior(corpus1_preprocessed, corpus2_preprocessed, prior_corpus)
    plot_top_words(z_scores)

    # Combine both corpora to create a prior corpus
    prior_corpus = corpus1_preprocessed + corpus2_preprocessed

    z_scores = log_odds_ratio_informative_dirichlet_prior(corpus1_preprocessed, corpus2_preprocessed, prior_corpus)
    plot_top_words(z_scores)

    Ejemplo de salida sobre corpus de prueba.

    Si te ha sido de utilidad, por favor sígueme para poder ver próximas publicaciones. Gracias!

    Scroll al inicio