Introducción

Este proyecto analiza dos frentes de la industria de Medios y Entretenimiento - el catálogo de títulos de Netflix 2023 y el catálogo de películas de IMDb de 2026

Para cada uno de estas fuentes de datos, se planean realizar una serie de análisis cuantitativos desde entre ellos covarianza, distribuciones de probabilidad, pruebas de hipótesis, χ2, t de Student y regresión simple.

Parte Fuente de datos Enfoque del análisis
I — Series (Netflix) Kaggle - Netflix 2023 Horas vistas, valoraciones, calificación y disponibilidad global
II — Películas (IMDb) Kaggle - IMDb 2023 Calificación IMDb, año de estreno, género y tendencias temporales

Parte I — Análisis de Netflix

ruta_netflix <- "Fuentes/total_netflix_2023 new.csv"
netflix <- read.csv(ruta_netflix, stringsAsFactors = FALSE)

netflix <- netflix %>%
  mutate(
    Hours.Viewed = as.numeric(gsub("[^0-9.]", "", Hours.Viewed)),
    Number.of.Ratings = as.numeric(gsub("[^0-9.]", "", Number.of.Ratings)),
    Rating = as.numeric(Rating),
    log_Hours = log10(Hours.Viewed),
    Available.Globally. = factor(Available.Globally., levels = c("No", "Yes")),
    GenreSimple = case_when(
      grepl("Comedy", Genre) ~ "Comedy",
      grepl("Drama", Genre) ~ "Drama",
      grepl("Action", Genre) ~ "Action",
      TRUE ~ "Otro"
    ),
    GenreSimple = factor(GenreSimple)
  ) %>%
  filter(
    !is.na(Hours.Viewed),
    !is.na(Rating),
    !is.na(Number.of.Ratings),
    Hours.Viewed > 0,
    Number.of.Ratings >= 0
  )

n_netflix <- nrow(netflix)

El set de datos cuenta con 9 variables y 14,633 observaciones despues de la limpieza donde se filtraron observaciones donde no había valores en todas las columnas.

Para cada Serie de Netflix se tienen datos como: - Nombre de la serie - Disponibilidad global - Fecha de lanzamiento - Horas vista - Etc.


1. Análisis de covarianza (Netflix)

Contexto

Deseamos saber si las horas vistas (horas totales que un show se ha visto), número de valoraciones (gente que le da calificaciones) y calificación (calificación total del 0–10) varían juntas. Utilizaremos la correlación de Pearson para estimar una relación lineal.

Hemos calculado el logaritmo de las horas vistas debido al sesgo presente en esta variable (muchas series se ven muy pocas horas, mientras que otras muy exitosas son vistas mucho más).

vars_cov <- netflix %>%
  select(log_Hours, Number.of.Ratings, Rating) %>%
  rename(
    `log10(Horas vistas)` = log_Hours,
    `N. valoraciones` = Number.of.Ratings,
    Calificacion = Rating
  )

cor_mat <- cor(vars_cov, use = "complete.obs")

tabla_proyecto(
  as.data.frame(round(cor_mat, 3)),
  caption = "Netflix — Matriz de correlación",
  align = "c",
  digits = 3
)
Netflix — Matriz de correlación
log10(Horas vistas) N. valoraciones Calificacion
log10(Horas vistas) 1.000 -0.003 -0.008
N. valoraciones -0.003 1.000 0.117
Calificacion -0.008 0.117 1.000
cor_largo <- as.data.frame(as.table(cor_mat))
names(cor_largo) <- c("Var1", "Var2", "r")

ggplot(cor_largo, aes(Var1, Var2, fill = r)) +
  geom_tile(color = "white", linewidth = 0.8) +
  geom_text(aes(label = sprintf("%.2f", r)), color = "white", fontface = "bold", size = 5) +
  scale_fill_gradientn(colours = paleta_cor, limits = c(-1, 1), name = "Correlación") +
  labs(
    title = titulo_grafica("Análisis de covarianza"),
    subtitle = "Correlación entre log10(horas), valoraciones y calificación",
    x = NULL, y = NULL
  ) +
  tema_proyecto +
  theme(axis.text.x = element_text(angle = 25, hjust = 1, face = "bold"))
Netflix: matriz de correlación.

Netflix: matriz de correlación.

Conclusión

La correlación más fuerte aparece entre N. valoraciones y Calificacion (0.117). Entre log-horas y calificación la relación es débil (-0.008): más horas vistas no garantizan mejor calificación en esta muestra.


2. Distribución de probabilidades (Netflix)

Contexto

Analizaremos si las calificaciones siguen una distribución normal. Sobrepondremos la curva normal teórica a la gráfica de barras como referencia visual.

mu_n <- mean(netflix$Rating)
sd_n <- sd(netflix$Rating)

tabla_proyecto(
  data.frame(
    Estadistico = c("Media", "Desv. est.", "Mínimo", "Máximo"),
    Valor = c(mu_n, sd_n, min(netflix$Rating), max(netflix$Rating))
  ),
  digits = 3,
  caption = "Netflix — Resumen de calificaciones",
  align = c("l", "r")
)
Netflix — Resumen de calificaciones
Estadistico Valor
Media 6.624
Desv. est. 1.256
Mínimo 1.400
Máximo 10.000
x_seq <- seq(min(netflix$Rating), max(netflix$Rating), length.out = 200)
curva_norm <- data.frame(Rating = x_seq, densidad = dnorm(x_seq, mu_n, sd_n))

ggplot(netflix, aes(x = Rating, fill = GenreSimple)) +
  geom_histogram(aes(y = after_stat(density)), bins = 25, alpha = 0.75, color = "white", position = "identity") +
  geom_line(data = curva_norm, aes(x = Rating, y = densidad), inherit.aes = FALSE, color = col_linea, linewidth = 1.4) +
  scale_fill_manual(values = col_genero, name = "Género") +
  labs(
    title = titulo_grafica("Distribución de probabilidades"),
    subtitle = "Histograma por género con curva normal teórica superpuesta",
    x = "Calificación (0–10)", y = "Densidad"
  ) +
  tema_proyecto +
  theme(legend.position = "bottom")
Netflix: distribución de calificaciones.

Netflix: distribución de calificaciones.

Conclusión

Las calificaciones se concentran cerca de 6.62 con desviación estándard de 1.26. El análisis visual muestra que las calificaciones parecen estar distribuidas normalmente, pero es necesario un análisis más riguros para asegurarlo.


3. Prueba de hipótesis y t de Student (Netflix)

Contexto

Queremos ver si \(las horas vistas entre series disponibles globalmente son diferentes frente a las que no lo son, para esto haremos una prueba visual con gráficos de caja y bigotes y la prueba **t** de Student (\)H_0$: medias iguales).

t_prueba <- t.test(log_Hours ~ Available.Globally., data = netflix, var.equal = FALSE)

tabla_proyecto(
  data.frame(
    Estadistico = c("t", "gl", "valor-p", "Media log10 (No)", "Media log10 (Yes)"),
    Valor = c(
      round(t_prueba$statistic, 4),
      round(t_prueba$parameter, 2),
      format.pval(t_prueba$p.value, digits = 4),
      round(t_prueba$estimate[1], 3),
      round(t_prueba$estimate[2], 3)
    )
  ),
  caption = "Netflix — Prueba t de Student",
  align = c("l", "r")
)
Netflix — Prueba t de Student
Estadistico Valor
t t -12.5488
df gl 7683.76
valor-p < 2.2e-16
mean in group No Media log10 (No) 6.381
mean in group Yes Media log10 (Yes) 6.553
ggplot(netflix, aes(x = Available.Globally., y = log_Hours, fill = Available.Globally.)) +
  geom_boxplot(alpha = 0.75, width = 0.55, color = color_vino, outlier.color = "#FFD166") +
  geom_jitter(aes(color = Available.Globally.), width = 0.15, alpha = 0.25, size = 0.6) +
  stat_summary(fun = mean, geom = "point", shape = 18, size = 3.5, color = col_punto) +
  scale_fill_manual(values = col_global, guide = "none") +
  scale_color_manual(values = col_global, guide = "none") +
  labs(
    title = titulo_grafica("Prueba t de Student"),
    subtitle = "Boxplots lado a lado; rombo negro = media muestral",
    x = "Disponible globalmente",
    y = expression(log[10](Horas~vistas))
  ) +
  annotate("text", x = 1.5, y = max(netflix$log_Hours) * 0.98, color = color_vino,
           label = paste0("valor-p = ", format.pval(t_prueba$p.value, digits = 3))) +
  tema_proyecto
Netflix: boxplots lado a lado (log10 horas).

Netflix: boxplots lado a lado (log10 horas).

Conclusión

Con un valor-p muy chico (menor a 2.2e-16) rechazamos la hipotesis nula a favor de la alterna - la disponibilidad global se asocia con mayor audiencia.


4. Prueba Chi-cuadrado (Netflix)

Contexto

Queremos saber si hay una correlación entre el hecho de que una serie tenga disponibilidad global y el género (Acción, Comedia, Drama, etc.). Por ejemplo la comedia es muy personal de cada país por lo que puede no ser redituable disponibilizarla globalmente. Para esto usaremos la \(\chi^2\) de Pearson.

tab_chi <- table(netflix$Available.Globally., netflix$GenreSimple)
dimnames(tab_chi) <- list(Disponibilidad = c("No", "Yes"), Genero = levels(netflix$GenreSimple))

tabla_chi_df <- data.frame(
  Disponibilidad = rownames(tab_chi),
  as.data.frame.matrix(tab_chi),
  check.names = FALSE
)
names(tabla_chi_df)[-1] <- colnames(tab_chi)

tabla_proyecto(
  tabla_chi_df,
  caption = "Netflix — Tabla de contingencia",
  align = c("l", rep("r", ncol(tab_chi)))
)
Netflix — Tabla de contingencia
Disponibilidad Action Comedy Drama Otro
No No 853 3493 2618 3420
Yes Yes 342 1442 1104 1361
chi_resultado <- chisq.test(tab_chi)

tabla_proyecto(
  data.frame(
    `Chi-cuadrado` = round(chi_resultado$statistic, 3),
    gl = chi_resultado$parameter,
    `valor-p` = format.pval(chi_resultado$p.value, digits = 4),
    check.names = FALSE
  ),
  caption = "Netflix — Resultado Chi-cuadrado",
  align = c("l", "r", "r", "r")
)
Netflix — Resultado Chi-cuadrado
Chi-cuadrado gl valor-p
X-squared 1.64 3 0.6504

Conclusión

Con una \(\chi^2\) de 1.64, por arriba del valor p (0.05), decidimos no rechazar la hipótesis nula - no hay una asociación entre la disponibilidad global y el género.


5. Análisis de regresión (Netflix)

Contexto

Queremos realizar un análisis de regreción entre la calificación y las horas vistas, así tendremos un modelo para predecir que califiación tendría una series si supieramos cuantas horas vistas tiene. Una vez más, tomaremos el logaritmo de las horas vistas.

mod_n <- lm(Rating ~ log_Hours, data = netflix)
coef_n <- as.data.frame(summary(mod_n)$coefficients)
coef_n$Termino <- rownames(coef_n)
rownames(coef_n) <- NULL

tabla_proyecto(
  coef_n[, c("Termino", "Estimate", "Std. Error", "Pr(>|t|)")],
  digits = 6, caption = "Netflix — Regresión simple",
  align = c("l", "r", "r", "r")
)
Netflix — Regresión simple
Termino Estimate Std. Error Pr(>|t|)
(Intercept) 6.708467 0.090008 0.000000
log_Hours -0.013170 0.013903 0.343518
tabla_proyecto(
  data.frame(
    Metrica = c("R²", "R² ajustado"),
    Valor = c(summary(mod_n)$r.squared, summary(mod_n)$adj.r.squared)
  ),
  digits = 4, caption = "Netflix — Bondad de ajuste",
  align = c("l", "r")
)
Netflix — Bondad de ajuste
Metrica Valor
1e-04
R² ajustado 0e+00
ggplot(netflix, aes(x = log_Hours, y = Rating, color = GenreSimple)) +
  geom_point(alpha = 0.35, size = 1.2) +
  geom_smooth(aes(group = 1), method = "lm", se = TRUE, color = col_linea, fill = col_relleno_banda, linewidth = 1.2) +
  scale_color_manual(values = col_genero, name = "Género") +
  labs(
    title = titulo_grafica("Análisis de regresión"),
    subtitle = "Modelo lineal: calificación ~ log10(horas vistas)",
    x = expression(log[10](Horas~vistas)), y = "Calificación"
  ) +
  tema_proyecto +
  theme(legend.position = "bottom")
Netflix: regresión calificación ~ log10(horas).

Netflix: regresión calificación ~ log10(horas).

Conclusión

Nuestro modelo de regresión no parece explicar la calificación inspeccionandolo visualmente ni utilizando la \(R^2\).


Parte II — Análisis de películas (IMDb)

El set de datos cuenta con 16 variables y 3201 observaciones despues de la limpieza donde se filtraron observaciones donde no había valores en todas las columnas.

Para cada película se tienen datos como: - Título - Las ganancias en taquilla en Estados Unidos y Globalmente - El presupuesto - La fecha de lanzamiento - Las calificaciones en las plataformas de Rotten Tomatoes y IMDb - Etc.

ruta_imdb <- "Fuentes/movies.json"
peliculas <- as.data.frame(fromJSON(ruta_imdb))

peliculas <- peliculas %>%
  mutate(
    `Release Date` = as.Date(`Release Date`, format = "%b %d %Y"),
    Year = as.numeric(format(`Release Date`, "%Y")),
    Rating = `IMDB Rating`,
    Decade = floor(Year / 10) * 10,
    Worldwide.Gross = as.numeric(gsub("[^0-9.]", "", as.character(`Worldwide Gross`))),
    Major.Genre = as.factor(`Major Genre`)
  ) %>%
  filter(!is.na(Rating), !is.na(Year))

n_peliculas <- nrow(peliculas)
top15 <- peliculas %>% arrange(desc(Rating)) %>% head(15)

peliculas_genero <- peliculas %>%
  filter(!is.na(Major.Genre)) %>%
  add_count(Major.Genre, name = "n_genero") %>%
  filter(n_genero >= 50) %>%
  mutate(Major.Genre = droplevels(Major.Genre))

resumen_genero <- peliculas_genero %>%
  group_by(Major.Genre) %>%
  summarise(
    Peliculas = n(),
    Promedio = mean(Rating, na.rm = TRUE),
    Mediana = median(Rating, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(Promedio))

generos_facet <- levels(peliculas_genero$Major.Genre)

decade_genero <- peliculas %>%
  filter(Major.Genre %in% generos_facet, !is.na(Decade), Decade >= 1920) %>%
  group_by(Major.Genre, Decade) %>%
  summarise(
    Promedio = mean(Rating, na.rm = TRUE),
    Peliculas = n(),
    .groups = "drop"
  ) %>%
  filter(Peliculas >= 5)

1. Top 15 películas (IMDb)

Contexto

Identificamos las 15 películas con mayor calificación IMDb del conjunto. Este gráfico de barras resume el extremo superior de la distribución y facilita comparar títulos destacados.

ggplot(top15, aes(x = reorder(Title, Rating), y = Rating, fill = Rating)) +
  geom_col(show.legend = FALSE) +
  scale_fill_gradient(low = "#C9A0A4", high = col_primario) +
  coord_flip() +
  labs(
    title = titulo_grafica("Top 15 películas mejor calificadas", "IMDb"),
    subtitle = "Barras ordenadas por calificación IMDb",
    x = NULL, y = "Calificación IMDb"
  ) +
  tema_proyecto
Películas (IMDb): Top 15 por calificación.

Películas (IMDb): Top 15 por calificación.

Conclusión

El tope del ranking supera 8.8 puntos; las mejores películas concentran calificaciones muy altas, lo que refleja la selección positiva típica de catálogos curados o muy valorados por usuarios.


2. Distribución de probabilidades (IMDb)

Contexto

El histograma muestra cómo se reparten las calificaciones IMDb en todo el catálogo. Superponemos la curva normal teórica (media y desviación muestrales) para comparar la forma empírica con el modelo gaussiano, igual que en las series de Netflix.

mu_p <- mean(peliculas$Rating)
sd_p <- sd(peliculas$Rating)

tabla_proyecto(
  data.frame(
    Estadistico = c("Media", "Desv. est.", "Mínimo", "Máximo"),
    Valor = c(mu_p, sd_p, min(peliculas$Rating), max(peliculas$Rating))
  ),
  digits = 3,
  caption = "IMDb — Resumen de calificaciones",
  align = c("l", "r")
)
IMDb — Resumen de calificaciones
Estadistico Valor
Media 6.283
Desv. est. 1.252
Mínimo 1.400
Máximo 9.200
x_seq_p <- seq(min(peliculas$Rating), max(peliculas$Rating), length.out = 200)
curva_norm_p <- data.frame(Rating = x_seq_p, densidad = dnorm(x_seq_p, mu_p, sd_p))

ggplot(peliculas, aes(x = Rating)) +
  geom_histogram(aes(y = after_stat(density)), binwidth = 0.2, fill = col_secundario, color = "white", alpha = 0.85) +
  geom_line(data = curva_norm_p, aes(x = Rating, y = densidad), inherit.aes = FALSE, color = col_linea, linewidth = 1.4) +
  labs(
    title = titulo_grafica("Distribución de probabilidades", "IMDb"),
    subtitle = "Histograma con curva normal teórica superpuesta",
    x = "Calificación IMDb", y = "Densidad"
  ) +
  tema_proyecto
Películas (IMDb): histograma de calificaciones con curva normal.

Películas (IMDb): histograma de calificaciones con curva normal.

Conclusión

Las calificaciones se concentran cerca de 6.28 (DE = 1.25). La mayoría de películas supera 6.0 puntos; la curva normal es referencia visual, no prueba formal de normalidad.


3. Calificación por género — Prueba de hipótesis (IMDb)

Contexto

¿La calificación IMDb depende del género principal? Agrupamos los géneros con al menos 50 películas y comparamos sus distribuciones con boxplots lado a lado. Además aplicamos ANOVA de un factor (\(H_0\): medias iguales entre géneros).

tabla_proyecto(
  resumen_genero,
  digits = 3,
  caption = "IMDb — Calificación promedio por género (mínimo 50 películas)",
  align = c("l", "r", "r", "r")
)
IMDb — Calificación promedio por género (mínimo 50 películas)
Major.Genre Peliculas Promedio Mediana
Drama 738 6.773 6.90
Musical 50 6.448 6.85
Thriller/Suspense 233 6.361 6.40
Adventure 251 6.345 6.40
Action 392 6.115 6.20
Romantic Comedy 130 5.873 5.85
Comedy 635 5.854 6.00
Horror 209 5.676 5.60
anova_genero_p <- aov(Rating ~ Major.Genre, data = peliculas_genero)
anova_tabla_p <- as.data.frame(summary(anova_genero_p)[[1]])
anova_tabla_p$Fuente <- rownames(anova_tabla_p)
rownames(anova_tabla_p) <- NULL

tabla_proyecto(
  anova_tabla_p[, c("Fuente", "Df", "Sum Sq", "Mean Sq", "F value", "Pr(>F)")],
  digits = 4,
  caption = "IMDb — ANOVA: calificación por género",
  align = c("l", "r", "r", "r", "r", "r"),
  na = ""
)
IMDb — ANOVA: calificación por género
Fuente Df Sum Sq Mean Sq F value Pr(>F)
Major.Genre 7 403.1377 57.5911 40.9151 0
Residuals 2630 3701.9285 1.4076 NA NA
genero_orden <- resumen_genero %>% arrange(Promedio) %>% pull(Major.Genre)

ggplot(peliculas_genero, aes(x = reorder(Major.Genre, Rating, FUN = median), y = Rating, fill = Major.Genre)) +
  geom_boxplot(alpha = 0.75, width = 0.65, color = col_primario, outlier.color = col_outlier) +
  geom_jitter(width = 0.12, alpha = 0.15, size = 0.5, color = col_punto) +
  stat_summary(fun = mean, geom = "point", shape = 18, size = 3, color = col_punto) +
  scale_fill_manual(values = paleta_genero_imdb, guide = "none") +
  scale_x_discrete(limits = genero_orden) +
  labs(
    title = titulo_grafica("Calificación por género cinematográfico", "IMDb"),
    subtitle = "Géneros con ≥ 50 películas; rombo negro = media",
    x = "Género principal", y = "Calificación IMDb"
  ) +
  tema_proyecto +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))
Películas (IMDb): calificación por género principal.

Películas (IMDb): calificación por género principal.

Conclusión

El género con mayor promedio es Drama (6.77); el menor, Horror (5.68). ANOVA: valor-p = <2e-16rechazamos \(H_0\). El género sí se asocia con diferencias en calificación, aunque el gráfico muestra matices (p. ej. comedia vs. terror).


4. Evolución del promedio por década (IMDb)

Contexto

Comparamos cómo cambia la calificación promedio por década de estreno dentro de cada género principal (con al menos 5 películas por década). El panel de gráficas con facet_wrap permite ver si la tendencia temporal es similar en drama, comedia, acción y otros géneros.

ggplot(decade_genero, aes(x = Decade, y = Promedio)) +
  geom_line(color = col_primario, linewidth = 1) +
  geom_point(color = col_secundario, size = 2) +
  facet_wrap(~Major.Genre, scales = "free_y", ncol = 3) +
  labs(
    title = titulo_grafica("Evolución de la calificación por década", "IMDb"),
    subtitle = "Un panel por género principal (mínimo 5 películas por década)",
    x = "Década de estreno", y = "Calificación promedio"
  ) +
  tema_proyecto +
  theme(strip.text = element_text(face = "bold", color = col_primario))
Películas (IMDb): evolución por década según género.

Películas (IMDb): evolución por década según género.

Conclusión

No todas las décadas ni todos los géneros evolucionan igual: algunos paneles muestran ligero ascenso o descenso según la época, mientras otros se mantienen estables. Esto indica que el año de estreno interactúa con el género; conviene interpretar la tendencia global junto con cada categoría, no solo el promedio de todo el catálogo.


5. Análisis de regresión (IMDb)

Contexto

¿Las películas más recientes reciben mejores o peores calificaciones? Ajustamos una regresión lineal simple (año → calificación), equivalente al análisis del script original.

mod_p <- lm(Rating ~ Year, data = peliculas)
coef_p <- as.data.frame(summary(mod_p)$coefficients)
coef_p$Termino <- rownames(coef_p)
rownames(coef_p) <- NULL

tabla_proyecto(
  coef_p[, c("Termino", "Estimate", "Std. Error", "Pr(>|t|)")],
  digits = 6, caption = "IMDb — Regresión calificación ~ año",
  align = c("l", "r", "r", "r")
)
IMDb — Regresión calificación ~ año
Termino Estimate Std. Error Pr(>|t|)
(Intercept) 27.642287 3.828858 0
Year -0.010689 0.001916 0
cor_ano <- cor(peliculas$Year, peliculas$Rating, use = "complete.obs")
ggplot(peliculas, aes(x = Year, y = Rating)) +
  geom_point(color = col_terciario, alpha = 0.35, size = 1) +
  geom_smooth(method = "lm", se = TRUE, color = col_linea, fill = col_relleno_banda, linewidth = 1.1) +
  labs(
    title = titulo_grafica("Análisis de regresión", "IMDb"),
    subtitle = "Modelo lineal: calificación ~ año de estreno",
    x = "Año de estreno", y = "Calificación IMDb"
  ) +
  tema_proyecto
Películas (IMDb): año de estreno vs. calificación con recta de regresión.

Películas (IMDb): año de estreno vs. calificación con recta de regresión.

Conclusión

Correlación año–rating: -0.102; \(R^2\) = 0.0103. La pendiente es significativa pero el modelo explica poca variación: el año de estreno por sí solo no determina la calificación IMDb en este conjunto amplio.


6. Nube de palabras — Títulos (IMDb)

Contexto

La nube de palabras resume las palabras más frecuentes en los títulos de las películas, tras eliminar muletillas y stop words (artículos, preposiciones y términos vacíos en inglés y español) para resaltar sustantivos y nombres relevantes.

if (requireNamespace("wordcloud", quietly = TRUE) &&
    requireNamespace("tm", quietly = TRUE)) {
  library(wordcloud)
  library(tm)

  palabras_vacias <- unique(c(
    stopwords("english"),
    stopwords("spanish"),
    "movie", "film", "story",
    "part", "chapter", "vs", "ii", "iii", "iv", "v", "vi",
    "the", "and", "of", "in", "to", "a", "an", "for", "on", "at", "by",
    "de", "la", "el", "los", "las", "y", "en", "un", "una", "del", "al"
  ))

  corpus_titulos <- Corpus(VectorSource(peliculas$Title))
  corpus_titulos <- tm_map(corpus_titulos, content_transformer(tolower))
  corpus_titulos <- tm_map(corpus_titulos, removePunctuation)
  corpus_titulos <- tm_map(corpus_titulos, removeNumbers)
  corpus_titulos <- tm_map(corpus_titulos, removeWords, palabras_vacias)
  corpus_titulos <- tm_map(corpus_titulos, stripWhitespace)

  tdm <- TermDocumentMatrix(corpus_titulos)
  m <- as.matrix(tdm)
  freq <- sort(rowSums(m), decreasing = TRUE)
  freq <- freq[nchar(names(freq)) > 2]

  par(bg = "white", fg = col_texto_eje, mar = rep(0.5, 4))
  wordcloud(
    words = names(freq),
    freq = freq,
    random.order = FALSE,
    colors = c(col_primario, col_secundario, col_terciario, col_linea, "#9B5DE5", "#F77F00"),
    max.words = 80,
    scale = c(3, 0.5)
  )
} else {
  plot.new()
  text(0.5, 0.5, "Instale los paquetes wordcloud y tm para ver esta figura")
}
Películas (IMDb): nube de palabras sin muletillas.

Películas (IMDb): nube de palabras sin muletillas.

Conclusión

Tras quitar muletillas, emergen términos con significado narrativo o temático (nombres, lugares, conceptos) en lugar de artículos repetidos. La nube refleja la diversidad léxica del catálogo y no debe leerse como ranking de películas, sino como exploración del lenguaje usado en los títulos.


Conclusión general

Netflix

  • Correlación fuerte entre valoraciones y horas (log), pero casi nula entre horas y calificación.
  • Disponibilidad global se vincula con más horas vistas (log).
  • Género y disponibilidad global parecen independientes (\(\chi^2\)).
  • La regresión confirma que audiencia ≠ calificación (\(R^2\) muy bajo).

Películas (IMDb)

  • Las calificaciones se concentran en valores medios-altos; la normal teórica es solo referencia.
  • El Top 15 muestra el extremo superior del ranking.
  • El género se relaciona con diferencias en calificación (ANOVA y boxplots).
  • La evolución por década varía según el género en los paneles de facet_wrap.
  • El año de estreno explica poca variación individual en la regresión.

Comparación entre ambos catálogos

Los dos catálogos difieren en tamaño y en tipos de variables disponibles. La tabla resume filas, columnas y cuántas variables son numéricas, categóricas, de fecha o lógicas.

Comparación estructural de los catálogos
Catalogo Filas Columnas Numéricas Categóricas Fechas Lógicas
Netflix (archivo original) 18332 9 0 9 0 0
Netflix (datos analizados) 14633 11 4 7 0 0
IMDb (archivo original) 3201 16 8 8 0 0
IMDb (datos analizados) 2988 21 12 8 1 0