January 21, 2020

1754 words 9 mins read

Monitoreando su velocidad real de Internet a lo largo del tiempo, utilizando R y Speedtest-CLI

Monitoreando su velocidad real de Internet a lo largo del tiempo, utilizando R y Speedtest-CLI

Pensé que esto solo sucedía en mi país (Perú), pero resulta que les sucede a personas en todo el mundo, uno contrata un plan de Internet que ofrece hasta X Mbps, donde X es un valor impresionante (relativo al contexto de cada país), pero la trampa está en esa palabra, Hasta.

En realidad, termina obteniendo velocidades de descarga mucho más lentas de lo anunciado, y los ISP (Proveedores de Servicios de Internet por sus siglas en inglés) se salen con la suya, ya que solo prueban la velocidad de descarga con usted, una vez, cuando instalan el servicio (sospechosamente funciona muy bien en ese momento) y la mayoría de personas no se molestan en realizar un seguimiento de sus velocidades de descarga a lo largo del tiempo, ya que no es práctico realizar pruebas de velocidad manualmente cada corto período de tiempo, afortunadamente hay una mejor manera, el servicio Speedtest de Ookla provee una Interfaz de Línea de Comandos (CLI por sus siglas en inglés), que le permite realizar pruebas desde la terminal de su sistema, dándole la capacidad de configurar scripts automáticos para recopilar datos de rendimiento de la conexión.

En las siguientes líneas, le mostraré cómo hacer esto desde R y obtener los resultados en forma de bonitos gráficos que puede twittear a su ISP para molestarlos un poco. 😆

Preparando su Configuración

Tenemos que configurar nuestro entorno de trabajo para este proyecto. Primero, necesitamos una máquina que esté constantemente encendida y conectada a la misma conexión de Internet que desea probar, para que pueda realizar las pruebas y almacenar los datos, podría ser cualquier computadora conectada a su red local pero la solución más práctica (y económica) que he encontrado es usando una Raspberry Pi, así que usaré una para este proyecto.

Dado que está leyendo un artículo relacionado con R, voy a suponer que ya tiene su entorno de R básico configurado, por lo que nos centraremos en las cosas adicionales, necesitamos instalar la interfaz de línea de comandos (CLI) de Speedtest, si está en una máquina Linux, puede hacerlo con estos comandos en una terminal del sistema:

✏️ Hay instrucciones de instalación para otros sistemas operativos en el sitio web de Speedtest.

sudo apt-get install curl
curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | sudo bash
sudo apt-get install speedtest

✏️ Compruebe si tiene instalada una versión anterior Python de la CLI en su sistema y asegúrese de desinstalarla con este comando sudo pip uninstall speedtes-cli, ya que podría causar conflictos con la versión oficial más reciente. Esto me sucedió y no me di cuenta 😳, gracias a @jasongrahn por el aviso.

Reuniendo Dato

Ahora podemos escribir un script R que recupere el resultado de la prueba, procese el contenido y almacene los datos. En este ejemplo, los voy a almacenar en una base de datos PostgreSQL, pero podría elegir otras opciones como otro servidor SQL, Hojas de cálculo de Google, un archivo CSV, etc. He llamado a este script speedtest_job.R.

#! /usr/bin/env Rscript

# Data adquisition
output <- read.csv(
  text = system(
    command = "speedtest -f csv --output-header --accept-license",
    intern = TRUE
  )
)

results <- data.frame(
  stringsAsFactors = FALSE,
  time = as.POSIXct(Sys.time()),
  ip = system("curl ifconfig.me", intern = TRUE), # This field is no longer provided on the new CLI version
  ping = output$latency,
  download = output$download * 7.629395E-6, # Convert to Mbps
  upload = output$upload * 7.629395E-6, # Convert to Mbps
  isp = "movistar" # This field is not provided on CSV format
)

# Data storing
con <- dbConnect(drv = odbc::odbc(),
                 driver = 'PostgreSQL ANSI',
                 server = 'localhost',
                 database = 'internet', # Name of the database on the sql server
                 port = 5432,
                 uid = Sys.getenv('MY_UID'),
                 pwd = Sys.getenv('MY_PWD'),
                 encoding = 'utf8')

DBI::dbAppendTable(
  conn = con,
  name = "speed_test", # Table name on the sql server
  value = results
)

odbc::dbDisconnect(conn = con)

# Cleaning log files
unlink(x = "*.log", force = TRUE)

Ahora podemos programar un trabajo cron para ejecutar este script a intervalos regulares, voy a configurarlo para que se ejecute una vez cada hora ejecutando estos comandos en un terminal del sistema:

✏️ Esto también se puede hacer desde R usando el paquete cronR.

env EDITOR=nano crontab -e
  # Agregue esta línea
  0 * * * * /usr/local/lib/R/bin/Rscript '/home/pi/speedtest_job.R' >> '/dev/null' 2>&1 # Modifique la ruta del archivo según sea necesario. 
sudo service cron reload

Después de dejar pasar un tiempo para que se acumulen los registros, podemos obtener datos del servidor con una consulta SQL:

library(odbc)

con <- dbConnect(drv = odbc::odbc(),
                 driver = 'PostgreSQL ANSI',
                 server = Sys.getenv('MY_REMOTE'),
                 database = 'internet',
                 port = 5432,
                 uid = Sys.getenv('MY_UID'),
                 pwd = Sys.getenv('MY_PWD'),
                 encoding = 'utf8')

query <- "
    SELECT * 
    FROM public.speed_test 
    ORDER BY time
    "

raw_data <- dbGetQuery(
  conn = con,
  statement = query
)

dbDisconnect(con)

Visualizando los Datos

Ahora que tenemos algunos datos para trabajar, podemos comenzar a hacer algunos gráficos. Dado que este no es un artículo sobre ggplopt2, no voy a entrar en detalles sobre esta parte, solo voy a mostrar algunos gráficos interesantes que puede obtener sobre la velocidad de su conexión a Internet.

En la Figura 1 podemos ver cómo se distribuyen las velocidades de descarga observadas, dejando de lado el hecho de que la velocidad anunciada ya es lenta (para los estándares de los países desarrollados), esto revela un patrón que es muy común, la mayoría de las veces usted obtiene velocidades de descarga que son mucho más lentas de lo anunciado por el ISP e incluso tan lentas que no cumplen con los términos de su contrato, como en mi caso (en realidad usé esta imagen para presentar una queja).

library(tidyverse)
library(xkcd)
library(lubridate)
library(tibbletime)
library(scales)

# Estas son mis configuraciones de tema personales, puede ignorarlas si lo prefiere
theme_set(
  theme_gray() +
    theme_xkcd() +
    theme(legend.position = "right",
          plot.title.position = "plot",
          axis.title.x = element_text(margin = margin(t = 10)),
          axis.title.y = element_text(margin = margin(r = 10)),
          axis.text.x = element_text(angle = 20, hjust = 1, vjust = 1),
          plot.margin = margin(10, 10, 10, 10),
          text = element_text(family = "Cloud Calligraphy"))
)

color <- c("Moda" = "#008B00", "Parámetros" = "orange")

raw_data %>% 
  ggplot(aes(x = download)) +
  geom_histogram(binwidth = 0.1, fill = "#00B2EE") +
  geom_vline(aes(xintercept = 20, color = "Parámetros"),  linetype = "dashed") +
  geom_vline(aes(xintercept = 8, color = "Parámetros"),  linetype = "dashed") +
  stat_bin(geom = "vline",
           aes(xintercept = stat(ifelse(count == max(count), x, NA)),
               color = "Moda"),
           binwidth = 0.1) +
  annotate("text",
           x = c(8.4, 20.4),
           y = c(8, 8),
           label = c("Velocidad Mínima Garantizada", "Velocidad Ofrecida"),
           family = "Cloud Calligraphy",
           size = 5,
           angle = 90) +
  stat_bin(geom = "label",
           aes(label = stat(ifelse(count == max(count), round(x, 1), NA))),
           binwidth = 0.1,
           family = "xkcd",
           color = "#008B00",
           vjust = -0.2) +
  labs(title = 'HISTOGRAMA DE VELOCIDADES DE DESCARGA OBSERVADAS',
       subtitle ='ISP: MOVISTAR 20 Mbps', 
       x = 'Velocidad (Mbps)',
       y = 'Frecuencia',
       colour = '') +
  scale_x_continuous(breaks = seq(0, 22, by = 2),
                     limits = c(0, 23)) +
  scale_colour_manual(values = color) +
  coord_cartesian(clip = 'off') +
  NULL
Histograma de Velocidades de Descarga Observadas

Figure 1: Histograma de Velocidades de Descarga Observadas

Con las Figuras 2 y 3, podemos mostrar con qué frecuencia las velocidades de descarga caen por debajo de la velocidad mínima garantizada.


colors <- c("Velocidad Promedio" = "blue",
            "Parámetros" = "orange",
            "Falla" = "red",
            "Velocidad de Descarga" = "#00B2EE")

plot_data <- raw_data %>%
  as_tbl_time(index = time) %>% 
  collapse_by('1 hour', side = 'start', clean = TRUE)

mean_speed <- plot_data %>% 
  pull(download) %>% 
  mean() %>%
  round(1)

plot_data %>% 
  ggplot(aes(x = time, y = download)) +
  geom_line(aes(color = "Velocidad de Descarga")) +
  geom_point(data = plot_data %>% filter(download < 8),
             aes(color = "Falla")) +
  geom_point(data = plot_data %>% filter(download < 8),
             shape = 1,
             color = "red",
             size = 5) +
  geom_hline(aes(yintercept = 20, color = "Parámetros"),  linetype = "dashed") +
  geom_hline(aes(yintercept = 8, color = "Parámetros"),  linetype = "dashed") +
  geom_hline(aes(yintercept = mean_speed, color = "Velocidad Promedio")) +
  annotate("text",
           x = as.POSIXct(c("2019-06-24 11:00:00 UTC", "2019-06-24 11:00:00 UTC")),
           y = c(7, 19),
           label = c("Velocidad Mínima Garantizada", "Velocidad Ofrecida"),
           family = "Cloud Calligraphy",
           size=5) +
  geom_label(x = as.POSIXct("2019-08-01 11:00:00 UTC"),
             y = mean_speed,
             label = mean_speed,
             family = "xkcd",
             show.legend = FALSE,
             inherit.aes = FALSE,
             color = "blue")  +
  labs(title = 'VELOCIDADES DE DESCARGA OBSERVADAS',
       subtitle ='ISP: MOVISTAR 20 Mbps', 
       x = 'Fecha',
       y = 'Velocidad (Mbps)',
       color = 'Leyenda:') +
  scale_x_datetime(date_breaks = "5 days",
                   labels = label_date_short(), 
                   expand = expansion(c(0, 0.04))) +
  scale_y_continuous(breaks = seq(0, 22, by = 2), limits = c(0, 23)) +
  scale_colour_manual(values = colors) +
  coord_cartesian(clip = 'off') +
  NULL
Velocidades de Descarga Observadas

Figure 2: Velocidades de Descarga Observadas

faults <- plot_data %>%
  mutate(event_type = ifelse(download <= 8, 'fault', 'normal')) %>% 
  filter(event_type == 'fault') %>% 
  mutate(tbf = as.numeric(as.period(interval(lag(time), time),
                                    unit = 'seconds')) / (3600)) %>% 
  tail(-1)

faults %>% 
  ggplot(aes(x = '', y = tbf)) +
  geom_boxplot(fill = '#FF303094') +
  coord_flip() +
  geom_label(y = median(faults$tbf),
             label = paste(round(median(faults$tbf),1), 'h'),
             family = "xkcd",
             show.legend = FALSE,
             color = "blue") +
  labs(title = 'DIAGRAMA DE CAJA DE TIEMPO ENTRE FALLAS',
       subtitle ='ISP: MOVISTAR 20 Mbps',
       x = '',
       y = 'Horas') +
  NULL
Diagrama de Caja de Tiempo Entre Fallas

Figure 3: Diagrama de Caja de Tiempo Entre Fallas

Y con la Figura 4 podemos saber cuándo ocurren las horas pico, para saber a qué hora del día es más probable que experimentemos velocidades de Internet lentas.

hour_data <- plot_data %>%
    as_data_frame() %>% 
    mutate(time = format(time, "%H:%M"))

hour_data %>% 
  ggplot(aes(x = time, y = download)) +
  geom_boxplot(fill = "#00B2EE") +
  geom_point(data = hour_data %>% filter(download < 8),
             shape = 1,
             color = "red",
             size = 5) +
  geom_hline(aes(yintercept = 20), color = "orange",  linetype = "dashed") +
  geom_hline(aes(yintercept = 8), color = "orange",  linetype = "dashed") +
  annotate("text",
           x = c("04:00", "04:00"),
           y = c(7, 19),
           label = c("Velocidad Mínima Garantizada", "Velocidad Ofrecida"),
           family = "Cloud Calligraphy",
           size = 5) +
  labs(title = 'DIAGRAMA DE CAJA DE VELOCIDADES DES DESCARGA OBSERVADAS\nPOR HORA DEL DÍA',
       subtitle ='ISP: MOVISTAR 20 Mbps',
       x = 'Hora',
       y = 'Velocidad (Mbps)') +
  scale_y_continuous(breaks = seq(0, 22, by = 2),
                     limits = c(0, 22)) +
  coord_cartesian(clip = 'off') +
  NULL
Diagrama de Caja de Velocidades de Descarga Observadas por Hora del Día

Figure 4: Diagrama de Caja de Velocidades de Descarga Observadas por Hora del Día

Una vez que tiene datos, las posibilidades de gráficos solo están limitadas por su imaginación, por lo que me detendré aquí, espero que haya disfrutado leyendo este artículo y ahora esté motivado para comenzar a monitorear su propia velocidad de Internet. ¡Que se divierta!, ¡lo veo pronto!.

Edit this page
comments powered by Disqus