Sección 3 Bases dispersas

El tipo de ruta de dispersión es ideal para dibujar geometría de bajo nivel (p. ej., puntos, líneas, texto y polígonos) y proporciona la base para muchas funciones add_*() (p. ej., add_markers(), add_lines(), add_paths(), ), add_segments(), add_ribbons(), add_area() y add_polygons()), así como muchas gráficas ggplotly(). Estas capas basadas en dispersión brindan una mejor interfaz para casos especiales de diagramas de dispersión al realizar cierta manipulación y transformación de datos antes de asignarlos a diagramas de dispersión. Para un ejemplo simple, add_lines() asegura que las líneas se dibujen de acuerdo con el orden x, lo cual es deseable para trazar series de tiempo.

library(plotly)
## 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
data(economics, package = "ggplot2")

# sort economics by psavert, just to 
# show difference between paths and lines
p <- economics %>%
  arrange(psavert) %>%
  plot_ly(x = ~date, y = ~psavert)

add_paths(p)
add_lines(p)

En la sección 2.1 se introdujeron argumentos de “mapeo estético” que facilitan la asignación de datos a propiedades visuales (por ejemplo, color, tipo de línea, etc.). Además de estos argumentos, se pueden utilizar agrupaciones dplyr para garantizar que haya al menos una geometría por grupo. El panel superior de la figura siguiente muestra cómo se puede utilizar group_by() para agrupar eficazmente las series temporales de la figura siguiente por años, lo que puede ser útil para visualizar la estacionalidad anual. Otro enfoque para generar al menos una geometría por “grupo” es proporcionar una variable categórica a una estética relevante.

library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
econ <- economics %>%
  mutate(yr = year(date), mnth = month(date))

# One trace (more performant, but less interactive)
econ %>%
  group_by(yr) %>%
  plot_ly(x = ~mnth, y = ~uempmed) %>%
  add_lines(text = ~yr)
# Multiple traces (less performant, but more interactive)
plot_ly(econ, x = ~mnth, y = ~uempmed) %>%
  add_lines(color = ~ordered(yr))
# The split argument guarantees one trace per group level (regardless 
# of the variable type). This is useful if you want a consistent
# visual property over multiple traces 
# plot_ly(econ, x = ~mnth, y = ~uempmed) %>%
#   add_lines(split = ~yr, color = I("black"))

Esto se debe a que el enfoque de agrupación utiliza un solo seguimiento de plotly.js (más eficiente, menos interactivo), mientras que el enfoque de color crea un solo gráfico por programa por año.

Estas características hacen que sea fácil comenzar con plotly.js, pero vale la pena aprender a usar plotly.js directamente. No encontrará atributos de plotly.js enumerados como argumentos explícitos en ninguna función de plotly (que no sea un atributo de tipo especial), pero se pasan literalmente a la definición de figura de plotly.js usando el operador …. Capas basadas en dispersión en este capítulo establece el atributo de tipo plotly.js en “dispersión” y también el modo (por ejemplo, add_markers() usa mode=‘markers’, etc.), pero también puede usar el nivel inferior add_trace() para trabajar más directamente con plotly. js. . También demuestra cómo usar atributos plotly.js anidados, como textfont y xaxis; estos atributos contienen otros atributos, por lo que debe proporcionar la lista correcta de nombres para esos argumentos.

library(praise)
set.seed(99)
plot_ly() %>%
 add_trace(
   type = "scatter",
   mode = "markers+lines+text",
   x = 4:6, 
   y = 4:6,
   text = replicate(3, praise::praise("You are ${adjective}! 🙌")),
   textposition = "right",
   hoverinfo = "text",
   textfont = list(family = "Roboto Condensed", size = 16)
 ) %>%
 layout(xaxis = list(range = c(3, 8)))

Si es nuevo en plotly.js, le sugiero que dedique un poco de tiempo a observar los atributos de plotly.js disponibles para los tipos de diagramas de dispersión y pensar en cómo podría usarlos. La mayoría de estos atributos también funcionan para otros tipos de gráficos, por lo que aprender atributos únicos para un gráfico en particular también puede ser útil en otros contextos. La referencia en línea de números de plotly.js, https://plot.ly/r/reference/#scatter, es un lugar decente para buscar y aprender sobre los atributos, pero recomiendo usar la función schema() en su lugar por varias razones.

library(listviewer)
schema()

Las secciones que siguen en este capítulo muestran varios tipos de vistas de datos utilizando capas basadas en dispersión. ## 3.1 Marcadores Esta sección detalla los trazados de dispersión con un modo de “marcadores” (es decir, add_markers()). Para simplificar, muchos de los ejemplos aquí utilizan add_markers() con un eje x e y numérico, lo que resulta en un gráfico de dispersión - una forma común de visualizar la asociación entre dos variables cuantitativas. El contenido que sigue sigue siendo relevante marcadores mostrados no numérico x e y (tambien conocido como dot pots) ### 3.1.1 Combinación alpha Como señala Unwin (2015), los gráficos de dispersión pueden ser útiles para exponer otras características importantes, como relaciones casuales, valores atípicos, conglomerados, lagunas, barreras y relaciones condicionales. Sin embargo, un problema común con los diagramas de dispersión es el sobretrazado, lo que significa que hay múltiples observaciones que ocupan las mismas (o similares) ubicaciones x/y. En la figura siguiente se muestra una forma de combatir el sobretrazado mediante la mezcla alfa. Cuando se trata de decenas de miles de puntos (o más), considere el uso de toWebGL() para representar parcelas utilizando Canvas en lugar de SVG.

subplot(
  plot_ly(mpg, x = ~cty, y = ~hwy, name = "default"),
  plot_ly(mpg, x = ~cty, y = ~hwy) %>% 
    add_markers(alpha = 0.2, name = "alpha")
)
## 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 scatter mode specifed:
##   Setting the mode to markers
##   Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode

3.1.2 Colores

la asignación de una variable discreta a un color produce una traza por categoría, lo que es deseable para sus propiedades de leyenda y hover. Por otro lado, la asignación de una variable numérica a color produce una traza, así como una guía de barra de color para decodificar visualmente los colores de vuelta a los valores de los datos. La función colorbar() puede utilizarse para personalizar el aspecto de esta guía generada automáticamente. La escala de colores por defecto es viridis, una escala de colores perceptualmente uniforme (incluso cuando se convierte a blanco y negro), y perceptible incluso para aquellos con formas comunes de daltonismo (Berkeley Institute for Data Science 2016).

p <- plot_ly(mpg, x = ~cty, y = ~hwy, alpha = 0.5)
subplot(
  add_markers(p, color = ~cyl, showlegend = FALSE) %>% 
    colorbar(title = "Viridis"),
  add_markers(p, color = ~factor(cyl))
)

Existen numerosas formas de modificar la escala de colores por defecto mediante el argumento colors. Este argumento exceptúa uno de los siguientes: (1) un nombre de paleta de color brewer (ver los nombres de fila de RColorBrewer::brewer.pal.info para nombres válidos), (2) un vector de colores a interpolar, o (3) una función de interpolación de color como colorRamp() o scales::color_ramp().

col1 <- c("#132B43", "#56B1F7")
col2 <- viridisLite::inferno(10)
col3 <- colorRamp(c("red", "white", "blue"))
subplot(
  add_markers(p, color = ~cyl, colors = col1) %>%
    colorbar(title = "ggplot2 default"),
  add_markers(p, color = ~cyl, colors = col2) %>% 
    colorbar(title = "Inferno"),
  add_markers(p, color = ~cyl, colors = col3) %>% 
    colorbar(title = "colorRamp")
) %>% hide_legend()
col1 <- "Accent"
col2 <- colorRamp(c("red", "blue"))
col3 <- c(`4` = "red", `5` = "black", `6` = "blue", `8` = "green")
subplot(
  add_markers(p, color = ~factor(cyl), colors = col1),
  add_markers(p, color = ~factor(cyl), colors = col2),
  add_markers(p, color = ~factor(cyl), colors = col3)
) %>% hide_legend()

Los códigos de color pueden especificarse manualmente (es decir, evitando asignar valores de datos a un rango visual) utilizando la función I(). La figura posterior ofrece un ejemplo sencillo utilizando add_markers(). Cualquier color entendido por la función col2rgb() del paquete grDevices puede ser usado de esta manera.

add_markers(p, color = I("black"))

El argumento color sirve para controlar el “color de relleno” de un objeto geométrico, mientras que trazo (Sección 3.1.4) sirve para controlar el “color de contorno” de un objeto geométrico. En el caso de add_markers(), esto significa que el color se asigna al atributo marker.color de plotly.js y el trazo se asigna a marker.line.color. ### 3.1.3 Símbolos El argumento symbol se puede utilizar para asignar valores de datos al atributo marker.symbol plotly.js. Utiliza la misma semántica que ya hemos visto para el color:

Un mapeo numérico genera trazas. Un mapeo discreto genera múltiples trazas (una traza por categoría). El plural, símbolos, se puede utilizar para especificar el rango visual para el mapeo. Los mapeos se evitan por completo mediante I().

p <- plot_ly(mpg, x = ~cty, y = ~hwy, alpha = 0.3) 
subplot(
  add_markers(p, symbol = ~cyl, name = "A single trace"),
  add_markers(p, symbol = ~factor(cyl), color = I("black"))
)

Hay dos formas de especificar el rango visual de los símbolos: (1) códigos numéricos (interpretados como códigos pch) o (2) una cadena de caracteres que especifique un valor válido de marker.symbol.

subplot(
  add_markers(p, symbol = ~cyl, symbols = c(17, 18, 19)),
  add_markers(
    p, color = I("black"),
    symbol = ~factor(cyl), 
    symbols = c("triangle-up", "diamond", "circle")
  )
)

Estos símbolos (es decir, el alcance visual) también pueden suministrarse directamente a symbol a través de I().

plot_ly(mpg, x = ~cty, y = ~hwy) %>%
  add_markers(symbol = I(18), alpha = 0.5)

3.1.4 Trazo y batea

El argumento trazo sigue la misma semántica que color y símbolo cuando se trata de mapear variables y especificar rangos visuales. Normalmente no se desea asignar valores de datos a trazo, sino especificar un color de contorno fijo.

plot_ly(mpg, x = ~cty, y = ~hwy, alpha = 0.5) %>%
  add_markers(symbol = I(18), stroke = I("black"), span = I(1))

Tamaño

Para los gráficos de dispersión, el argumento tamaño controla el área de los marcadores (a menos que se especifique lo contrario mediante sizemode), y debe ser una variable numérica. El argumento sizes controla el tamaño mínimo y máximo de los círculos, en píxeles:

p <- plot_ly(mpg, x = ~cty, y = ~hwy, alpha = 0.3) 
subplot(
  add_markers(p, size = ~cyl, name = "default"),
  add_markers(p, size = ~cyl, sizes = c(1, 500), name = "custom")
)
## Warning: `line.width` does not currently support multiple values.

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

Al igual que otros argumentos, I() puede utilizarse para especificar el tamaño directamente. En el caso de los marcadores, el tamaño controla el atributo marker.size plotly.js.

plot_ly(mpg, x = ~cty, y = ~hwy, alpha = 0.3, size = I(30))
## 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 scatter mode specifed:
##   Setting the mode to markers
##   Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode

3.1.6 Gráficos de puntos & barras de errores

En este contexto, se prefieren los diagramas de puntos a los gráficos circulares, porque comparar posiciones a lo largo de una escala común es mucho más fácil que comparar ángulos o áreas (Cleveland y McGill 1984; Bostock 2010). Además, cuando se presentan estimaciones puntuales y las incertidumbres asociadas con esas estimaciones, los gráficos de barras tienden a exagerar las diferencias entre las estimaciones puntuales y se olvidan de las incertidumbres (Messing 2012).

Una aplicación popular de los gráficos de puntos (con barras de error) es el llamado “gráfico de coeficientes” para mostrar estimaciones puntuales de los coeficientes y sus errores estándar. Dado que este paquete usa puntos y segmentos para graficar los coeficientes, la información flotante no es la mejor y sería mejor usar un objeto de error. La Figura siguiente usa la función “trapy()” del paquete broom (Robinson 2016) para obtener un marco de datos con coeficientes de una fila por modelo y genera un gráfico de los coeficientes con barras de error a lo largo del eje x.

library(broom)
library(forcats)
# Fit a full-factorial linear model
m <- lm(
  Sepal.Length ~ Sepal.Width * Petal.Length * Petal.Width, 
  data = iris
)

# (1) get a tidy() data structure of covariate-level info 
# (e.g., point estimate, standard error, etc)
# (2) make sure term column is a factor ordered by the estimate
# (3) plot estimate by term with an error bar for the standard error
broom::tidy(m) %>% 
  mutate(term = forcats::fct_reorder(term, estimate)) %>%
  plot_ly(x = ~estimate, y = ~term) %>%
  add_markers(
    error_x = ~list(value = std.error), 
    color = I("black"),
    hoverinfo = "x"
  )

3.2 Líneas

Muchos de los mismos principios que aprendimos sobre la estética de los marcadores (Sección 3.1) se aplican a las líneas. asegúrese de que cada grupo tenga al menos una geometría (en este caso, una línea). También aprendimos la diferencia entre add_paths() y add_lines(): el primero dibuja líneas en orden de línea, mientras que el segundo dibuja líneas en x.

3.2.1 Tipos de línea

Aquí usamos el paquete dplyr para encontrar las 5 ciudades principales en términos de ventas mensuales promedio (top5) y luego filtramos los datos de origen para que solo contengan esas ciudades usando semi_join(). Como se muestra en la fig. 3.17, después de filtrar los datos, es fácil asignar un color o tipo de línea a una ciudad. La paleta de tipos de línea se puede modificar con el argumento tipos de línea y acepta valores lty de R o valores de guión de plotly.js.

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(ggplot2)
library(rockchalk)
## 
## Attaching package: 'rockchalk'
## The following object is masked from 'package:dplyr':
## 
##     summarize
library(plotly)
top5 <- txhousing %>%
  group_by(city) %>%
  summarise(m = mean(sales, na.rm = TRUE)) %>%
  arrange(desc(m)) %>%
  top_n(5)
## Selecting by m
tx5 <- semi_join(txhousing, top5, by = "city")

plot_ly(tx5, x = ~date, y = ~median) %>%
  add_lines(linetype = ~city)

Si desea controlar exactamente qué tipo de línea se utiliza para codificar un determinado valor de datos, puede proporcionar un vector de caracteres con nombres.

ltys <- c(
  Austin = "dashdot",
  `Collin County` = "longdash",
  Dallas = "dash",
  Houston = "solid",
  `San Antonio` = "dot"
)

plot_ly(tx5, x = ~date, y = ~median) %>%
  add_lines(linetype = ~city, linetypes = ltys)

3.2.2 Segmentos

La función add_segments() permite conectar dos puntos [(x, y) a (xend, yend)] con una línea. Los segmentos forman los bloques de construcción de numerosos tipos de gráficos útiles, incluidos los gráficos inclinados, los gráficos de campana, los gráficos de velas y otros. Los gráficos inclinados y los gráficos de mancuernas son útiles para comparar valores numéricos en numerosas categorías. Los gráficos de velas suelen utilizarse para visualizar los cambios de un activo financiero a lo largo del tiempo. ### 3.2.2.1 Gráfico de pendiente !range@} l gráfico de pendiente, popularizado por Tufte (2001b), es una excelente forma de comparar el cambio en una medición a través de numerosos grupos. Este cambio puede producirse a lo largo de un eje discreto o continuo. Para un eje continuo, el slopegraph podría considerarse como una descomposición de un gráfico lineal en múltiples segmentos. El paquete slopegraph R proporciona una interfaz sucinta para crear slopegraphs con gráficos base o ggplot2 y también algunos conjuntos de datos convenientes que utilizaremos aquí (Leeper 2017).

library("vembedr")
## 
## Attaching package: 'vembedr'
## The following object is masked from 'package:lubridate':
## 
##     hms
embed_url("https://vimeo.com/327585190?embedded=true&source=vimeo_logo&owner=29107273")

3.2.2.2 Dumbell

Los denominados gráficos de mancuernas son similares en concepto a los gráficos de pendientes, pero no tan generales. Suelen utilizarse para comparar dos clases diferentes de valores numéricos entre numerosos grupos.

mpg %>%
  group_by(model) %>%
  summarise(c = mean(cty), h = mean(hwy)) %>%
  mutate(model = forcats::fct_reorder(model, c)) %>%
  plot_ly() %>%
  add_segments(
    x = ~c, y = ~model,
    xend = ~h, yend = ~model, 
    color = I("gray"), showlegend = FALSE
  ) %>%
  add_markers(
    x = ~c, y = ~model, 
    color = I("blue"), 
    name = "mpg city"
  ) %>%
  add_markers(
    x = ~h, y = ~model, 
    color = I("red"),
    name  = "mpg highway"
  ) %>%
  layout(xaxis = list(title = "Miles per gallon"))

3.2.2.3 Candlestick

Se utiliza el paquete quantmod (Ryan 2016) para obtener datos sobre el precio de las acciones de Microsoft y traza dos segmentos para cada día: uno para codificar los valores de apertura/cierre y otro para codificar los máximos/mínimos diarios.

library(quantmod)
## Loading required package: xts
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## ################################### WARNING ###################################
## # We noticed you have dplyr installed. The dplyr lag() function breaks how    #
## # base R's lag() function is supposed to work, which breaks lag(my_xts).      #
## #                                                                             #
## # Calls to lag(my_xts) that you enter or source() into this session won't     #
## # work correctly.                                                             #
## #                                                                             #
## # All package code is unaffected because it is protected by the R namespace   #
## # mechanism.                                                                  #
## #                                                                             #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## # You can use stats::lag() to make sure you're not using dplyr::lag(), or you #
## # can add conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop   #
## # dplyr from breaking base R's lag() function.                                #
## ################################### WARNING ###################################
## 
## Attaching package: 'xts'
## The following objects are masked from 'package:dplyr':
## 
##     first, last
## Loading required package: TTR
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## 
## Attaching package: 'quantmod'
## The following object is masked from 'package:rockchalk':
## 
##     addLines
msft <- getSymbols("MSFT", auto.assign = F)
dat <- as.data.frame(msft)
dat$date <- index(msft)
dat <- subset(dat, date >= "2016-01-01")

names(dat) <- sub("^MSFT\\.", "", names(dat))

plot_ly(dat, x = ~date, xend = ~date, color = ~Close > Open, 
        colors = c("red", "forestgreen"), hoverinfo = "none") %>%
  add_segments(y = ~Low, yend = ~High, size = I(1)) %>%
  add_segments(y = ~Open, yend = ~Close, size = I(3)) %>%
  layout(showlegend = FALSE, yaxis = list(title = "Price")) %>%
  rangeslider()

3.2.3 Gráficos de densidad

Aprovechamos una serie de algoritmos en R para calcular el número “óptimo” de intervalos para un histograma, a través de hist(), y dirigir esos resultados a add_bars(). Podemos aprovechar la función density() para calcular las estimaciones de densidad del núcleo de forma similar, y dirigir los resultados a add_lines().

kerns <- c("gaussian", "epanechnikov", "rectangular", 
          "triangular", "biweight", "cosine", "optcosine")
p <- plot_ly()
for (k in kerns) {
  d <- density(economics$pce, kernel = k, na.rm = TRUE)
  p <- add_lines(p, x = d$x, y = d$y, name = k)
}
p

3.2.4 Coordenadas paralelas

Una técnica de visualización muy útil, pero que a menudo se pasa por alto, es el gráfico de coordenadas paralelas. Las coordenadas paralelas permiten comparar valores a lo largo de una(s) escala(s) posicional(es) común(es) (o no alineada(s)) -la más básica de todas las tareas perceptivas- en más de 3 dimensiones (Cleveland y McGill 1984). Normalmente, cada línea representa cada medida de una fila (u observación) determinada de un conjunto de datos. Es cierto que plotly.js proporciona un tipo de trazado, parcoords, específico para coordenadas paralelas que ofrece capacidades interactivas deseables (por ejemplo, resaltar y reordenar los ejes).

iris$obs <- seq_len(nrow(iris))
iris_pcp <- function(transform = identity) {
  iris[] <- purrr::map_if(iris, is.numeric, transform)
  tidyr::gather(iris, variable, value, -Species, -obs) %>% 
    group_by(obs) %>% 
    plot_ly(x = ~variable, y = ~value, color = ~Species) %>% 
    add_lines(alpha = 0.3)
}
subplot(
  iris_pcp(), 
  iris_pcp(scale),
  iris_pcp(scales::rescale),
  nrows = 3, shareX = TRUE
) %>% hide_legend()
## Warning: attributes are not identical across measure variables; they will be
## dropped

Gracias al marco de resaltado vinculado, las coordenadas paralelas creadas de esta manera podrían vincularse a gráficos de datos relacionados de menor dimensión (pero a veces de mayor resolución) para guiar el estudio de datos multivariados. El paquete Pedestrian proporciona algunos ejemplos de vinculación de coordenadas paralelas a otras vistas, p. B. un gran recorrido para descubrir características inusuales en un espacio de alta dimensión (Sievert 2019a).

3.3 Polígonos

Los polígonos forman la base para otras capas basadas en dispersión de nivel superior (por ejemplo, add_ribbons() y add_sf()) que no tienen un tipo de gráfico dedicado en plotly.js. Los polígonos se pueden usar para dibujar muchas cosas, pero quizás la aplicación más conocida en la que querrá usar add_polygons() es cuando se dibujan objetos geoespaciales. Cuando use add_polygons() para trazar un mapa, asegúrese de establecer la relación de aspecto (por ejemplo, xaxis.scaleanchor) y también considere usar plotly_empty() sobre plot_ly() para agregar etiquetas de ejes, marcas y ocultar la cuadrícula de fondo.

las capas poligonales basadas en dispersión (es decir, add_polygons(), add_ribbons(), etc) renderizan todos los polígonos utilizando un trazado plotly.js por defecto. Este enfoque es computacionalmente eficiente, pero no siempre es deseable (por ejemplo, no puede tener múltiples rellenos por trazo, la interactividad es relativamente limitada). Para evitar estas limitaciones, considere el uso de split (o color con una variable discreta) para dividir los datos del polígono en múltiples trazos.

3.3.1 Lazos

Las cintas son útiles para mostrar los límites de incertidumbre como una función de x. La función add_ribbons() crea cintas y requiere los argumentos: x, ymin, y ymax.

library(plotly)
library(broom)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ purrr   1.0.1     ✔ tibble  3.2.1
## ✔ readr   2.1.4     ✔ tidyr   1.3.0
## ✔ stringr 1.5.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter()        masks plotly::filter(), stats::filter()
## ✖ xts::first()           masks dplyr::first()
## ✖ vembedr::hms()         masks lubridate::hms()
## ✖ dplyr::lag()           masks stats::lag()
## ✖ xts::last()            masks dplyr::last()
## ✖ rockchalk::summarize() masks dplyr::summarize()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
m <- lm(mpg ~ wt, data = mtcars)
broom::augment(m) %>%
  plot_ly(x = ~wt, showlegend = FALSE) %>%
  add_markers(y = ~mpg, color = I("black")) %>%
  add_ribbons(ymin = ~.fitted - qt(0.975, df = 1), 
              ymax = ~.fitted + qt(0.975, df = 1), 
              color = I("gray80")) %>%
  add_lines(y = ~.fitted, color = I("steelblue"))

References

Berkeley Institute for Data Science. 2016. “Mpl Colormaps.” 2016. http://web.archive.org/web/20160601125258/http://bids.github.io/colormap/.

Bostock, Jeffrey Heer AND Michael. 2010. “Crowdsourcing Graphical Perception: Using Mechanical Turk to Assess Visualization Design.” In ACM Human Factors in Computing Systems (Chi), 203–12. http://vis.stanford.edu/papers/crowdsourcing-graphical-perception.

Cleveland, William S, and Robert McGill. 1984. “Graphical Perception: Theory, Experimentation, and Application to the Development of Graphical Methods.” Journal of the American Statistical Association 79 (September): 531–54.

Few, Stephen. 2006. “Data Visualization: Rules for Encoding Values in Graph.” 2006. https://web.archive.org/web/20160404214629/http://www.perceptualedge.com/articles/b-eye/encoding_values_in_graph.pdf.

Lander, Jared P. 2016. Coefplot: Plots Coefficients from Fitted Models. https://CRAN.R-project.org/package=coefplot.

Leeper, Thomas J. 2017. Slopegraph: Edward Tufte-Inspired Slopegraphs.

Messing, Solomon. 2012. “Visualization Series: Insight from Cleveland and Tufte on Plotting Numeric Data by Groups.” 2012. http://web.archive.org/web/20160602202734/https://solomonmessing.wordpress.com/2012/03/04/visualization-series-insight-from-cleveland-and-tufte-on-plotting-numeric-data-by-groups/.

Robinson, David. 2016. Broom: Convert Statistical Analysis Objects into Tidy Data Frames. https://CRAN.R-project.org/package=broom.

Ryan, Jeffrey A. 2016. Quantmod: Quantitative Financial Modelling Framework. https://CRAN.R-project.org/package=quantmod.

Sievert, Carson. 2019a. Pedestrians: Tools for Exploring Melbourne’s Pedestrian Data. https://github.com/cpsievert/pedestrians.

Tufte, Edward. 2001b. The Visual Display of Quantitative Information. Cheshire, Conn: Graphics Press.

Unwin, Antony. 2015. Graphical Data Analysis with R. CRC Press. http://www.gradaanwr.net.