Trabajando con Librerías en Python
1. Importación de Librerías
import math: importa todo el módulo →math.piimport math as mt: usa un alias →mt.pifrom math import pi: importa solo un elemento →pifrom math import *: importa todos los elementos sin prefijo (⚠️ no recomendable)
2. Instalación de Librerías en Colab
- Se utiliza
!pip install nombre_librería- Ejemplo:
!pip install ipdb
- Ejemplo:
3. Lectura y Escritura de Archivos
- Abrir archivo:
open('archivo.txt', 'modo') - Modos:
- ‘r’: lectura
- ‘w’: escritura
- ‘a’: añadir
- ‘r+’: lectura y escritura
- Escribir:
f.write("texto") - Leer todo:
contenido = f.read() - Leer línea a línea:
for line in f: - No olvides
f.close()
4. Uso de Archivos con Google Drive
- Montar Drive:
from google.colab import drivedrive.mount('/content/drive') - Definir ruta:
MYDRIVE = "/content/drive/MyDrive/..." - Leer archivo desde Drive:
f = open(MYDRIVE + 'archivo.csv', 'r') - Ver contenido del directorio
!ls /content/drive/MyDrive/pln/pln/ -1
5. Serialización con Pickle
Pickle es un módulo que permite convertir objetos de Python a un formato binario para guardarlos y recuperarlos más tarde (serialización y deserialización).
- Serialización de una lista:
mylistserialized = pickle.dumps(mylist)
- Recuperar una lista
mylistrecovered = pickle.loads(mylistserialized)
- Guardar objetos:
import picklewith open('archivo.pkl', 'wb') as f:pickle.dump(objeto, f)
pickled_file = open(filename, 'wb') pickle.dump(mylist, pickled_file)
- Leer objetos:
with open('archivo.pkl', 'rb') as f:objeto = pickle.load(f)
pickled_file = open(filename, 'rb') mylistrecovered = pickle.load(pickled_file)
- Puedes guardar listas, diccionarios, múltiples variables, etc.
import pickle
1. Lista
frutas = [«manzana», «pera», «naranja»]
2. Diccionario
datos_personales = { «nombre»: «Laura», «edad»: 25, «email»: «laura@example.com» }
3. Múltiples variables combinadas
paquete = { «frutas»: frutas, «datos»: datos_personales, «año»: 2025 }
Guardar en un archivo pickle
with open(«archivo.pkl», «wb») as f: pickle.dump(paquete, f)
with open(«archivo.pkl», «rb») as f: datos_recuperados = pickle.load(f)
leer el archivo y recuperar los datos
print(datos_recuperados[«frutas»]) # [‘manzana’, ‘pera’, ‘naranja’] print(datos_recuperados[«datos»][«nombre»]) # Laura print(datos_recuperados[«año»]) # 2025
6. Archivos MATLAB (.mat)
- Librería:
from scipy.io import savemat, loadmat - Guardar:
savemat("archivo.mat", {"clave": datos}) - Leer:
pythonCopiarEditardatos = loadmat("archivo.mat")
7. Creación de Librerías Propias
- Crea un
.pycon funciones. - Ejemplo:
def mi_funcion(): return "Hola" - Importar en Colab:
from google.colab import drivefrom importlib.machinery import SourceFileLoadermi_lib = SourceFileLoader('nombre', ruta).load_module()
8. Creación de Paquetes (Packages)
- Estructura de carpetas con
__init__.pypara que Python lo reconozca como paquete. - Ejemplo:
mipackage/
__init__.py
modulo.py - Importar:
from mipackage import modulo
9. Programación Orientada a Objetos (POO)
Clases y Objetos
class Persona: def __init__(self, nombre): self.nombre = nombre
Métodos:
__init__: constructor- Métodos propios:
def mostrar(self): ...
Atributos:
- Públicos:
self.nombre - Puedes calcular edad con
datetime
Ejemplo de clase HUMAN:
Atributos: nombre, apellido, fecha_nacimiento, estado_civil, hijos
Métodos: get_age(), add_child(), change_civil_status(), etc.
10. Herencia
Permite extender clases:
class Estudiante(HUMAN): def __init__(...): super().__init__(...)
Se pueden definir nuevas clases como Teacher, Student, Course basadas en HUMAN o PROFESSIONAL.
NumPy y Matplotlib: Resumen
NumPy
import numpy as np
Introducción
- NumPy es el paquete clave para computación científica en Python.
- Usa arrays n-dimensionales (ndarray) para representar datos de forma eficiente.
- Permite broadcasting, álgebra lineal, generación de datos aleatorios y operaciones vectorizadas.
Creación de Arrays
np.array([1, 2, 3])crea un array 1D.np.ones.shapedevuelve su forma (dimensiones)..dtypeindica el tipo de dato.- Métodos Útiles:
np.arange(inicio, fin, paso)np.linspace(inicio, fin, num).tolist(): convierte array a lista.ravel(): aplanar array.reshape(): cambiar dimensiones.flatten()
flatten()always returns a copy of the original vector, whereasravel()andshape()returns a view of the original array whenever possible.
Dimensiones y Manipulación
np.newaxis: añade dimensión (útil para transformar vectores columna/fila).np.squeeze(): elimina dimensiones de tamaño 1.- Arrays pueden tener más de 2 dimensiones (tensores).
np.random.randn(m, n): matriz de números aleatorios con distribución normal estándar.
Indexación y Slicing
- Igual que listas:
a[0],a[1:3],a[:,1], etc. .where(condición): devuelve índices donde se cumple la condición.x[y > 5]: acceso condicional.
Concatenación de Arrays
np.hstack(): concatena horizontalmente.np.vstack(): concatena verticalmente..T: transpuesta del array.
Operaciones con Arrays
- Element-wise:
+,-,*,/,** - Operaciones columna/fila automáticamente por broadcasting.
- Producto Escalar:
np.dot(a, b)oa @ b
Multiplicación de Matrices y Transpuestas
np.dot(): producto de matrices.np.matmul(): igual que dot para 2D..T,np.transpose()onp.ndarray.T: transposición.
Inversas y Pseudo-inversas
- Inversa:
np.linalg.inv(A) - Pseudo-inversa (Moore–Penrose):
np.linalg.pinv(A) - Verificación:
A @ A⁺ ≈ Iprint(M2@M2I)
Generación Útil de Arrays
np.linspace(a, b, n): n puntos entre a y b.np.logspace(0,10,10)# 10 elements equally spaced in the 10^0,10^10 rangenp.arange(start, stop, step)
Guardar Datos con Pickle
- Serialización de Objetos:
import picklewith open('archivo.pkl', 'wb') as f:pickle.dump(datos, f) - Carga de Objetos:
with open('archivo.pkl', 'rb') as f:datos = pickle.load(f)
Puntos Clave para Repasar
- ¿Qué devuelven
.shape,.dtype,.T,.squeeze(),.reshape()? - ¿Cómo se concatena un array? ¿Qué diferencia hay entre
ravel()yreshape(-1)? - ¿Qué función se usa para: crear distribuciones, guardar arrays, visualizar datos?
- ¿Qué hace
np.dot()? ¿Ynp.matmul()? - ¿Qué devuelve
np.where(x > 5)?
Introducción a Pandas: Resumen
¿Qué es Pandas?
Pandas es una librería de Python para el análisis de datos. Permite trabajar con:
- Series: estructuras unidimensionales (como arrays con índice).
- DataFrames: tablas de datos con filas y columnas etiquetadas.
1. Carga Inicial de Librerías y Archivos
import pandas as pd
from google.colab import drive
drive.mount("/content/drive")- Se usa
pd.read_csv("ruta")para leer ficheros.csv.
2. Exploración Básica de Datos
print(data.shape) # filas y columnas print(data.columns) # nombres de columnas print(data.head()) # primeras filas
simple_data.tail(10) # `.tail()` shows the last 10 entries
3. Operaciones con Columnas
- Renombrar Columnas:
data.columns = ["new1", "new2", ...] - Crear Columna Nueva:
data["new_col"] = data["col1"] + data["col2"]
4. Operaciones con .apply()
Permite aplicar funciones a columnas:
data["edad"] = data["birth_year"].apply(lambda x: funcion())
⚠️ **Nota:** Cuidado con el tipo de dato (string vs int), puede provocar errores.
5. Selección de Datos (iloc y loc)
iloc → por posición (índice numérico):
data.iloc[0] # primera fila data.iloc[:, 1] # segunda columna (todas las filas)
loc → por nombre:
data.loc[0, "columna"]
6. Filtrado Condicional
data[data["valor"] > 300] # casas con valor > 300 data[(cond1) & (cond2)] # múltiples condiciones
7. Manejo de Valores Nulos
data.isnull() # detecta nulos data.dropna() # elimina filas con nulos data.fillna(valor) # reemplaza nulos
8. Guardar Datos
data.to_csv(ruta_o_archivo)
Procesamiento de Lenguaje Natural (PLN)
1. Introducción al PLN
- En PLN (Procesamiento de Lenguaje Natural) se trabajan textos en forma de datos para analizarlos con algoritmos de aprendizaje automático.
- Se usan técnicas para limpiar y transformar el texto en vectores comprensibles por los algoritmos.
- Ejemplos de aplicación: motores de búsqueda, asistentes conversacionales, análisis de sentimientos, detección de spam, etc.
- El objetivo es convertir texto no estructurado en datos útiles para modelos de aprendizaje automático.
2. Pipeline del Procesamiento de Texto
El texto pasa por una serie de etapas:
- Tokenización (separa en palabras o frases)
- Homogeneización (normaliza formato)
- Limpieza (elimina símbolos innecesarios)
- Vectorización (transforma a números)
- Modelo de aprendizaje (usa los datos para entrenar un algoritmo)
3. Acceso a Corpus con NLTK
3.1 El Objeto CorpusReader
- Se usa para explorar los corpus de texto que ofrece nltk.
- Ejemplo: movie_reviews contiene reseñas etiquetadas como positivas o negativas.
- Métodos útiles:
movie_reviews.fileids()→ lista de documentos..categories()→ categorías disponibles..raw(fileid)→ texto sin procesar..words(fileid)→ texto tokenizado en palabras..split(',')hacer frases usando punto como separador (Nota: esto no es un método estándar deCorpusReader).
from nltk.corpus import movie_reviews
nltk.download('movie_reviews')
movie_reviews4. Preprocesamiento del Corpus
4.1 Tokenización
- Separar el texto en unidades (tokens): palabras, signos de puntuación, etc.
- Es útil para analizar el contenido de un texto palabra por palabra.
- **Token**: unidad mínima del texto (palabras, signos, números).
- Ejemplo en NLTK:
nltk.word_tokenize().
nltk.download('punkt_tab').- Se analiza cómo se separan frases y palabras.
# Ejemplo de tokenización usando NLTK:
nltk.download('punkt_tab')
from nltk.tokenize import sent_tokenize
los signos de puntuación se convierten también en «tokens»
Procesamos el texto número 8
raw_text = movie_reviews.raw(doc_ids[7])
Dividimos el texto en frases
frases = sent_tokenize(raw_text)
Tokenizamos todas las frases
frases_tokenizadas = [word_tokenize(frase) for frase in frases]
Mostramos los tokens de las frases indicadas
print(‘Estos son los tokens de las frases número 3, 4 y 7:’) print(frases_tokenizadas[2]) print(frases_tokenizadas[3]) print(frases_tokenizadas[6])
Se retienen sólo los tokens de más de un caracter de largo:
frases_tokenizadas_filtradas = [[token for token in frase if len(token)>1] for frase in frases_tokenizadas]
Mostramos los tokens de las frases indicadas, tras el filtrado
print(‘\nEstos son los tokens de las frases número 3, 4 y 7 tras el filtrado:’) print(frases_tokenizadas_filtradas[2]) print(frases_tokenizadas_filtradas[3]) print(frases_tokenizadas_filtradas[6])
4.2 Homogeneización
- Normaliza el texto para reducir variaciones irrelevantes.
- Pasos comunes:
- Convertir a minúsculas
- Eliminar signos de puntuación
- Eliminar palabras vacías (stop words)
- **Stemming** y **Lematización**
**Stemming**: recorta palabras a su raíz (más agresivo).
**Lematización**: convierte a la forma base del diccionario (más preciso).
4.3 Preprocesamiento de Texto con spaCy
- **spaCy** es una biblioteca potente para procesamiento en tiempo real.
- Elementos clave:
Doc,Token,Span→ objetos básicos de análisis.- Se puede analizar POS tags, detectar stop words, puntuación, etc.
- Instalación:
!pip install spacyy descarga del modelo.
# Descargamos el modelo para inglés !python -m spacy download en_core_web_sm
Cargamos el modelo
nlp = spacy.load(«en_core_web_sm»)
El **acceso a las palabras del vocabulario** se hace a través del atributo .vocab.strings
lista_vocab = list(nlp.vocab.strings)
print(type(nlp.vocab.strings))
print(type(lista_vocab))
print("\nEl tamaño del diccionario es de {} 'palabras' de todo tipo, algunos ejemplos:".format(len(lista_vocab)))
Las 20 primeras palabras y las 20 en posición 1000 (muchas son alfanuméricas)
print(lista_vocab[:20]) print(lista_vocab[1000:1020]) print(lista_vocab[10000:10020])
Suprimimos las palabras no alfabéticas
alpha_words = [word for word in nlp.vocab.strings if word.isalpha()] no_alpha_words = [word for word in nlp.vocab.strings if not word.isalpha()]
**NOTA:** el formato de entrada a spaCy siempre es un texto plano.
sentence = 'Japan’s economy, struggling to emerge from the pandemic, is sagging under rising food and energy costs and a weak yen'
print("Esta es la frase de partida, tipo `string`:\n")
print(sentence)
print(type(sentence))
doc_tokenized = nlp(sentence)
print(«\nAunque al hacer un print se nos muestra el texto, vemos que no es un string habitual…\n»)
print(doc_tokenized)
print(type(doc_tokenized))
doc_tokenized es un objeto indexado e iterable, compuesto por objetos tipo token.
print(doc_tokenized[0]) print(type(doc_tokenized[0]))
Los atributos del tipo is_ nos serán especialmente útiles, por ejemplo is_digit para reconocer números, is_alpha para reconocer tokens alfanuméricos, is_stop para detectar las stop words, etc. En el siguiente bucle imprimimos algunas de las propiedades de dichos tokens determinadas por el pipeline que hemos cargado, incluyendo el **POS tag** («Part Of Speech»):
Con spacy.explain() podemos obtener una descripción de los distintos tags …
En lo referente a **stop words**, spaCy incluye una amplia lista (326 elementos en idioma inglés) que podemos personalizar para nuestra propia aplicación si es necesario.
spacy_stopwords = nlp.Defaults.stop_words
Printing the total number of stop words:
print(‘Number of stop words: %d’ % len(spacy_stopwords))
Printing first ten stop words:
print(‘First ten stop words: %s’ % list(spacy_stopwords)[:20])
Añadir una palabra al conjunto de stop words
nlp.Defaults.stop_words.add(«my_new_stopword») print(‘Number of stop words: %d’ % len(nlp.Defaults.stop_words))
Quitar una palabra del conjunto de stop words
nlp.Defaults.stop_words.remove(«my_new_stopword») print(‘Number of stop words: %d’ % len(nlp.Defaults.stop_words))
4.4 spaCy para Idioma Español
- spaCy ofrece modelos entrenados con textos en español.
- Instalación:
es_core_news_sm.
!python -m spacy download es_core_news_sm
nlp = spacy.load("es_core_news_sm")
lista_vocab = list(nlp.vocab.strings)
print("El tamaño del diccionario es de {} palabras. Las primeras 10 tras la posición 20000 son \n".format(len(lista_vocab)))
print(lista_vocab[20000:20010])
Printing the total number of stop words:
print(‘\nHay un total de {0} stop words en el modelo’.format(len(spacy.lang.es.stop_words.STOP_WORDS)))
- Permite analizar texto, obtener lemas, y clasificar palabras por tipo (sustantivo, verbo, etc.).
4.5 Uso de spaCy para Normalización
- Se implementa una función para eliminar puntuación, números y stop words.
- Se lematiza todo el texto, quedando solo palabras útiles.
print('Volvemos a cargar el modelo para inglés a fin de procesar el corpus de películas:')
nlp = spacy.load("en_core_web_sm")
def normalize_Spacy(text): text2 = nlp(text) # Ejercicio: interpretar el sentido de esta list comprehension: normalizedtext = [w.lemma.lower() for w in text2 if not w.is_stop and not w.is_punct and (w.is_alpha or w.is_digit)] return normalized_text
corpus_prec = [] for fileid in movie_reviews.fileids(): text = movie_reviews.raw(fileid) text_preproc = normalize_Spacy(text) corpus_prec.append(text_preproc)
4.6 Conteo de Palabras Frecuentes
- Se usa
collections.Counterpara encontrar las palabras más comunes.
from collections import Counter all_normalized_words = [w for d in corpus_prec for w in d] words_freq = Counter(all_normalized_words)
- Ejercicio Final: Contar los **20 Adjetivos** más frecuentes.
- Se filtra por POS tags (
ADJ). - Se compara con el vocabulario del modelo.
- Se filtra por POS tags (
# We make a list with all the words in the movies corpus, in lower case
all_words = [w.lower() for d in corpus_prec for w in d]
print("\nHay un total de {} palabras en el corpus.".format(len(all_words)))
We select the alpha words in the vocabulary, in lower case and eliminating possible duplicates
vocab_list_alpha_words = list(set([word.lower() for word in nlp.vocab.strings if word.isalpha()])) print(«\nHay un total de {} palabras alfanuméricas en el vocabulario.».format(len(vocab_list_alpha_words))) print(vocab_list_alpha_words[0:100])
We select the words in the vocabulary that are adjectives
adjectives_list = [word for word in vocab_list_alphawords if nlp(word)[0].tag == «JJ»] print(«\nHay un total de {} adjetivos en el vocabulario.».format(len(adjectives_list))) print(adjectives_list[:100])
We filter the words in the corpus, retaining only the adjectives:
adjectives_in_corpus = [word for word in all_words if word in adjectives_list] print(«\nHay un total de {} adjetivos en el corpus, algunos de ellos son:».format(len(adjectives_in_corpus))) print(adjectives_in_corpus[:100])
Now we count the adjectives in the corpus and print the most common ones
print(«\nEsta es la lista de los 20 adjetivos más comunes, así como su conteo en el corpus:») adjectives_frequency = Counter(adjectives_in_corpus) most_common_adjectives = adjectives_frequency.most_common(20) for word, freq in most_common_adjectives[:20]: print(word, freq)
Representación Vectorial de Corpus
1. Preparación del Corpus
Antes de representar documentos como vectores, es necesario limpiar y normalizar los textos. El corpus final es una lista de listas, donde cada sublista contiene los tokens (palabras lematizadas, sin stopwords ni signos) de un documento.
import nltk
from nltk.corpus import inaugural
nltk.download('inaugural')
first_id = list(inaugural.fileids())[0] print(«Este es el identificador del primer documento: {}».format(first_id)) print(«\nEste es el texto de dicho documento:\n») text = inaugural.raw(first_id)
Descargamos el modelo spaCy y procesamos este texto
!python -m spacy download en_core_web_sm import spacy
Cargamos el modelo
nlp = spacy.load(«en_core_web_sm»)
Procesamos el texto
text_spacy = nlp(text) print(type(text_spacy))
2. Bag of Words (BoW)
2.1. Generación del Diccionario con Gensim
- Se utiliza **gensim** para crear un diccionario del corpus preprocesado:
from gensim import corporadiccionario = corpora.Dictionary(corpus_proc)
!pip install gensim import gensim n_docs = len(corpus_prec)
Create dictionary of tokens: the input is the preprocessed corpus
D = gensim.corpora.Dictionary(corpus_prec) # Nos devuelve cada elemento como string n_tokens = len(D)
Información del Diccionario:
token2id: mapea tokens a IDs.cfs: frecuencia total del token en el corpus.dfs: número de documentos en que aparece el token.
diccionario.token2id["president"] diccionario.cfs[id] diccionario.dfs[id]
2.2. Gestión del Vocabulario
- Se pueden aplicar filtros al diccionario:
- **
no_below**: se queda con los **tokens que están contenidos en al menos**no_below**documentos**. - **
no_above** (fracción del tamaño total del corpus, no un número absoluto): retiene los tokens que no están en más de una fracciónno_abovede los documentos. - **
keep_n**: se queda exactamente con loskeep_ntokens más relevantes, **eliminando los más y menos frecuentes**. - **
keep_tokens**: listado de **tokens que deben permanecer en el diccionario** después de ser filtrados. - Ejemplo:
diccionario.filter_extremes(no_below=10, no_above=0.7)
- **
- Esto reduce ruido eliminando palabras poco frecuentes o demasiado comunes.
2.3. Visualización del Diccionario
- Se pueden analizar manualmente cuántos documentos contienen ciertas palabras:
diccionario.dfs[diccionario.token2id["president"]] - También se puede representar gráficamente cuántos documentos contiene cada palabra (
doc_freq).
Para la visualización necesitaremos los métodos de la clase Dictionary de gensim: .dfs() (calcula en cuántos documentos del corpus aparece cada palabra del vocabulario) y .cfs() (calcula cuántas veces aparece cada palabra del vocabulario en todo el corpus).
3. Vectorización de Documentos
- Cada documento se convierte en una **bolsa de palabras (BoW)**: una lista de tuplas
(id_palabra, frecuencia).corpus_bow = [diccionario.doc2bow(doc) for doc in corpus_proc]
doc0 = corpus_prec[0] bow0 = D.doc2bow(doc0)
- Ejemplo de documento vectorizado:
[(0, 2), (1, 1), (2, 1)]# palabra 0 aparece 2 veces, la 1 una vez, etc.
4. Representación TF-IDF
**Frecuencia de Término (TF)**: número de veces que una palabra w dada ocurre en un documento, dividido por el número total de palabras en dicho documento.
**Frecuencia de Documento Inversa (IDF)**: medida de cuánta información proporciona la palabra w, es decir, si es común o rara en todos los documentos del corpus D.
- TF-IDF da más peso a palabras importantes:
A diferencia de la vectorización BoW, para el **TF-IDF tenemos que obtener la vectorización conjuntamente con todo el corpus**. No obstante, una vez que hemos calculado el BoW para todos los documentos, aprender el modelo TF-IDF es sencillo usando la función TfidfModel de **Gensim**.
- En gensim se implementa así:
from gensim.models import TfidfModelmodelo_tfidf = TfidfModel(corpus_bow)corpus_tfidf = modelo_tfidf[corpus_bow]
5. Limitaciones de BoW y TF-IDF
Las **representaciones vectoriales** que acabamos de ver son sencillas de comprender y aplicar y ofrecen mucha **flexibilidad** para manejo de información textual. De hecho, se han utilizado con gran éxito en problemas de **predicción y clasificación de documentos**.
Sin embargo, sufren de algunas **deficiencias** que tenemos que tener en cuenta:
- **Vocabulario**: **requiere un diseño cuidadoso**, más específicamente para gestionar su tamaño, lo que afecta a la dispersión de las representaciones de los documentos.
- **Dispersión**: Las **representaciones dispersas son más difíciles de modelar** tanto por razones computacionales como por razones de información, en las que el reto es que los modelos aprovechen la poca información común en un espacio de representación tan grande.
- **Significado**: Al **descartar el orden de las palabras se ignora el contexto** y, a su vez, el significado de las palabras del documento (**semántica**). El contexto y el significado pueden ofrecer mucho al modelo, que si se modela podría diferenciar entre las mismas palabras dispuestas de manera diferente («esto es interesante» vs «es esto interesante?»), sinónimos («bicicleta vieja» vs «bicicleta usada»), y mucho más.
6. Uso de Representaciones Vectoriales en Aprendizaje Automático
6.1. Codificación Dispersa
Vamos a usar aquí las librerías de **Sklearn**, tras convertir nuestra representación vectorial dispersa en **numpy arrays** «densos». Para ello, **Gensim** nos incluye dos funciones: corpus2dense, corpus2csc
**Importante**: note que hace falta conocer el número de documentos y el número de elementos en el diccionario.
Una vez convertidos a **Numpy array denso** o **matriz dispersa CSC de Scipy**, ambas parametrizaciones tienen el mismo formato y se pueden tratar de la misma forma. Incluso las librerías que pueden trabajar con datos en formato disperso, suelen requerir formato CSC de Scipy, no trabajan directamente con el formato de lista de tuplas que hemos visto.
En el caso del TF-IDF denso, aparecen rellenas con ceros las posiciones vacías.
El formato disperso CSC de Scipy tiene estructura propia, según sea una fila del BoW o una parte del mismo:
- Tanto BoW como TF-IDF generan vectores con muchos ceros. Se usan matrices dispersas para optimizar memoria:
from scipy.sparse import csr_matrixX_bow = csr_matrix(corpus_bow)
6.2. Cálculo de Distancias
Una vez que tenemos la representación vectorial de nuestros documentos (cada una de las filas de la matriz BoW o TF-IDF), muchos de nuestros modelos de ML necesitarán **calcular similitudes entre ellos**, como puede ser un algoritmo **K-NN para regresión** o un **K-means para agrupamiento**. Para ello, cuando trabajamos con características BoW o TF-IDF, donde la magnitud de los vectores no es tan decisiva, suele utilizarse como métrica la **similitud del coseno**.
Podríamos suponer que cuando una palabra (por ejemplo, ciencia) aparece con más frecuencia en el documento 1 que en el 2, ese documento 1 está más relacionado con el tema de la ciencia. Sin embargo, también podría darse el caso de que estemos trabajando con documentos de longitudes desiguales (artículos de Wikipedia, por ejemplo). En ese caso, es probable que la ciencia aparezca más en el documento 1 sólo porque es mucho más largo que el documento 2. La **similitud del coseno corrige estos sesgos**.
Por esta razón, cuando se trabaja con documentos codificados con BoW o TF-IDF, se tiende a utilizar la similitud del coseno (**coseno del ángulo que forman ambos vectores**). Si v₁ y v₂ son dos vectores TF-IDF, la similitud del coseno se calcula como sigue:
Permite comparar documentos con independencia de su longitud.
Otro aspecto que tenemos que tener en cuenta es que **la distancia coseno no es formalmente una «distancia» como tal** (no cumple la desigualdad triangular) e **implementaciones por defecto** de nuestros modelos de aprendizaje como el K-NN o K-means de sklearn usan la **distancia euclídea por defecto** y no permiten incluir la distancia coseno.
Para solventar este problema tenemos dos opciones:
- **Usar otras implementaciones** como K-means de NLTK que nos permite usar la distancia coseno. Aunque el problema de esta implementación es que **no nos deja trabajar con matrices sparse**.
- **Reescalar nuestros datos para poder utilizar implementaciones basadas en la distancia euclídea**… (luego explicaremos en qué consiste esta aproximación).
Sin embargo, cuando nuestros **vectores** x **e** y **están normalizados** (||x||₂=*x*ᴾ*x*=1), sus distancias euclídeas ||x−y||₂ y coseno d(*x*,*y*) están relacionadas por la siguiente igualdad:
||x−y||₂ = xᴾ x + yᴾ y − 2xᴾ y = 2(1 − xᴾ y) = 2d(x,y)
En este caso, usar la distancia euclídea nos va a dar los mismos resultados que la distancia coseno.
Nótese que esta normalización es parecida al cálculo TF que compensa en la representación BoW la longitud de los documentos.
Así que comencemos normalizando nuestros datos para que cada vector tenga norma unidad.
7. Clustering con KMeans
7.1 y 7.2. Clustering con BoW y TF-IDF
- Se aplica KMeans para agrupar documentos similares:
from sklearn.cluster import KMeansmodelo_kmeans = KMeans(n_clusters=3)modelo_kmeans.fit(X_tfidf)# oX_bow - Se analiza qué documentos hay en cada grupo y se inspeccionan los **centroides** para interpretar cada clúster.
7.3. Entrenamiento de KMeans
- Se entrena con
fit()y se obtiene el grupo de cada documento con.predict()o.labels_.
No podemos usar las librerías de K-means o K-NN de sklearn con representaciones BoW o TF-IDF porque **solo permiten usar la distancia euclídea**.
Sin embargo, cuando nuestros **vectores** x **e** y **están normalizados** (||x||₂=*x*ᴾ*x*=1), sus distancias euclídeas ||x−y||₂ y coseno d(*x*,*y*) están relacionadas por la siguiente igualdad:
7.4 y 7.5. Resultados del Clustering
- Se representan los resultados, mostrando qué documentos hay en cada grupo y qué palabras son más representativas de cada clúster.
