/
creating-overview.Rmd
342 lines (251 loc) · 29 KB
/
creating-overview.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# (PART) Creating views {-}
# Overview
This part of the book teaches you how to leverage the **plotly** R package to create a variety of interactive graphics. There are two main ways to creating a **plotly** object: either by transforming a **ggplot2** object (via `ggplotly()`) into a **plotly** object or by directly initializing a **plotly** object with `plot_ly()`/`plot_geo()`/`plot_mapbox()`. Both approaches have somewhat complementary strengths and weaknesses, so it can pay off to learn both approaches. Moreover, both approaches are an implementation of the Grammar of Graphics and both are powered by the JavaScript graphing library plotly.js, so many of the same concepts and tools that you learn for one interface can be reused in the other.
The subsequent chapters within this 'Creating views' part dive into specific examples and use cases, but this introductory chapter outlines some over-arching concepts related to **plotly** in general. It also provides definitions for terminology used throughout the book and introduces some concepts useful for understanding the infrastructure behind any **plotly** object. Most of these details aren't necessarily required to get started with **plotly**, but it will inevitably help you get 'un-stuck', write better code, and do more advanced things with **plotly**.
## Intro to `plot_ly()` {#intro-plotly}
\index{plot\_ly()@\texttt{plot\_ly()}}
Any graph made with the **plotly** R package is powered by the JavaScript library [plotly.js](https://github.com/plotly/plotly.js). The `plot_ly()` function provides a 'direct' interface to plotly.js with some additional abstractions to help reduce typing. These abstractions, inspired by the Grammar of Graphics and **ggplot2**, make it much faster to iterate from one graphic to another, making it easier to discover interesting features in the data [@Wilkinson:2005; @ggplot2]. To demonstrate, we'll use `plot_ly()` to explore the `diamonds` dataset from **ggplot2** and learn a bit how **plotly** and plotly.js work along the way.
```{r}
# load the plotly R package
library(plotly)
# load the diamonds dataset from the ggplot2 package
data(diamonds, package = "ggplot2")
diamonds
```
If we assign variable names (e.g., `cut`, `clarity`, etc.) to visual properties (e.g., `x`, `y`, `color`, etc.) within `plot_ly()`, as done in Figure \@ref(fig:intro-defaults), it tries to find a sensible geometric representation of that information for us. Shortly we'll cover how to specify these geometric representations (as well as other visual encodings) to create different kinds of charts.
```r
# create three visualizations of the diamonds dataset
plot_ly(diamonds, x = ~cut)
plot_ly(diamonds, x = ~cut, y = ~clarity)
plot_ly(diamonds, x = ~cut, color = ~clarity, colors = "Accent")
```
```{r intro-defaults, echo = FALSE, fig.cap = "(ref:intro-defaults)", out.extra = if (knitr::is_html_output()) 'data-url="interactives/intro-defaults.html"'}
knitr::include_graphics("images/intro-defaults.svg")
```
The `plot_ly()` function has numerous arguments that are unique to the R package (e.g., `color`, `stroke`, `span`, `symbol`, `linetype`, etc.) and make it easier to encode data variables (e.g., diamond clarity) as visual properties (e.g., color). By default, these arguments map values of a data variable to a visual range defined by the plural form of the argument. For example, in the bottom panel of \@ref(fig:intro-defaults), `color` is used to map each level of diamond clarity to a different color, then `colors` is used to specify the range of colors (which, in this case, the `"Accent"` color palette from the **RColorBrewer** package, but one can also supply custom color codes or a color palette function like `colorRamp()`). Figure \@ref(fig:color-mapping) provides a visual diagram of how this particular mapping works, but the same sort of idea can be applied to other visual properties like size, shape, linetype, etc.
```{r color-mapping, echo = FALSE, fig.cap = "(ref:color-mapping)", out.width = "45%"}
knitr::include_graphics("images/color-mapping.svg")
```
Since these arguments map data values to a visual range by default, you will obtain unexpected results if you try to specify the visual range directly, as in the top portion of Figure \@ref(fig:intro-range). If you want to specify the visual range directly, use the `I()` function to declare this value to be taken 'AsIs', as in the bottom portion of Figure \@ref(fig:intro-range). Throughout this book, you'll see lots of examples that leverage these arguments, especially in Chapter \@ref(scatter-traces). Another good resource to learn more about these arguments (especially their defaults) is the R documentation page available by entering `help(plot_ly)` in your R console.
\index{plot\_ly()@\texttt{plot\_ly()}!I()@Interpret values 'AsIs' with \texttt{I()}}
```r
# doesn't produce black bars
plot_ly(diamonds, x = ~cut, color = "black")
# produces red bars with black outline
plot_ly(
diamonds,
x = ~cut,
color = I("red"),
stroke = I("black"),
span = I(2)
)
```
```{r intro-range, echo = FALSE, fig.cap = "(ref:intro-range)", out.extra = if (knitr::is_html_output()) 'data-url="/interactives/intro-range.html"'}
knitr::include_graphics("images/intro-range.svg")
```
The **plotly** package takes a purely functional approach to a layered grammar of graphics [@ggplot2-paper].^[If you aren't already familiar with the grammar of graphics or **ggplot2**, we recommend reading the Data Visualization chapter from the _R for Data Science_ book. <https://r4ds.had.co.nz/data-visualisation.html>] The purely functional part means, (almost) every function anticipates a **plotly** object as input to its first argument and returns a modified version of that **plotly** object. Furthermore, that modification is completely determined by the input values to the function (i.e., it doesn't rely on any side effects, unlike, for example, base R graphics). For a quick example, the `layout()` function anticipates a **plotly** object in its first argument and its other arguments add and/or modify various layout components of that object (e.g., the title):
```r
layout(
plot_ly(diamonds, x = ~cut),
title = "My beatiful histogram"
)
```
For more complex plots that modify a **plotly** graph many times over, code written in this way can become cumbersome to read. In particular, we have to search for the innermost part of the R expression, then work outwards towards the end result. The `%>%` operator from the **magrittr** package allows us to rearrange this code so that we can read the sequence of modifications from left-to-right rather than inside-out [@magrittr]. The `%>%` operator enables this by placing the object on the left-hand side of the `%>%` into the first argument of the function of the right-hand side.
```r
diamonds %>%
plot_ly(x = ~cut) %>%
layout(title = "My beatiful histogram")
```
In addition to `layout()` for adding/modifying part(s) of the graph's layout, there are also a family of `add_*()` functions (e.g., `add_histogram()`, `add_lines()`, etc.) that define how to render data into geometric objects. Borrowing terminology from the layered grammar of graphics, these functions add a graphical layer to a plot. A *layer* can be thought of as a group of graphical elements that can be sufficiently described using only 5 components: data, aesthetic mappings (e.g., assigning `clarity` to `color`), a geometric representation (e.g., rectangles, circles, etc.), statistical transformations (e.g., sum, mean, etc.), and positional adjustments (e.g., dodge, stack, etc.). If you're paying attention, you'll notice that in the examples thus far, we have not specified a layer! The layer has been added for us automatically by `plot_ly()`. To be explicit about what `plot_ly(diamonds, x = ~cut)` generates, we should add a `add_histogram()` layer:
```r
diamonds %>%
plot_ly() %>%
add_histogram(x = ~cut)
```
As you'll learn more about in Chapter \@ref(bars-histograms), **plotly** has both `add_histogram()` and `add_bars()`. The difference is that `add_histogram()` performs *statistics* (i.e., a binning algorithm) dynamically in the web browser, whereas `add_bars()` requires the bar heights to be pre-specified. That means, to replicate the last example with `add_bars()`, the number of observations must be computed ahead of time.
```r
diamonds %>%
dplyr::count(cut) %>%
plot_ly() %>%
add_bars(x = ~cut, y = ~n)
```
There are numerous other `add_*()` functions that calculate statistics in the browser (e.g., `add_histogram2d()`, `add_contour()`, `add_boxplot()`, etc.), but most other functions aren't considered statistical. Making the distinction might not seem useful now, but they have their own respective trade-offs when it comes to speed and interactivity. Generally speaking, non-statistical layers will be faster and more responsive at runtime (since they require less computational work), whereas the statistical layers allow for more flexibility when it comes to client-side interactivity, as covered in Chapter \@ref(client-side-linking). Practically speaking, the difference in performance is often negligible. The more common bottleneck occurs when attempting to render lots of graphical elements at a time (e.g., a scatterplot with a million points). In those scenarios, you likely want to render your plot in Canvas rather than SVG (the default) via `toWebGL()`. For more information on improving performance, see Chapter \@ref(performance).
\index{plot\_ly()@\texttt{plot\_ly()}!Inherited attributes}
\index{plot\_ly()@\texttt{plot\_ly()}!dplyr integration}
In many scenarios, it can be useful to combine multiple graphical layers into a single plot. In this case, it becomes useful to know a few things about `plot_ly()`:
* Arguments specified in `plot_ly()` are *global*, meaning that any downstream `add_*()` functions inherit these arguments (unless `inherit = FALSE`).
* Data manipulation verbs from the **dplyr** package may be used to transform the `data` underlying a **plotly** object.^[Technically speaking, these **dplyr** verbs are S3 generic functions that have a **plotly** method. In nearly every case, that method simply queries the data underlying the **plotly** object, applies the **dplyr** function, then adds the transformed data back into the resulting **plotly** object.]
Using these two properties of `plot_ly()`, Figure \@ref(fig:intro-dplyr) demonstrates how we could leverage these properties of `plot_ly()` to do the following:
1. _Globally_ assign `cut` to `x`.
2. Add a histogram layer (inherits the `x` from `plot_ly()`).
3. Use **dplyr** verbs to modify the `data` underlying the **plotly** object. Here we just count the number of diamonds in each `cut` category.
4. Add a layer of text using the summarized counts. Note that the global `x` mapping, as well as the other mappings local to this text layer (`text` and `y`), reflects data values from step 3.
```r
library(dplyr)
diamonds %>%
plot_ly(x = ~cut) %>%
add_histogram() %>%
group_by(cut) %>%
summarise(n = n()) %>%
add_text(
text = ~scales::comma(n), y = ~n,
textposition = "top middle",
cliponaxis = FALSE
)
```
```{r intro-dplyr, echo = FALSE, fig.cap = "(ref:intro-dplyr)", out.extra = if (knitr::is_html_output()) 'data-url="/interactives/intro-dplyr.html"'}
knitr::include_graphics("images/intro-dplyr.png")
```
Before using multiple `add_*()` in a single plot, make sure that you actually want to show those layers of information on the same set of axes. If it makes sense to display the information on the same axes, consider making multiple **plotly** objects and combining them into a grid-like layout using `subplot()`, as described in Chapter \@ref(arranging-views). Also, when using **dplyr** verbs to modify the `data` underlying the **plotly** object, you can use the `plotly_data()` function to obtain the data at any point in time, which is primarily useful for debugging purposes (i.e., inspecting the data of a particular graphical layer).
\indexc{plotly\_data()}
```{r}
diamonds %>%
plot_ly(x = ~cut) %>%
add_histogram() %>%
group_by(cut) %>%
summarise(n = n()) %>%
plotly_data()
```
This introduction to `plot_ly()` has mainly focused on concepts unique to the R package **plotly** that are generally useful for creating most kinds of data views. The next section outlines how **plotly** generates plotly.js figures and how to inspect the underlying data structure that plotly.js uses to render the graph. Not only is this information useful for debugging, but it's also a nice way to learn how to work with plotly.js directly, which you may need to improve performance in **shiny** apps (Section \@ref(proxies)) and/or for adding custom behavior with JavaScript (Chapter \@ref(javascript)).
## Intro to plotly.js {#intro-plotly-js}
\indexc{plotly\_build()}
\index{plotly\_json()@\texttt{plotly\_json()}}
\index{plotly.js}
To recreate the plots in Figure \@ref(fig:intro-defaults) using plotly.js *directly*, it would take significantly more code and knowledge of plotly.js. That being said, learning how **plotly** generates the underlying plotly.js figure is a useful introduction to plotly.js itself, and knowledge of plotly.js becomes useful when you need more flexible control over **plotly**. As Figure \@ref(fig:intro-printing) illustrates, when you print any **plotly** object, the `plotly_build()` function is applied to that object, and that generates an R list which adheres to a syntax that plotly.js understands. This syntax is a JavaScript Object Notation (JSON) specification that plotly.js uses to represent, serialize, and render web graphics. A lot of documentation you'll find online about plotly (e.g., the online [figure reference](https://plot.ly/r/reference/)) implicitly refers to this JSON specification, so it can be helpful to know how to "work backwards" from that documentation (i.e., translate JSON into to R code). If you'd like to learn details about mapping between R and JSON, Chapter \@ref(json) provides an introduction aimed at R programmers, and @jsonlite provides a cohesive overview of the **jsonlite** package, which is what **plotly** uses to map between R and JSON.
```{r intro-printing, echo = FALSE, fig.cap = "(ref:intro-printing)"}
knitr::include_graphics("images/printing.svg")
```
For illustration purposes, Figure \@ref(fig:intro-printing) shows how this workflow applies to a simple bar graph (with values directly supplied instead of a data column name reference like Figure \@ref(fig:intro-defaults)), but the same concept applies for any graph created via **plotly**. As the diagram suggests, both the `plotly_build()` and `plotly_json()` functions can be used to inspect the underlying data structure on both the R and JSON side of things. For example, Figure \@ref(fig:intro-json) shows the `data` portion of the JSON created for the last graph in Figure \@ref(fig:intro-json).
```r
p <- plot_ly(diamonds, x = ~cut, color = ~clarity, colors = "Accent")
plotly_json(p)
```
```{r intro-json, echo = FALSE, fig.cap = "(ref:intro-json)", out.width="70%", out.extra = if (knitr::is_html_output()) 'data-url="/interactives/intro-json.html"'}
knitr::include_graphics("images/intro-json.png")
```
\index{Plotly figure!Definition}
\index{Plotly trace!Definition}
In plotly.js terminology, a *figure* has two key components: `data` (aka, traces) and a `layout`. A *trace* defines a mapping from data and visuals.^[A trace is similar in concept to a layer (as defined in Section \@ref(intro-plotly)), but it's not quite the same. In many cases, like the bottom panel of Figure \@ref(fig:intro-defaults), it makes sense to implement a single layer as multiple traces. This is due to the design of plotly.js and how traces are tied to legends and hover behavior.] Every trace has a *type* (e.g., histogram, pie, scatter, etc.) and the trace type determines what other attributes (i.e., visual and/or interactive properties, like `x`, `hoverinfo`, `name`) are available to control the trace mapping. That is, not every trace attribute is available to every trace type, but many attributes (e.g., the `name` of the trace) are available in every trace type and serve a similar purpose. From Figure \@ref(fig:intro-json), we can see that it takes multiple traces to generate the dodged bar chart, but instead of clicking through JSON viewer, sometimes it's easier to use `plotly_build()` and compute on the plotly.js figure definition to verify certain things exist. Since **plotly** uses the **htmlwidgets** standard^[The **htmlwidgets** package provides a foundation for other packages to implement R bindings to JavaScript libraries so that those bindings work in various contexts (e.g., the R console, RStudio, inside **rmarkdown** documents, **shiny** apps, etc.). For more info and examples, see the website <http://www.htmlwidgets.org>.], the actual plotly.js figure definition appears under a list element named `x` [@htmlwidgets].
```r
# use plotly_build() to get at the plotly.js definition
# behind *any* plotly object
b <- plotly_build(p)
# Confirm there 8 traces
length(b$x$data)
#> [1] 8
# Extract the `name` of each trace. plotly.js uses `name` to
# populate legend entries and tooltips
purrr::map_chr(b$x$data, "name")
#> [1] "IF" "VVS1" "VVS2" "VS1" "VS2" "SI1" "SI2" "I1"
# Every trace has a type of histogram
unique(purrr::map_chr(b$x$data, "type"))
#> [1] "histogram"
```
Here we've learned that **plotly** creates 8 histogram traces to generate the dodged bar chart: one trace for each level of `clarity`.^[Although the x-axis is discrete, plotly.js still considers this a histogram because it generates counts in the browser. Learn more about the difference between histograms and bar charts in Chapter \@ref(bars-histograms).] Why one trace per category? As illustrated in Figure \@ref(fig:intro-show-hide), there are two main reasons: to populate a tooltip and legend entry for each level of `clarity` level.
```{r intro-show-hide, echo = FALSE, fig.cap = "(ref:intro-show-hide)"}
include_vimeo("315707813")
```
If we investigated further, we'd notice that `color` and `colors` are not officially part of the plotly.js figure definition; the `plotly_build()` function has effectively transformed that information into a sensible plotly.js figure definition (e.g., `marker.color` contains the actual bar color codes). In fact, the `color` argument in `plot_ly()` is just one example of an abstraction the R package has built on top of plotly.js to make it easier to map data values to visual attributes, and many of these are covered in Chapter \@ref(scatter-traces).
## Intro to `ggplotly()` {#intro-ggplotly}
\index{ggplotly()@\texttt{ggplotly()}}
The `ggplotly()` function from the **plotly** package has the ability to translate **ggplot2** to **plotly**. This functionality can be really helpful for quickly adding interactivity to your existing **ggplot2** workflow.^[This section is not meant to teach you **ggplot2**, but rather to help point out when and why it might be preferable to `plot_ly()`. If you're new to **ggplot2** and would like to learn it, see Section \@ref(ggplot2).] Moreover, even if you know `plot_ly()` and plotly.js well, `ggplotly()` can still be desirable for creating visualizations that aren't necessarily straightforward to achieve without it. To demonstrate, let's explore the relationship between `price` and other variables from the well-known `diamonds` dataset.
Hexagonal binning (i.e., `geom_hex()`) is useful way to visualize a 2D density^[See Section \@ref(frequencies-2D) for approaches using `plot_ly()`], like the relationship between `price` and `carat` as shown in Figure \@ref(fig:hexbin). From Figure \@ref(fig:hexbin), we can see there is a strong positive linear relationship between the _log_ of carat and price. It also shows that for many, the carat is only rounded to a particular number (indicated by the light blue bands) and no diamonds are priced around $1500. Making this plot interactive makes it easier to decode the hexagonal colors into the counts that they represent.
\index{ggplotly()@\texttt{ggplotly()}!ggplot2!geom\_hex()@\texttt{geom\_hex()}}
```r
p <- ggplot(diamonds, aes(x = log(carat), y = log(price))) +
geom_hex(bins = 100)
ggplotly(p)
```
```{r hexbin, echo = FALSE, fig.cap = "(ref:hexbin)", out.extra = if (knitr::is_html_output()) 'data-url="/interactives/hexbin.html"'}
knitr::include_graphics("images/hexbin.png")
```
I often use `ggplotly()` over `plot_ly()` to leverage **ggplot2**'s consistent and expressive interface for exploring statistical summaries across groups. For example, by including a discrete `color` variable (e.g., `cut`) with `geom_freqpoly()`, you get a frequency polygon for each level of that variable. This ability to quickly generate visual encodings of statistical summaries across an arbitrary number of groups works for basically any geom (e.g., `geom_boxplot()`, `geom_histogram()`, `geom_density()`, etc.) and is a key feature of **ggplot2**.
\index{ggplotly()@\texttt{ggplotly()}!ggplot2!geom\_freqpoly()@\texttt{geom\_freqpoly()}}
```r
p <- ggplot(diamonds, aes(x = log(price), color = clarity)) +
geom_freqpoly()
ggplotly(p)
```
```{r freqpoly, echo = FALSE, fig.cap = "(ref:freqpoly)"}
knitr::include_graphics("images/freqpoly.svg")
```
Now, to see how `price` varies with both `cut` and `clarity`, we could repeat this same visualization for each level of `cut`. This is where **ggplot2**'s `facet_wrap()` comes in handy. Moreover, to facilitate comparisons, we can have `geom_freqpoly()` display relative rather than absolute frequencies. By making this plot interactive, we can more easily compare particular levels of clarity (as shown in Figure \@ref(fig:freqpoly-facet)) by leveraging the legend filtering capabilities.
\index{ggplotly()@\texttt{ggplotly()}!ggplot2!facet\_wrap()@\texttt{facet\_wrap()}}
```r
p <- ggplot(diamonds, aes(x = log(price), color = clarity)) +
geom_freqpoly(stat = "density") +
facet_wrap(~cut)
ggplotly(p)
```
```{r freqpoly-facet, echo = FALSE, fig.cap = "(ref:freqpoly-facet)"}
include_vimeo("322318131")
```
In addition to supporting most of the 'core' **ggplot2** API, `ggplotly()` can automatically convert any **ggplot2** extension packages that return a 'standard' **ggplot2** object. By standard, I mean that the object is comprised of 'core' **ggplot2** data structures and not the result of custom geoms.^[As discussed in Chapter \@ref(custom-geoms), `ggplotly()` can actually convert custom geoms as well, but each one requires a custom hook, and many custom geoms are not yet supported.] Some great examples of R packages that extend **ggplot2** using core data structures are **ggforce**, **naniar**, and **GGally** [@ggforce; @naniar; @GGally].
Figure \@ref(fig:geom-sina) demonstrates another way of visualizing the same information found in Figure \@ref(fig:freqpoly-facet) using `geom_sina()` from the **ggforce** package (instead of `geom_freqpoly()`). This visualization jitters the raw data within the density for each group allowing us not only to see where the majority observations fall within a group, but also across all groups. By making this layer interactive, we can query individual points for more information and zoom into interesting regions. The second layer of Figure \@ref(fig:geom-sina) uses **ggplot2**'s `stat_summary()` to overlay a 95% confidence interval estimated via a Bootstrap algorithm via the **Hmisc** package [@Hmisc].
\index{ggplotly()@\texttt{ggplotly()}!ggforce!geom\_sina()@\texttt{geom\_sina()}}
\index{ggplotly()@\texttt{ggplotly()}!ggplot2!stat\_summary()@\texttt{stat\_summary()}}
\index{toWebGL()@\texttt{toWebGL()}!ggplotly()@\texttt{ggplotly()}}
```r
p <- ggplot(diamonds, aes(x=clarity, y=log(price), color=clarity)) +
ggforce::geom_sina(alpha = 0.1) +
stat_summary(fun.data = "mean_cl_boot", color = "black") +
facet_wrap(~cut)
# WebGL is a lot more efficient at rendering lots of points
toWebGL(ggplotly(p))
```
```{r geom-sina, echo=FALSE, fig.cap="(ref:geom-sina)"}
knitr::include_graphics("images/geom-sina.svg")
```
As noted by @r4ds, it's surprising that the diamond price would decline with an increase of diamond clarity. As it turns out, if we account for the carat of the diamond, then we see that better diamond clarity does indeed lead to a higher diamond price, as shown in Figure \@ref(fig:geom-sina-resid). Seeing such a strong pattern in the residuals of simple linear model of carat vs. price indicates that our model could be greatly improved by adding `clarity` as a predictor of `price`.
```r
m <- lm(log(price) ~ log(carat), data = diamonds)
diamonds <- modelr::add_residuals(diamonds, m)
p <- ggplot(diamonds, aes(x = clarity, y = resid, color = clarity)) +
ggforce::geom_sina(alpha = 0.1) +
stat_summary(fun.data = "mean_cl_boot", color = "black") +
facet_wrap(~cut)
toWebGL(ggplotly(p))
```
```{r geom-sina-resid, echo=FALSE, fig.cap="(ref:geom-sina-resid)"}
knitr::include_graphics("images/geom-sina-resid.svg")
```
\index{ggplotly()@\texttt{ggplotly()}!GGally!ggcoef()@\texttt{ggcoef()}}
As discussed in Section \@ref(ggally-ggnostic), the **GGally** package provides a convenient interface for making similar types of model diagnostic visualizations via the `ggnostic()` function. It also provides a convenience function for visualizing the coefficient estimates and their standard errors via the `ggcoef()` function. Figure \@ref(fig:ggally) shows how injecting interactivity into this plot allows us to query exact values and zoom in on the most interesting regions.
```r
library(GGally)
m <- lm(log(price) ~ log(carat) + cut, data = diamonds)
gg <- ggcoef(m)
# dynamicTicks means generate new axis ticks on zoom
ggplotly(gg, dynamicTicks = TRUE)
```
```{r ggally, echo=FALSE, fig.cap="(ref:ggally)"}
include_vimeo("322362701")
```
Although the `diamonds` dataset does not contain any missing values, it's a very common problem in real data analysis problems. The **naniar** package provides a suite of computational and visual resources for working with and revealing structure in missing values. All the **ggplot2** based visualizations return an object that can be converted by `ggplotly()`. Moreover, **naniar** provides a custom geom, `geom_miss_point()`, that can be useful for visualizing missingness structure. Figure \@ref(fig:naniar) demonstrates this by introducing fake missing values to the diamond price.
\index{ggplotly()@\texttt{ggplotly()}!naniar!geom\_miss\_point()@\texttt{geom\_miss\_point()}}
```r
library(naniar)
# fake some missing data
diamonds$price_miss <- ifelse(diamonds$depth>60, diamonds$price, NA)
p <- ggplot(diamonds, aes(x = clarity, y = log(price_miss))) +
geom_miss_point(alpha = 0.1) +
stat_summary(fun.data = "mean_cl_boot", colour = "black") +
facet_wrap(~cut)
toWebGL(ggplotly(p))
```
```{r naniar, echo=FALSE, fig.cap="(ref:naniar)"}
knitr::include_graphics("images/naniar.svg")
```
In short, the **ggplot2** ecosystem provides a world-class exploratory visualization toolkit, and having the ability to quickly insert interactivity such as hover, zoom, and filter via `ggplotly()` makes it even more powerful for exploratory analysis. In this introduction to `ggplotly()`, we've only seen relatively simple techniques that come for free out-of-the-box, but the true power of interactive graphics lies in linking multiple views. In that part of the book, you can find lots of examples of linking multiple (`ggplotly()` and `plot_ly()`) graphs purely client-side as well as with **shiny**.
It's also worth mentioning that `ggplotly()` conversions are not always perfect and **ggplot2** doesn't provide an API for interactive features, so sometimes it's desirable to modify the return values of `ggplotly()`. Chapter \@ref(improving-ggplotly) talks generally about modifying the data structure underlying `ggplotly()` (which, by the way, uses the same a plotly.js figure definition as discussed in Section \@ref(intro-plotly-js)). Moreover, Section \@ref(tooltip-text-ggplotly) outlines various ways to customize the tooltip that `ggplotly()` produces.
<!--
* Cover ggplot2 geoms that fill a void left by `plot_ly()`
* `stat_summary()` https://twitter.com/tjmahr/status/1095728500852621313
* More ideas http://r-statistics.co/Top50-Ggplot2-Visualizations-MasterList-R-Code.html
-->
<!--
Recall from Section \@ref(intro-plotly) that in the layered grammar of graphics, a layer comprised of 5 components: aesthetic mappings (e.g., assigning clarity to color), a geometric representation (e.g., rectangles, circles, etc.), statistical transformations (e.g., sum, mean, etc.), and positional adjustments (e.g., dodge, stack, etc.). Generally speaking, `ggplotly()` is going to be a more complete and consistent approach to expressing layers that leverage statistical transformation(s). This is partly due to its thoughtful integration with other statistical computing R packages like **MASS**, **mgcv**, and **Hmisc** [@MASS; @Hmisc; @mgcv]. For example, the `stat_summary()` layer al
-->
<!--
This chapter demonstrates the rendering capabilities of `plot_ly()` through a series of examples. The `plot_ly()` function provides a direct interface to plotly.js, so anything in [the figure reference](https://plot.ly/r/reference/) can be specified via `plot_ly()`, but this chapter will focus more on the special semantics unique to the R package that can't be found on the figure reference. Along the way, we will touch on some best practices in visualization.
-->