Mapas

Existen diferentes formas para crear un mapa con ploty. De esta forma, hay dos categorías: integrados o personalizados. El mapa integrado es conveniente si necesita un mapa rápido y no representaciones sotisficadas de objetos geoespaciales. Por otra parte, el mapa personalizado proporciona toda la informaicón necesaria para representan objetos geoespaciales. En este caso, esta sección subre la creación de mapas con ayuda de la herramienta R; sin embargo, es válido recalcar que existen otros paquetes de geocomputación.

Ploty es una biblioteca de visualización de propósito general y no pretende ser una herramienta de visualización geoespacial.

Mapas integrados

Descripción general

Si tienes datos de latitud/longitud bastantes simples y la finalidad es hacer un mapa rápido, se puede hacer con herramientas de mapeo integradas de plotly, ya sea plo_mapbox() y plo_geo (). Con esto obtienes un mapa dinámico que representa una base de datos.

library(ggplot2)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.3     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ lubridate 1.9.2     ✔ tibble    3.2.1
## ✔ purrr     1.0.2     ✔ tidyr     1.3.0
## ── 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)
## 
## 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
Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')

data("mapas", package = "ggplot2")
## Warning in data("mapas", package = "ggplot2"): data set 'mapas' not found
plot_mapbox(maps::canada.cities) %>%
add_markers(
  x = ~long,
  y = ~lat,
  size = ~pop,
  color = ~country.etc,
  colors = "Accent",
  text = ~paste(name,pop),
  hoverinfo = "text"
)
## Warning: `line.width` does not currently support multiple values.
## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.

## Warning: `line.width` does not currently support multiple values.
## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Accent is 8
## Returning the palette you asked for with that many colors

Figura 1.

El presente gráfico es un mapa de burbujas que representa la población de varias ciudades de Canadá con tecnología Mapbox.

El estilo de mapa en Mapbox se controla mediante el atributo de estilo layour map-box. Este paquete contiene 7 estilos diferentes, pero proporciona también un estilo personalizado.

###Datos

library(ggplot2)
library(tidyverse)
library(plotly)
library(listviewer)

styles <- schema ()$layout$layoutAttributes$mapbox$style$values
styles
##  [1] "basic"             "streets"           "outdoors"         
##  [4] "light"             "dark"              "satellite"        
##  [7] "satellite-streets" "carto-darkmatter"  "carto-positron"   
## [10] "open-street-map"   "stamen-terrain"    "stamen-toner"     
## [13] "stamen-watercolor" "white-bg"
#> [1] "basic"               "streets"
#> [3] "outdoors"            "light"
#> [5] "dark"                "satellite"
#> [7] "satellite-streets"   "open-street-map"
#> [9] "white-bg"            "carto-positron"
#> [11] "carto-darmatter"    "stamen-terrain"
#> [13] "stamen-toner"       "stamen-watercolor" 

Este es un esquema oficial el cual presenta un paquete plotly que contiene 7 estilos diferentes para mapas base de Mapbox.

Cualquiera de estas caracteristicas pueden usarse para determinar un estilo de mapbox. Como en la siguiente imagen.

library(ggplot2)
library(tidyverse)
library(plotly)
library(listviewer)

Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')
layout(
  plot_mapbox(),
  mapbox = list(style = "satellite")
)
## No scattermapbox mode specifed:
##   Setting the mode to markers
##   Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode

figura 2.

Esta imagen representa un zoom de fotos satelitales de la Tierra usando alguno de los valores presentes en el esquema anterior.

Ahora, se mostrará un ejemplo de cómo crear un menú desplegable de trazado integrado para controlar el estilo del mapa base a través del atributo de diseño updatesenus. La idea de este menú es brindar una lista de opciones donde cada una, da como resultado un método que logra modificar el cuadro de mapa de diseño.

library(ggplot2)
library(tidyverse)
library(plotly)
library(listviewer)

Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')
style_buttons <- lapply(styles, function(s) {
  list(
    label = s,
    method = "relayout",
    args = list("mapbox.style",s)
    
  )
})
layout(
  plot_mapbox(),
  mapbox = list(style = "dark"),
  updatemenus = list (
    list(y = 0.8, buttons = style_buttons)
  )
)
## No scattermapbox mode specifed:
##   Setting the mode to markers
##   Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode

Figura 3.

Esta imagen representa un mapa donde tienes un menú desplegable que dependiendo de lo que quieras ver, presionas el botón y puedes controlar el estilo de la capa base del mapbox.

También hay otra solución para mapas integrados en plotly y es usando plot_geo(). En este caso, se tienen en cuenta diferentes proyecciones cartográficas, pero el diseño es más difícil y limitado. Mientras que plot_mapbox() está fijado a una proyección, plot_geo() tiene un puñado de proyecciones diferentes incluyendo la proyección ortográfica que da la ilusión 3D del globo.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)

Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')
#airport locations

data("airport", package = "ggplot2")
## Warning in data("airport", package = "ggplot2"): data set 'airport' not found
air <- read.csv("https://plotly-r.com/data-raw/airport_locations.csv")

# flights between airports
flights <- read.csv("https://plotly-r.com/data-raw/flight_paths.csv")
  

flights$id <- seq_len(nrow(flights))

#map projection
geo <- list(
  projection = list(
    type = 'orthographic',
    rotation = list(lon = -100, lat = 40, roll = 0)
  ),
  showland = TRUE,
  landcolor = toRGB("gray95"),
  countrycolor = toRGB("gray80")
)

plot_geo(color = I("red")) %>%
  add_markers(
    data = air, x = ~long, y = ~lat, text = ~airport,
    size = ~cnt, hoverinfo = "text", alpha = 0.5
  ) %>%
  add_segments(
    data = group_by(flights, id),
    x = ~start_lon, xend = ~end_lon,
    y = ~start_lat, yend = ~end_lat,
    alpha = 0.3, size = I(1), hoverinfo = "none"
  ) %>%
  layout(geo = geo, showlegend = FALSE)
## Warning: `line.width` does not currently support multiple values.

Figura 4.

En esta imagen se usa la proyección ortográfica integrada de plot_geo() para visualizar patrones de vuelo de Estados Unidos en un globo 3D.

Algo positivo para el plot_geo() es que tiene la facilidad de proyectar geometrías automáticamente en el sistema de coordenadas adecuados.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)

Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')

map1 <- plot_mapbox() %>%
  add_segments(x = -100, xend = -50, y = 50, yend = 75) %>%
  layout(
    mapbox = list(
      zoom = 0,
      center = list(lat = 65, lon = -75)
    )
  )

map2 <- plot_geo() %>%
  add_segments(x = -100, xend = -50, y = 50, yend = 75) %>%
  layout(geo = list(projection = list(type = "mercator")))

library(htmltools)
browsable(tagList(map1, map2))

Figura 5.

El segmento que presenta el primer mapa es recto debido a que se usa plot_mapbox(); sin embargo, cuando se usa plot_geo() se curva. Es posible hacer esto con plot_mapbox(9 o plot_ly() pero debe colocarse en una estructura de datos. En resumidas, se tiene una comparación de las soluciones de mapeo ingtegradas de plotly, de esta forma se puede ver que plot_geo() transforma los segmentos de línea para reflejar correctamente la proyección en un sistema de coordenadas no cartesiano.

##Choropleths

A parte de los rastros de dispersión, se puede decir que ambas soluciones de mapeo integradas tienen un tipo de rastro de choropleth optimizado. Describiendo a fondo, en el caso de mapbox es más poderoso porque especifica, a veces, la colección de características usando GeoJSON, pero puede ser más fácil si se ajusta a su caso de uso.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)

Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')

density <- state.x77[, "Population"] / state.x77[, "Area"]

g <- list(
  scope = 'usa',
  projection = list(type = 'albers usa'),
  lakecolor = toRGB('white')
)

plot_geo() %>%
  add_trace(
    z = ~density, text = state.name, span = I(0),
    locations = state.abb, locationmode = 'USA-states'
  ) %>%
  layout(geo = g)

Figura 6.

Esta imagen muestra la densidad de población de los Estados Unidos por medio del trazo de choropleth usando los datos estatales de Estados Unidos del paquete de conjuntos de datos. Para esto se proporciona un atributo y así los objetos de plotly_geo() intentarán crear una choropleth; sin embargo, se tiene que proporcionar ubicaciones y modo de ubicaciones. En este caso el modo ubicaciones está limitado a países y estados de Estados Unidos por lo que si necesita trazar una unidad geográfica diferente, debe utilizar choroplethmapbox o un método de mapeo personalizado.

Usar choroplethmapbox es más útil debido a que usted proporciona su propia definición GeoJSON de la choropleth a través de un atributo. Normalmente este atributo es una URL con archivo geojson.

Así se tiene la siguiente figura que muestra lo mismo que la anterior, pero usando choroplethmapbox.

#Mapas Personalizados ##Funciones simples

El paquete sf R es un enfoque moderno para trabajar con estructuras de datos geoespaciales basado en principios de tidy data. La idea clave detrás de esto es que almacena geometrías geoespaciales en una columna de lista de un marco de datos, lo que permite que cada fila represente la unidad real de observación/interés. En resumen, esta función sf no proporciona datos geoespaciales, proporciona el marco y las utilidades para almacenar y calcular estructuras de datos geoespaciales de manera objetiva.

El paquete sf R es un enfoque moderno para trabajar con estructuras de datos geoespaciales basado en principios de tidy data. La idea clave detrás de esto es que almacena geometrías geoespaciales en una columna de lista de un marco de datos, lo que permite que cada fila represente la unidad real de observación/interés. En resumen, esta función sf no proporciona datos geoespaciales, proporciona el marco y las utilidades para almacenar y calcular estructuras de datos geoespaciales de manera objetiva.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)


Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')

plot_ly() %>%
  add_trace(
    type = "choroplethmapbox",
    # See how this GeoJSON URL was generated at
    # https://plotly-r.com/data-raw/us-states.R
    geojson = paste(c(
      "https://gist.githubusercontent.com/cpsievert/",
      "7cdcb444fb2670bd2767d349379ae886/raw/",
      "cf5631bfd2e385891bb0a9788a179d7f023bf6c8/", 
      "us-states.json"
    ), collapse = ""),
    locations = row.names(state.x77),
    z = state.x77[, "Population"] / state.x77[, "Area"],
    span = I(0)
  ) %>%
  layout(
    mapbox = list(
      style = "light",
      zoom = 4,
      center = list(lon = -98.58, lat = 39.82)
    )
  ) %>%
  config(
    mapboxAccessToken = Sys.getenv("MAPBOX_TOKEN"),
    # Workaround to make sure image download uses full container
    # size https://github.com/plotly/plotly.js/pull/3746
    toImageButtonOptions = list(
      format = "svg", 
      width = NULL, 
      height = NULL
    )
  )
library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
## The legacy packages maptools, rgdal, and rgeos, underpinning the sp package,
## which was just loaded, will retire in October 2023.
## Please refer to R-spatial evolution reports for details, especially
## https://r-spatial.org/r/2023/05/15/evolution4.html.
## It may be desirable to make the sf package available;
## package maintainers should consider adding sf to Suggests:.
## The sp package is now running under evolution status 2
##      (status 2 uses the sf package in place of rgdal)
## Support for Spatial objects (`sp`) will be deprecated in {rnaturalearth} and will be removed in a future release of the package. Please use `sf` objects with {rnaturalearth}. For example: `ne_download(returnclass = 'sf')`
world <- ne_countries(returnclass = "sf")
class(world)
## [1] "sf"         "data.frame"
#> [1] "sf"    "data.frame"
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

Figura 8.

Representación de todos los países del mundo usando plot_ly() y la función ne_countries() del paquete rnaturalearth. Pero ¿Cómo sabe plot_ly() representar los países? Esto sucede porque las características geoespaciales están codificadas en una columna de lista especial. Además los meta-datos sobre la estructura geoespacial se conservan como atributos espaciales de los datos.

library(sf)
## Linking to GEOS 3.11.2, GDAL 3.6.2, PROJ 9.2.0; 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:  WGS 84
## First 4 features:
##        name                       geometry
## 1      Fiji MULTIPOLYGON (((180 -16.067...
## 2  Tanzania MULTIPOLYGON (((33.90371 -0...
## 3 W. Sahara MULTIPOLYGON (((-8.66559 27...
## 4    Canada MULTIPOLYGON (((-122.84 49,...

Figura 9.

Esta figura aumenta el método de impresión para sf a marcos de datos para demostrar que toda la información necesaria para renderizar los países está contenida dentro del marco de datos mundiales. Una característica de este método es que la columna de geometría especial siempre se conserva, si se intenta seleccionar simplemente la columna de nombre, obtenemos tanto el nombre como la geometría. En resumen, en esta imagen se tiene un diagrama de marco de datos de características simples. La columna de geometría rastrea las características espaciales adjuntas a cada fila en el marco de datos. En realidad, hay 4 formas de representar objetos sf con plotly: plot_ly(), plot_mapbox(), plot_geo() y mediante geon_f() de ggplotz.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(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

Figura 10.

Se usa del color y del Split para crear un mapa coroplético de provincias de Canadá. Otras características importantes para los mapas que pueden requerir que usted divida múltiples polígonos en múltiples trazas es la capacidad de mostrar un relleno diferente para cada polígono.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)

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

Figura 11.

En este mapa se proporciona un texto que es único dentro del polígono, el comportamiento de la información sobre herramientas está vinculado al relleno del trazado. Así se usa el Split, el texto y hoveron=’ para mostrar información especifica sobre cada provincia.

Aunque los enfoques de mapeo integrados pueden representar objetos sf, los enfoques de mapeo personalizados son más flexibles porque permiten cualquier proyección de mapeo bien definida. Hoy en día para saber sobre proyecciones cartográficas es mucho más fácil porque hay buenos recursos para buscar como lo es una interfaz amiglable: http://spatialreference.org/. En este sitio, uno puede buscar proyecciones para una parte determinada del globo y extraer comandos para proyectar sus objetos geoespaciales en esa proyección.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)

# 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)

Figura 12.

En esta imagen se muestra una forma de llevar a cabo la proyección usando el comando PROJ4 . Aquí se muestra la población de varias ciudades canadienses representada en un mapa base personalizado usando una proyección de Mollweide.

Algunos objetos geoespaciales tienen una resolución innecesariamente alta para una visualización determinada. En estos casos, es posible que desee considerar simplificar el objeto geoespacial para mejorar la velocidad del código R y la capacidad de respuesta de la visualización.

###códigos

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)

sum(rapply(world$geometry, nrow))
## [1] 10654
#> [1] 10586

world_large <- ne_countries(scale = "large", returnclass = "sf")
sum(rapply(world_large$geometry, nrow))
## [1] 548471
#> [1] 548121
library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)

plot_mapbox(
  world_large, 
  color = NA, 
  stroke = I("black"), 
  span = I(0.5)
)
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

Manera de representar todas las características simples en un solo trazo, lo cual es eficaz, pero no tiene mucha interactividad.

#Cartogramas

Los cartogramas distorsionan el tamaño de los polígonos geoespaciales para codificar una variable numérica distinta del tamaño del terreno. Existen numerosos tipos de cartogramas y normalmente se clasifican por su capacidad para preservar la forma y mantener regiones contiguas. Este método es algo eficaz tanto para codificar como para enseñar datos geoespaciales, aunque los efectos varían según el tipo de cartograma. El cartograma del paquete R proporciona una interfaz para varios algoritmos de cartogramas populares. Lo bueno de los cartogramas es que todas las funciones pueden toman un objeto sf como entrada y devolver el objeto sf.

library(cartogram)
library(albersusa)

us_cont <- cartogram_cont(usa_sf("laea"), "pop_2014")

plot_ly(us_cont)%>%
  add_sf(
    color = ~pop_2014,
    split = ~name,
    span= I(1),
    text = paste(name, scales :: number_si(pop_2014)),
    hoverinfo = "text",
    hoveron = "fills",
  ) %>%
  layout(showlegend = FALSE)%>%
  colorbar(title = "Population \n 2014")

Figura 13.

Esta figura mestra un cartograma de área continua de la población de los Estadios Unidos en 2014. EL cartograma dimensiona el área de los objetos geoespaciales proporcionalmente a alguna métrica. Sin embargo, esta figura no pudo ser ejecutada por R porque el comando cartogram_cont es muy antiguo para el R descargado y no lo ejecuta.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)
## Please note that 'maptools' will be retired during October 2023,
## plan transition at your earliest convenience (see
## https://r-spatial.org/r/2023/05/15/evolution4.html and earlier blogs
## for guidance);some functionality will be moved to 'sp'.
##  Checking rgeos availability: TRUE
## Please note that rgdal will be retired during October 2023,
## plan transition to sf/stars/terra functions using GDAL and PROJ
## at your earliest convenience.
## See https://r-spatial.org/r/2023/05/15/evolution4.html and https://github.com/r-spatial/evolution
## rgdal: version: 1.6-7, (SVN revision 1203)
## Geospatial Data Abstraction Library extensions to R successfully loaded
## Loaded GDAL runtime: GDAL 3.6.2, released 2023/01/02
## Path to GDAL shared files: C:/Users/W11/AppData/Local/R/win-library/4.3/rgdal/gdal
##  GDAL does not use iconv for recoding strings.
## GDAL binary built with GEOS: TRUE 
## Loaded PROJ runtime: Rel. 9.2.0, March 1st, 2023, [PJ_VERSION: 920]
## Path to PROJ shared files: C:/Users/W11/AppData/Local/R/win-library/4.3/rgdal/proj
## PROJ CDN enabled: FALSE
## Linking to sp version:2.0-0
## To mute warnings of possible GDAL/OSR exportToProj4() degradation,
## use options("rgdal_show_exportToProj4_warnings"="none") before loading sp or rgdal.
## rgeos version: 0.6-4, (SVN revision 699)
##  GEOS runtime version: 3.11.2-CAPI-1.17.2 
##  Please note that rgeos will be retired during October 2023,
## plan transition to sf or terra functions using GEOS at your earliest convenience.
## See https://r-spatial.org/r/2023/05/15/evolution4.html for details.
##  GEOS using OverlayNG
##  Linking to sp version: 2.0-0 
##  Polygon checking: TRUE
Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')

us <- usa_sf("laea")
## old-style crs object detected; please recreate object with a recent sf::st_crs()
us_dor <- cartogram_dorling(us, "pop_2014")

plot_ly(
  stroke = I("black"), span = I(1)
  ) %>% 
  add_sf(
    data = us, 
    color = I("gray95"),
    hoverinfo = "none"
  ) %>%
  add_sf(
    data = us_dor, 
    color = ~pop_2014,
    split = ~name, 
    text = ~paste(name, scales::number(pop_2014)), 
    hoverinfo = "text", 
    hoveron = "fills"
  ) %>%
  layout(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
## 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

Figura 14.

En este caso, también se muestra un mapa de la población de los Estados Unidos en el 2014 pero en un cartograma Dorling de área no continua. En este caso, los cartogramas no intentan preservar la forma de los polígonos, sino que se utilizan círculos para representar cada objeto geoespacial y luego codifica la variable de interés utilizando el área del círculo.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)

Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')

us <- usa_sf("laea")
us_dor <- cartogram_ncont(us, "pop_2014")

plot_ly(
  stroke = I("black"), span = I(1)
  ) %>% 
  add_sf(
    data = us, 
    color = I("gray95"),
    hoverinfo = "none"
  ) %>%
  add_sf(
    data = us_dor, 
    color = ~pop_2014,
    split = ~name, 
    text = ~paste(name, scales::number(pop_2014)), 
    hoverinfo = "text", 
    hoveron = "fills"
  ) %>%
  layout(showlegend = FALSE)

Figura 15.

En este caso, se muestra la población de los Estados Unidos en el 2014 en un área discontinua. A diferencia del cartograma anterior, este si conserva la figura del polígono. Sin embargo, esta figura no pudo ser ejecutada por R porque el comando cartogram_ncont() es muy antiguo para el R descargado. Una clase popular de cartogramas continuos que no conservan la forma a veces se llama cartograma en mosaico.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)
library(geojsonio)

Sys.setenv('MAPBOX_TOKEN' = 'pk.eyJ1IjoibGloa2lyIiwiYSI6ImNsZnIxNmM2YTAzeTkzc3BvMjRtYWk3cDUifQ.cmuXPv09JIs6GInX5K1uCQ')

tiles <- as.location("~/Downloads/tiles.topo.json", what = "sp")
tiles_sf <- st_as_sf(tiles)q
plot_ly(tiles_sf, split = ~name)
s

Figura 16.

Esta figura muestra un mosaico de la población de los Estado Unidos en 2016 exportado directamente desde el servicio web gratuito de Pitch.

Sin embargo, esta figura no pudo ser ejecutada por R porque el programa presenta problemas para leer el documento .json. Muestra un error al encontrar el documento.

##5. Barras e histogramas

Las funciones add_bars() y add_histogram() envuelven los tipos de seguimiento bar e histogram plotly.js. La principal diferencia entre ellos es que los trazos de barra requieren alturas de barra, mientras que los trazos de histogramas requieren solo una variable. Ambas funciones se pueden usar para visualizar la distribución de una variable numérica o discreta. Así que la única diferencia entre ellos es la agrupación.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)

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
)

Figura 5.1

Esta imagen muestra el algoritmo de agrupamiento predeterminado de pltly.js contra el algoritmo de hist() predeterminado de R.

La siguiente figura muestra dos formas de crear un gráfico de barras básico. Aunque se ve lo mismo, vale la pena anotar la diferencia en la implementación. La función addhistogram() envía todos los valores observados al navegador y permite trazar y realizar la agrupación. Se necesita mucho trabajo humano para realizar la agrupación en R, pero hacerlo tiene la ventaja de enviar menos datos y requerir menos trabajo de cálculo por parte del navegador. En este caso porque son 50.000 registros por lo que no hay mucha diferencia, pero con 1 millón de datos ya el tiempo es diferente.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)
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()

Figura 5.2

Representación del número de diamantes por talla.

#5.1 Distribuciones numéricas múltiples

Con frecuencia se ve útil como cambia la distribución numérica con respecto a una variable discreta. Cuando utilice barras para visualizar múltiples distribuciones numéricas, se recomienda trazar cada distribución en su propio eje usando una pequeña visualización de múltiplos, en lugar de intentar superponerlas en un solo eje.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)
library(dplyr)

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

Figura 5.3.

Visualización enrejada del precio del diamante según la claridad del diamante.

#5.2 Distribución discretas múltiples

Visualizar múltiples distribuciones discretas es difícil. La sutil complejidad se debe al hecho de que tanto los recuentos como las proporciones son importantes para comprender las distribuciones discretas multivariadas.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)
library(dplyr)

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

Figura 5.4.

En esta figura se presenta el recuento de diamantes, divido tanto por su talla como por su claridad utilizando un gráfico de barras agrupados. Esta grafica es útil para comparar el numero de diamantes por claridad, dado un tipo de talla. Por ejemplo, dentro de los diamantes ideales. La talla VS1 es la más popular y la VS2 es la segunda más popular y la I1 la menos popular. La distribución de la claridad dentro de los diamantes ideales parece ser bastante similar a la de otros diamantes, pero es difícil hacer esta comparación utilizando recuentos brutos.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)
library(dplyr)

# 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")

Figura 5.5

Esta imagen muestra un grafico de barras apilados que muestra la proporción de diamantes de claridad dentro de la talla.

Este tipo de plot, también conocido como plot espinal es un caso especial de plot mosaico. Es un grafico mosaico, puede escalar tanto lo ancho como la altura de las barras según distribuciones discretas. Para gráficas de mosaico se recomienda usar el paqeute ggmosaic.

library(ggplot2)
library(tidyverse)
library(listviewer)
library(plotly)
library(dplyr)
library(rnaturalearth)
library(rnaturalearthhires)
library(cartogram)
library(albersusa)
library(dplyr)
library(ggmosaic)

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 <https://github.com/haleyjeppson/ggmosaic>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Figura 5.6

Esta figura muestra un diagram de mosaico de corte por claridad. Se observa como los anchos de las barras escalan proporcionalmente a la frecuencia de corte. Se usó ggmosaic y ggplotly() para crear visualizaciones interactivas avanzadas de datos categóricos.