Sección 4: Mapa

Existen numerosas formas de realizar un mapa con plotly, cada una con sus propios pros y contras. En términos generales, los enfoques se dividen en dos categorías: integrados o personalizados. Los mapas integrados utilizan el soporte integrado de plotly.js para representar una capa de mapa base. Actualmente, hay dos formas compatibles de hacer mapas integrados: ya sea a través de Mapbox o a través de un mapa base integrado con d3.js powered . El enfoque integrado es conveniente si se necesita un mapa rápido y no necesariamente representaciones sofisticadas de objetos geoespaciales. Por otro lado, el enfoque de mapeo personalizado ofrece un control completo, ya que proporciona toda la información necesaria para representar los objetos geoespaciales.

Mapas personalizados

Características simples

El paquete sf R es un enfoque moderno para trabajar con estructuras de datos geoespaciales basadas en principios de datos ordenados. La función de este es almacenar geometrías geoespaciales en una columna de lista de un marco de datos. Esto permite que cada fila represente la unidad real de observación/interés, ya sea un polígono, un polígono, un punto, una línea o incluso una colección de estas características, y como resultado, funciona a la perfección dentro de flujos de trabajo ordenados más grandes. El paquete en sí mismo no proporciona realmente datos geoespaciales, proporciona el marco y las utilidades para almacenar y calcular estructuras de datos geoespaciales de una manera obstinada.

Existen numerosos paquetes para acceder a datos geoespaciales como estructuras de datos de entidades simples. Un par de ejemplos notables incluyen rnaturalearth y USAboundaries. El paquete rnaturalearth es mejor para obtener cualquier dato de mapa en el mundo, esto a través de una API proporcionada por y es el que se usó para trabajar en los siguientes ejemplos.

#install.packages("rnaturalearth")
library(rnaturalearth)
## Warning: package 'rnaturalearth' was built under R version 4.2.3
world <- ne_countries(returnclass = "sf")
class(world)
## [1] "sf"         "data.frame"
#> [1] "sf"    "data.frame"
#install.packages("plotly")
library("plotly")
## Warning: package 'plotly' was built under R version 4.2.3
## Loading required package: ggplot2
## 
## Attaching package: '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
plot_ly(world, color = I("gray90"), stroke = I("black"), span = I(1))
## No trace type specified:
##   Based on info supplied, a 'scatter' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#scatter

¿Cómo saber cómo representar los países? Las entidades geoespaciales están codificadas en una columna de lista especial (geometría). Además, los metadatos sobre la estructura geoespacial se conservan como atributos especiales de los datos.

En la figura anterior se incrementa el método de impresión de sf a marcos de datos para poder demostrar que toda la información necesaria para representar los países (es decir, polígonos) se encuentra contenido dentro del marco de datos. También hay que tener en cuenta que sf proporciona métodos dplyr especiales para esta clase especial de marco de datos para que pueda tratar las manipulaciones de datos como si fuera una estructura de datos “ordenada”.

library(sf)
## Warning: package 'sf' was built under R version 4.2.3
## Linking to GEOS 3.9.3, GDAL 3.5.2, PROJ 8.2.1; sf_use_s2() is TRUE
world %>%
  select(name) %>%
  print(n = 4)
## Simple feature collection with 177 features and 1 field
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -180 ymin: -90 xmax: 180 ymax: 83.64513
## Geodetic CRS:  +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
## First 4 features:
##        name                       geometry
## 0      Fiji MULTIPOLYGON (((180 -16.067...
## 1  Tanzania MULTIPOLYGON (((33.90371 -0...
## 2 W. Sahara MULTIPOLYGON (((-8.66559 27...
## 3    Canada MULTIPOLYGON (((-122.84 49,...

Hay varias formas de renderizar objetos de este tipo. Estas funciones representan varios polígonos utilizando un solo seguimiento de forma predeterminada, lo cual es rápido, pero es posible que desee aprovechar la flexibilidad adicional de varios seguimientos, como se hace en el siguiente gráfico.

#install.packages("devtools")
#devtools::install_github("ropensci/rnaturalearthhires")
canada <- ne_states(country = "Canada", returnclass = "sf")
plot_ly(canada, split = ~name, color = ~provnum_ne)
## No trace type specified:
##   Based on info supplied, a 'scatter' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#scatter

Otra característica importante para los mapas que pueden requerir varios polígonos en múltiples trazas es la capacidad de mostrar un desplazamiento diferente en el relleno para cada polígono. Al proporcionar que es único dentro de cada polígono y especificar , el comportamiento de la información sobre herramientas está vinculado al relleno de la traza (en lugar de mostrarse en cada punto a lo largo del polígono).

plot_ly(
  canada, 
  split = ~name, 
  color = I("gray90"), 
  text = ~paste(name, "is \n province number", provnum_ne),
  hoveron = "fills",
  hoverinfo = "text",
  showlegend = FALSE
)
## No trace type specified:
##   Based on info supplied, a 'scatter' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#scatter

Aunque los enfoques de mapeo integrado pueden representar objetos sf, los enfoques de mapeo personalizados son más flexibles porque permiten cualquier proyección de mapeo bien definida. Trabajar y comprender las proyecciones de mapas puede ser intimidante para un creador de mapas causal.

# filter the world sf object down to canada
canada <- filter(world, name == "Canada")
# coerce cities lat/long data to an official sf object
cities <- st_as_sf(
  maps::canada.cities, 
  coords = c("long", "lat"),
  crs = 4326
)

# A PROJ4 projection designed for Canada
# http://spatialreference.org/ref/sr-org/7/
# http://spatialreference.org/ref/sr-org/7/proj4/
moll_proj <- "+proj=moll +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 
+units=m +no_defs"

# perform the projections
canada <- st_transform(canada, moll_proj)
cities <- st_transform(cities, moll_proj)

# plot with geom_sf()
p <- ggplot() + 
  geom_sf(data = canada) +
  geom_sf(data = cities, aes(size = pop), color = "red", alpha = 0.3)
ggplotly(p)

Sección 5: Barras e histogramas

Las funciones add_bars() y add_histogram() generan la barra y el histograma gráficamente. La principal diferencia entre ellos es que las trazas de barras requieren alturas de barra (ambas y ), mientras que las trazas de histograma requieren una sola variable. Ambas funciones se pueden usar para visualizar la distribución de una variable numérica o discreta. Entonces, esencialmente, la única diferencia entre ellos es dónde ocurre el binning.

A continuación se compara el algoritmo de binning predeterminado en plotly.js con algunos algoritmos diferentes disponibles en R a través de la función. Aunque plotly.js tiene la capacidad de personalizar los contenedores de histograma a través de xbins / ybins, R tiene diversas instalaciones para estimar el número óptimo de contenedores en un histograma que podemos aprovechar fácilmente.hist()16 La función por sí sola nos permite hacer referencia a 3 algoritmos famosos por nombre (Sturges 1926; Freedman y Diaconis 1981; Scott 1979), pero también hay paquetes que amplían esta interfaz para incorporar más metodología. La función siguiente ajusta la función para obtener los resultados de agrupación y asignar esas bandejas a una versión gráfica del histograma.

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(plotly)
p1 <- plot_ly(diamonds, x = ~price) %>%
  add_histogram(name = "plotly.js")

price_hist <- function(method = "FD") {
  h <- hist(diamonds$price, breaks = method, plot = FALSE)
  plot_ly(x = h$mids, y = h$counts) %>% add_bars(name = method)
}

subplot(
  p1, price_hist(), price_hist("Sturges"),  price_hist("Scott"),
  nrows = 4, shareX = TRUE
)

Allí se muestran dos formas de crear un gráfico de barras básico. Aunque los resultados visuales son los mismos, vale la pena señalar la diferencia en la implementación. La función envía todos los valores observados al navegador y permite realizar el binning de forma gráfica.js. Se necesita más esfuerzo humano para realizar el binning en R, pero hacerlo tiene la ventaja de enviar menos datos y requerir menos trabajo de cálculo del navegador web. En este caso, solo tenemos alrededor de 50,000 registros, por lo que no hay mucha diferencia en los tiempos de carga de la página o el tamaño de la página. Sin embargo, con 1 millón de registros, el tiempo de carga de la página se duplica con creces y el tamaño de la página casi se duplica.

library(dplyr)
p1 <- plot_ly(diamonds, x = ~cut) %>%
  add_histogram()

p2 <- diamonds %>%
  count(cut) %>%
  plot_ly(x = ~cut, y = ~n) %>% 
  add_bars()

subplot(p1, p2) %>% hide_legend()

Distribuciones numéricas múltiples

A menudo es útil ver cómo cambia la distribución numérica con respecto a una variable discreta. Cuando se usan barras para visualizar múltiples distribuciones numéricas, recomiendo trazar cada distribución en su propio eje usando una pequeña pantalla de múltiplos, en lugar de tratar de superponerlos en un solo eje.

Lo siguiente demuestra cómo se debe hacer mostrar en cada panel, luego se emplea una estrategia de dividir-aplicar-recombinar para generar la visualización del enrejado.

one_plot <- function(d) {
  plot_ly(d, x = ~price) %>%
    add_annotations(
      ~unique(clarity), x = 0.5, y = 1, 
      xref = "paper", yref = "paper", showarrow = FALSE
    )
}

diamonds %>%
  split(.$clarity) %>%
  lapply(one_plot) %>% 
  subplot(nrows = 2, shareX = TRUE, titleX = FALSE) %>%
  hide_legend()
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
##   Based on info supplied, a 'histogram' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#histogram

Multiples distribuciones discretas

Visualizar múltiples distribuciones discretas es difícil. La complejidad sutil se debe al hecho de que tanto los recuentos como las proporciones son importantes para comprender las distribuciones discretas multivariadas. El gráfico siguiente presenta los recuentos de diamantes, divididos por su corte y claridad, utilizando un gráfico de barras agrupado.

plot_ly(diamonds, x = ~cut, color = ~clarity) %>%
  add_histogram()

Lo anterior es de gran utilidad para comparar el número de diamantes por claridad, dado un tipo de corte. Por ejemplo, dentro de los diamantes “Ideal”, un corte de “VS1” es el más popular, “VS2” es el segundo más popular y “I1” el menos popular. La distribución de la claridad dentro de los diamantes “ideales” parece ser bastante similar a otros diamantes, pero es difícil hacer esta comparación utilizando recuentos brutos. La gráfica que sigue facilita esta comparación al mostrar la frecuencia relativa de los diamantes por claridad, dado un corte.

# number of diamonds by cut and clarity (n)
cc <- count(diamonds, cut, clarity)
# number of diamonds by cut (nn)
cc2 <- left_join(cc, count(cc, cut, wt = n, name = 'nn'))
## Joining with `by = join_by(cut)`
cc2 %>%
  mutate(prop = n / nn) %>%
  plot_ly(x = ~cut, y = ~prop, color = ~clarity) %>%
  add_bars() %>%
  layout(barmode = "stack")

Esta también es conocida como parcela de columna vertebral, es un caso especial de una parcela de mosaico. En un diagrama de mosaico, se puede escalar tanto anchos de barra como altos de acuerdo con distribuciones discretas. Para gráficos de mosaico, la recomendación es utilizar el paquete ggmosaic, que implementa un geom ggplot2 personalizado diseñado para gráficos de mosaico, que podemos convertir a plotly a través de . A continuación se muestra un diagrama de mosaico de corte por claridad. Observe cómo los anchos de barra se escalan proporcionalmente a la frecuencia de corte.

#install.packages("ggmosaic")
library(ggmosaic)
## Warning: package 'ggmosaic' was built under R version 4.2.3
p <- ggplot(data = cc) +
  geom_mosaic(aes(weight = n, x = product(cut), fill = clarity))
ggplotly(p)
## Warning: `unite_()` was deprecated in tidyr 1.2.0.
## ℹ Please use `unite()` instead.
## ℹ The deprecated feature was likely used in the ggmosaic package.
##   Please report the issue at <]8;;https://github.com/haleyjeppson/ggmosaichttps://github.com/haleyjeppson/ggmosaic]8;;>.