1 Descripción del texto

Para esta actividad, he seleccionado tres descripciones breves de películas de ciencia ficción. Este texto es apropiado porque comparte términos técnicos (lexical overlap) pero difiere en el contexto narrativo, lo que permite evaluar la efectividad de la similitud de coseno.

Doc 1: Un viaje interestelar para salvar la humanidad a través de un agujero de gusano.

Doc 2: Una misión interestelar en una nave espacial para colonizar un nuevo planeta.

Doc 3: Un detective persigue replicantes en una ciudad futurista y oscura.

corpus_raw <- data.frame(
  doc_id = c("Doc_1", "Doc_2", "Doc_3"),
  text = c(
    "Un viaje interestelar para salvar la humanidad a través de un agujero de gusano.",
    "Una misión interestelar en una nave espacial para colonizar un nuevo planeta.",
    "Un detective persigue replicantes en una ciudad futurista y oscura."
  ),
  stringsAsFactors = FALSE
)

2 Preprocesamiento

Realizamos limpieza: minúsculas, eliminación de puntuación, stopwords y stemming para reducir la varianza léxica.

stop_es <- data.frame(word = stopwords("es"))

corpus_clean <- corpus_raw %>%
  unnest_tokens(word, text) %>%
  anti_join(stop_es) %>%
  mutate(word = wordStem(word, language = "spanish")) %>%
  group_by(doc_id) %>%
  summarise(text_proc = paste(word, collapse = " "))

kable(corpus_clean) %>% kable_styling(full_width = F)
doc_id text_proc
Doc_1 viaj interestel salv human traves agujer gusan
Doc_2 mision interestel nav espacial coloniz nuev planet
Doc_3 detectiv persig replic ciud futur oscur

3 Representación Bag-of-Words (BoW)

Construimos la Matriz de Documento-Término (DTM) basada en frecuencias absolutas.

dtm_bow <- corpus_clean %>%
  unnest_tokens(word, text_proc) %>%
  count(doc_id, word) %>%
  pivot_wider(names_from = word, values_from = n, values_fill = 0)

# Vocabulario aprendido
vocabulario <- colnames(dtm_bow)[-1]
print(paste("Vocabulario:", paste(vocabulario, collapse = ", ")))
## [1] "Vocabulario: agujer, gusan, human, interestel, salv, traves, viaj, coloniz, espacial, mision, nav, nuev, planet, ciud, detectiv, futur, oscur, persig, replic"
kable(dtm_bow, caption = "Matriz Bag-of-Words") %>% kable_styling()
Matriz Bag-of-Words
doc_id agujer gusan human interestel salv traves viaj coloniz espacial mision nav nuev planet ciud detectiv futur oscur persig replic
Doc_1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
Doc_2 0 0 0 1 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
Doc_3 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1

Interpretación: La matriz es dispersa (muchos ceros) debido a que los documentos no comparten todas las palabras del vocabulario global.

4 Representación TF-IDF

4.0.1 Fórmulas de Pesado TF-IDF

La importancia de un término se calcula mediante el producto de su frecuencia local y su rareza global:

Frecuencia de Término (TF): Mide la importancia local de una palabra dentro de un documento específico. La forma más común de calcularla es la frecuencia relativa:

\[TF(t, d) = \frac{f_{t,d}}{\sum_{k \in d} f_{k,d}}\]

Frecuencia Inversa de Documento (IDF): Mide la importancia global de una palabra en todo el corpus (conjunto de documentos), penalizando las palabras que aparecen con demasiada frecuencia en todos lados.

\[IDF(t, D) = \log \left( \frac{N}{|\{d \in D : t \in d\}|} \right)\]

Peso Final TF-IDF: Es el producto de ambas medidas, logrando un balance entre la relevancia local y la rareza global.

\[TF\text{-}IDF(t, d, D) = TF(t, d) \cdot IDF(t, D)\]

Calculamos el peso TF-IDF para reequilibrar la importancia de los términos.

dtm_tfidf <- corpus_clean %>%
  unnest_tokens(word, text_proc) %>%
  count(doc_id, word) %>%
  bind_tf_idf(word, doc_id, n) %>%
  select(doc_id, word, tf_idf) %>%
  pivot_wider(names_from = word, values_from = tf_idf, values_fill = 0)

kable(dtm_tfidf, caption = "Matriz TF-IDF") %>% kable_styling()
Matriz TF-IDF
doc_id agujer gusan human interestel salv traves viaj coloniz espacial mision nav nuev planet ciud detectiv futur oscur persig replic
Doc_1 0.1569446 0.1569446 0.1569446 0.0579236 0.1569446 0.1569446 0.1569446 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
Doc_2 0.0000000 0.0000000 0.0000000 0.0579236 0.0000000 0.0000000 0.0000000 0.1569446 0.1569446 0.1569446 0.1569446 0.1569446 0.1569446 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
Doc_3 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.183102 0.183102 0.183102 0.183102 0.183102 0.183102

Comparación: TF-IDF asigna valores menores a términos comunes en el corpus (como “interestelar”) y mayor peso a términos únicos de cada documento.

5 Análisis de Similitud de Coseno

Se utiliza para cuantificar qué tan parecidos son dos documentos interpretándolos como vectores en un espacio multidimensional. En lugar de medir la distancia euclidiana, medimos el ángulo entre ellos.

Para medir la similitud entre dos documentos representados como vectores (\(\mathbf{A}\) y \(\mathbf{B}\)), calculamos el coseno del ángulo que forman en el espacio vectorial:

\[\text{sim}(\mathbf{A}, \mathbf{B}) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|}\]

De forma expandida para el cálculo computacional:

\[\text{sim}(\mathbf{A}, \mathbf{B}) = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}}\]

Medimos el ángulo entre los vectores de los documentos.

calc_coseno <- function(m) {
  mat <- as.matrix(m[,-1])
  sim <- as.matrix(proxy::simil(mat, method = "cosine"))
  diag(sim) <- 1
  return(sim)
}

sim_bow <- calc_coseno(dtm_bow)
sim_tfidf <- calc_coseno(dtm_tfidf)

print("Similitud BoW (Doc 1 vs Doc 2):")
## [1] "Similitud BoW (Doc 1 vs Doc 2):"
sim_bow[1,2]
## [1] 0.1428571

Par más similar: Doc 1 y Doc 2 (comparten el concepto de misión/viaje interestelar).

Par menos similar: Doc 2 y Doc 3 (no hay solapamiento léxico significativo).

6 Representación One-hot (Conceptual)

Seleccionamos tres tokens: “viaj”, “planet”, “detectiv”.

Viaj: [1, 0, 0]

Planet: [0, 1, 0]

Detectiv: [0, 0, 1]

Reflexión: Las representaciones One-hot son inadecuadas para la similitud semántica porque todos los vectores son ortogonales entre sí; la distancia entre cualquier par de palabras distintas es siempre la misma, ignorando si son sinónimos o están relacionadas.

7 Resumen y Reflexión

La vectorización permite tratar el lenguaje como puntos en un espacio multidimensional, facilitando comparaciones matemáticas mediante el álgebra lineal. El esquema TF-IDF es fundamental porque penaliza términos globales que no ayudan a distinguir documentos. Sin embargo, al ser métodos puramente léxicos, no capturan el significado profundo; por ejemplo, “nave” y “cohete” serían tratados como términos totalmente distintos a pesar de su cercanía semántica.

8 Requisito de Reproducibilidad

Para garantizar que este análisis pueda ser ejecutado independientemente y genere los mismos resultados, se reportan las especificaciones técnicas del entorno.

  • Semilla Aleatoria: Se utilizó set.seed(2026)

8.0.1 Versiones de las Librerías Utilizadas

A continuación, se detallan las versiones de los paquetes instalados en el sistema al momento de la compilación:

  • tidyverse (Manipulación de datos): 2.0.0
  • tidytext (Minería de texto): 0.4.3
  • proxy (Cálculo de similitud de coseno): 0.4.27
  • SnowballC (Stemming): 0.7.1
  • stopwords (Léxicos de parada): 2.3

Información del Sistema: R version 4.4.2 (2024-10-31 ucrt)