It uses synthetic data (generated on the fly) to tell a story about a fictional city’s mobility, energy demand, and air quality across a year.
We simulate daily measurements for a fictional city, Nuevo Arcade, with:
set.seed(42)
dates <- seq.Date(from = as.Date("2025-01-01"), to = as.Date("2025-12-31"), by = "day")
n <- length(dates)
df <- tibble(
date = dates,
doy = yday(date),
dow = wday(date, label = TRUE, week_start = 1),
month = month(date, label = TRUE)
) %>%
mutate(
# Smooth yearly seasonality: winter peaks in heating demand; summer peaks in cooling
season = sin(2*pi*(doy/365)),
weekday = if_else(dow %in% c("Sat","Sun"), 0, 1),
# Mobility index: higher on weekdays, plus a festival boost in May
mobility = 55 +
10*weekday +
8*season +
12*if_else(month == "May", 1, 0) +
rnorm(n, sd = 4),
# Energy demand: U-shaped with both heating and cooling, plus a bit of mobility coupling
energy_mwh = 380 +
55*abs(season) +
0.8*mobility +
rnorm(n, sd = 18),
# PM2.5: worsens in winter (inversions), improves in summer, and rises slightly with mobility
pm25 = 22 +
10*(season < -0.2) + # winter-ish boost
(-6)*season + # summer improves
0.12*mobility +
rnorm(n, sd = 3)
) %>%
mutate(
mobility = pmax(mobility, 0),
pm25 = pmax(pm25, 1)
)
head(df)Use the legend to toggle series, hover to see exact values, and zoom into a time window.
p_year <- plot_ly(df, x = ~date) %>%
add_lines(y = ~mobility, name = "Mobility index", hovertemplate = "Date: %{x}<br>Mobility: %{y:.1f}<extra></extra>") %>%
add_lines(y = ~energy_mwh, name = "Energy (MWh)", yaxis = "y2",
hovertemplate = "Date: %{x}<br>Energy: %{y:.0f} MWh<extra></extra>") %>%
add_lines(y = ~pm25, name = "PM2.5 (µg/m³)", yaxis = "y3",
hovertemplate = "Date: %{x}<br>PM2.5: %{y:.1f} µg/m³<extra></extra>") %>%
layout(
title = "Nuevo Arcadia: Mobility, Energy & Air Quality (Synthetic, Daily)",
xaxis = list(title = "", rangeselector = list(
buttons = list(
list(count = 1, label = "1m", step = "month", stepmode = "backward"),
list(count = 3, label = "3m", step = "month", stepmode = "backward"),
list(count = 6, label = "6m", step = "month", stepmode = "backward"),
list(step = "all", label = "All")
)
), rangeslider = list(visible = TRUE)),
yaxis = list(title = "Mobility index"),
yaxis2 = list(title = "Energy (MWh)", overlaying = "y", side = "right", showgrid = FALSE),
yaxis3 = list(title = "PM2.5 (µg/m³)", overlaying = "y", side = "right", position = 0.95, showgrid = FALSE),
legend = list(orientation = "h", x = 0, y = -0.15)
)
p_yearHere we color points by season (month). A quick regression line hints at the relationship.
p_scatter <- plot_ly(
df,
x = ~mobility,
y = ~pm25,
color = ~month,
colors = "Set2",
type = "scatter",
mode = "markers",
marker = list(size = 7, opacity = 0.7),
hovertemplate = paste0(
"Date: %{customdata}<br>",
"Mobility: %{x:.1f}<br>",
"PM2.5: %{y:.1f} µg/m³<extra></extra>"
),
customdata = ~as.character(date)
) %>%
add_lines(
x = ~mobility,
y = ~fitted(lm(pm25 ~ mobility, data = df)),
name = "Linear trend",
inherit = FALSE
) %>%
layout(
title = "When the city moves, does the air suffer?",
xaxis = list(title = "Mobility index"),
yaxis = list(title = "PM2.5 (µg/m³)"),
legend = list(orientation = "h", x = 0, y = -0.2)
)
p_scatter## R version 4.4.3 (2025-02-28 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
##
##
## locale:
## [1] LC_COLLATE=Spanish_Mexico.utf8 LC_CTYPE=Spanish_Mexico.utf8
## [3] LC_MONETARY=Spanish_Mexico.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Mexico.utf8
##
## time zone: America/Mexico_City
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] plotly_4.10.4 ggplot2_3.5.2 lubridate_1.9.4 tidyr_1.3.1
## [5] dplyr_1.1.4
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 jsonlite_2.0.0 compiler_4.4.3 tidyselect_1.2.1
## [5] dichromat_2.0-0.1 jquerylib_0.1.4 scales_1.4.0 yaml_2.3.10
## [9] fastmap_1.2.0 R6_2.6.1 generics_0.1.3 knitr_1.50
## [13] htmlwidgets_1.6.4 tibble_3.2.1 bslib_0.9.0 pillar_1.10.2
## [17] RColorBrewer_1.1-3 rlang_1.1.6 cachem_1.1.0 xfun_0.52
## [21] sass_0.4.10 lazyeval_0.2.2 viridisLite_0.4.2 timechange_0.3.0
## [25] cli_3.6.5 withr_3.0.2 magrittr_2.0.3 crosstalk_1.2.1
## [29] digest_0.6.37 grid_4.4.3 rstudioapi_0.17.1 lifecycle_1.0.4
## [33] vctrs_0.6.5 data.table_1.17.0 evaluate_1.0.3 glue_1.8.0
## [37] farver_2.1.2 httr_1.4.7 rmarkdown_2.29 purrr_1.0.4
## [41] tools_4.4.3 pkgconfig_2.0.3 htmltools_0.5.8.1