📦 Configuración y Limpieza de Datos

Esta sección carga los paquetes necesarios, importa el dataset de TikTok y realiza una limpieza general: renombrado de columnas, conversión de tipos de datos, eliminación de valores nulos y creación de nuevas variables como la tasa de engagement y categorías temáticas detectadas en las transcripciones.

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(plotly)
## 
## Adjuntando el paquete: 'plotly'
## 
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## The following object is masked from 'package:graphics':
## 
##     layout
library(shiny)
library(DT)
## 
## Adjuntando el paquete: 'DT'
## 
## The following objects are masked from 'package:shiny':
## 
##     dataTableOutput, renderDataTable
library(lubridate)

# Cargar los datos
tiktok_data <- read_csv("C:/R/Unidad_3/tiktok_dataset/tiktok_dataset.csv")
## Rows: 19382 Columns: 12
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (9): claim_status, video_transcription_text, verified_status, author_ban...
## dbl (3): #, video_id, video_duration_sec
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Limpieza inicial
clean_data <- tiktok_data %>%
  select(-`#`) %>%
  rename_all(~str_to_lower(.)) %>%
  rename_all(~str_replace_all(., " ", "_")) %>%
  mutate(
    claim_status = as.factor(claim_status),
    verified_status = as.factor(verified_status),
    author_ban_status = as.factor(author_ban_status),
    video_transcription_text = str_trim(video_transcription_text),
    video_view_count = as.numeric(video_view_count),
    video_like_count = as.numeric(video_like_count),
    video_share_count = as.numeric(video_share_count),
    video_download_count = as.numeric(video_download_count),
    video_comment_count = as.numeric(video_comment_count)
  ) %>%
  drop_na(video_view_count, video_like_count) %>%
  mutate(
    engagement_rate = (video_like_count + video_comment_count + video_share_count) / video_view_count,
    category = case_when(
      str_detect(video_transcription_text, "drone|deliver") ~ "Technology",
      str_detect(video_transcription_text, "microorganism|soil|animal|plant|tree") ~ "Nature/Science",
      str_detect(video_transcription_text, "carnegie|million|billion|gdp|business") ~ "Economy/Business",
      str_detect(video_transcription_text, "metro|subway|bus|network") ~ "Transportation",
      str_detect(video_transcription_text, "olympics|sport|player|match") ~ "Sports",
      str_detect(video_transcription_text, "moon|earth|planet|space|sun") ~ "Space",
      str_detect(video_transcription_text, "internet|web|mobile|device") ~ "Technology/Digital",
      str_detect(video_transcription_text, "music|song|band|album") ~ "Music",
      str_detect(video_transcription_text, "animal|dog|cat|bird") ~ "Animals",
      TRUE ~ "Other"
    ),
    upload_date = as.Date("2023-01-01") + days(row_number() %% 365)
  )
## Warning: There were 5 warnings in `mutate()`.
## The first warning was:
## ℹ In argument: `video_view_count = as.numeric(video_view_count)`.
## Caused by warning:
## ! NAs introducidos por coerción
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 4 remaining warnings.

🎯 Filtros Interactivos

Esta sección permite a los usuarios filtrar los datos por categoría, duración del video y estado de verificación del autor.

selectInput("category", "Seleccionar Categoría:",
            choices = unique(clean_data$category),
            multiple = TRUE,
            selected = "Nature/Science")
sliderInput("duration", "Duración del Video (segundos):",
            min = min(clean_data$video_duration_sec),
            max = max(clean_data$video_duration_sec),
            value = c(10, 60))
checkboxGroupInput("verification", "Estado de Verificación:",
                   choices = levels(clean_data$verified_status),
                   selected = levels(clean_data$verified_status))

📊 Gráfico: Distribución de Vistas

Este gráfico muestra la distribución de vistas de videos agrupados por estado de verificación del autor.

filtered_data <- reactive({
  clean_data %>%
    filter(
      category %in% input$category,
      video_duration_sec >= input$duration[1],
      video_duration_sec <= input$duration[2],
      verified_status %in% input$verification
    )
})

plot_ly(
  data = clean_data %>%
    filter(
      category %in% c("Nature/Science"),  # Selección estática
      video_duration_sec >= 10,  # Duración estática
      video_duration_sec <= 60,  # Duración estática
      verified_status %in% c("verified")  # Estado de verificación estático
    ),
  x = ~verified_status,
  y = ~log10(video_view_count),
  color = ~verified_status,
  type = "box"
) %>%
  layout(title = "Distribución de Vistas por Verificación",
         xaxis = list(title = "Verificación"),
         yaxis = list(title = "Log10(Vistas)"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

Interpretación: Esta gráfica compara las vistas de los videos entre usuarios verificados y no verificados. Se utiliza una escala logarítmica para facilitar la visualización de grandes diferencias. Permite ver si la verificación está asociada con un mayor alcance.

💬 Gráfico: Engagement vs Duración

Esta sección genera un gráfico de dispersión que relaciona la duración del video con su tasa de engagement.

plot_ly(
  data = clean_data %>%
    filter(
      category %in% c("Nature/Science"),  # Selección estática
      video_duration_sec >= 10,  # Duración estática
      video_duration_sec <= 60,  # Duración estática
      verified_status %in% c("verified")  # Estado de verificación estático
    ),
  x = ~video_duration_sec,
  y = ~engagement_rate,
  color = ~category,
  size = ~video_view_count,
  type = "scatter",
  mode = "markers",
  hoverinfo = "text",
  text = ~paste("ID:", video_id, "<br>Duración:", video_duration_sec,
                "<br>Engagement:", round(engagement_rate, 4),
                "<br>Vistas:", video_view_count)) %>%
  layout(title = "Engagement vs Duración",
         xaxis = list(title = "Duración (s)"),
         yaxis = list(title = "Engagement Rate"))
## Warning: `line.width` does not currently support multiple values.
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

Interpretación: Esta gráfica evalúa la relación entre la duración de los videos y la tasa de engagement. Permite detectar si los videos más largos o cortos generan más interacción relativa. También se comparan temáticas (colores) y volumen de vistas (tamaño).

📚 Gráfico: Distribución por Categoría y Estado de Ban

Este gráfico de barras muestra la cantidad de videos por categoría diferenciando si el autor ha sido baneado o no.

plot_ly(
  data = clean_data %>% count(category, author_ban_status),
  x = ~category,
  y = ~n,
  color = ~author_ban_status,
  type = "bar",
  hoverinfo = "text",
  text = ~paste("Categoría:", category, "<br>Estado:", author_ban_status, "<br>Conteo:", n)
) %>%
  layout(
    title = "Distribución de Videos por Categoría y Estado de Ban del Autor",
    xaxis = list(title = "Categoría", categoryorder = "total descending"),
    yaxis = list(title = "Número de Videos"),
    barmode = "stack",
    hovermode = "x unified"
)

Interpretación: Muestra la cantidad de videos por categoría, diferenciando si el autor ha sido baneado o no. Esto puede ayudar a detectar temáticas más propensas a violaciones de políticas.

📈 Gráfico: Tendencia Temporal de Publicaciones

Esta sección representa cómo cambió la frecuencia de publicaciones a lo largo del tiempo, diferenciada por categoría.

plot_ly(
  data = clean_data %>% count(upload_date, category),
  x = ~upload_date,
  y = ~n,
  color = ~category,
  type = "scatter",
  mode = "lines+markers",
  hoverinfo = "text",
  text = ~paste("Fecha:", upload_date, "<br>Categoría:", category, "<br>Videos:", n)
) %>%
  layout(
    title = "Tendencia Temporal de Publicaciones por Categoría",
    xaxis = list(title = "Fecha"),
    yaxis = list(title = "Número de Videos Publicados"),
    hovermode = "x unified"
)
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

Interpretación: Representa cómo cambió la frecuencia de publicaciones a lo largo del tiempo, diferenciada por categoría. Permite detectar tendencias, estacionalidad o picos de actividad en temáticas específicas.

✅ Conclusión

Este análisis interactivo proporciona una visión integral del rendimiento de los videos en TikTok, considerando variables clave como la duración, engagement, verificación del autor, categoría del contenido y su evolución temporal. Las visualizaciones permiten descubrir patrones y tendencias útiles para la toma de decisiones estratégicas en la creación de contenido, revelando, por ejemplo, qué temáticas generan mayor interacción, cómo influye la duración del video, y el impacto de la verificación del autor en el alcance del contenido.