El paquete Plotly en R surge como la adaptación de plotly.js, una librerĆa desarrollada en JavaScript en 2012 con el objetivo de generar grĆ”ficos interactivos basados en tecnologĆas web. En sus inicios fue una plataforma cerrada, pero a partir de 2015 se liberó su código bajo licencia MIT, lo que permitió su rĆ”pida expansión y la creación de interfaces en diferentes lenguajes como Python, Julia y R.
En el ecosistema de R, Plotly se integra mediante el paquete plotly, que utiliza la infraestructura de htmlwidgets. Esto le permite generar grƔficos dinƔmicos que pueden visualizarse tanto en RStudio como en entornos reproducibles como R Markdown y aplicaciones interactivas en Shiny.
La finalidad principal de este paquete es clara: convertir la visualización en una herramienta de exploración y no en un producto terminado. Mientras que librerĆas como ggplot2 o lattice generan grĆ”ficos estĆ”ticos muy estĆ©ticos, Plotly aƱade una capa extra al permitir acercar, desplazar, filtrar y obtener detalles al pasar el cursor sobre los datos.
De esta forma, Plotly representa una evolución natural en el flujo de trabajo de quienes ya utilizan grĆ”ficos en R, pues incluso permite transformar un grĆ”fico de ggplot2 en uno interactivo con una sola lĆnea de código mediante la función ggplotly().
## Cargando paquete requerido: ggplot2
##
## 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
La base de datos gapminder contiene información sobre el desarrollo socioeconómico y demogrĆ”fico de distintos paĆses del mundo entre los aƱos 1952 y 2007, en intervalos de cinco aƱos.
Fue creada por la organización Gapminder Foundation, fundada por el estadĆstico sueco Hans Rosling, con el propósito de mostrar cómo el progreso global en salud y economĆa ha cambiado a lo largo del tiempo.
La función plot_ly()
es la función principal del paquete
Plotly en R.
Permite construir grƔficos interactivos especificando los datos, las
variables que se asignan a los ejes y el tipo de grƔfico a generar.
CategorĆa |
Valor de type
|
Descripción |
---|---|---|
GrƔficos bƔsicos (2D) | scatter |
GrĆ”fico de dispersión o lĆneas. Admite modos como āmarkersā
o ālinesā .
|
bar | GrƔfico de barras (vertical u horizontal). | |
box | Diagrama de cajas que muestra mediana, cuartiles y outliers. | |
histogram | Histograma para distribución de frecuencias. | |
violin | GrĆ”fico de violĆn (densidad y distribución). | |
pie | GrƔfico circular o de pastel. | |
funnel | GrƔfico de embudo (etapas de un proceso). | |
GrĆ”ficos geogrĆ”ficos | scattergeo | Puntos o lĆneas en mapas geogrĆ”ficos (latitud/longitud). |
choropleth | Mapa coloreado por regiones o paĆses. | |
scattermapbox | Puntos o lĆneas sobre mapas de Mapbox. | |
densitymapbox | Mapa de densidad geogrƔfica. | |
scattergl | Versión optimizada de dispersión para grandes volúmenes de datos (WebGL). | |
GrÔficos 3D | scatter3d | Dispersión tridimensional (x, y, z). |
surface | Superficie tridimensional generada por una matriz de valores. | |
mesh3d | Malla tridimensional para formas o volĆŗmenes. | |
line3d | LĆneas tridimensionales. | |
Calor y densidad | heatmap | Mapa de calor bidimensional (x, y, z). |
contour | GrƔfico de curvas de nivel o isovalores. | |
density | Visualización de densidad de puntos. | |
JerƔrquicos y radiales | sunburst | Diagrama jerƔrquico radial tipo sol. |
treemap | JerarquĆas en rectĆ”ngulos anidados. | |
icicle | Diagrama jerƔrquico descendente. | |
scatterpolar | GrƔfico polar (coordenadas angulares y radiales). | |
Financieros | candlestick | GrĆ”fico de velas (precios de apertura, cierre, mĆ”ximo, mĆnimo). |
ohlc | GrƔfico financiero Open-High-Low-Close. | |
waterfall | GrÔfico de cascada (acumulación de ganancias/pérdidas). | |
Especiales | parcoords | Coordenadas paralelas para variables multivariadas. |
sankey | Diagrama de flujo entre categorĆas. | |
table | Tabla interactiva. | |
indicator | Indicadores numƩricos o KPIs (paneles de control). |
El paquete Plotly tambiƩn permite representar funciones
tridimensionales de forma interactiva mediante el parƔmetro
type = "surface"
. Este tipo de visualización es
especialmente útil para explorar superficies matemÔticas, modelos
predictivos o relaciones no lineales entre variables.
x <- seq(-5, 5, length.out = 50)
y <- seq(-5, 5, length.out = 50)
z <- outer(x, y, function(a, b) a^2 + b^2) # Paraboloide
# GrƔfico 3D de superficie
plot_ly(x = ~x, y = ~y, z = ~z, type = "surface",
colorscale = "Viridis") %>%
layout(title = "Superficie 3D del paraboloide (z = x² + y²)",
scene = list(
xaxis = list(title = "Eje X"),
yaxis = list(title = "Eje Y"),
zaxis = list(title = "Eje Z")
))
Otra de las capacidades destacadas del paquete Plotly es la
generación de mapas interactivos, los cuales permiten representar
información geogrÔfica mediante visualizaciones dinÔmicas. En este
ejemplo se utiliza la función plot_ly()
con el argumento
type = āchoroplethā para construir un mapa mundial que muestra la
esperanza de vida por paĆs en el aƱo 2007, utilizando los datos del
conjunto gapminder.
# Filtramos un aƱo (2007)
gap_map <- subset(gapminder, year == 2007)
plot_ly(
data = gap_map,
type = "choropleth",
locations = ~country, # Nombre del paĆs
locationmode = "country names", # Coincidir con nombres de paĆses
z = ~lifeExp, # Variable a representar
text = ~paste(country, "<br>Esperanza de vida:", round(lifeExp, 1)),
colorscale = "Viridis",
colorbar = list(title = "AƱos"),
marker = list(line = list(color = "gray", width = 0.3))
) %>%
layout(
title = "Esperanza de vida por paĆs (2007)",
geo = list(
showframe = FALSE,
showcoastlines = TRUE,
projection = list(type = "natural earth")
)
)
El paquete Plotly tambiƩn permite representar matrices de datos
mediante grƔficos de calor, utilizando el argumento
type = "heatmap"
. En este caso, se construyó un mapa de
calor de correlaciones a partir de tres variables numƩricas del conjunto
gapminder
: la esperanza de vida (lifeExp
), la
población (pop
) y el PIB per cƔpita
(gdpPercap
).
# Seleccionamos variables numƩricas
gap_corr <- gapminder %>%
select(lifeExp, pop, gdpPercap)
# Calculamos la matriz de correlación
mat_corr <- cor(gap_corr, method = "pearson")
# Visualización con plotly
plot_ly(
x = colnames(mat_corr),
y = rownames(mat_corr),
z = mat_corr,
type = "heatmap",
colorscale = "Viridis", # Escala de color
reversescale = TRUE
) %>%
layout(
title = "Mapa de calor de correlaciones (Gapminder)",
xaxis = list(title = ""),
yaxis = list(title = "")
)
Una de las innovaciones mÔs relevantes en Plotly es la incorporación de WebGL (Web Graphics Library), un motor de renderizado que aprovecha la aceleración grÔfica del navegador para trabajar con grandes volúmenes de datos de manera fluida.
En grÔficos tradicionales, representar mÔs de unos pocos miles de puntos puede ralentizar la visualización, dificultando el zoom, el desplazamiento o incluso la carga del grÔfico. Con WebGL, es posible manejar decenas de miles o incluso millones de observaciones sin pérdida de rendimiento, lo que resulta clave en contextos de Big Data o anÔlisis exploratorio de grandes bases de datos.
En R, esta capacidad se activa mediante el uso de trazas especĆficas como scattergl para diagramas de dispersión o heatmapgl para mapas de calor. El beneficio es inmediato: la experiencia interactiva se mantiene Ć”gil aunque el nĆŗmero de observaciones sea muy alto.
El siguiente ejemplo muestra un diagrama de dispersión con 20,000 puntos, que gracias a WebGL conserva fluidez al aplicar acciones de zoom y desplazamiento:
library(plotly)
set.seed(42)
n <- 20000
df <- data.frame(
x = rnorm(n),
y = rnorm(n),
grupo = sample(LETTERS[1:5], n, replace = TRUE)
)
library(plotly)
set.seed(123)
n <- 15000
df_comp <- data.frame(
x = rnorm(n),
y = rnorm(n),
grupo = sample(letters[1:3], n, replace = TRUE)
)
Una ventaja de Plotly es su capacidad para convertir grƔficos de
ggplot2 en versiones interactivas mediante la función
ggplotly()
.
library(ggplot2)
p_ggplot <- ggplot(gapminder, aes(x = continent, y = lifeExp, fill = continent)) +
geom_boxplot() +
labs(title = "Esperanza de vida por continente") +
theme_minimal()
p_ggplot
Otra de las innovaciones recientes en Plotly es su soporte para
librerĆas externas como ggridges, que permiten representar
distribuciones superpuestas de manera compacta y visualmente
atractiva.
En este tipo de grĆ”ficos, cada categorĆa se representa con una densidad,
lo que facilita comparar cómo se distribuyen los valores sin necesidad
de recurrir a mĆŗltiples paneles.
library(ggridges)
# Seleccionamos un subconjunto para evitar sobrecarga visual
data_ridges <- subset(gapminder, year %in% c(1982, 2007))
# GrƔfico base con ggridges
p_ridges <- ggplot(data_ridges, aes(x = lifeExp, y = factor(year), fill = continent)) +
geom_density_ridges(alpha = 0.8, scale = 1.5, color = "white") +
labs(
title = "Distribución de la esperanza de vida por año y continente",
x = "Esperanza de vida",
y = "AƱo"
) +
theme_minimal()
p_ridges
## Picking joint bandwidth of 2.18
## Picking joint bandwidth of 2.18
Plotly tambiĆ©n soporta la librerĆa ggalluvial
, que
permite crear diagramas de flujo o diagramas aluviales. Estos grƔficos
son ideales para representar cómo cambian los elementos de un grupo a
otro a lo largo del tiempo o de distintas etapas. Por ejemplo, se pueden
visualizar transiciones de estudiantes entre carreras universitarias o
cambios de clientes entre categorĆas de productos.
library(ggalluvial)
# Datos que simulan estudiantes que cambian de carrera
datos_aluvial <- data.frame(
AƱo1 = c(rep("IngenierĆa", 30), rep("Medicina", 25), rep("Derecho", 20)),
AƱo2 = c(rep("IngenierĆa", 25), rep("Medicina", 5),
rep("Medicina", 20), rep("IngenierĆa", 5),
rep("Derecho", 18), rep("Medicina", 2)),
Frecuencia = 1
) %>%
group_by(AƱo1, AƱo2) %>%
summarise(Freq = sum(Frecuencia), .groups = "drop")
head(datos_aluvial)
# Diagrama aluvial
p_aluvial <- ggplot(datos_aluvial,
aes(y = Freq, axis1 = AƱo1, axis2 = AƱo2)) +
geom_alluvium(aes(fill = AƱo1), width = 1/12) +
geom_stratum(width = 1/12, fill = "gray", color = "white") +
geom_label(stat = "stratum", aes(label = after_stat(stratum))) +
scale_x_discrete(limits = c("AƱo 1", "AƱo 2"), expand = c(.05, .05)) +
labs(title = "Flujo de estudiantes entre carreras") +
theme_minimal()
p_aluvial
Plotly se ha consolidado como un aliado para la construcción de aplicaciones interactivas. En R, su integración con Shiny permite añadir controles dinÔmicos (filtros, menús, sliders) que modifican los grÔficos en tiempo real, sin necesidad de conocimientos avanzados de desarrollo web.
library(shiny)
library(dplyr)
# Datos
datos <- gapminder
datos$continent <- as.character(datos$continent)
ui <- dashboardPage(
dashboardHeader(title = "Dashboard Gapminder"),
dashboardSidebar(
sidebarMenu(
menuItem("Visualizaciones", tabName = "viz", icon = icon("chart-bar")),
sliderInput("year_sel", "Selecciona el aƱo:",
min = min(datos$year), max = max(datos$year),
value = 2007, step = 5, sep = ""),
selectInput("cont_sel", "Selecciona continente:",
choices = c("Todos", unique(datos$continent)),
selected = "Todos")
)
),
dashboardBody(
tabItems(
tabItem(tabName = "viz",
fluidRow(
box(width = 12, plotlyOutput("bubble"))
),
fluidRow(
box(width = 6, plotlyOutput("top10")),
box(width = 6, plotlyOutput("boxplot"))
),
fluidRow(
box(width = 12, plotlyOutput("line"))
)
)
)
)
)
server <- function(input, output, session) {
# Datos filtrados por aƱo y continente
datos_filtrados <- reactive({
if (input$cont_sel == "Todos") {
filter(datos, year == input$year_sel)
} else {
filter(datos, year == input$year_sel, continent == input$cont_sel)
}
})
# GrƔfico de burbujas
output$bubble <- renderPlotly({
plot_ly(datos_filtrados(), x = ~gdpPercap, y = ~lifeExp,
size = ~pop, color = ~continent, text = ~country,
type = "scatter", mode = "markers",
sizes = c(10, 70),
marker = list(opacity = 0.7, line = list(width = 1))) %>%
layout(title = paste("Esperanza de vida vs PIB per cƔpita -", input$year_sel),
xaxis = list(title = "PIB per cƔpita"),
yaxis = list(title = "Esperanza de vida"),
legend = list(title = list(text = "Continente")))
})
# Top 10 paĆses mĆ”s poblados
output$top10 <- renderPlotly({
datos_top <- datos_filtrados() %>%
arrange(desc(pop)) %>%
slice_head(n = 10)
plot_ly(datos_top, x = ~reorder(country, pop), y = ~pop,
type = "bar", marker = list(color = "coral")) %>%
layout(title = paste("Top 10 paĆses mĆ”s poblados -", input$year_sel,
ifelse(input$cont_sel == "Todos", "Global", input$cont_sel)),
xaxis = list(title = "PaĆs", tickangle = -45),
yaxis = list(title = "Población"))
})
# Boxplot
output$boxplot <- renderPlotly({
plot_ly(datos_filtrados(), x = ~continent, y = ~lifeExp,
color = ~continent, type = "box") %>%
layout(title = paste("Esperanza de vida por continente -", input$year_sel),
xaxis = list(title = "Continente"),
yaxis = list(title = "Esperanza de vida"))
})
# LĆnea temporal
output$line <- renderPlotly({
datos_linea <- if (input$cont_sel == "Todos") {
datos %>% group_by(year) %>% summarise(promedio_vida = mean(lifeExp))
} else {
datos %>% filter(continent == input$cont_sel) %>%
group_by(year) %>% summarise(promedio_vida = mean(lifeExp))
}
plot_ly(datos_linea, x = ~year, y = ~promedio_vida,
type = "scatter", mode = "lines+markers",
line = list(color = "steelblue", width = 3)) %>%
layout(title = paste("Tendencia de la esperanza de vida -",
ifelse(input$cont_sel == "Todos", "Global", input$cont_sel)),
xaxis = list(title = "AƱo"),
yaxis = list(title = "Esperanza de vida promedio"))
})
}
shinyApp(ui, server)
Link de la publicación en Shiny: https://g-geral.shinyapps.io/Plotly_con_Shiny/