[r-cran-plotly] 01/07: New upstream version 4.7.1+dfsg
Andreas Tille
tille at debian.org
Thu Dec 21 14:24:19 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository r-cran-plotly.
commit ce8f0e7aabd3bf2b25bfec90f17eb281d722df4d
Author: Andreas Tille <tille at debian.org>
Date: Thu Dec 21 14:45:42 2017 +0100
New upstream version 4.7.1+dfsg
---
DESCRIPTION | 47 +
LICENSE | 2 +
MD5 | 244 +
NAMESPACE | 281 +
NEWS.md | 1045 +++
R/add.R | 644 ++
R/animate.R | 246 +
R/api.R | 252 +
R/api_exports.R | 196 +
R/coord.R | 66 +
R/data.R | 23 +
R/deprecated.R | 119 +
R/dev.R | 56 +
R/embed.R | 52 +
R/export.R | 105 +
R/ggplotly-legacy.R | 534 ++
R/ggplotly.R | 1333 +++
R/group2NA.R | 99 +
R/helpers.R | 217 +
R/highlight.R | 215 +
R/imports.R | 176 +
R/last_plot.R | 21 +
R/layers2layout.R | 29 +
R/layers2traces.R | 1082 +++
R/layout.R | 118 +
R/modeBarButtons.R | 23 +
R/pipe.R | 4 +
R/plotly.R | 414 +
R/plotly_IMAGE.R | 44 +
R/plotly_build.R | 878 ++
R/plotly_data.R | 253 +
R/plotly_example.R | 52 +
R/print.R | 104 +
R/process.R | 55 +
R/proxy.R | 110 +
R/sf.R | 141 +
R/shiny.R | 71 +
R/signup.R | 58 +
R/style.R | 34 +
R/subplots.R | 425 +
R/sysdata.rda | Bin 0 -> 14018 bytes
R/toRGB.R | 104 +
R/utils.R | 930 ++
README.md | 90 +
data/hobbs.rda | Bin 0 -> 4845 bytes
data/mic.rda | Bin 0 -> 1321 bytes
data/wind.rda | Bin 0 -> 365 bytes
demo/00Index | 14 +
demo/animation-tour-USArrests.R | 75 +
demo/animation-tour-basic.R | 52 +
demo/crosstalk-filter-dynamic-axis.R | 15 +
demo/crosstalk-filter-lines.R | 14 +
demo/crosstalk-highlight-binned-target.R | 65 +
demo/crosstalk-highlight-epl.R | 30 +
demo/crosstalk-highlight-ggpairs.R | 4 +
demo/crosstalk-highlight-ggplotly.R | 35 +
demo/crosstalk-highlight-intro.R | 66 +
demo/crosstalk-highlight-leaflet.R | 22 +
demo/crosstalk-highlight-pipeline.R | 27 +
demo/crosstalk-highlight-subplot.R | 10 +
demo/rotate.R | 53 +
demo/ternary.R | 58 +
inst/build-push-comment.R | 73 +
inst/docs.R | 1160 +++
inst/examples/rmd/flexdashboard/index.Rmd | 88 +
inst/examples/rmd/onRenderHover/index.Rmd | 50 +
inst/examples/rmd/printing/index.Rmd | 85 +
inst/examples/shiny/DT/app.R | 39 +
inst/examples/shiny/Diamonds/server.R | 27 +
inst/examples/shiny/Diamonds/ui.R | 28 +
inst/examples/shiny/Movies/server.R | 22 +
inst/examples/shiny/Movies/ui.R | 12 +
.../shiny/UN_Advanced/Data/UN_IdealPoints.csv | 9121 ++++++++++++++++++++
inst/examples/shiny/UN_Advanced/global.R | 1 +
inst/examples/shiny/UN_Advanced/server.R | 56 +
inst/examples/shiny/UN_Advanced/ui.R | 26 +
.../shiny/UN_Simple/Data/UN_IdealPoints.csv | 1 +
inst/examples/shiny/UN_Simple/global.R | 1 +
inst/examples/shiny/UN_Simple/server.R | 19 +
inst/examples/shiny/UN_Simple/ui.R | 27 +
inst/examples/shiny/basic/DESCRIPTION | 4 +
inst/examples/shiny/basic/app.R | 51 +
inst/examples/shiny/event_data/DESCRIPTION | 8 +
inst/examples/shiny/event_data/app.R | 43 +
inst/examples/shiny/event_data_3D/app.R | 28 +
inst/examples/shiny/event_data_click/DESCRIPTION | 8 +
inst/examples/shiny/event_data_click/app.R | 53 +
inst/examples/shiny/event_data_click_map/app.R | 33 +
inst/examples/shiny/event_data_modules/app.R | 48 +
inst/examples/shiny/event_data_select/app.R | 82 +
inst/examples/shiny/lmGadget/app.R | 80 +
inst/examples/shiny/proxy_mapbox/app.R | 28 +
inst/examples/shiny/proxy_relayout/app.R | 43 +
inst/examples/shiny/proxy_restyle_canada/app.R | 36 +
inst/examples/shiny/proxy_restyle_economics/app.R | 39 +
.../lib/plotlyjs/plotly-htmlwidgets.css | 9 +
inst/htmlwidgets/lib/typedarray/LICENSE | 21 +
inst/htmlwidgets/lib/typedarray/typedarray.min.js | 1 +
inst/htmlwidgets/plotly.js | 823 ++
inst/htmlwidgets/plotly.yaml | 0
inst/plotlyjs.R | 56 +
inst/stars.R | 22 +
man/add_annotations.Rd | 39 +
man/add_data.Rd | 20 +
man/add_fun.Rd | 45 +
man/add_trace.Rd | 183 +
man/animation.Rd | 93 +
man/api.Rd | 151 +
man/as.widget.Rd | 18 +
man/as_widget.Rd | 23 +
man/attrs_selected.Rd | 20 +
man/bbox.Rd | 22 +
man/colorbar.Rd | 44 +
man/config.Rd | 29 +
man/embed_notebook.Rd | 25 +
man/event_data.Rd | 31 +
man/export.Rd | 64 +
man/geom2trace.Rd | 21 +
man/get_figure.Rd | 16 +
man/get_l.Rd | 14 +
man/get_x.Rd | 14 +
man/get_y.Rd | 14 +
man/gg2list.Rd | 42 +
man/ggplotly.Rd | 90 +
man/group2NA.Rd | 58 +
man/hide_colorbar.Rd | 23 +
man/hide_guides.Rd | 17 +
man/hide_legend.Rd | 22 +
man/highlight.Rd | 107 +
man/hobbs.Rd | 15 +
man/knit_print.api_grid.Rd | 21 +
man/knit_print.api_grid_local.Rd | 21 +
man/knit_print.api_plot.Rd | 21 +
man/last_plot.Rd | 14 +
man/layout.Rd | 23 +
man/mic.Rd | 15 +
man/offline.Rd | 31 +
man/plot_dendro.Rd | 41 +
man/plot_geo.Rd | 33 +
man/plot_ly.Rd | 125 +
man/plot_mapbox.Rd | 42 +
man/plotly-shiny.Rd | 33 +
man/plotly.Rd | 25 +
man/plotlyProxy.Rd | 43 +
man/plotly_IMAGE.Rd | 38 +
man/plotly_POST.Rd | 43 +
man/plotly_build.Rd | 28 +
man/plotly_data.Rd | 113 +
man/plotly_empty.Rd | 14 +
man/plotly_example.Rd | 22 +
man/plotly_json.Rd | 24 +
man/print.api.Rd | 16 +
man/print.api_grid.Rd | 16 +
man/print.api_grid_local.Rd | 16 +
man/print.api_plot.Rd | 16 +
man/rangeslider.Rd | 41 +
man/raster2uri.Rd | 39 +
man/reexports.Rd | 42 +
man/remove_typedarray_polyfill.Rd | 32 +
man/schema.Rd | 32 +
man/showRGB.Rd | 24 +
man/signup.Rd | 49 +
man/style.Rd | 33 +
man/subplot.Rd | 87 +
man/toRGB.Rd | 38 +
man/toWebGL.Rd | 21 +
man/to_basic.Rd | 27 +
man/wind.Rd | 15 +
tests/testthat.R | 191 +
tests/testthat/test-animate-highlight.R | 318 +
tests/testthat/test-api.R | 149 +
tests/testthat/test-cookbook-axes.R | 196 +
tests/testthat/test-cookbook-lines.R | 205 +
tests/testthat/test-cookbook-scatterplots.R | 82 +
tests/testthat/test-ggplot-abline.R | 46 +
tests/testthat/test-ggplot-annotation_.R | 59 +
tests/testthat/test-ggplot-area.R | 70 +
tests/testthat/test-ggplot-bar.R | 176 +
tests/testthat/test-ggplot-blank.R | 14 +
tests/testthat/test-ggplot-boxplot.R | 86 +
tests/testthat/test-ggplot-col.R | 29 +
tests/testthat/test-ggplot-contour.R | 15 +
tests/testthat/test-ggplot-coord-fixed.R | 96 +
tests/testthat/test-ggplot-crossbar.R | 49 +
tests/testthat/test-ggplot-date.R | 37 +
tests/testthat/test-ggplot-density.R | 74 +
tests/testthat/test-ggplot-density2d.R | 72 +
tests/testthat/test-ggplot-device.R | 8 +
tests/testthat/test-ggplot-dynamicTicks.R | 106 +
tests/testthat/test-ggplot-errorbar-horizontal.R | 23 +
tests/testthat/test-ggplot-errorbar.R | 42 +
tests/testthat/test-ggplot-facets.R | 133 +
tests/testthat/test-ggplot-ggplotly.R | 31 +
tests/testthat/test-ggplot-heatmap.R | 62 +
tests/testthat/test-ggplot-hex.R | 22 +
tests/testthat/test-ggplot-histogram.R | 232 +
tests/testthat/test-ggplot-hline.R | 77 +
tests/testthat/test-ggplot-jitter.R | 26 +
tests/testthat/test-ggplot-labels.R | 36 +
tests/testthat/test-ggplot-legend.R | 97 +
tests/testthat/test-ggplot-lines.R | 120 +
tests/testthat/test-ggplot-map.R | 16 +
tests/testthat/test-ggplot-path.R | 79 +
tests/testthat/test-ggplot-point.R | 59 +
tests/testthat/test-ggplot-polygons.R | 204 +
tests/testthat/test-ggplot-quantile.R | 53 +
tests/testthat/test-ggplot-rect.R | 141 +
tests/testthat/test-ggplot-ribbon.R | 50 +
tests/testthat/test-ggplot-rug.R | 58 +
tests/testthat/test-ggplot-segment.R | 48 +
tests/testthat/test-ggplot-sf.R | 56 +
tests/testthat/test-ggplot-size.R | 30 +
tests/testthat/test-ggplot-smooth.R | 65 +
tests/testthat/test-ggplot-spoke.R | 44 +
tests/testthat/test-ggplot-step.R | 36 +
tests/testthat/test-ggplot-text.R | 44 +
tests/testthat/test-ggplot-theme.R | 76 +
tests/testthat/test-ggplot-ticks.R | 191 +
tests/testthat/test-ggplot-tooltip.R | 119 +
tests/testthat/test-ggplot-violin.R | 26 +
tests/testthat/test-ggplot-vline.R | 35 +
tests/testthat/test-ggplot-ylim.R | 33 +
tests/testthat/test-group2NA.R | 76 +
tests/testthat/test-mean-error-bars.R | 56 +
tests/testthat/test-plotly-color.R | 113 +
tests/testthat/test-plotly-colorbar.R | 56 +
tests/testthat/test-plotly-data.R | 46 +
tests/testthat/test-plotly-filename.R | 17 +
tests/testthat/test-plotly-getfigure.R | 52 +
tests/testthat/test-plotly-group.R | 67 +
tests/testthat/test-plotly-internals.R | 10 +
tests/testthat/test-plotly-linetype.R | 64 +
tests/testthat/test-plotly-pie.R | 21 +
tests/testthat/test-plotly-subplot.R | 223 +
tests/testthat/test-plotly-symbol.R | 70 +
tests/testthat/test-plotly.R | 191 +
tests/testthat/test-rotated-ticks.R | 69 +
tests/testthat/test-toRGB.R | 10 +
238 files changed, 32041 insertions(+)
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..e57670a
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,47 @@
+Package: plotly
+Title: Create Interactive Web Graphics via 'plotly.js'
+Version: 4.7.1
+Authors at R: c(person("Carson", "Sievert", role = c("aut", "cre"),
+ email = "cpsievert1 at gmail.com"),
+ person("Chris", "Parmer", role = "aut",
+ email = "chris at plot.ly"),
+ person("Toby", "Hocking", role = "aut",
+ email = "tdhock5 at gmail.com"),
+ person("Scott", "Chamberlain", role = "aut",
+ email = "myrmecocystus at gmail.com"),
+ person("Karthik", "Ram", role = "aut",
+ email = "karthik.ram at gmail.com"),
+ person("Marianne", "Corvellec", role = "aut",
+ email = "marianne at plot.ly"),
+ person("Pedro", "Despouy", role = "aut",
+ email = "pedro at plot.ly"),
+ person("Plotly Technologies Inc.", role = "cph"))
+License: MIT + file LICENSE
+Description: Easily translate 'ggplot2' graphs to an interactive web-based version and/or create custom web-based visualizations directly from R. Once uploaded to a 'plotly' account, 'plotly' graphs (and the data behind them) can be viewed and modified in a web browser.
+URL: https://plot.ly/r, https://cpsievert.github.io/plotly_book/,
+ https://github.com/ropensci/plotly
+BugReports: https://github.com/ropensci/plotly/issues
+Depends: R (>= 3.2.0), ggplot2 (>= 2.2.1)
+Imports: tools, scales, httr, jsonlite, magrittr, digest, viridisLite,
+ base64enc, htmltools, htmlwidgets (>= 0.9), tidyr, hexbin,
+ RColorBrewer, dplyr, tibble, lazyeval (>= 0.2.0), crosstalk,
+ purrr, data.table
+Suggests: MASS, maps, ggthemes, GGally, testthat, knitr, devtools,
+ shiny (>= 0.14), curl, rmarkdown, Rserve, RSclient, Cairo,
+ broom, webshot, listviewer, dendextend, sf, RSelenium, png,
+ IRdisplay
+LazyData: true
+RoxygenNote: 6.0.1
+NeedsCompilation: no
+Packaged: 2017-07-28 15:49:30 UTC; cpsievert
+Author: Carson Sievert [aut, cre],
+ Chris Parmer [aut],
+ Toby Hocking [aut],
+ Scott Chamberlain [aut],
+ Karthik Ram [aut],
+ Marianne Corvellec [aut],
+ Pedro Despouy [aut],
+ Plotly Technologies Inc. [cph]
+Maintainer: Carson Sievert <cpsievert1 at gmail.com>
+Repository: CRAN
+Date/Publication: 2017-07-29 05:16:25 UTC
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..34e65ef
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+YEAR: 2017
+COPYRIGHT HOLDER: Plotly, Inc.
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..c0060e0
--- /dev/null
+++ b/MD5
@@ -0,0 +1,244 @@
+430215b9aa8a357176298aabc104e1c1 *DESCRIPTION
+dcd388f52af8e3cb24839540e1b67823 *LICENSE
+a24419669b21d5de7a5e5f9de7b1ee8f *NAMESPACE
+2f61144c2013d4817942f3861571416d *NEWS.md
+8ff080dcf333b2b7dcb29a3d57dacfc3 *R/add.R
+9e9f8366bb61b83dbd5933ba3e8bd9c4 *R/animate.R
+aa316aa1242ced4aaafec86d95c67cbc *R/api.R
+c853ae1b1267060d4043475ad3c2ce3d *R/api_exports.R
+a08021d51b8356d7e822b4ce622891b3 *R/coord.R
+906031eb95ff08e19ef818ed15cd53d9 *R/data.R
+c649b8e1083d07732415c21d5d44aab3 *R/deprecated.R
+618641d3eae35473d8a11356c6138b17 *R/dev.R
+da28abb0a6231f7519ecfe3d88c686a2 *R/embed.R
+84ffd4e3029f6f8c3198bfa06a714047 *R/export.R
+35f1748abe6983c06ccec710e4a3d332 *R/ggplotly-legacy.R
+e987673853a22a013c127679172a439e *R/ggplotly.R
+e8a2b49ff5d99621debec16b7799583c *R/group2NA.R
+fc3280cb49344a62e4df66eaca05402b *R/helpers.R
+b5d205dba92e47f15b3b5021ad2d9a1d *R/highlight.R
+411d572d9510b628e37b10915cce5626 *R/imports.R
+cfc79b68343904ffcb0eb0ec6ac6ace1 *R/last_plot.R
+643d009bb3dee2f3488d96c743af62be *R/layers2layout.R
+4cfde5039b86ae68386031b523fe5b5f *R/layers2traces.R
+7c49d9745c6a5060507d8b428006db5e *R/layout.R
+fd6eaeb7fda1f3baf28b41c3a2080375 *R/modeBarButtons.R
+9ae0bda363178574fd7a165eea1ae1a8 *R/pipe.R
+8b5faa25a4017b27d07ea9a65e1da1fb *R/plotly.R
+0dd272bd8321b2515cd0a8d5f9e62cbe *R/plotly_IMAGE.R
+6e035ae8a138ac41a82305da51a6a906 *R/plotly_build.R
+44e002640eac7d9f64fee406e629a809 *R/plotly_data.R
+5859e4c514e93718a28a1f04107052ff *R/plotly_example.R
+4b2fe38a356333a07456e8bc3b56c0cb *R/print.R
+c78106268662d6de725818312b8935cb *R/process.R
+d0a0af229c1485d68e66d94b9c13686a *R/proxy.R
+aba828ce35b2bf6b2455edfbe260e88d *R/sf.R
+811b73baffc7b89cc02680188844c30f *R/shiny.R
+c57731acef58b6165303f304a27d03a1 *R/signup.R
+fe1e15f4a60ca5708c8b9b1d9398c8c0 *R/style.R
+dee034c8b0b7fdc13c23af57f1616f13 *R/subplots.R
+89bc494d7ddd709ab1b6aa6ff95fdcb2 *R/sysdata.rda
+a60063b9385deb54f16513de53d60db7 *R/toRGB.R
+15f062c65e34699cce897c33e72ad4bb *R/utils.R
+b2e9d69895ea335b04e1fe65760c695b *README.md
+81a69ecca67949eed8808b99d6edcfc9 *data/hobbs.rda
+f8f6a6b9bb660afdf3497622ee43fe5b *data/mic.rda
+5e1d254955b9853d073ecbc39953d7ed *data/wind.rda
+4927ed3f532f9e493b707d0ef9e270b8 *demo/00Index
+d8f29df78e5e748580a005a42bbee660 *demo/animation-tour-USArrests.R
+e900ed66e23d1dfd56a148db1fcc22a4 *demo/animation-tour-basic.R
+2fbb748b68a2f62acbc0205264ff1d47 *demo/crosstalk-filter-dynamic-axis.R
+361351b7b8f30b541a79b37af96cff29 *demo/crosstalk-filter-lines.R
+9cd45b43d0541da6f9cfea26146d0fcd *demo/crosstalk-highlight-binned-target.R
+3e6430f0bb13d9069d4f91ed3971ad0d *demo/crosstalk-highlight-epl.R
+775e9b899de076d36f5e241ea46ec22e *demo/crosstalk-highlight-ggpairs.R
+9ea226194c11eca91c67efd163868129 *demo/crosstalk-highlight-ggplotly.R
+e1fdd62e8d2976b6b9792ffe69c51739 *demo/crosstalk-highlight-intro.R
+8be1fa10acc0eb5662e7da63d348f35c *demo/crosstalk-highlight-leaflet.R
+8aeba633029f53f7e1360af69214d056 *demo/crosstalk-highlight-pipeline.R
+7e1760e414e5b0e271d90a3b7c9cdf43 *demo/crosstalk-highlight-subplot.R
+004367e8202e3a7acaaa743093d19d00 *demo/rotate.R
+1490c5205ee2b1e856e5be8725a75b89 *demo/ternary.R
+f17f4798057963339daf24be28c7676b *inst/build-push-comment.R
+0efa24608352f8cdecb353c8cbf2a562 *inst/docs.R
+3155171cf7b6369fe2ba821c6abf1097 *inst/examples/rmd/flexdashboard/index.Rmd
+daf2384f6e18a51baeaf010d240be646 *inst/examples/rmd/onRenderHover/index.Rmd
+27c3100b5425fd9572fc379dbef01dfd *inst/examples/rmd/printing/index.Rmd
+4898cb07077a95f923bc4f127721356b *inst/examples/shiny/DT/app.R
+8fc8065e963589788698a7d534bf7bf0 *inst/examples/shiny/Diamonds/server.R
+08ee8ee0258a486db54e833ff2523602 *inst/examples/shiny/Diamonds/ui.R
+0cd5ed72210427f67d1470555a63c2a4 *inst/examples/shiny/Movies/server.R
+f863f15c27562ad49c87fafbed6cc44b *inst/examples/shiny/Movies/ui.R
+33f0fa134600f392e5ce107d6f8ce4b4 *inst/examples/shiny/UN_Advanced/Data/UN_IdealPoints.csv
+338a3dbdd13f550fbb0963a16344c016 *inst/examples/shiny/UN_Advanced/global.R
+c3125293757a1909a5b752f72fc1525b *inst/examples/shiny/UN_Advanced/server.R
+cd0bdcf0e1e7210a870898d286b1ba44 *inst/examples/shiny/UN_Advanced/ui.R
+ded6e52fa9bde48d7b404823aec78e20 *inst/examples/shiny/UN_Simple/Data/UN_IdealPoints.csv
+338a3dbdd13f550fbb0963a16344c016 *inst/examples/shiny/UN_Simple/global.R
+1aba264ed67caa0d230d14d4d0062183 *inst/examples/shiny/UN_Simple/server.R
+84f4df0968888c02d8a434a7ddbcdd90 *inst/examples/shiny/UN_Simple/ui.R
+97e5c7ea2205c2c95b58a84df62aaeae *inst/examples/shiny/basic/DESCRIPTION
+23d0a96317ee114d9c8f4b61ef4137c5 *inst/examples/shiny/basic/app.R
+054b4171f371b7b8dbc944c24fcc752f *inst/examples/shiny/event_data/DESCRIPTION
+8f6b615cf7c0a75a870bd58f5323068c *inst/examples/shiny/event_data/app.R
+ab3db262f80e6a0893e2bc53e588b5c8 *inst/examples/shiny/event_data_3D/app.R
+054b4171f371b7b8dbc944c24fcc752f *inst/examples/shiny/event_data_click/DESCRIPTION
+a16765a25a0d84aee7242f07eed99202 *inst/examples/shiny/event_data_click/app.R
+f87491ab8f924b8b7311e80448683189 *inst/examples/shiny/event_data_click_map/app.R
+f067a810b9bc1fcd850bfad99a41c576 *inst/examples/shiny/event_data_modules/app.R
+8d3b3506702f59386d9fe06b7fbaaefc *inst/examples/shiny/event_data_select/app.R
+0400906e926f9a39e177f4012e0e13d7 *inst/examples/shiny/lmGadget/app.R
+5cd7bb52a3430d697411f0eaa27d39e6 *inst/examples/shiny/proxy_mapbox/app.R
+f3ba3e205351f6742da4beb76ba1dd6a *inst/examples/shiny/proxy_relayout/app.R
+0408e441788e83b748dae2d1a3e98938 *inst/examples/shiny/proxy_restyle_canada/app.R
+61b72d299f37c7cbf9e2ca3aa2b6ed6e *inst/examples/shiny/proxy_restyle_economics/app.R
+ebf70b59f17ab360a1a9a2099ef4c806 *inst/htmlwidgets/lib/colourpicker/LICENSE
+77da99b7eccf0e79093be060e5617887 *inst/htmlwidgets/lib/colourpicker/colourpicker.min.css
+fbb9912baf6643516438ce4dd17a0417 *inst/htmlwidgets/lib/colourpicker/colourpicker.min.js
+b00254ac7ddba335b1d581f7a5b6b74e *inst/htmlwidgets/lib/plotlyjs/LICENSE
+150e4929cfd53a8a39835d40a108a54e *inst/htmlwidgets/lib/plotlyjs/plotly-htmlwidgets.css
+e3189ae7a03d5a2e7b6a0e7de205360a *inst/htmlwidgets/lib/plotlyjs/plotly-latest.min.js
+573633e5eb0860d61ba2e59fd6b41389 *inst/htmlwidgets/lib/selectize/selectize.bootstrap3.css
+146435eeda32f0e12bca8519f0da5ad9 *inst/htmlwidgets/lib/selectize/selectize.min.js
+ac2ed8233b48affa48a87c6001343572 *inst/htmlwidgets/lib/typedarray/LICENSE
+4db25a9ed5c5fcf2dd1746f27be2ef39 *inst/htmlwidgets/lib/typedarray/typedarray.min.js
+60464ed74868585496f95f02e1c243cd *inst/htmlwidgets/plotly.js
+d41d8cd98f00b204e9800998ecf8427e *inst/htmlwidgets/plotly.yaml
+fb324463c6d913aacc841c734a7bdf5e *inst/plotlyjs.R
+224a15e3a56693d67847aebd5c3d5ae7 *inst/stars.R
+c95f27cbcec069788ad15f819fa017c2 *man/add_annotations.Rd
+bbd855c4d0be7243bb237a1cdc1b0c34 *man/add_data.Rd
+49053fe0a2b23bf4f8cc58f969b2e1cc *man/add_fun.Rd
+714e3486bbfb8485fb1252e8e68a0dfb *man/add_trace.Rd
+a7aaac33e2d4d6014472699bd473499e *man/animation.Rd
+1635a1a0054ec5bd3214288a3583bad2 *man/api.Rd
+a44f627df4e74f4834c53d5c41fd036b *man/as.widget.Rd
+725ebba3db10d9dc9da2c4b18906187f *man/as_widget.Rd
+ea02a0d92fac83264dc71d0a7f6b575c *man/attrs_selected.Rd
+c35017b629a710ca37f10f1846738894 *man/bbox.Rd
+e39a28c60be8506f17b42b0cff822f01 *man/colorbar.Rd
+b3c80e118e835bec7845ed02b4034958 *man/config.Rd
+4267c22c0bc6969f49ee5dcff300e589 *man/embed_notebook.Rd
+5f08450e73191bdc86459dfb3b45d16f *man/event_data.Rd
+ce379f6d2318a96a37cdbd4bb50aaef1 *man/export.Rd
+5f7cdff6a04d2e68f9c9ddb97b0af831 *man/geom2trace.Rd
+19a907dea724d4323b26e61551e85e12 *man/get_figure.Rd
+153b2e65b77cc164f92c926a5d9188cb *man/get_l.Rd
+33835afef2053f0500da63cf51bc9240 *man/get_x.Rd
+28546ea0a7a6722b4f4972b86d99ebe7 *man/get_y.Rd
+1f95501d239e4401e533ed5bc3de1b1f *man/gg2list.Rd
+f759a7d0b16c6a6646f28484e7fae952 *man/ggplotly.Rd
+1b1b280618b9b7a0d881ec0be93dc2a2 *man/group2NA.Rd
+be5798a30ef6120993d571e6f94580b3 *man/hide_colorbar.Rd
+1df92bdeeae66ef089f9d9d0caaa6c28 *man/hide_guides.Rd
+c925dc5337583a364a3986182d5aef87 *man/hide_legend.Rd
+d19e7d829f0dfda22f09e142f19606d8 *man/highlight.Rd
+30941f38736f8fdb3a2ecef87c14183f *man/hobbs.Rd
+16f06853a16c5621d18fa09a9b340590 *man/knit_print.api_grid.Rd
+356903db8a6ab1a57906cbb29215bc22 *man/knit_print.api_grid_local.Rd
+4117d0aa2e9b4760baefe865f5c1c9a3 *man/knit_print.api_plot.Rd
+18378168a79acb2ad443fdc29afeb9a2 *man/last_plot.Rd
+4076b771cf0d12ba4d85d6a5401646d7 *man/layout.Rd
+ad11c2482c0944c5e0f6abc41f475160 *man/mic.Rd
+7f4236157f63ac58cc53ae9524a6860c *man/offline.Rd
+28f23b6bebbf97b0b4cd52a460c9b877 *man/plot_dendro.Rd
+08d2e892457cb7654d098f1c0f431545 *man/plot_geo.Rd
+3642e2be0d1af2bb47783dbfbca9c1ff *man/plot_ly.Rd
+9ba3d8db04c4cb111b0644ee0947bed4 *man/plot_mapbox.Rd
+1a26cfb1ef314457d648095b1e2ac9fb *man/plotly-shiny.Rd
+f931dcac49a86c8b299f3f0bfa332ca0 *man/plotly.Rd
+a5ff8a61a69d820237a70ed068966150 *man/plotlyProxy.Rd
+dc6413861a61254f1264bcb4cdcfb4b1 *man/plotly_IMAGE.Rd
+6fb1bd24825899108084a88b1d8dcdf2 *man/plotly_POST.Rd
+7e67b6e10a1e1d05fa567935f624e9e8 *man/plotly_build.Rd
+21bdefed6cf65965d6f62877f8a5b50e *man/plotly_data.Rd
+5cd54b3abaf2a2cfa075001a97b9f831 *man/plotly_empty.Rd
+b46a590f1bd028e823bb0c99d82de697 *man/plotly_example.Rd
+727ad5ffce6c52fb5558b4e56af17029 *man/plotly_json.Rd
+84676a66a29770aff2512d502ec9ffe4 *man/print.api.Rd
+bf57bf90060072f8c13a322a3c05d34d *man/print.api_grid.Rd
+4518356d0f268094528f1b5101db2d1a *man/print.api_grid_local.Rd
+d93950d14b41360e3c7d42f2e8947ecb *man/print.api_plot.Rd
+089fbb9bd16459287bc3210d81a2bbf6 *man/rangeslider.Rd
+d5ea870025cb64a49aa810ce7a5b3ada *man/raster2uri.Rd
+d0c626e02e6b3e50958a93f2d9b0a42e *man/reexports.Rd
+aa8cdd98b46fd40c5cfb77abc6a331a6 *man/remove_typedarray_polyfill.Rd
+431427aa2b24163d80e2175830b6ad03 *man/schema.Rd
+54a0954131bba347d6fb944d56d771e9 *man/showRGB.Rd
+23593a6764820322efed2ab5a6377e08 *man/signup.Rd
+b72c9171ef755c3360773c5b5711c98e *man/style.Rd
+9f01b23925718aa0eeaab21e0eb820e3 *man/subplot.Rd
+15f6ef9fe794b742b295c8290e044651 *man/toRGB.Rd
+966145c9754195c42c44993e4b6ec402 *man/toWebGL.Rd
+8b7e3130a01a581931a1a09d53e2155f *man/to_basic.Rd
+1d69a93921fb9219cbfd09801a07a785 *man/wind.Rd
+66944770c3c6fb55a3e3b3c28ff32366 *tests/testthat.R
+69e2e0655f16717c3f179782f1c510be *tests/testthat/test-animate-highlight.R
+20bb730d5e4818d1cc9a0db9e425cfac *tests/testthat/test-api.R
+3312ecad7b4cb4aa2214f1fa9e514a48 *tests/testthat/test-cookbook-axes.R
+42e9e16ef7e1b4decaa90ad88f27bf07 *tests/testthat/test-cookbook-lines.R
+3335275683030a4cbb4f261954e045a0 *tests/testthat/test-cookbook-scatterplots.R
+0d2a4a1f359809b8aadac640c6b3608a *tests/testthat/test-ggplot-abline.R
+217aef4247f51f7973c25e57e2bdf009 *tests/testthat/test-ggplot-annotation_.R
+585d695fe12d02304fb03b99457d1828 *tests/testthat/test-ggplot-area.R
+8c59008049459b0f33c0c517097f5e92 *tests/testthat/test-ggplot-bar.R
+d5d80d9b60f5512269762d39af9ac2c4 *tests/testthat/test-ggplot-blank.R
+f7290c4a315dedae7298e12666cf5f11 *tests/testthat/test-ggplot-boxplot.R
+0a9303a0d26b3ff4baac690ade1f1bc9 *tests/testthat/test-ggplot-col.R
+d8ceef57a77079870833e399b8b21c0f *tests/testthat/test-ggplot-contour.R
+a5631e3f95a6dd51f630c9c0533dc63c *tests/testthat/test-ggplot-coord-fixed.R
+003fe24544a2da9c523690929b420014 *tests/testthat/test-ggplot-crossbar.R
+bbfc60a7da6b0ddd1f638bc8f57d31ae *tests/testthat/test-ggplot-date.R
+f9cf765a101a2e552c7befcb20686db5 *tests/testthat/test-ggplot-density.R
+6dd7e64fe91a0d4cefe8664b2427aacc *tests/testthat/test-ggplot-density2d.R
+0d16135fc9ce46cdb333105b2641fe79 *tests/testthat/test-ggplot-device.R
+64ec73199773a44a457c29dac699bf6b *tests/testthat/test-ggplot-dynamicTicks.R
+479879053cc7160d0705b714a3ea2f48 *tests/testthat/test-ggplot-errorbar-horizontal.R
+692f12916a543d8775ac88ee5a6d1b3e *tests/testthat/test-ggplot-errorbar.R
+8c4a118f4975bb72c7fccd42345a8ef3 *tests/testthat/test-ggplot-facets.R
+0e4da4b824511ca4a67f12d857d46c12 *tests/testthat/test-ggplot-ggplotly.R
+b5da255dd09ae56bfc460edd5d69ef3f *tests/testthat/test-ggplot-heatmap.R
+634c0882f6f9bc07a4aeba6187b56f8c *tests/testthat/test-ggplot-hex.R
+4633269dc186548e3c25d39997722646 *tests/testthat/test-ggplot-histogram.R
+ef8a6765fc99afd461a89633f01f1431 *tests/testthat/test-ggplot-hline.R
+7bf5e976d68b3c8465579fc52d607292 *tests/testthat/test-ggplot-jitter.R
+d896dfdb1cc9d8f1ccc4977a46c533bb *tests/testthat/test-ggplot-labels.R
+f473cdc56c8b78bb7ffb4c038cbb8dbf *tests/testthat/test-ggplot-legend.R
+cfa81383e268c945c02baddde39378d2 *tests/testthat/test-ggplot-lines.R
+b1acb4bcc0dd30cf585764d6ca97a335 *tests/testthat/test-ggplot-map.R
+9930b867f616d3684d200652516e203c *tests/testthat/test-ggplot-path.R
+01681f82b3ca647331df724a5242a93d *tests/testthat/test-ggplot-point.R
+d006c8dc070b4bbc8284b086a586dfaf *tests/testthat/test-ggplot-polygons.R
+993784c10c3e0c93cb9eb8dce14189b2 *tests/testthat/test-ggplot-quantile.R
+4c88aa49bbbd0c0e6564114c8bab50a0 *tests/testthat/test-ggplot-rect.R
+624e8a5c7e862745acc67a22a3cdfc82 *tests/testthat/test-ggplot-ribbon.R
+71592664ea42dccf5f1c599db447fd1a *tests/testthat/test-ggplot-rug.R
+984c2bce51c329d05b8f1710fc93601d *tests/testthat/test-ggplot-segment.R
+cad3eee64b45e230984eb1ca89756279 *tests/testthat/test-ggplot-sf.R
+a24b9a9208731a1edc9bc49cde20e4e2 *tests/testthat/test-ggplot-size.R
+423008a6191ad923c35dd10db9539a8e *tests/testthat/test-ggplot-smooth.R
+3625d3f7ee4db0659abd58d31565ccf7 *tests/testthat/test-ggplot-spoke.R
+7d70021fbe503ea1d2cec22787bbc3bb *tests/testthat/test-ggplot-step.R
+61209040a0807c507f8f6ac4f9d9655c *tests/testthat/test-ggplot-text.R
+97b590f06068f6e43997128098a84ffe *tests/testthat/test-ggplot-theme.R
+fdc5a24b15f403092fdd4d8ae6425063 *tests/testthat/test-ggplot-ticks.R
+de6d1b734e6549680f93dd5d157b8a5b *tests/testthat/test-ggplot-tooltip.R
+c621f78eb2f66dfca4bd1f513627cf38 *tests/testthat/test-ggplot-violin.R
+3e1bb67680820ffd4aa6f103234553b2 *tests/testthat/test-ggplot-vline.R
+d59e4f115edf3fc97b6e916b9e34860c *tests/testthat/test-ggplot-ylim.R
+cc9000e854ce6fab3e132c8a38794499 *tests/testthat/test-group2NA.R
+919115f206b0320630e7dd97af599cec *tests/testthat/test-mean-error-bars.R
+dc818ea59dba16e1a6bf7f79188b4fc4 *tests/testthat/test-plotly-color.R
+fea77780c003391f61ed495332543ade *tests/testthat/test-plotly-colorbar.R
+334e6d16fc4b25368e52efee0d6eaf56 *tests/testthat/test-plotly-data.R
+a9879e1f38ab37bbf513c4010b12ffe8 *tests/testthat/test-plotly-filename.R
+4f6116ae2a8f67b9e69d7b3deb224a19 *tests/testthat/test-plotly-getfigure.R
+62de9250aad47c246c3ff7d6bc89bb83 *tests/testthat/test-plotly-group.R
+0b723c0cefeeba64e19615ebb39ff80b *tests/testthat/test-plotly-internals.R
+f3059d6725bd02cecc1d88911ab3d218 *tests/testthat/test-plotly-linetype.R
+5b5706c16e328d03d6c85ced7a291b56 *tests/testthat/test-plotly-pie.R
+8709408cca32ce34b880c26c697a52cf *tests/testthat/test-plotly-subplot.R
+638a99949deda57446808b16ae3e81d5 *tests/testthat/test-plotly-symbol.R
+04067b952249874c66ce77dad6c2b2c5 *tests/testthat/test-plotly.R
+a2d18fbfc60afa1c40e36c4b0bab0603 *tests/testthat/test-rotated-ticks.R
+eb441f4abca8bedfd0a929e48612e94a *tests/testthat/test-toRGB.R
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100644
index 0000000..84b1826
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,281 @@
+# Generated by roxygen2: do not edit by hand
+
+S3method(api_create,data.frame)
+S3method(api_create,ggplot)
+S3method(api_create,plotly)
+S3method(arrange_,plotly)
+S3method(distinct_,plotly)
+S3method(do_,plotly)
+S3method(embed_notebook,plotly)
+S3method(filter_,plotly)
+S3method(fortify,SharedData)
+S3method(geom2trace,GeomBar)
+S3method(geom2trace,GeomBlank)
+S3method(geom2trace,GeomBoxplot)
+S3method(geom2trace,GeomErrorbar)
+S3method(geom2trace,GeomErrorbarh)
+S3method(geom2trace,GeomPath)
+S3method(geom2trace,GeomPoint)
+S3method(geom2trace,GeomPolygon)
+S3method(geom2trace,GeomText)
+S3method(geom2trace,GeomTile)
+S3method(geom2trace,default)
+S3method(get_l,LINESTRING)
+S3method(get_l,MULTILINESTRING)
+S3method(get_l,MULTIPOINT)
+S3method(get_l,MULTIPOLYGON)
+S3method(get_l,POINT)
+S3method(get_l,POLYGON)
+S3method(get_x,LINESTRING)
+S3method(get_x,MULTILINESTRING)
+S3method(get_x,MULTIPOINT)
+S3method(get_x,MULTIPOLYGON)
+S3method(get_x,POINT)
+S3method(get_x,POLYGON)
+S3method(get_y,LINESTRING)
+S3method(get_y,MULTILINESTRING)
+S3method(get_y,MULTIPOINT)
+S3method(get_y,MULTIPOLYGON)
+S3method(get_y,POINT)
+S3method(get_y,POLYGON)
+S3method(ggplot,plotly)
+S3method(ggplotly,ggmatrix)
+S3method(ggplotly,ggplot)
+S3method(ggplotly,plotly)
+S3method(group_by_,plotly)
+S3method(groups,plotly)
+S3method(layout,matrix)
+S3method(layout,plotly)
+S3method(layout,shiny.tag.list)
+S3method(mutate_,plotly)
+S3method(plotly_build,gg)
+S3method(plotly_build,list)
+S3method(plotly_build,plotly)
+S3method(print,api)
+S3method(print,api_grid)
+S3method(print,api_grid_local)
+S3method(print,api_plot)
+S3method(rename_,plotly)
+S3method(select_,plotly)
+S3method(slice_,plotly)
+S3method(summarise_,plotly)
+S3method(to_basic,GeomAbline)
+S3method(to_basic,GeomAnnotationMap)
+S3method(to_basic,GeomArea)
+S3method(to_basic,GeomBoxplot)
+S3method(to_basic,GeomCol)
+S3method(to_basic,GeomContour)
+S3method(to_basic,GeomCrossbar)
+S3method(to_basic,GeomDensity)
+S3method(to_basic,GeomDensity2d)
+S3method(to_basic,GeomDotplot)
+S3method(to_basic,GeomErrorbar)
+S3method(to_basic,GeomErrorbarh)
+S3method(to_basic,GeomHex)
+S3method(to_basic,GeomHline)
+S3method(to_basic,GeomJitter)
+S3method(to_basic,GeomLine)
+S3method(to_basic,GeomLinerange)
+S3method(to_basic,GeomMap)
+S3method(to_basic,GeomPointrange)
+S3method(to_basic,GeomQuantile)
+S3method(to_basic,GeomRaster)
+S3method(to_basic,GeomRasterAnn)
+S3method(to_basic,GeomRect)
+S3method(to_basic,GeomRibbon)
+S3method(to_basic,GeomRug)
+S3method(to_basic,GeomSegment)
+S3method(to_basic,GeomSf)
+S3method(to_basic,GeomSmooth)
+S3method(to_basic,GeomSpoke)
+S3method(to_basic,GeomStep)
+S3method(to_basic,GeomTile)
+S3method(to_basic,GeomViolin)
+S3method(to_basic,GeomVline)
+S3method(to_basic,default)
+S3method(transmute_,plotly)
+S3method(ungroup,plotly)
+export("%>%")
+export(add_annotations)
+export(add_area)
+export(add_bars)
+export(add_boxplot)
+export(add_choropleth)
+export(add_contour)
+export(add_data)
+export(add_fun)
+export(add_heatmap)
+export(add_histogram)
+export(add_histogram2d)
+export(add_histogram2dcontour)
+export(add_lines)
+export(add_markers)
+export(add_mesh)
+export(add_paths)
+export(add_pie)
+export(add_polygons)
+export(add_ribbons)
+export(add_scattergeo)
+export(add_segments)
+export(add_surface)
+export(add_text)
+export(add_trace)
+export(animation_button)
+export(animation_opts)
+export(animation_slider)
+export(api)
+export(api_create)
+export(api_download_grid)
+export(api_download_plot)
+export(arrange)
+export(arrange_)
+export(as.widget)
+export(as_widget)
+export(attrs_selected)
+export(colorbar)
+export(config)
+export(distinct)
+export(distinct_)
+export(do)
+export(do_)
+export(embed_notebook)
+export(event_data)
+export(export)
+export(filter)
+export(filter_)
+export(geom2trace)
+export(get_figure)
+export(get_l)
+export(get_x)
+export(get_y)
+export(gg2list)
+export(ggplotly)
+export(group2NA)
+export(group_by)
+export(group_by_)
+export(groups)
+export(hide_colorbar)
+export(hide_guides)
+export(hide_legend)
+export(highlight)
+export(knit_print.api_grid)
+export(knit_print.api_grid_local)
+export(knit_print.api_plot)
+export(last_plot)
+export(layout)
+export(mutate)
+export(mutate_)
+export(offline)
+export(plot_dendro)
+export(plot_geo)
+export(plot_ly)
+export(plot_mapbox)
+export(plotly)
+export(plotlyOutput)
+export(plotlyProxy)
+export(plotlyProxyInvoke)
+export(plotly_IMAGE)
+export(plotly_POST)
+export(plotly_build)
+export(plotly_data)
+export(plotly_empty)
+export(plotly_example)
+export(plotly_json)
+export(rangeslider)
+export(raster2uri)
+export(remove_typedarray_polyfill)
+export(rename)
+export(rename_)
+export(renderPlotly)
+export(schema)
+export(select)
+export(select_)
+export(showRGB)
+export(signup)
+export(slice)
+export(slice_)
+export(style)
+export(subplot)
+export(summarise)
+export(summarise_)
+export(toRGB)
+export(toWebGL)
+export(to_basic)
+export(transmute)
+export(transmute_)
+export(ungroup)
+import(ggplot2)
+importFrom(data.table,as.data.table)
+importFrom(data.table,setorderv)
+importFrom(dplyr,arrange)
+importFrom(dplyr,arrange_)
+importFrom(dplyr,distinct)
+importFrom(dplyr,distinct_)
+importFrom(dplyr,do)
+importFrom(dplyr,do_)
+importFrom(dplyr,filter)
+importFrom(dplyr,filter_)
+importFrom(dplyr,group_by)
+importFrom(dplyr,group_by_)
+importFrom(dplyr,groups)
+importFrom(dplyr,mutate)
+importFrom(dplyr,mutate_)
+importFrom(dplyr,rename)
+importFrom(dplyr,rename_)
+importFrom(dplyr,select)
+importFrom(dplyr,select_)
+importFrom(dplyr,slice)
+importFrom(dplyr,slice_)
+importFrom(dplyr,summarise)
+importFrom(dplyr,summarise_)
+importFrom(dplyr,transmute)
+importFrom(dplyr,transmute_)
+importFrom(dplyr,ungroup)
+importFrom(grDevices,as.raster)
+importFrom(grDevices,col2rgb)
+importFrom(grDevices,dev.list)
+importFrom(grDevices,dev.off)
+importFrom(grDevices,extendrange)
+importFrom(grDevices,rgb)
+importFrom(graphics,layout)
+importFrom(htmltools,browsable)
+importFrom(htmltools,tagList)
+importFrom(htmltools,tags)
+importFrom(htmlwidgets,createWidget)
+importFrom(htmlwidgets,onRender)
+importFrom(htmlwidgets,prependContent)
+importFrom(htmlwidgets,saveWidget)
+importFrom(htmlwidgets,shinyRenderWidget)
+importFrom(htmlwidgets,shinyWidgetOutput)
+importFrom(htmlwidgets,sizingPolicy)
+importFrom(httr,GET)
+importFrom(httr,PATCH)
+importFrom(httr,POST)
+importFrom(httr,add_headers)
+importFrom(httr,config)
+importFrom(httr,content)
+importFrom(httr,stop_for_status)
+importFrom(jsonlite,fromJSON)
+importFrom(jsonlite,toJSON)
+importFrom(lazyeval,all_dots)
+importFrom(lazyeval,f_eval)
+importFrom(lazyeval,f_new)
+importFrom(lazyeval,is_formula)
+importFrom(lazyeval,is_lang)
+importFrom(magrittr,"%>%")
+importFrom(purrr,transpose)
+importFrom(stats,complete.cases)
+importFrom(stats,is.leaf)
+importFrom(stats,quantile)
+importFrom(stats,setNames)
+importFrom(tibble,as_tibble)
+importFrom(tidyr,unnest)
+importFrom(tools,file_ext)
+importFrom(tools,file_path_sans_ext)
+importFrom(utils,browseURL)
+importFrom(utils,data)
+importFrom(utils,getFromNamespace)
+importFrom(utils,modifyList)
+importFrom(utils,packageVersion)
+importFrom(utils,str)
+importFrom(viridisLite,viridis)
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..0880341
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,1045 @@
+# 4.7.1
+
+## NEW FEATURES & IMPROVEMENTS
+
+* It is now possible to modify (i.e., update without a full redraw) plotly graphs inside of a shiny app via the new `plotlyProxy()` and `plotlyProxyInvoke()` functions. For examples, see `plotly_example("shiny", "proxy_relayout")` and `plotly_example("shiny", "proxy_mapbox")`. Closes #580.
+* Added a new `plotly_example()` function to make it easier to run shiny/rmarkdown examples included with the package under the `inst/examples` directory.
+* The `schema()` function now returns the plot schema (rather just printing it), making it easier to acquire/use values from the official plot schema. See `help(schema)` for an example. Fixes #1038.
+
+## CHANGES
+
+* Upgraded to plotly.js v1.29.2 -- https://github.com/plotly/plotly.js/releases/tag/v1.29.2
+
+## BUG FIXES
+
+* The default sizing in `ggplotly()` is no longer fixed to the device size inside RStudio. Fixes #1033.
+* Removed use of `ArrayBuffer.isView()`, which should fix rendering issues on plaforms that don't have a typed array polyfill (e.g., RStudio on Windows). Fixes #1055.
+* `event_data("plotly_relayout")` no longer fires `NULL` for any event. Fixes #1039.
+* Fixed a bug when using `color` with scattermapbox/scattergeo. Fixes #1038.
+* Fixed a highlighting bug when brushing multiple points with `marker.color` as an array. Fixes #1084.
+
+
+# 4.7.0
+
+## NEW FEATURES & IMPROVEMENTS
+
+* Added support for fixed coordinates (i.e., the aspect ratio component of `coord_equal()`, `coord_fixed()`, `coord_map()`, `coord_quickmap()`).
+* Added support for `geom_sf()` and `coord_sf()`.
+* The (previously internal) `group2NA()` function is now exported and its performance has been greatly improved thanks to the new **data.table** dependency. Essentially any geom conversion that reduces to a polygon/path should see speed improvements. Similarly, any `plot_ly()` graph that use `group_by()` in conjunction with `add_lines()`, `add_paths()`, `add_segments()`, etc will also see improvements, especially when there is a large number of groups. For details on the speed improvemen [...]
+* The `api_create()` function gains a new `fileopt` argument, which is inspired from the `fileopt` argument in the (deprecated) `plotly_POST()` function (fixes #976). It currently supports to values: `"new"` and `"overwrite"`. The default, `"overwrite"`, will overwrite existing file(s) with a matching `filename`.
+* The `filename` argument in `api_create()` now accepts a character vector of length 2, the first string is used to name the plot, and the second is used to name the grid (i.e., data behind the plot).
+
+## CHANGES
+
+* Upgraded to plotly.js v1.27.1 -- https://github.com/plotly/plotly.js/releases/tag/v1.27.1
+* The `traces` argument in the `style()` function now defaults to `NULL` (instead of 1). Meaning that, by default, supplied attributes now modify _every_ trace (instead of the first one).
+
+## Bug fixes
+
+* Fixes numerous problems with `coord_flip()` (fixes #1012).
+* The typed array polyfill is now included *before* the plotly.js bundle, which should fix some rendering issues in some browsers, including RStudio (fixes #1010).
+* When creating private plots (via `api_create()`), both the plot and the data behind the plot are private (fixes #976).
+* Creating a plot with multiple traces (or frames) via (via `api_create()`) no longer creates multiple grids (fixes #1004).
+* The `api_create()` function should now create grid references for all data array attributes (fixes #1014).
+* `ggplotly()` no longer opens an (off-screen) graphics device in RStudio for sizing. It will now correctly use the size of the viewer panel when querying the size of the graphics device.
+* Margins are no longer always set to `NULL` for pie charts (fixes #1002)
+* Fixed bug when highlight multiple 'simple key' traces (fixes #974).
+
+# 4.6.0
+
+## NEW FEATURES & IMPROVEMENTS
+
+* Added a significant amount of support for "multiple linked views". For some relatively basic examples, see the demos (the ones prefixed with "highlight" are most relevant) -- `demo(package = "plotly")`. For a more comprehensive overview, see <https://cpsievert.github.io/plotly_book/linking-views-without-shiny.html>. For some more complex examples, see <https://cpsievert.github.io/pedestrians/>
+* Added the `highlight()` function for configuring selection modes/sequences/options.
+* Added support for animation. For some relatively basic examples, see the examples section of `help(animation)`. For a more thorough overview, see <https://cpsievert.github.io/plotly_book/key-frame-animations.html>
+* Added a `frame` argument to `plot_ly()` for creating animations. Also added the `animation_opts()`, `animation_slider()`, and `animation_button()` functions for configuring animation defaults.
+* Added a new interface to [v2 of the REST API](https://api.plot.ly/v2). This new interface makes the `plotly_POST()` and `get_figure()` functions obsolete (use `api_create()` and `api_download_plot()` instead), and thus, are now deprecated, but remain around for backwards-compatibility. For more details, see `help(api)`.
+* Added support for conversion of more **ggplot2** geoms via `ggplotly()`: `GeomCol`, `GeomRug`, `GeomCrossbar`, `GeomQuantile`, `GeomSpoke`, `GeomDotplot`, `GeomRasterAnn` (i.e., `annotation_raster()`), and `GeomAnnotationMap` (i.e., `annotation_map()`).
+* Added a new function `raster2uri()` which makes it easier to embed raster objects as [images](https://plot.ly/r/reference/#layout-images) via data URIs. For examples, see `help(raster2uri)`.
+* `ggplotly()` gains a new argument, `dynamicTicks`, which allows axis ticks to update upon zoom/pan interactions (fixes #485).
+* Sensible sizing and positioning defaults are now provided for subplots multiple colorbars.
+* R linebreaks are translated to HTML linebreaks (i.e., '\n' translates to '<br />') (fixes #851).
+* Added a `plot_dendro()` function for a quick and dirty interactive dendrogram with support for hierarchial selection. For more, see -- <https://cpsievert.github.io/plotly_book/linking-views-without-shiny.html#nested-selections>
+* The `export()` function gains a `selenium` argument for rendering/exporting WebGL plots and exporting to 'svg'/'webp' file formats (via the plotly.js function [Plotly.downloadImage()](https://plot.ly/javascript/plotlyjs-function-reference/#plotlydownloadimage)).
+* Better type checking of trace attributes will now automatically reduce a single-valued vector to a constant (when appropriate). This is particularly useful for anchoring multiple traces to a single legend entry via `legendgroup` (see #675, #817, #826).
+* The `plotlyOutput()` function gains a `inline` argument which makes it easier to place multiple plots in the same row (in a shiny application).
+
+## CHANGES
+
+* Upgraded to plotly.js v1.26.1 -- https://github.com/plotly/plotly.js/releases/tag/v1.26.1
+* `ggplotly()` now applies `format()` to automatically generated hoverinfo. This will allow for finer control over the text displayed (for instance, `options(digits = 4)` can now be used to choose the number of significant digits used). See #834 for an example.
+* `HTMLwidgets.renderValue()` should now avoid creating too many active WebGL contexts (thanks @AleksandrIanevski).
+* A TypedArray polyfill is now included by default, and the function `remove_typedarray_polyfill()` was added to make it easy to remove it. Fixes #824, #717, #825.
+* If no graphics device is already open, `ggplotly()` now tries to open/close a Cairo graphics device, then a bitmap (png/jpeg) device. If neither is available, it errors. This helps to ensure that a *screen* device is never opened by `ggplotly()` (which fixes #829). Furthermore, if `width`/`height` is not specified *and* no graphics device is currently open, a default of 640/480 is used for width/height of the device.
+
+## BUG FIXES
+
+
+* Placement of bars (in all cases, even when representing a negative count) should now be correct (applies to `geom_bar()`, `geom_histogram()`, `geom_col()`). Fixes #560, #874, #901, #831.
+* Fix for hoverinfo displaying the heights of bars in the translation `geom_bar()` via `ggplotly()`. Fixes #557 and #662.
+* `embed_notebook()` now works in *nteract* notebooks (see #768).
+* Axis categories are no longer reordered for matrices (see #863).
+* Fix for hoverinfo displaying values after scale transformations (in `ggplotly()`). Fixes #804.
+* Font faces for axis titles are now translated (in `ggplotly()`). Fixes #861.
+
+# 4.5.6
+
+## NEW FEATURES
+
+* Added support for the `timezone` argument in __ggplot2__'s `scale_datetime()`. Fixes (#743, thanks @earowang).
+
+## CHANGES
+
+* Now requires a version of __ggplot2__ higher than 2.1.0 because the new ggproto faceting infrastructure introduced breaking changes.
+* A book icon is added to the mode bar, by default, which links to the plotly book. If you want to remove this icon from a plot `p`, do `config(p, modeBarButtonsToRemove = "Collaborate")`
+* Specifying height/width in `layout()` is now deprecated. Specify in `ggplotly()` or `plot_ly()`.
+* The `ggplotly()` function now preserves all information about the layer mapping. This makes it possible to access input/output data from any layer.
+
+## BUG FIXES
+
+* HTMLwidget.resize() used to ignore specified `layout.width`/`layout.height`.
+* When `height`/`width` are specified in `ggplotly()`, relative sizes are now translated correctly. Fixes #489 and #510.
+* More careful handling of font when expanding annotation arrays. Fixes #738.
+* Ignore data arrays of non-tidy traces. Fixes #737.
+* When using `ggplotly()` on a plot with `geom_line` and `group` aesthetic wrong tooltip information was shown. Fixes #774.
+
+# 4.5.5 -- 28 September 2016
+
+## NEW FEATURES
+
+* histogram2d/histogram2dcontour traces now respect the `colors` argument.
+
+## BUG FIX
+
+* Don't traceify by non-existant levels, fixes #735.
+
+# 4.5.4 -- 27 September 2016
+
+## BUG FIX
+
+* Only insert missing values to differentiate groups when it's relevant.
+
+# 4.5.3 -- 27 September 2016
+
+## NEW FEATURES
+
+* The `colorbar()` function gains a new `limits` arguments for controlling the colorscale
+limits.
+
+## BUG FIX
+
+* The `z` is now required in `add_heatmap()`. If you want a `z` to be computed, use `add_histogram()`.
+
+# 4.5.2 -- 23 September 2016
+
+## NEW FEATURES
+
+* The new argument, `split`, replaces the old functionality of the now deprecated `group` argument by creating one trace per value.
+
+## BUG FIXES
+
+* Passing plots to `subplot()` without a specified color (once again) match the coloring defaults supplied by plotly.js (see #724).
+
+# 4.5.1 -- 23 September 2016
+
+## NEW FEATURES
+
+* A tibble with a list-column of plotly objects can now be input directly into `subplot()`
+
+## BUG FIXES
+
+* The `colorbar()` function now works on colorbars generated via `z` mapping.
+
+# 4.5.0 -- 22 September 2016
+
+## NEW FEATURES
+
+* Added the `plot_mapbox()` and `plot_geo()` functions, which make it easier to work with the "scattermapbox", "scattergeo", and "choropleth" trace types. See the maps chapter of the plotly book for some examples -- <https://cpsievert.github.io/plotly_book/maps.html>
+* `subplot()` now accepts, and correctly scales mapbox objects.
+* Added the `add_mesh3d()` and `add_pie()` functions as wrappers around the "mesh3d", and "pie" trace types.
+
+## CHANGES
+
+* The `add_scattergeo()` and `add_choropleth()` functions have been deprecated in favor of `plot_geo()`.
+* The `add_area(...)` function changed it's meaning from `add_lines(..., fill = 'tozeroy')` to a wrapper around the area trace <https://plot.ly/r/reference/#area>. This is more consistent with the naming conventions already in place for other `add_()` functions.
+* `add_ribbons()` now shows points (instead of fill) on hover.
+
+# 4.4.5 -- 19 September 2016
+
+## NEW FEATURES
+
+* Added a `rangeslider()` function to make it easier to add a range slider to the x-axis.
+* Added a `colorbar()` function to make it easier to modify an automatically generated colorbar.
+
+## BUG FIXES
+
+* Bug fix for data arranging (introduced in 4.4.2).
+* If the same (discrete) variable is mapped to two different aesthetics, redundant text is no longer generated in the legend entries (e.g., `plot_ly(mpg, x = ~cty, y = ~hwy, symbol = ~factor(cyl), color = ~factor(cyl))`)
+
+# 4.4.4 -- 15 September 2016
+
+## NEW FEATURES
+
+* Added `inherit` argument for all `add_()` functions to avoid inheriting attributes from `plot_ly()`.
+* Added the `add_fun()` function to add layers to a plot without modifying the original data associated with a plotly object.
+* Added the `add_annotations()` function to make it easier to add annotations.
+* Added the `layerData` argument to `ggplotly()` to make it possible to retrieve the data from a particular __ggplot2__ layer.
+
+# 4.4.3 -- 15 September 2016
+
+## CHANGES
+
+* Downgrade to plotly.js v1.16.3 (which is proven to be a stable release and avoids #717) -- https://github.com/plotly/plotly.js/releases/tag/v1.17.2
+
+# 4.4.2 -- 14 September 2016
+
+## BUG FIXES
+
+* Arrange data by trace index _before_ computing groups, fixes #716.
+
+# 4.4.1 -- 14 September 2016
+
+## BUG FIXES
+
+* Restrict to atomic vectors when gathering data for training; otherwise, formulas referencing variables that don't exist in the data, but reference a function can cause problems.
+
+# 4.4.0 -- 13 September 2016
+
+## CHANGES
+
+* To enhance visibility of small markers, `marker.line.color` is now transparent by default.
+* Upgraded to plotly.js v1.17.2 -- https://github.com/plotly/plotly.js/releases/tag/v1.17.2
+
+## BUG FIXES
+
+* It is now possible (again) to set/change attributes of autogenerated `marker.colorbar`.
+* The `add_choropleth()` previously wasn't relaying the `z` attribute.
+* Factors were being treated as characters in `add_segments()` (resulting in incorrect axis category order).
+* No more error in `plot_ly()` when the number of traces is a multiple of ten.
+
+# 4.3.7 -- 11 September 2016
+
+## BUG FIXES
+
+* `event_data()` now works inside shiny modules (#659). For an example, see <https://github.com/ropensci/plotly/tree/master/inst/examples/shiny/event_data_modules>
+
+# 4.3.6 -- 9 September 2016
+
+## CHANGES
+
+* Upgraded to plotly.js v1.17.0 -- https://github.com/plotly/plotly.js/releases/tag/v1.17.0
+
+## BUG FIXES
+
+* Fix for error handling in `add_bars()`.
+* More careful logic for inferring data variables. Fixes #712
+
+# 4.3.5 -- 5 September 2016
+
+## NEW FEATURES
+
+* The internal `as_widget()` function was exported to make it easier to convert a list
+(adhering to the plotly spec) to a plotly htmlwidget object. This should only be needed when "manually" editing the underlying list object.
+* Warnings about missing attributes now supply information about the relevant trace.
+
+## CHANGES
+
+* vignettes were removed and that documentation will now be hosted at <https://cpsievert.github.io/plotly_book/>
+
+## BUG FIXES
+
+* Get event_data() working with subplots. Fixes #663
+
+# 4.3.4 -- 31 August 2016
+
+## CHANGES
+
+* Expressions yielding a ggplot2 object can now, once again, be provided to `plotlyOutput()`. In order to make this possible, `ggplotly()` now has a method for plotly objects (the identity function), and `ggplotly()` called on any expression provided to `plotlyOutput()`.
+
+# 4.3.3 -- 29 August 2016
+
+## BUG FIXES
+
+* Bug fix for translation of ggplot2's `geom_text()`.
+
+# 4.3.2 -- 26 August 2016
+
+## NEW FEATURES
+
+* The function `last_plot()` can now be used to retrieve the most recently _printed_ plotly graph. Thanks to this new feature, when `plotly_POST()` is called with no plotly object supplied, the most recently _printed_ plotly graph is sent to the users plotly account.
+
+# 4.3.1 -- 23 August 2016
+
+## CHANGES
+
+* Upgraded to plotly.js v1.16.3 -- https://github.com/plotly/plotly.js/releases/tag/v1.16.3
+
+# 4.3.0 -- 22 August 2016
+
+## NEW FEATURES
+
+* The `colors`/`symbols`/`linetypes` arguments now accept _named_ character vectors.
+The names specify the domain (i.e., data values) and the values specify the range
+(i.e., visual encodings). This is mainly useful to ensure a particular
+(discrete) data value is mapped to a particular visual attribute (yes, this is similar, in spirit, to ggplot2's `scale_*_manual()`).
+
+## CHANGES
+
+* Symbol and linetype palette defaults are now determined by `scales::shape_pal()` and `scales::linetype_pal()`.
+* viridis is the default color scale for ordered factors.
+* When mapping a character string to `color`/`symbol`/`linetype`, domain values are
+sorted alphabetically before scales are applied. Also, when mapping a factor to `color`/`symbol`/`linetype`, domain values are sorted according to their factor levels before scales are applied. This leads to more consistent (categorical axis ordering behaves similarly) and predictable (compared to having values sorted in the order in which they appear) behavior.
+
+## BUG FIXES
+
+# 4.2.1 -- 22 August 2016
+
+## BUGFIX
+
+* `alpha` is now applied when `color` isn't specified (fixes #658).
+
+# 4.2.0 -- 11 August 2016
+
+## CHANGES
+
+* `plot_ly()` now orders the categories of a discrete x/y axis according the level ordering (if a factor) or alphabetical order (if a character string). Fixes #578.
+
+# 4.1.1 -- 8 August 2016
+
+## CHANGES
+
+* Upgraded to plotly.js v1.16.1 -- https://github.com/plotly/plotly.js/releases/tag/v1.16.1
+
+# 4.1.0 -- 27 June 2016
+
+## NEW FEATURES
+
+* `ggplotly()` gains a new `originalData` argument which allows one to attach either the original (global) data, or a "scaled"/"trained" version of the data used by __ggplot2__ to draw the graph (for a quick example, `ggplotly(qplot(1:10), originalData = FALSE) %>% plotly_data()`).
+* Hoverinfo is now shown for fill, instead of points, for several geoms (`geom_polygon()`/`geom_hex()`/`geom_rect()`/`geom_map()`).
+* If `stat_identity()` is used, group domain values are preserved and displayed in hoverinfo.
+* New functions `hide_guides()`/`hide_legend()` were added (these work similarly to the existing `hide_colorbar()`) to simply the hiding of guides (i.e., legends/colorbars).
+
+## BUG FIXES
+
+* Legend titles (annotations) are no longer generated when no legend is displayed (#635, #607)
+* Hoverinfo is no longer displayed if no tooltip variables are present in a layer (#563).
+* Facets with 10 or more columns/rows should now render correctly (#640).
+* x-axis anchors in `facet_wrap()` should now be correct.
+
+## OTHER CHANGES
+
+* Upgraded to plotly.js v1.15.0 -- https://github.com/plotly/plotly.js/releases/tag/v1.15.0
+
+# 4.0.2 -- 25 June 2016
+
+## BUG FIXES
+
+* Bug fix for formulas evaluating to a logical vector (#650)
+
+# 4.0.1 -- 14 June 2016
+
+## BUG FIXES
+
+* Duplicated values of positional attributes are no longer removed (bug was introduced in v4.0.0).
+
+## OTHER CHANGES
+
+* Upgraded to plotly.js v1.14.2 -- https://github.com/plotly/plotly.js/releases/tag/v1.14.2
+
+# 4.0.0 -- 13 June 2016
+
+## BREAKING CHANGES & IMPROVEMENTS:
+
+* Formulas (instead of plain expressions) are now required when using variable mappings. For example, `plot_ly(mtcars, x = wt, y = mpg, color = vs)` should now be `plot_ly(mtcars, x = ~wt, y = ~mpg, color = ~vs)`. This is a major breaking change, but it is necessary to ensure that evaluation is correct in all contexts (as a result, `evaluate` argument is now deprecated as it is no longer needed). It also has the benefit of being easier to program with (i.e., writing your own custom funct [...]
+* The data structure used to represent plotly objects is now an htmlwidget object (instead of a data frame with a special attribute tracking visual mappings). As a result, the `as.widget()` function has deprecated, and [serialization/memory leak problems](https://github.com/rstudio/shiny/issues/1151) are no longer an issue. This change also implies that arbitrary data manipulation functions can no longer be intermingled inside a plot pipeline, but plotly methods for dplyr's data manipula [...]
+* The `group` variable mapping no longer create multiple traces, but instead defines "gaps" within a trace (fixes #418, #381, #577). Groupings should be declared via the new `group_by()` function (see `help(plotly_data)` for examples) instead of the `group` argument (which is now deprecated).
+* `plot_ly()` now _initializes_ a plotly object (i.e., won't add a scatter trace by default), meaning that something like `plot_ly(x = 1:10, y = 1:10) %>% add_trace(y = 10:1)` creates one trace, instead of two. That being said, if you manually specify a trace type in `plot_ly()`, it will add a layer with that trace type (e.g. `plot_ly(x = 1:10, y = 1:10, type = "scatter") %>% add_trace(y = 10:1)` draws two scatter traces). If no trace type is provided, a sensible type is inferred from th [...]
+* The `inherit` argument is deprecated. Any arguments/attributes specified in `plot_ly()` will automatically be passed along to additional traces added via `add_trace()` (or any of it's `add_*()` siblings).
+* Aesthetic scaling (e.g., `color`, `symbol`, `size`) is applied at the plot-level, instead of the trace level.
+* Size is no longer automatically included in hovertext (closes #549).
+
+## NEW FEATURES & IMPROVEMENTS:
+
+* Added `linetype`/`linetypes` arguments for mapping discrete variables to line types (works very much like the `symbol`/`symbols`).
+* Scaling for aesthetics can be avoided via `I()` (closes #428). This is mainly useful for changing default appearance (e.g. `plot_ly(x = 1:10, y = 1:10, color = I("red"))`).
+* Symbols and linetypes now recognize `pch` and `lty` values (e.g. `plot_ly(x = 1:25, y = 1:25, symbol = I(0:24))`)
+* A new `alpha` argument controls the alpha transparency of `color` (e.g. `plot_ly(x = 1:10, y = 1:10, color = I("red"), alpha = 0.1)`).
+* Added a `sizes` argument for controlling the range of marker size scaling.
+* New `add_polygons()`/`add_ribbons()`/`add_area()`/`add_segments()`/`add_lines()`/`add_markers()`/`add_paths()`/`add_text()` functions provide a shorthand for common special cases of `add_trace()`.
+* New `toWebGL()` function for easy conversion from SVG to WebGL.
+* New `export()` function makes it easy to save plots as png/jpeg/pdf (fixes #311).
+* Misspecified trace/layout attributes produce a warning.
+* New `plotly_data()` function for returning/inspecting data frame(s) associated with a plotly object.
+* New `plotly_json()` function for inspecting the data sent to plotly.js (as an R list or JSON).
+* `layout()` is now a generic function and uses method dispatch to avoid conflicts with `graphics::layout()` (fixes #464).
+
+## OTHER CHANGES:
+
+* Upgraded to plotly.js v1.14.1 -- https://github.com/plotly/plotly.js/releases/tag/v1.14.1
+
+3.6.5 -- 10 June 2016
+
+IMPROVEMENT:
+
+Multiple rows of facet strips will now be separated by <br> (i.e., line breaks) instead of ,. See #593.
+
+3.6.4 -- 31 May 2016
+
+BUG FIX:
+
+embed_notebook() will no longer use a '.embed' extension in the iframe src attribute. See #613.
+
+3.6.3 -- 24 May 2016
+
+CHANGES:
+
+Provided a better way of reexporting magrittr::`%>%`. See #597.
+
+3.6.2 -- 24 May 2016
+
+CHANGES:
+
+Removed unnecessary plyr dependency.
+
+3.6.1 -- 23 May 2016
+
+BUG FIX:
+
+Add a default method for plotly_build. Fixes #592.
+
+3.6.0 -- 16 May 2016
+
+NEW FEATURES & CHANGES:
+
+* Many improvements to the subplot() function:
+ * ggplot2 objects are now officially supported (#520).
+ * Several new arguments allow one to synchronize x/y axes (#298), height/width (#376), hide/show x/y axis titles.
+ * A list of plots can now be passed to the first argument.
+ * A new vignette with examples and more explanation can be accessed via `vignette("subplot")`.
+
+* ggplotly() is now a generic function with a method for ggmatrix objects.
+* plotly_build() is now a generic function.
+
+BUG FIX:
+
+Column facet strips will no longer be drawn when there is only one column.
+
+3.5.7 -- 13 May 2016
+
+CHANGES:
+
+Better defaults for defaultWidth/defaultHeight in the htmlwidget's sizing policy.
+
+BUG FIX:
+
+Pass knitr options to the named argument options. Fixes #582.
+
+3.5.6 -- 12 May 2016
+
+BUG FIX:
+
+Use .embed suffix in iframe src attribute. Fixes #581.
+
+3.5.5 -- 5 May 2016
+
+CHANGES:
+
+ggplotly() will now use plotly's layout.axisid.title (instead of
+layout.annotations) for axis titles on non-faceted plots.
+This will make for a better title placement experience (see #510).
+
+BUG FIX:
+
+Space for interior facet_wrap() strips are now accounted for.
+
+3.5.4 -- 5 May 2016
+
+BUG FIX:
+
+gg2list() now returns an object of class "plotly_built" instead of "plotly"
+to ensure a sensible print method is invoked.
+
+3.5.3 -- 3 May 2016
+
+CHANGES:
+
+Upgrade to plotlyjs v1.10.1 -- https://github.com/plotly/plotly.js/releases/tag/v1.10.1
+
+3.5.2 -- 2 May 2016
+
+BUG FIX:
+
+Added missing key properties in ggplotly() converter so selections can be accessible via event_data().
+
+3.5.1 -- 26 Apr 2016
+
+CHANGES:
+
+Upgrade to plotlyjs v1.10.0 -- https://github.com/plotly/plotly.js/releases/tag/v1.10.0
+
+Distinguish between "built" (plotly_built) and "non-built" (plotly_hash) plotly objects. See #562
+
+
+3.5.0 -- 19 Apr 2016
+
+NEW FEATURES:
+
+The toRGB() function will now respect alpha channels in hex color codes and can recursively apply alpha.
+
+CHANGES:
+
+The toRGB() function will always output color codes with an alpha channel (e.g. toRGB('black') is now 'rgba(0,0,0,1)' instead of 'rgb(0,0,0)')
+
+3.4.15 -- 18 Apr 2016
+
+BUGFIX:
+
+The alpha in geom_smooth was incorrectly inheriting from other layers. See #551.
+
+3.4.14 -- 15 Apr 2016
+
+CHANGES:
+
+Upgrade to plotlyjs v1.9.0 -- https://github.com/plotly/plotly.js/releases/tag/v1.9.0
+
+3.4.13 -- 6 Apr 2016
+
+BUGFIX:
+
+In some cases, marker color was inheriting from the marker line color when
+it shouldn't have. See ##537.
+
+3.4.12 -- 5 Apr 2016
+
+CHANGES:
+
+Upgrade to plotlyjs v1.8.0 -- https://github.com/plotly/plotly.js/releases/tag/v1.8.0
+
+3.4.11 -- 2 Apr 2016
+
+BUGFIX:
+
+Fix bug when altering modebar button defaults
+
+3.4.10 -- 1 Apr 2016
+
+BUGFIX:
+
+Fix a geom_errorbar bug introduced in 3.4.9. See #513.
+
+3.4.9 -- 25 Mar 2016
+
+BUGFIX:
+
+Upgrade to plotlyjs 1.7.0. Fixes #513
+
+3.4.8 -- 23 Mar 2016
+
+BUGFIX:
+
+* Safeguard against null fields in selections. See #530.
+
+3.4.7 -- 19 Mar 2016
+
+BUGFIX:
+
+* Added custom CSS which allows plotly to work nicely in ioslides.
+
+3.4.6 -- 17 Mar 2016
+
+NEW FEATURES:
+
+The 'plotly_relayout' event is now accessible via the event_data() function.
+
+Fixed #514.
+
+3.4.5 -- 17 Mar 2016
+
+BUGFIX:
+
+Fixed #514.
+
+3.4.4 -- 17 Mar 2016
+
+BUGFIX:
+
+Show discrete positional values in tooltip (see #515); better GeomTile conversion; pass plot object into layers2traces.
+
+3.4.3 -- 14 Mar 2016
+
+BUGFIX:
+
+Custom facet labeller functions will now translate correctly. See #507.
+
+3.4.2 -- 14 Mar 2016
+
+BUGFIX:
+
+Automatic resizing will now occur only when layout.autosize is true (the default). See #403.
+
+3.4.1 -- 13 Mar 2016
+
+BUGFIX:
+
+Legend titles are now supported.
+
+3.4.0 -- 12 Mar 2016
+
+NEW FEATURES:
+
+* geom_map() and geom_hex() are now supported.
+
+CHANGES:
+
+* The default value of the fileopt argument was changed from "new" to "overwrite".
+
+BUGFIX:
+
+* Made a number of bugfixes/improvements to hoverinfo & conversion of geom_tile()/geom_point().
+
+3.3.1 -- 10 Mar 2016
+
+CHANGES:
+
+* Changed the mapping argument name to tooltip (which seems like a better name).
+
+BUGFIX:
+
+* Redundant legend entries are no longer shown.
+
+3.2.1 -- 10 Mar 2016
+
+BUGFIX:
+
+* Proper formatting for date tooltips.
+
+3.2.0 -- 10 Mar 2016
+
+CHANGES:
+
+* Legend titles no longer appear in legend entries.
+* Tooltips now reflect aesthetic mappings. This makes it easier to decode
+data values from a given visual marking.
+
+NEW FEATURES:
+
+* geom_violin() is now supported.
+* ggplotly() gains a mapping argument to control the set of aesthetics to appears in the tooltip as well as their order.
+
+3.1.0 -- 8 Mar 2016
+
+CHANGES:
+
+* The "hidden" sharing option in plotly_POST() was renamed to "secret".
+* The default value in the scale argument in plotly_IMAGE() is now 1.
+
+3.0.0 -- 8 Mar 2016
+
+NEW FEATURES:
+
+* ggplotly() is now about 20x faster (it avoids calling ggplot_build() 20+ times). In some cases, it might be even faster since a lot of other redundant computation is avoided.
+
+CHANGES:
+
+* Instead of (trying to) translate both major and minor grid lines, we now translate only major grid lines. This generally produces a result closer to the actual ggplot2 result since ggplot2 doesn't draw ticks on minor grid lines.
+
+BUG FIXES:
+
+* ggplotly() now supports most of scale_*()/theme()/guides(). As a result, this fixes a lot of issues (#482, #481, #479, #476, #473, #460, #456, #454, #453, #447, #443, #434, #422, #421, #399, #379, #378, #357, #318, #316, #242, #232, #211, #203, #185, #184, #161). In order to support all of scale_x_*() an scale_y_*(), we always use linear axis types, and supply ticktext/tickvals to plotly.js. This has some unfortunate consequences on hoverformatting, which may be addressed in future rel [...]
+
+2.5.0 -- 1 Mar 2016
+
+NEW FEATURES
+
+* New event_data() function provides easy access to plotly events in shiny.
+For an example, see https://github.com/ropensci/plotly/tree/master/inst/examples/plotlyEvents
+
+* plot_ly() and ggplotly() gain a source argument to differentiate between
+plotly events in shiny apps with multiple plots. ggplotly() also gains width
+and height arguments.
+
+CHANGES
+
+The arguments filename, fileopt, world_readable in ggplotly() were removed as
+they should be provided to plotly_POST() instead.
+
+2.4.4 -- 13 Feb 2016
+
+as.widget() now returns htmlwidget objects untouched. See #449.
+
+2.4.3 -- 11 Feb 2016
+
+Ensure that we always return HTTPS links. Fixes #455
+
+2.4.2 -- 9 Feb 2016
+
+Fix for on-premise domain configuration.
+
+2.4.1 -- 2 Feb 2016
+
+Attach base_url in as.widget() so it works in multiple contexts
+
+2.4.0 -- 1 Feb 2016
+
+* Pass plot configuration using ... to avoid conflicts in defaults/documentation
+* Upgrade to plotly.js 1.5.1
+
+2.3.4 -- 1 Feb 2016
+
+Added a plotly_api_domain environment variable for configuring the API domain. Fixes #441
+
+2.3.3 -- 27 Jan 2016
+
+Bump axis number for each trace matching a panel number. fixes #318
+
+2.3.2 -- 25 Jan 2016
+
+More accurate list of data_array properties. Fixes #415
+
+2.3.1 -- 25 Jan 2016
+
+More accurate conversion of path width. Fixes #373.
+
+2.3.0 -- 19 Jan 2016
+
+Add sharing argument and deprecate world_readable. Fixes #332
+
+2.2.4 -- 18 Jan 2016
+
+Fix for error in embed_notebook(). See #409.
+
+2.2.3 -- 18 Jan 2016
+
+Fix for geom_vline(). See #402.
+
+2.2.2 -- 18 Jan 2016
+
+Fix bar orientation when we detect geom_bar() + coord_flip() in ggplotly(). Fixes #390.
+
+2.2.1 -- 18 Jan 2016
+
+Search for axis title in scene object. fixes #393.
+
+2.2.0 -- 13 Jan 2016
+
+The default for layout.hovermode is now 'closest' for non-line scatter traces
+
+2.1.3 -- 12 Jan 2016
+
+Fix size and alpha translation for geom_point. Fixes #386
+
+2.1.2 -- 11 Jan 2016
+
+Upgraded to plotlyjs 1.4.1. For a list of changes, see https://github.com/plotly/plotly.js/releases/tag/v1.4.1
+
+2.1.1 -- 11 Jan 2016
+
+Upgraded to plotlyjs 1.4. For a list of changes, see https://github.com/plotly/plotly.js/releases/tag/v1.4.0
+
+2.1.0 -- 29 Dec 2015
+
+plot_ly() now defaults to inherit=FALSE and plotly_build() is now idempotent. Fixes #280 and #277. See #368 for details.
+
+2.0.19 -- 23 Dec 2015
+
+Added as.widget() function for conveniency in converting plotly object to htmlwidget objects. See #294.
+
+2.0.18 -- 22 Dec 2015
+
+Fix #365
+
+2.0.17 -- 22 Dec 2015
+
+Fix #358
+
+2.0.16 -- 18 Dec 2015
+
+Require ggplot2 2.0.0 or higher. For details, see #269.
+
+2.0.15 -- 13 Dec 2015
+
+Fix #346
+
+2.0.14 -- 13 Dec 2015
+
+Fix #212
+
+2.0.13 -- 12 Dec 2015
+
+Fix #286
+
+2.0.12 -- 11 Dec 2015
+
+Fix #221
+
+2.0.11 -- 11 Dec 2015
+
+Fix #250
+
+2.0.10 -- 10 Dec 2015
+
+Fix #225
+
+2.0.9 -- 10 Dec 2015
+
+Fix #333
+
+2.0.8 -- 10 Dec 2015
+
+Fix a bug with geom_segment (see #321 & #228)
+
+2.0.7 -- 10 Dec 2015
+
+Fix #233
+
+2.0.6 -- 2 Dec 2015
+
+Upgrade to plotlyjs 1.1.1. Fixes #319.
+
+2.0.5 -- 1 Dec 2015
+
+Fix for legend names. See #236.
+
+2.0.4 -- 28 Nov 2015
+
+Fix #313.
+
+2.0.3 -- 18 Nov 2015
+
+Fixed bug causing knitr options to be ignored. Also added VignetteBuilder to DESCRIPTION to vignette is available.
+
+2.0.2 -- 17 Nov 2015
+
+Using plotly_build() on a ggplot object should always return a plotly object
+
+2.0.1 -- 17 Nov 2015
+
+Better printing of server figures. Documentation and other fixes for initial CRAN release!
+
+2.0.0 -- 2 Nov 2015
+
+Added a dependency on htmlwidgets and 'offline' plots are now the default. If you want to create a figure on a plotly server, you need to use `plotly_POST()`. Also added a `config()` function to control the default appearance of the interactive plot
+
+1.0.10 -- 3 Nov 2015
+
+Fixed #292.
+
+1.0.9 -- 28 Sep 2015
+
+Fixed filename, fileopt arguments in plot_ly. Specifying the same filename will now overwrite the plot if it exists.
+
+1.0.8 -- 14 Sep 2015
+
+Added the plotly_IMAGES() function which interfaces to the images endpoint https://api.plot.ly/v2/#images
+
+Details -> https://github.com/ropensci/plotly/pull/279
+
+1.0.7 -- 26 Aug 2015
+
+See https://github.com/ropensci/plotly/pull/275
+
+1.0.6 -- 25 Aug 2015
+
+Fix a bug with subplot domain calculations (see https://github.com/ropensci/plotly/pull/274)
+
+1.0.5 -- 20 Aug 2015
+
+Fix issue converting plotly offline markdown documents to HTML when using `markdown::markdownToHTML`
+
+1.0.4 -- 14 Aug 2015
+
+Bug fix for subplot. See #265
+
+1.0.3 -- 7 Aug 2015
+
+Improved legend positioning. See #241
+
+1.0.2 -- 2 Aug 2015
+
+* last_plot() will now look for the last plotly object; if not found, it will try to find the last ggplot object.
+* Officially added the filename, fileopt, and world_readable arguments to plot_ly() and ggplotly().
+* If plotly offline is not available, the shiny.launch.browser option is changed to open a web brower. See #245.
+* Various namespace/documentation improvements for R CMD check.
+
+1.0.1 -- 2 Aug 2015
+
+Removed the stream() function as it wasn't ready to be included.
+
+1.0.0 -- 31 July 2015
+
+A major reworking of package internals which includes a few backwards incompatible changes.
+
+Major changes include:
+
+(1) New high-level grammar for expressing Plotly graphs from R (see the `plot_ly()`, `add_trace()`, `layout()`, and `style()` functions).
+(2) New print methods which make it easier to create, modify, and embed Plotly graphs.
+(3) Added a `subplot()` function for putting several graphs on a single page.
+(4) Added the `renderPlotly()` and `plotlyOutput()` functions for embedding plotly graphs in shiny applications.
+(5) Added `offline()` function for creating standalone HTML pages via Plotly Offline (see http://purchasing.plot.ly/)
+
+For more details, see the new vignettes with `browseVignettes(package = "plotly")` and/or the pull request -> https://github.com/ropensci/plotly/pull/226
+
+0.6.3 -- 2 June 2015
+
+Add new tests inspired by the R Cookbook distributions #214
+
+0.6.2 -- 19 May 2015
+
+In geom_bar(stat = "identity"), sum y values if multiple for a given x.
+
+0.6.1 -- 5 May 2015
+
+Add test-cookbook-lines.R and fix bugs that showed up in those tests.
+
+0.6 -- 4 May 2015
+
+Let gg2list() return a figure object (backwards incompatible change).
+
+0.5.29 -- 16 April 2015
+
+geom_density() as filled area chart #202
+
+0.5.28 -- 15 April 2015
+
+Let ggplot handle histogram binning. Fix #198
+
+0.5.27 -- 19 Mar 2015
+
+Reimplement geom_ribbon as a basic polygon. Fix #191. Fix #192.
+
+0.5.26 -- 18 Mar 2015
+
+Implemented geom_rect #178
+
+0.5.25 -- 10 March 2015
+
+Implemented geom_smooth() #183
+
+0.5.24 -- 10 March 2015
+
+Implemented facet_wrap(scales="free") #167
+
+0.5.23 -- 10 March 2015.
+
+geom_ribbon() now respects alpha transparency
+
+0.5.22 -- 2 March 2015.
+
+Fixes for ylim() #171.
+
+0.5.21 -- 23 February 2015.
+
+Fixes for error bars and tick marks.
+
+0.5.20 -- 9 February 2015.
+
+Add alpha transparency to fill conversion.
+Let geom_area support colour and fill aesthetics.
+
+0.5.19 -- 23 January 2015.
+
+Support class conversion such as as.Date() within ggplot code.
+
+0.5.18 -- 22 January 2015.
+
+Return proper filepath when filename contains directories.
+
+0.5.17 -- 30 December 2014.
+
+Support date-time binning in histograms.
+
+0.5.16 -- 29 December 2014.
+
+Support colour aesthetic in geom_text().
+
+0.5.15 -- 19 December 2014.
+
+Use proper RCurlOptions in get_figure() method.
+
+0.5.14 -- 1 December 2014.
+
+Make layers geom_line + geom_point only one trace in Plotly.
+
+0.5.13 -- 27 November 2014.
+
+Rename translation file and server endpoint parameter to be hip.
+
+0.5.12 -- 12 November 2014.
+
+Improve legend title position.
+
+0.5.11 -- 11 November 2014.
+
+Show legend title.
+
+0.5.10 -- 7 November 2014.
+
+Improve showlegend and fix legend’s `x` position.
+
+0.5.9 -- 3 November 2014.
+
+Default colours for geom_polygon().
+
+0.5.8 -- 30 October 2014.
+
+Support hline over a factor x range.
+Default colours for geom_boxplot().
+
+0.5.7 -- 29 October 2014.
+
+Default colours for geom_area() and geom_ribbon().
+
+0.5.6 -- 28 October 2014.
+
+Convert line size faithfully.
+
+0.5.5 -- 24 October 2014.
+
+Support category histograms (with factors).
+
+0.5.4 -- 22 October 2014.
+
+Support conversion of geom_vline().
+
+0.5.3 -- 21 October 2014.
+
+Support conversion of geom_bar() with position_dodge().
+
+0.5.2 -- 18 October 2014.
+
+Support aesthetic shape in geom_path() and, hence, geom_line() (conversion).
+
+0.5.1 -- 15 October 2014.
+
+Do not show zero lines by default (as in ggplot2 plots).
+
+0.5.0 -- 15 October 2014.
+
+From now on, version numbers are meaningful again...
+Many changes meanwhile, especially support for more geoms.
+
+0.4 -- 7 April 2014.
+
+Re-write geom to trace conversion code.
+
+0.3.8 -- 21 March 2014.
+
+ggplotly takes the last_plot() by default.
+
+Support for ggplotly layout elements title, tickcolor, gridcolor,
+showlegend, plot_bgcolor, paper_bgcolor, tickangle, axis titles, plot
+border colors.
+
+0.3.7 -- 14 March 2014.
+
+For ggplotly:
+
+- if on the command line, open a web browser (as before).
+
+- if in knitr/Rmd in a chunk with plotly=TRUE, embed the plot.
+
+0.3.6 -- 10 March 2014.
+
+Merge ggplotly code.
+
+0.3.5
diff --git a/R/add.R b/R/add.R
new file mode 100644
index 0000000..d145b40
--- /dev/null
+++ b/R/add.R
@@ -0,0 +1,644 @@
+#' Add data to a plotly visualization
+#'
+#' @param p a plotly visualization
+#' @param data a data frame.
+#' @export
+#' @examples
+#'
+#' plot_ly() %>% add_data(economics) %>% add_trace(x = ~date, y = ~pce)
+add_data <- function(p, data = NULL) {
+ if (is.null(data)) return(p)
+ if (!is.plotly(p)) {
+ stop("Don't know how to add traces to an object of class: ",
+ class(p), call. = FALSE)
+ }
+ id <- new_id()
+ p$x$visdat[[id]] <- function() data
+ p$x$cur_data <- id
+ # TODO: should this also override the data used for the most recent trace?
+ p
+}
+
+#' Add trace(s) to a plotly visualization
+#'
+#' @inheritParams plot_ly
+#' @param p a plotly object
+#' @param inherit inherit attributes from [plot_ly()]?
+#' @param z a numeric matrix
+#' @param x the x variable.
+#' @param y the y variable.
+#' @param text textual labels.
+#' @param ymin a variable used to define the lower boundary of a polygon.
+#' @param ymax a variable used to define the upper boundary of a polygon.
+#' @param xend "final" x position (in this context, x represents "start")
+#' @param yend "final" y position (in this context, y represents "start")
+#' @seealso [plot_ly()]
+#' @references \url{https://plot.ly/r/reference/}
+#' @author Carson Sievert
+#' @export
+#' @rdname add_trace
+#' @examples
+#'
+#' p <- plot_ly(economics, x = ~date, y = ~uempmed)
+#' p
+#' p %>% add_markers()
+#' p %>% add_lines()
+#' p %>% add_text(text = ".")
+#'
+#' # attributes declared in plot_ly() carry over to downstream traces,
+#' # but can be overwritten
+#' plot_ly(economics, x = ~date, y = ~uempmed, color = I("red")) %>%
+#' add_lines() %>%
+#' add_markers(color = ~pop) %>%
+#' layout(showlegend = FALSE)
+#'
+add_trace <- function(p, ...,
+ data = NULL, inherit = TRUE) {
+
+ # "native" plotly arguments
+ attrs <- list(...)
+
+ if (!is.null(attrs[["group"]])) {
+ warning("The group argument has been deprecated. Use group_by() or split instead.")
+ }
+
+ p <- add_data(p, data)
+
+ # inherit attributes from the "first layer" (except the plotly_eval class)
+ if (inherit) {
+ attrs <- modify_list(unclass(p$x$attrs[[1]]), attrs)
+ }
+
+ p$x$attrs <- c(
+ p$x$attrs %||% list(),
+ setNames(list(attrs), p$x$cur_data)
+ )
+
+ p
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+add_markers <- function(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(x) || is.null(y)) {
+ stop("Must supply `x` and `y` attributes", call. = FALSE)
+ }
+ type <- if (!is.null(z)) "scatter3d" else "scatter"
+ add_trace(
+ p, x = x, y = y, z = z, type = type, mode = "markers", ...,
+ data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+add_text <- function(p, x = NULL, y = NULL, z = NULL, text = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ text <- text %||% p$x$attrs[[1]][["text"]]
+ }
+ if (is.null(x) || is.null(y) || is.null(text)) {
+ stop("Must supply `x`, `y` and `text` attributes", call. = FALSE)
+ }
+ type <- if (!is.null(z)) "scatter3d" else "scatter"
+ add_trace(p, x = x, y = y, z = z, text = text, type = type, mode = "text",
+ ..., data = data, inherit = inherit)
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+add_paths <- function(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(x) || is.null(y)) {
+ stop("Must supply `x` and `y` attributes", call. = FALSE)
+ }
+ type <- if (!is.null(z)) "scatter3d" else "scatter"
+ add_trace_classed(
+ p, x = x, y = y, z = z, class = "plotly_path", type = type, mode = "lines",
+ ..., data = data, inherit = inherit
+ )
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' txhousing %>%
+#' group_by(city) %>%
+#' plot_ly(x = ~date, y = ~median) %>%
+#' add_lines(fill = "black")
+add_lines <- function(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(x) || is.null(y)) {
+ stop("Must supply `x` and `y` attributes", call. = FALSE)
+ }
+ type <- if (!is.null(z)) "scatter3d" else "scatter"
+ add_trace_classed(
+ p, x = x, y = y, class = "plotly_line", type = type, mode = "lines",
+ ..., data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+add_segments <- function(p, x = NULL, y = NULL, xend = NULL, yend = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ xend <- xend %||% p$x$attrs[[1]][["xend"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ yend <- yend %||% p$x$attrs[[1]][["yend"]]
+ }
+ if (is.null(x) || is.null(y) || is.null(xend) || is.null(yend)) {
+ stop("Must supply `x`/`y`/`xend`/`yend` attributes", call. = FALSE)
+ }
+ add_trace_classed(
+ p, x = x, y = y, xend = xend, yend = yend,
+ class = "plotly_segment", type = "scatter", mode = "lines",
+ ..., data = data, inherit = inherit
+ )
+}
+
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#'
+#' ggplot2::map_data("world", "canada") %>%
+#' group_by(group) %>%
+#' plot_ly(x = ~long, y = ~lat) %>%
+#' add_polygons(hoverinfo = "none") %>%
+#' add_markers(text = ~paste(name, "<br />", pop), hoverinfo = "text",
+#' data = maps::canada.cities) %>%
+#' layout(showlegend = FALSE)
+add_polygons <- function(p, x = NULL, y = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ }
+ if (is.null(x) || is.null(y)) {
+ stop("Must supply `x`/`y` attributes", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_polygon", x = x, y = y,
+ type = "scatter", fill = "toself", mode = "lines",
+ ..., data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#'
+#' plot_ly(economics, x = ~date) %>%
+#' add_ribbons(ymin = ~pce - 1e3, ymax = ~pce + 1e3)
+
+add_ribbons <- function(p, x = NULL, ymin = NULL, ymax = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ ymin <- ymin %||% p$x$attrs[[1]][["ymin"]]
+ ymax <- ymax %||% p$x$attrs[[1]][["ymax"]]
+ }
+ if (is.null(x) || is.null(ymin) || is.null(ymax)) {
+ stop("Must supply `x`/`ymin`/`ymax` attributes", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = c("plotly_ribbon", "plotly_polygon"),
+ x = x, ymin = ymin, ymax = ymax, type = "scatter", mode = "lines",
+ hoveron = "points", fill = "toself", ..., data = data, inherit = inherit
+ )
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @param r For polar chart only. Sets the radial coordinates.
+#' @param t For polar chart only. Sets the radial coordinates.
+#' @export
+#' @examples
+#' p <- plot_ly(plotly::wind, r = ~r, t = ~t) %>% add_area(color = ~nms)
+#' layout(p, radialaxis = list(ticksuffix = "%"), orientation = 270)
+add_area <- function(p, r = NULL, t = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ r <- t %||% p$x$attrs[[1]][["r"]]
+ t <- t %||% p$x$attrs[[1]][["t"]]
+ }
+ if (is.null(r) || is.null(t)) {
+ stop("Must supply `r`/`t` attributes", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_area", r = r, t = t, type = "area",
+ ..., data = data, inherit = inherit
+ )
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @param values the value to associated with each slice of the pie.
+#' @param labels the labels (categories) corresponding to `values`.
+#' @export
+#' @examples
+#' ds <- data.frame(
+#' labels = c("A", "B", "C"),
+#' values = c(10, 40, 60)
+#' )
+#'
+#' plot_ly(ds, labels = ~labels, values = ~values) %>%
+#' add_pie() %>%
+#' layout(title = "Basic Pie Chart using Plotly")
+add_pie <- function(p, values = NULL, labels = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ values <- values %||% p$x$attrs[[1]][["values"]]
+ labels <- labels %||% p$x$attrs[[1]][["labels"]]
+ }
+ if (is.null(values)) {
+ stop("Must supply `values`", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_pie", values = values, labels = labels, type = "pie",
+ ..., data = data, inherit = inherit
+ )
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' library(dplyr)
+#' mtcars %>%
+#' count(vs) %>%
+#' plot_ly(x = ~vs, y = ~n) %>%
+#' add_bars()
+add_bars <- function(p, x = NULL, y = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ }
+ if (is.null(x) || is.null(y)) {
+ stop("Must supply `x`/`y` attributes", call. = FALSE)
+ }
+ # TODO: provide type checking in plotly_build for this trace type
+ add_trace_classed(
+ p, class = "plotly_bar", x = x, y = y, type = "bar",
+ ..., data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#'
+#' plot_ly(x = ~rnorm(100)) %>% add_histogram()
+add_histogram <- function(p, x = NULL, y = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ }
+ if (is.null(x) && is.null(y)) {
+ stop("Must supply `x` and/or `y` attributes", call. = FALSE)
+ }
+ # TODO: provide type checking in plotly_build for this trace type
+ add_trace_classed(
+ p, class = "plotly_histogram", x = x, y = y, type = "histogram",
+ ..., data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' plot_ly(x = ~LETTERS, y = ~LETTERS) %>% add_histogram2d()
+#' z <- as.matrix(table(LETTERS, LETTERS))
+#' plot_ly(x = ~LETTERS, y = ~LETTERS, z = ~z) %>% add_histogram2d()
+add_histogram2d <- function(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(z)) {
+ if (is.null(x) || is.null(y)) {
+ stop("Must supply both `x` and `y` attributes if `z` is NULL", call. = FALSE)
+ }
+ }
+ # TODO: provide type checking in plotly_build for this trace type
+ add_trace_classed(
+ p, class = "plotly_histogram2d", x = x, y = y, z = z,
+ type = "histogram2d", ..., data = data, inherit = inherit
+ )
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' plot_ly(MASS::geyser, x = ~waiting, y = ~duration) %>%
+#' add_histogram2dcontour()
+add_histogram2dcontour <- function(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(z)) {
+ if (is.null(x) || is.null(y)) {
+ stop("Must supply both `x` and `y` attributes if `z` is NULL", call. = FALSE)
+ }
+ }
+ # TODO: provide type checking in plotly_build for this trace type
+ add_trace_classed(
+ p, class = "plotly_histogram2dcontour", x = x, y = y, z = z,
+ type = "histogram2dcontour", ..., data = data, inherit = inherit
+ )
+}
+
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' plot_ly(z = ~volcano) %>% add_heatmap()
+add_heatmap <- function(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(z)) {
+ stop("Must supply `z` attribute", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_heatmap", z = z, x = x, y = y,
+ type = "heatmap", ..., data = data, inherit = inherit
+ )
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' plot_ly(z = ~volcano) %>% add_contour()
+add_contour <- function(p, z = NULL, ..., data = NULL, inherit = TRUE) {
+ if (inherit) {
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(z)) {
+ stop("Must supply `z` attribute", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_contour", z = z, type = "contour", ...,
+ data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' plot_ly(mtcars, x = ~factor(vs), y = ~mpg) %>% add_boxplot()
+add_boxplot <- function(p, x = NULL, y = NULL, ..., data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ }
+ if (is.null(x) && is.null(y)) {
+ stop("Must supply either `x` or `y` attribute", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_boxplot", x = x, y = y, type = "box",
+ ..., data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' plot_ly(z = ~volcano) %>% add_surface()
+add_surface <- function(p, z = NULL, ..., data = NULL, inherit = TRUE) {
+ if (inherit) {
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(z)) {
+ stop("Must supply `z` attribute", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_surface", z = z, type = "surface",
+ ..., data = data, inherit = inherit
+ )
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+#' @examples
+#' plot_ly(x = c(0, 0, 1), y = c(0, 1, 0), z = c(0, 0, 0)) %>% add_mesh()
+add_mesh <- function(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ if (inherit) {
+ x <- x %||% p$x$attrs[[1]][["x"]]
+ y <- y %||% p$x$attrs[[1]][["y"]]
+ z <- z %||% p$x$attrs[[1]][["z"]]
+ }
+ if (is.null(x) || is.null(y) || is.null(z)) {
+ stop("Must supply `x`/`y`/`z` attributes", call. = FALSE)
+ }
+ add_trace_classed(
+ p, class = "plotly_mesh", x = x, y = y, z = z, type = "mesh3d",
+ ..., data = data, inherit = inherit
+ )
+}
+
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+add_scattergeo <- function(p, ...) {
+ .Deprecated("geo")
+ p
+}
+
+#' @inheritParams add_trace
+#' @rdname add_trace
+#' @export
+add_choropleth <- function(p, z = NULL, ...,
+ data = NULL, inherit = TRUE) {
+ .Deprecated("geo")
+ p
+}
+
+# attach a class to a trace which informs data processing in plotly_build
+add_trace_classed <- function(p, class = "plotly_polygon", ...) {
+ p <- add_trace(p, ...)
+ nAttrs <- length(p$x$attrs)
+ p$x$attrs[[nAttrs]] <- prefix_class(p$x$attrs[[nAttrs]], class)
+ p
+}
+
+# retrieve the non-plotly.js attributes for a given trace
+special_attrs <- function(trace) {
+ attrs <- switch(
+ class(trace)[[1]],
+ plotly_segment = c("xend", "yend"),
+ plotly_ribbon = c("ymin", "ymax")
+ )
+ # for data training, we temporarily rename lat/lon as x/y
+ if (isTRUE(trace[["type"]] %in% c("scattermapbox", "scattergeo"))) {
+ attrs <- c(attrs, c("x", "y"))
+ }
+ attrs
+}
+
+
+
+# #'
+# #'
+# #' @export
+# #' @examples
+# #'
+# #' x <- rnorm(10)
+# #' plot_ly(x = ~x) %>%
+# #' add_chull()
+# add_chull <- function(p, ...) {
+# stop("not yet implemented")
+# ch <- chull(x, y = NULL)
+# # TODO: Should mode='markers+lines'? If so, retrace first points?
+# add_polygons(...)
+# }
+
+
+
+
+
+#' Apply function to plot, without modifying data
+#'
+#' Useful when you need two or more layers that apply a summary statistic
+#' to the original data.
+#'
+#' @param p a plotly object.
+#' @param fun a function. Should take a plotly object as input and return a
+#' modified plotly object.
+#' @param ... arguments passed to `fun`.
+#' @export
+#' @examples
+#'
+#' txhousing %>%
+#' group_by(city) %>%
+#' plot_ly(x = ~date, y = ~median) %>%
+#' add_lines(alpha = 0.2, name = "Texan Cities") %>%
+#' add_fun(function(plot) {
+#' plot %>% filter(city == "Houston") %>% add_lines(name = "Houston")
+#' }) %>%
+#' add_fun(function(plot) {
+#' plot %>% filter(city == "San Antonio") %>% add_lines(name = "San Antonio")
+#' })
+#'
+#' plot_ly(mtcars, x = ~wt, y = ~mpg) %>%
+#' add_markers() %>%
+#' add_fun(function(p) {
+#' p %>% slice(which.max(mpg)) %>%
+#' add_annotations("Good mileage")
+#' }) %>%
+#' add_fun(function(p) {
+#' p %>% slice(which.min(mpg)) %>%
+#' add_annotations(text = "Bad mileage")
+#' })
+#'
+add_fun <- function(p, fun, ...) {
+ oldDat <- p$x$cur_data
+ p <- fun(p, ...)
+ p$x$cur_data <- oldDat
+ p$x$attrs[length(p$x$attrs)] <- setNames(
+ list(p$x$attrs[[length(p$x$attrs)]]), oldDat
+ )
+ p
+}
+
+
+#' Add an annotation(s) to a plot
+#'
+#' @param p a plotly object
+#' @param text annotation text (required).
+#' @param ... these arguments are documented at
+#' \url{https://github.com/plotly/plotly.js/blob/master/src/components/annotations/attributes.js}
+#' @param data a data frame.
+#' @param inherit inherit attributes from [plot_ly()]?
+#' @author Carson Sievert
+#' @export
+#' @examples
+#'
+#' # single annotation
+#' plot_ly(mtcars, x = ~wt, y = ~mpg) %>%
+#' slice(which.max(mpg)) %>%
+#' add_annotations(text = "Good mileage")
+#'
+#' # multiple annotations
+#' plot_ly(mtcars, x = ~wt, y = ~mpg) %>%
+#' filter(gear == 5) %>%
+#' add_annotations("five cylinder", ax = 40)
+#'
+
+add_annotations <- function(p, text = NULL, ..., data = NULL, inherit = TRUE) {
+ p <- add_data(p, data)
+ attrs <- list(text = text, ...)
+ # x/y/text inherit from plot_ly()
+ for (i in c("x", "y", "text")) {
+ attrs[[i]] <- attrs[[i]] %||% p$x$attrs[[1]][[i]]
+ }
+ if (is.null(attrs[["text"]])) {
+ stop("Must supply text to annotation", call. = FALSE)
+ }
+ attrs <- list(annotations = attrs)
+ # similar to layout()
+ p$x$layoutAttrs <- c(
+ p$x$layoutAttrs %||% list(),
+ setNames(list(attrs), p$x$cur_data)
+ )
+ p
+}
diff --git a/R/animate.R b/R/animate.R
new file mode 100644
index 0000000..111423c
--- /dev/null
+++ b/R/animate.R
@@ -0,0 +1,246 @@
+#' Animation configuration options
+#'
+#' Animations can be created by either using the `frame` argument in
+#' [plot_ly()] or the (unofficial) `frame` ggplot2 aesthetic in
+#' [ggplotly()]. By default, animations populate a play button
+#' and slider component for controlling the state of the animation
+#' (to pause an animation, click on a relevant location on the slider bar).
+#' Both the play button and slider component transition between frames according
+#' rules specified by [animation_opts()].
+#'
+#' @param p a plotly object.
+#' @param frame The amount of time between frames (in milliseconds).
+#' Note that this amount should include the `transition`.
+#' @param transition The duration of the smooth transition between
+#' frames (in milliseconds).
+#' @param easing The type of transition easing. See the list of options here
+#' \url{https://github.com/plotly/plotly.js/blob/master/src/plots/animation_attributes.js}
+#' @param redraw Trigger a redraw of the plot at completion of the transition?
+#' A redraw may significantly impact performance, but may be necessary to
+#' update graphical elements that can't be transitioned.
+#' @param mode Describes how a new animate call interacts with currently-running
+#' animations. If `immediate`, current animations are interrupted and
+#' the new animation is started. If `next`, the current frame is allowed
+#' to complete, after which the new animation is started. If `afterall`
+#' all existing frames are animated to completion before the new animation
+#' is started.
+#' @export
+#' @rdname animation
+#' @aliases animation
+#' @author Carson Sievert
+#' @examples
+#'
+#' df <- data.frame(
+#' x = c(1, 2, 2, 1, 1, 2),
+#' y = c(1, 2, 2, 1, 1, 2),
+#' z = c(1, 1, 2, 2, 3, 3)
+#' )
+#' plot_ly(df) %>%
+#' add_markers(x = 1.5, y = 1.5) %>%
+#' add_markers(x = ~x, y = ~y, frame = ~z)
+#'
+#' # it's a good idea to remove smooth transitions when there is
+#' # no relationship between objects in each view
+#' plot_ly(mtcars, x = ~wt, y = ~mpg, frame = ~cyl) %>%
+#' animation_opts(transition = 0)
+#'
+#' # works the same way with ggplotly
+#' if (interactive()) {
+#' p <- ggplot(txhousing, aes(month, median)) +
+#' geom_line(aes(group = year), alpha = 0.3) +
+#' geom_smooth() +
+#' geom_line(aes(frame = year, ids = month), color = "red") +
+#' facet_wrap(~ city)
+#'
+#' ggplotly(p, width = 1200, height = 900) %>%
+#' animation_opts(1000)
+#' }
+#'
+#'
+#' #' # for more, see https://cpsievert.github.io/plotly_book/key-frame-animations.html
+#'
+animation_opts <- function(p, frame = 500, transition = frame, easing = "linear",
+ redraw = TRUE, mode = "immediate") {
+
+ p$animation <- animation_opts_format(
+ frame = frame,
+ transition = transition,
+ easing = easing,
+ redraw = redraw,
+ mode = mode
+ )
+
+ p
+}
+
+
+animation_opts_format <- function(frame, transition, easing, redraw, mode) {
+ if (frame < 0) {
+ stop("frame must be non-negative.", call. = FALSE)
+ }
+ if (transition < 0) {
+ stop("transition must be non-negative.", call. = FALSE)
+ }
+ if (frame < transition) {
+ stop("frame must be a value larger than transition (it includes the transition)", call. = FALSE)
+ }
+
+ e <- match.arg(easing, easingOpts())
+ m <- match.arg(mode, c('immediate', 'next', 'afterall'))
+
+ list(
+ transition = list(
+ duration = transition,
+ easing = easing
+ ),
+ frame = list(
+ duration = frame,
+ redraw = redraw
+ ),
+ mode = mode
+ )
+}
+
+# a la highlight_defaults()
+animation_opts_defaults <- function() {
+ opts <- formals(animation_opts)[-1]
+
+ # yayyyy for lazy evaluation of arguments
+ isQuoted <- identical(opts$transition, quote(frame))
+ opts$transition <- if (isQuoted) opts$frame else opts$transition
+
+ # flag these as plotly defaults
+ opts <- rapply(opts, default, how = "list")
+
+ animation_opts_format(
+ frame = opts$frame,
+ transition = opts$transition,
+ easing = opts$easing,
+ redraw = opts$redraw,
+ mode = opts$mode
+ )
+}
+
+
+#' @inheritParams animation_opts
+#' @param hide remove the animation slider?
+#' @param ... for `animation_slider`, attributes are passed to a special
+#' layout.sliders object tied to the animation frames.
+#' The definition of these attributes may be found here
+#' \url{https://github.com/plotly/plotly.js/blob/master/src/components/sliders/attributes.js}
+#' For `animation_button`, arguments are passed to a special
+#' layout.updatemenus button object tied to the animation
+#' \url{https://github.com/plotly/plotly.js/blob/master/src/components/updatemenus/attributes.js}
+#' @export
+#' @rdname animation
+animation_slider <- function(p, hide = FALSE, ...) {
+
+ p <- plotly_build(p)
+ isAniSlider <- vapply(p$x$layout$sliders, is_ani_slider, logical(1))
+ if (hide) {
+ p$x$layout$sliders[isAniSlider] <- NULL
+ return(p)
+ }
+ p$x$layout$sliders[[which(isAniSlider)]] <- modify_list(
+ p$x$layout$sliders[[which(isAniSlider)]], list(...)
+ )
+ p
+
+}
+
+
+#' @inheritParams animation_slider
+#' @export
+#' @rdname animation
+animation_button <- function(p, ...) {
+
+ p <- plotly_build(p)
+ isAniButton <- vapply(p$x$layout$updatemenus, is_ani_button, logical(1))
+ p$x$layout$updatemenus[[which(isAniButton)]] <- modify_list(
+ p$x$layout$updatemenus[[which(isAniButton)]], list(...)
+ )
+ p
+}
+
+
+# supply an animation button if it doesn't exist,
+# and _replace_ an existing animation button
+animation_button_supply <- function(p) {
+ nmenus <- length(p$x$layout$updatemenus)
+ isAniButton <- vapply(p$x$layout$updatemenus, is_ani_button, logical(1))
+ idx <- if (sum(isAniButton) == 1) which(isAniButton) else nmenus + 1
+ p$x$layout$updatemenus[[idx]] <- animation_button_create(p$animation)
+ p
+}
+
+animation_button_create <- function(opts = animation_opts_defaults()) {
+
+ button <- list(
+ type = 'buttons',
+ direction = 'right',
+ showactive = FALSE,
+ y = 0,
+ x = 0,
+ yanchor = 'top',
+ xanchor = 'right',
+ pad = list(t = 60, r = 5),
+ # https://github.com/plotly/plotly.js/issues/1221#issuecomment-264870980
+ buttons = list(list(
+ label = 'Play',
+ method = 'animate',
+ args = list(NULL, modify_list(list(fromcurrent = TRUE, mode = "immediate"), opts))
+ ))
+ )
+ structure(button, class = "aniButton")
+}
+
+is_ani_button <- function(obj) {
+ class(obj) %in% "aniButton"
+}
+
+# supply an animation slider if it doesn't exist,
+# and _replace_ an existing animation slider
+animation_slider_supply <- function(p, ...) {
+ nsliders <- length(p$x$layout$sliders)
+ isAniSlider <- vapply(p$x$layout$sliders, is_ani_slider, logical(1))
+ hasAniSlider <- sum(isAniSlider) == 1
+ idx <- if (hasAniSlider) which(isAniSlider) else nsliders + 1
+ p$x$layout$sliders[[idx]] <- animation_slider_create(p, ...)
+ p
+}
+
+animation_slider_create <- function(p, ...) {
+ steps <- lapply(p$x$frames, function(f) {
+ # frame names should already be formatted
+ nm <- f[["name"]]
+ args <- list(list(nm))
+ args[[2]] <- p$animation %||% animation_opts_defaults()
+ list(method = "animate", args = args, label = nm, value = nm)
+ })
+
+ # inherit defaults from any existing slider
+ slider <- modify_list(
+ p$x$layout$sliders[[vapply(p$x$layout$sliders, is_ani_slider, logical(1))]], list(...)
+ )
+ # don't let the user override steps
+ slider$steps <- steps
+
+ # set some opinionated defaults
+ slider$visible <- slider$visible %||% TRUE
+ slider$pad$t <- slider$pad[["t"]] %||% 40
+ structure(slider, class = "aniSlider")
+}
+
+is_ani_slider <- function(obj) {
+ class(obj) %in% "aniSlider"
+}
+
+easingOpts <- function() {
+ c('linear', 'quad', 'cubic', 'sin', 'exp', 'circle', 'elastic', 'back',
+ 'bounce', 'linear-in', 'quad-in', 'cubic-in', 'sin-in', 'exp-in',
+ 'circle-in', 'elastic-in', 'back-in', 'bounce-in', 'linear-out',
+ 'quad-out', 'cubic-out', 'sin-out', 'exp-out', 'circle-out', 'elastic-out',
+ 'back-out', 'bounce-out', 'linear-in-out', 'quad-in-out', 'cubic-in-out',
+ 'sin-in-out', 'exp-in-out', 'circle-in-out', 'elastic-in-out',
+ 'back-in-out', 'bounce-in-out')
+}
diff --git a/R/api.R b/R/api.R
new file mode 100644
index 0000000..32028d4
--- /dev/null
+++ b/R/api.R
@@ -0,0 +1,252 @@
+api_create_plot <- function(x = last_plot(), filename = NULL, fileopt = "overwrite",
+ sharing = "public", ...) {
+
+ x <- plotly_build(x)[["x"]]
+
+ # filename can be of length 2 (in that case, first filename is plotname)
+ len <- length(filename)
+ plotname <- if (len > 1) filename[[1]] else filename
+ gridname <- if (len > 1) filename[[2]] else if (len == 1) paste(filename, "Grid")
+
+ # if file already exists, determine if we can overwrite it
+ origfile <- api_lookup_file(plotname)
+ overwrite <- is.file(origfile) && identical(fileopt, "overwrite")
+ if (overwrite && !identical(origfile$filetype, "plot")) {
+ stop(
+ sprintf("Can overwrite a file of type '%s' with a plot", origfile$filetype),
+ call. = FALSE
+ )
+ }
+
+ # retrieve the parent path, and ensure it exists
+ parent_path <- api_pave_path(plotname)
+
+ # in v2, traces must reference grid data, so create grid references first
+ # http://moderndata.plot.ly/simple-rest-apis-for-charts-and-datasets/
+ x <- api_srcify(x, filename = gridname, fileopt = fileopt, sharing = sharing)
+
+ bod <- compact(list(
+ figure = compact(x[c("data", "layout", "frames")]),
+ filename = if (!is.null(plotname)) basename(plotname),
+ parent_path = if (!is.null(parent_path)) parent_path,
+ world_readable = identical(sharing, "public"),
+ share_key_enabled = identical(sharing, "secret"),
+ ...
+ ))
+
+ # overwrite the original file; otherwise, let plotly create it
+ res <- if (overwrite) {
+
+ message(sprintf(
+ "Found a plot already named: '%s'. Since fileopt='overwrite', I'll try to update it",
+ origfile$filename
+ ))
+ api(paste0("plots/", origfile$fid), "PATCH", to_JSON(bod))
+
+ } else {
+
+ api("plots", "POST", to_JSON(bod))$file
+
+ }
+
+ prefix_class(res, c("api_plot", "api_file"))
+}
+
+api_create_grid <- function(x, filename = NULL, fileopt = "overwrite",
+ sharing = "public", ...) {
+
+ # if file already exists, determine if we can overwrite it
+ origfile <- api_lookup_file(filename)
+ overwrite <- is.file(origfile) && identical(fileopt, "overwrite")
+ if (overwrite && !identical(origfile$filetype, "grid")) {
+ stop(
+ sprintf("Can overwrite a file of type '%s' with a grid", origfile$filetype),
+ call. = FALSE
+ )
+ }
+
+ # retrieve the parent path, and ensure it exists
+ parent_path <- api_pave_path(filename)
+
+ bod <- compact(list(
+ data = df2grid(x),
+ filename = if (!is.null(filename)) basename(filename),
+ parent_path = if (!is.null(parent_path)) parent_path,
+ world_readable = identical(sharing, "public"),
+ share_key_enabled = identical(sharing, "secret"),
+ ...
+ ))
+
+ # At least for now, 'overwrite' really means append new columns
+ # It shouldn't be so convoluted/hard to update a grid! -- https://api.plot.ly/v2/grids#col
+ res <- if (overwrite) {
+
+ message(sprintf(
+ "Found a grid already named: '%s'. Since fileopt='overwrite', I'll try to update it",
+ origfile$filename
+ ))
+ cols <- bod$data$cols
+ colz <- Map(function(x, y) {
+ list(name = paste0(x, "-", new_id()), data = y$data)
+ }, names(cols), cols)
+ colString <- as.character(to_JSON(setNames(colz, NULL)))
+ resp <- api(sprintf("grids/%s/col", origfile$fid), "POST", to_JSON(list(cols = colString)))
+ modify_list(origfile, resp)
+
+ } else {
+
+ api("grids", "POST", to_JSON(bod))$file
+
+ }
+
+ prefix_class(res, c("api_grid", "api_file"))
+}
+
+
+# ---------------------------------------------------------------------------
+# Helper functions
+# ---------------------------------------------------------------------------
+
+api_lookup_file <- function(filename = NULL) {
+ file <- tryNULL(api(paste0("files/lookup?path=", filename)))
+ if (is.null(file)) file else prefix_class(file, "api_file")
+}
+
+# returns the "parent" directory of the filename (and NULL if none exists)
+api_pave_path <- function(filename = NULL) {
+ parent <- dirname(filename %||% ".")
+ # no directory required
+ if (identical(parent, ".")) return(NULL)
+ # does this directory already exist?
+ file <- api_lookup_file(parent)
+ if (is.file(file)) return(parent)
+ fold <- api("folders", "POST", list(path = parent))
+ parent
+}
+
+#api_trash_file <- function(file) {
+# if (!is.file(file)) stop("Can't trash a non-file object:", call. = FALSE)
+# res <- api(sprintf("files/%s/trash", file$fid), "POST")
+# res2 <- api(sprintf("files/%s/permanent_delete", file$fid), "DELETE")
+# invisible(TRUE)
+#}
+
+# upload *one* grid of data array attributes, and replace actual trace data
+# with src/uid references
+api_srcify <- function(p, filename = new_id(), fileopt = "overwrite", sharing = "public") {
+
+ # track the trace/frame index with official element names
+ # this makes it easier to map uids to attributes (& vice versa)
+ p$data <- setNames(p$data, seq_along(p$data))
+ for (i in seq_along(p$frames)) {
+ p$frames <- setNames(p$frames, seq_along(p$frames))
+ d <- p$frames[[i]]$data
+ p$frames[[i]]$data <- setNames(d, seq_along(d))
+ }
+
+ # grab just the src-able attributes
+ pSrc <- filter_to_src(p)
+
+ # flatten the list
+ # http://stackoverflow.com/questions/42739419/r-convert-nested-list-into-a-one-level-list
+ pSrcFlat <- lapply(rapply(pSrc, enquote, how = "unlist"), function(x) I(eval(x)))
+
+ if (length(pSrcFlat)) {
+
+ resp <- api_create_grid(
+ pSrcFlat, filename = filename,
+ fileopt = fileopt, sharing = sharing
+ )
+
+ # replace data values with their uid references
+ idx <- strsplit(names(pSrcFlat), "\\.")
+ idxSrc <- strsplit(paste0(names(pSrcFlat), "src"), "\\.")
+ for (i in seq_along(pSrcFlat)) {
+ p <- re_place(p, idx[[i]], NULL)
+ uid <- paste(resp$fid, resp[["cols"]][[i]]$uid, sep = ":")
+ p <- re_place(p, idxSrc[[i]], uid)
+ }
+ }
+
+ # restore sanity
+ p$data <- setNames(p$data, NULL)
+ for (i in seq_along(p$frames)) {
+ p$frames <- setNames(p$frames, NULL)
+ p$frames[[i]]$data <- setNames(p$frames[[i]]$data, NULL)
+ }
+
+ p
+}
+
+filter_to_src <- function(x) {
+ if (!is.list(x)) {
+ val <- if (isTRUE(attr(x, "apiSrc"))) x else NULL
+ return(val)
+ }
+ rmNullObs(lapply(x, filter_to_src))
+}
+
+# http://stackoverflow.com/questions/26539441/r-remove-null-elements-from-list-of-lists
+is.NullOb <- function(x) is.null(x) | all(sapply(x, is.null))
+rmNullObs <- function(x) {
+ x <- Filter(Negate(is.NullOb), x)
+ lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x)
+}
+
+# transform a data frame to plotly's grid schema
+df2grid <- function(df) {
+ idx <- seq_len(ncol(df) %||% length(df)) - 1
+ columns <- Map(function(x, y) list(data = x, order = y), df, idx)
+ list(cols = columns)
+}
+
+api_expect_filetype <- function(file, type = "plot") {
+ if (!is.file(file)) {
+ stop("Can't read the filetype of a non-file object:", call. = FALSE)
+ }
+ if (!identical(type, file[["filetype"]])) {
+ stop(
+ sprintf("This file is of filetype '%s', not '%s'", file[["filetype"]], type),
+ call. = FALSE
+ )
+ }
+ invisible(file)
+}
+
+# verify `endpoint` is a valid endpoint
+api_check_endpoint <- function(endpoint = "/") {
+ # check the endpoint -- is this a fool-proof way to get the "root path"?
+ rootpoint <- strsplit(endpoint, "(/|\\?)")[[1]][[1]]
+ if (!rootpoint %in% api_endpoints()) {
+ stop(
+ "`endpoint` must point to one of these endpoints:\n",
+ "'", paste(api_endpoints(), collapse = "', '"), "'",
+ call. = FALSE
+ )
+ }
+ endpoint
+}
+
+
+api_endpoints <- function() {
+ c(
+ "", "search", "files", "grids", "plots", "extras", "folders", "images",
+ "comments", "plot-schema", "users", "memberships", "organizations",
+ "subscriptions", "jupyter-notebooks", "shapefiles", "external-images",
+ "spectacle-presentations", "dashboards", "analysis", "dash-apps"
+ )
+}
+
+is.file <- function(x) {
+ inherits(x, "api_file")
+}
+is.plot <- function(x) {
+ inherits(x, "api_plot")
+}
+is.grid <- function(x) {
+ inherits(x, "api_grid")
+}
+
+tryFALSE <- function(expr) {
+ tryCatch(expr, error = function(e) FALSE)
+}
diff --git a/R/api_exports.R b/R/api_exports.R
new file mode 100644
index 0000000..55238f7
--- /dev/null
+++ b/R/api_exports.R
@@ -0,0 +1,196 @@
+#' Tools for working with plotly's REST API (v2)
+#'
+#' Convenience functions for working with version 2 of plotly's REST API.
+#' Upload R objects to a plotly account via `api_create()` and download
+#' plotly objects via `api_download_plot()`/`api_download_grid()`.
+#' For anything else, use `api()`.
+#'
+#' @param id a filename id.
+#' @param username a plotly username.
+#'
+#' @param x An R object to hosted on plotly's web platform.
+#' Can be a plotly/ggplot2 object or a \link{data.frame}.
+#' @param filename character vector naming file(s). If `x` is a plot,
+#' can be a vector of length 2 naming both the plot AND the underlying grid.
+#' @param fileopt character string describing whether to "overwrite" existing
+#' files or ensure "new" file(s) are always created.
+#' @param sharing If 'public', anyone can view this graph. It will appear in
+#' your profile and can appear in search engines. You do not need to be
+#' logged in to Plotly to view this chart.
+#' If 'private', only you can view this plot. It will not appear in the
+#' Plotly feed, your profile, or search engines. You must be logged in to
+#' Plotly to view this graph. You can privately share this graph with other
+#' Plotly users in your online Plotly account and they will need to be logged
+#' in to view this plot.
+#' If 'secret', anyone with this secret link can view this chart. It will
+#' not appear in the Plotly feed, your profile, or search engines.
+#' If it is embedded inside a webpage or an IPython notebook, anybody who is
+#' viewing that page will be able to view the graph.
+#' You do not need to be logged in to view this plot.
+#'
+#' @param endpoint the endpoint (i.e., location) for the request.
+#' To see a list of all available endpoints, call `api()`.
+#' Any relevant query parameters should be included here (see examples).
+#' @param verb name of the HTTP verb to use (as in, [httr::VERB()]).
+#' @param body body of the HTTP request(as in, [httr::VERB()]).
+#' If this value is not already converted to JSON
+#' (via [jsonlite::toJSON()]), it uses the internal `to_JSON()`
+#' to ensure values are "automatically unboxed" (i.e., vec.
+#'
+#' @param ... For `api()`, these arguments are passed onto
+#' [httr::VERB()]. For `api_create()`, these arguments are
+#' included in the body of the HTTP request.
+#'
+#' @export
+#' @rdname api
+#' @author Carson Sievert
+#' @references \url{https://api.plot.ly/v2}
+#' @seealso [signup()]
+#' @examples
+#'
+#' \dontrun{
+#'
+#' # ------------------------------------------------------------
+#' # api_create() makes it easy to upload ggplot2/plotly objects
+#' # and/or data frames to your plotly account
+#' # ------------------------------------------------------------
+#'
+#' # A data frame creates a plotly "grid". Printing one will take you
+#' # to the it's web address so you can start creating!
+#' (m <- api_create(mtcars))
+#'
+#' # A plotly/ggplot2 object create a plotly "plot".
+#' p <- plot_ly(mtcars, x = ~factor(vs))
+#' (r <- api_create(p))
+#'
+#' # api_create() returns metadata about the remote "file". Here is
+#' # one way you could use that metadata to download a plot for local use:
+#' fileID <- strsplit(r$file$fid, ":")[[1]]
+#' layout(
+#' api_download_plot(fileID[2], fileID[1]),
+#' title = sprintf("Local version of <a href='%s'>this</a> plot", r$file$web_url)
+#' )
+#'
+#' ------------------------------------------------------------
+#' # The api() function provides a low-level interface for performing
+#' # any action at any endpoint! It always returns a list.
+#' # ------------------------------------------------------------
+#'
+#' # list all the endpoints
+#' api()
+#'
+#' # search the entire platform!
+#' # see https://api.plot.ly/v2/search
+#' api("search?q=overdose")
+#' api("search?q=plottype:pie trump fake")
+#'
+#' # these examples will require a user account
+#' usr <- Sys.getenv("plotly_username", NA)
+#' if (!is.na(usr)) {
+#' # your account info https://api.plot.ly/v2/#users
+#' api(sprintf("users/%s", usr))
+#' # your folders/files https://api.plot.ly/v2/folders#user
+#' api(sprintf("folders/home?user=%s", usr))
+#' }
+#'
+#' # Retrieve a specific file https://api.plot.ly/v2/files#retrieve
+#' api("files/cpsievert:14681")
+#'
+#' # change the filename https://api.plot.ly/v2/files#update
+#' # (note: this won't work unless you have proper credentials to the relevant account)
+#' api("files/cpsievert:14681", "PATCH", list(filename = "toy file"))
+#'
+#' # Copy a file https://api.plot.ly/v2/files#lookup
+#' api("files/cpsievert:14681/copy", "POST")
+#'
+#' # Create a folder https://api.plot.ly/v2/folders#create
+#' api("folders", "POST", list(path = "/starts/at/root/and/ends/here"))
+#'
+#' }
+#'
+
+
+#' @rdname api
+#' @export
+api_create <- function(x = last_plot(), filename = NULL,
+ fileopt = c("overwrite", "new"),
+ sharing = c("public", "private", "secret"), ...) {
+ fileopt <- match.arg(fileopt, c("overwrite", "new"))
+ sharing <- match.arg(sharing, c("public", "private", "secret"))
+ UseMethod("api_create")
+}
+
+
+#' @rdname api
+#' @export
+api_create.plotly <- api_create_plot
+
+#' @rdname api
+#' @export
+api_create.ggplot <- api_create_plot
+
+#' @rdname api
+#' @export
+api_create.data.frame <- api_create_grid
+
+
+
+#' @rdname api
+#' @export
+api_download_plot <- function(id, username) {
+ f <- api_download_file(id, username)
+ api_expect_filetype(f, "plot")
+
+ as_widget(
+ api_download_file(id, username, "plots", "content?inline_data=true")
+ )
+}
+
+
+#' @rdname api
+#' @export
+api_download_grid <- function(id, username) {
+ f <- api_download_file(id, username)
+ api_expect_filetype(f, "grid")
+
+ prefix_class(
+ api_download_file(id, username, "grids"), "api_grid_local"
+ )
+}
+
+# TODO: should this be exposed to users?
+api_download_file <- function(id, username, endpoint = "files", ...) {
+ if (missing(id)) stop("Please provide a figure id number")
+ if (missing(username)) username <- verify("username")
+ fid <- paste(username, id, sep = ":")
+ prefix_class(
+ api(file.path(endpoint, fid, ...)), "api_file"
+ )
+}
+
+
+#' @rdname api
+#' @export
+api <- function(endpoint = "/", verb = "GET", body = NULL, ...) {
+ api_check_endpoint(endpoint)
+
+ # construct the url
+ url <- httr::modify_url(
+ get_domain("api"),
+ scheme = "https",
+ # TODO: should anything else in the endpoint (besides whitespace) be escaped?
+ path = file.path("v2", gsub("\\s+", "+", endpoint))
+ )
+
+ # default to unboxing (i.e., no arrays of length 1)
+ if (!is.null(body) && !inherits(body, "json")) {
+ body <- to_JSON(body)
+ }
+
+ resp <- httr::VERB(
+ verb = verb, url = url, api_headers(), api_auth(),
+ body = body, ...
+ )
+
+ structure(process(resp), class = "api")
+}
diff --git a/R/coord.R b/R/coord.R
new file mode 100644
index 0000000..c489eb9
--- /dev/null
+++ b/R/coord.R
@@ -0,0 +1,66 @@
+#' *** This won't be possible until plotly.js implements aspect ratios... ***
+#'
+#' #' Force the aspect ratio according to x and y scales
+#' #'
+#' #' When x and y are numeric variables measured on the same scale,
+#' #' or are related in some meaningful way, forcing the aspect ratio of the
+#' #' plot to be proportional to the ratio of a unit change in x versus y improves
+#' #' our ability to correctly perceive the data.
+#' #'
+#' #' @param p a plotly object
+#' #' @param ratio aspect ratio, expressed as y / x
+#' #' @export
+#' #' @examples
+#' #'
+#' #' canada <- map_data("world", "canada")
+#' #'
+#' #' canada %>%
+#' #' group_by(group) %>%
+#' #' plot_ly(x = ~long, y = ~lat, alpha = 0.2) %>%
+#' #' add_polygons(hoverinfo = "none", color = I("black")) %>%
+#' #' coord_fix()
+#' #'
+#' #' # works on (non-faceted) ggplot2 plots, too
+#' #' gg <- ggplot(canada, aes(long, lat, group = group)) +
+#' #' geom_polygon() + coord_fixed()
+#' #'
+#' #' gg %>%
+#' #' ggplotly() %>%
+#' #' coord_fix()
+#' #'
+#'
+#' coord_fix <- function(p, ratio = 1) {
+#' p <- plotly_build(p)
+#' # this won't work for subplots, or categorical data
+#' x <- grepl("^xaxis", names(p$x$layout))
+#' y <- grepl("^yaxis", names(p$x$layout))
+#' if (sum(x) > 1 || sum(y) > 1) {
+#' stop("Can not impose aspect ratio a plot with more than one x/y axis", call. = FALSE)
+#' }
+#' xDat <- unlist(lapply(p$x$data, "[[", "x"))
+#' yDat <- unlist(lapply(p$x$data, "[[", "y"))
+#' if (!is.numeric(xDat) || !is.numeric(yDat)) {
+#' stop("Must have numeric data on both x and y axes to enforce aspect ratios", call. = FALSE)
+#' }
+#'
+#' # warn about any pre-populated domains, they will get squashed
+#' xDom <- p$x$layout[["xaxis"]]$domain %||% c(0, 1)
+#' yDom <- p$x$layout[["yaxis"]]$domain %||% c(0, 1)
+#' if (!identical(yDom, c(0, 1)) || !identical(xDom, c(0, 1))) {
+#' warning(
+#' "coord_fix() won't respect prespecified axis domains (other than the default)",
+#' call. = FALSE
+#' )
+#' }
+#'
+#' xRng <- range(xDat, na.rm = TRUE)
+#' yRng <- range(yDat, na.rm = TRUE)
+#' asp <- ratio * diff(yRng) / diff(xRng)
+#' if (asp < 1) {
+#' p$x$layout[["yaxis"]]$domain <- c(0 + asp / 2, 1 - asp / 2)
+#' } else {
+#' asp <- 1 / asp
+#' p$x$layout[["xaxis"]]$domain <- c(0 + asp / 2, 1 - asp / 2)
+#' }
+#' p
+#' }
diff --git a/R/data.R b/R/data.R
new file mode 100644
index 0000000..6defe37
--- /dev/null
+++ b/R/data.R
@@ -0,0 +1,23 @@
+#' Wind data
+#'
+#' Description TBD.
+#'
+#' @format A data frame with three variables: `r`, `t`,
+#' `nms`.
+"wind"
+
+#' Mic data
+#'
+#' Description TBD.
+#'
+#' @format A data frame with three variables: `r`, `t`,
+#' `nms`.
+"mic"
+
+#' Hobbs data
+#'
+#' Description TBD.
+#'
+#' @format A data frame with three variables: `r`, `t`,
+#' `nms`.
+"hobbs"
diff --git a/R/deprecated.R b/R/deprecated.R
new file mode 100644
index 0000000..4c05026
--- /dev/null
+++ b/R/deprecated.R
@@ -0,0 +1,119 @@
+#' Create/Modify plotly graphs
+#'
+#' Deprecated: see [api_create()].
+#'
+#' @param x either a ggplot object, a plotly object, or a list.
+#' @param filename character string describing the name of the plot in your
+#' plotly account. Use / to specify directories. If a directory path does not
+#' exist it will be created. If this argument is not specified and the title
+#' of the plot exists, that will be used for the filename.
+#' @param fileopt character string describing whether to create a "new" plotly,
+#' "overwrite" an existing plotly, "append" data to existing plotly,
+#' or "extend" it.
+#' @param sharing If 'public', anyone can view this graph. It will appear in
+#' your profile and can appear in search engines. You do not need to be
+#' logged in to Plotly to view this chart.
+#' If 'private', only you can view this plot. It will not appear in the
+#' Plotly feed, your profile, or search engines. You must be logged in to
+#' Plotly to view this graph. You can privately share this graph with other
+#' Plotly users in your online Plotly account and they will need to be logged
+#' in to view this plot.
+#' If 'secret', anyone with this secret link can view this chart. It will
+#' not appear in the Plotly feed, your profile, or search engines.
+#' If it is embedded inside a webpage or an IPython notebook, anybody who is
+#' viewing that page will be able to view the graph.
+#' You do not need to be logged in to view this plot.
+#' @param ... not used
+#' @export
+#' @seealso [plot_ly()], [signup()]
+plotly_POST <- function(x = last_plot(), filename = NULL, fileopt = "overwrite",
+ sharing = c("public", "private", "secret"), ...) {
+ .Deprecated("api_create")
+ api_create(x, filename = filename, sharing = sharing, fileopt = fileopt, ...)
+}
+
+#' Request a figure object
+#'
+#' Deprecated: see [api_download_plot()].
+#'
+#' @param username corresponding username for the figure.
+#' @param id of the Plotly figure.
+#' @export
+get_figure <- function(username, id) {
+ .Deprecated("api_download_plot")
+ api_download_plot(id, username)
+}
+
+#' Main interface to plotly
+#'
+#' @description This function is now deprecated. It used to provide a way to store plotly
+#' account credentials, but that can now be done with environment variables.
+#' For more details and examples, see \url{https://plot.ly/r/getting-started/}.
+#'
+#' If you're here looking for an intro/overview of the package, see the
+#' \href{https://github.com/ropensci/plotly/#getting-started}{readme}
+#'
+#' @keywords internal
+#' @param username plotly username
+#' @param key plotly API key
+#' @export
+#' @seealso [ggplotly()], [plot_ly()], [signup()]
+plotly <- function(username, key) {
+
+ if (!missing(username)) {
+ message("Storing 'username' as the environment variable 'plotly_username'")
+ Sys.setenv("plotly_username" = username)
+ } else {
+ usr <- verify("username")
+ }
+ if (!missing(key)) {
+ message("Storing 'key' as the environment variable 'plotly_api_key'")
+ Sys.setenv("plotly_api_key" = key)
+ } else {
+ key <- verify("api_key")
+ }
+
+ .Deprecated("ggplotly")
+ .Deprecated("plot_ly")
+ invisible(NULL)
+}
+
+#' Plotly Offline
+#'
+#' Deprecated in version 2.0 (offline plots are now the default)
+#'
+#' @param p a plotly object
+#' @param height A valid CSS unit. (like "100\%", "600px", "auto") or a number,
+#' which will be coerced to a string and have "px" appended.
+#' @param width A valid CSS unit. (like "100\%", "600px", "auto") or a number,
+#' which will be coerced to a string and have "px" appended.
+#' @param out_dir a directory to place the visualization.
+#' If `NULL`, a temporary directory is used when the offline object is printed.
+#' @param open_browser open the visualization after creating it?
+#' @author Carson Sievert
+#' @return a plotly object of class "offline"
+#' @export
+
+offline <- function(p, height, width, out_dir, open_browser) {
+ .Deprecated("plot_ly")
+ p
+}
+
+
+#' Convert a plotly object to an htmlwidget object
+#'
+#' This function was deprecated in 4.0.0,
+#' as plotly objects are now htmlwidget objects,
+#' so there is no need to convert them.
+#'
+#' @param x a plotly object.
+#' @param ... other options passed onto `htmlwidgets::createWidget`
+#' @export
+
+as.widget <- function(x, ...) {
+ .Deprecated("as_widget")
+ x
+}
+
+# for legacy reasons
+toWidget <- as.widget
diff --git a/R/dev.R b/R/dev.R
new file mode 100644
index 0000000..604b7c9
--- /dev/null
+++ b/R/dev.R
@@ -0,0 +1,56 @@
+#' Inspect JSON sent to plotly.js
+#'
+#' This function is useful for obtaining/viewing/debugging JSON
+#' sent to plotly.js.
+#'
+#' @param p a plotly or ggplot object.
+#' @param jsonedit use `listviewer::jsonedit` to view the JSON?
+#' @param ... other options passed onto `listviewer::jsonedit`
+#' @export
+#' @examples
+#'
+#' plotly_json(plot_ly())
+#' plotly_json(plot_ly(), FALSE)
+
+plotly_json <- function(p = last_plot(), jsonedit = interactive(), ...) {
+ plotlyJSON <- to_JSON(plotly_build(p)$x, pretty = TRUE)
+ if (jsonedit) {
+ try_library("listviewer", "plotly_json")
+ listviewer::jsonedit(plotlyJSON, mode = "form", ...)
+ } else {
+ plotlyJSON
+ }
+}
+
+#' Acquire (and optionally display) plotly's plot schema
+#'
+#' The schema contains valid attributes names, their value type,
+#' default values (if any), and min/max values (if applicable).
+#'
+#' @param jsonedit use `listviewer::jsonedit` to view the JSON?
+#' @param ... other options passed onto `listviewer::jsonedit`
+#' @export
+#' @examples
+#' s <- schema()
+#'
+#' # retrieve acceptable `layout.mapbox.style` values
+#' if (!is.na(Sys.getenv('MAPBOX_TOKEN', NA))) {
+#' styles <- s$layout$layoutAttributes$mapbox$style$values
+#' subplot(
+#' plot_mapbox() %>% layout(mapbox = list(style = styles[3])),
+#' plot_mapbox() %>% layout(mapbox = list(style = styles[5]))
+#' )
+#' }
+#'
+#'
+#'
+
+schema <- function(jsonedit = interactive(), ...) {
+
+ if (jsonedit) {
+ try_library("listviewer", "schema")
+ print(listviewer::jsonedit(Schema, mode = "form"))
+ }
+
+ invisible(Schema)
+}
diff --git a/R/embed.R b/R/embed.R
new file mode 100644
index 0000000..2b28de4
--- /dev/null
+++ b/R/embed.R
@@ -0,0 +1,52 @@
+#' Embed a plot as an iframe into a Jupyter Notebook
+#' @param x a plotly object
+#' @param width attribute of the iframe. If `NULL`, the width in
+#' `plot_ly` is used. If that is also `NULL`, '100\%' is the default.
+#' @param height attribute of the iframe. If `NULL`, the height in
+#' `plot_ly` is used. If that is also `NULL`, '400px' is the default.
+#' @param file deprecated.
+#' @author Carson Sievert
+#' @export
+embed_notebook <- function(x, width = NULL, height = NULL, file = NULL) {
+ try_library("IRdisplay", "embed_notebook")
+ if (!is.null(file)) {
+ warning("The file argument is no longer used", call. = FALSE)
+ }
+ UseMethod("embed_notebook")
+}
+
+#' @export
+embed_notebook.plotly <- function(x, width = NULL, height = NULL, file = NULL) {
+ # TODO: get rid of this and provide method for api_figure objects
+ display <- getFromNamespace("display_html", asNamespace("IRdisplay"))
+
+ if (!is.null(x$x$url)) {
+ html <- plotly_iframe(
+ x$x$url,
+ width %||% x$width %||% "100%",
+ height %||% x$height %||% 400
+ )
+ return(display(html))
+ }
+ p <- plotly_build(x)
+ tmp <- tempfile(fileext = ".html")
+ on.exit(unlink(tmp), add = TRUE)
+ res <- htmlwidgets::saveWidget(p, tmp)
+ # wrap in an iframe as *nteract* won't do this automatically
+ html <- plotly_iframe(
+ base64enc::dataURI(mime = "text/html;charset=utf-8", file = tmp),
+ width %||% p$width %||% "100%",
+ height %||% p$height %||% 400,
+ ""
+ )
+ display(html)
+}
+
+
+plotly_iframe <- function(url = "", width = NULL, height = NULL, url_ext = ".embed") {
+ url <- paste0(url, url_ext)
+ sprintf(
+ '<iframe src="%s" width="%s" height="%s" id="igraph" scrolling="no" seamless="seamless" frameBorder="0"> </iframe>',
+ url, width %||% "100%", height %||% "400"
+ )
+}
diff --git a/R/export.R b/R/export.R
new file mode 100644
index 0000000..213b44d
--- /dev/null
+++ b/R/export.R
@@ -0,0 +1,105 @@
+#' Export a plotly graph to a static file
+#'
+#' @details For SVG plots, a screenshot is taken via `webshot::webshot()`.
+#' Since `phantomjs` (and hence `webshot`) does not support WebGL,
+#' the RSelenium package is used for exporting WebGL plots.
+#'
+#' @param p a plotly or ggplot object.
+#' @param file a filename. The file type is inferred from the file extension.
+#' Valid extensions include 'jpeg' | 'png' | 'webp' | 'svg' | 'pdf'
+#' @param selenium used only when `p` is a WebGL plot or the output
+#' format is 'webp' or 'svg'. Should be an object of class "rsClientServer"
+#' returned by `RSelenium::rsDriver` (see examples).
+#' @param ... if `p` is non-WebGL and the output file format is
+#' jpeg/png/pdf arguments are passed along to `webshot::webshot()`.
+#' Otherwise, they are ignored.
+#' @export
+#' @author Carson Sievert
+#' @examples
+#' # The webshot package handles non-WebGL conversion to jpeg/png/pdf
+#' \dontrun{
+#' export(plot_ly(economics, x = ~date, y = ~pce))
+#' export(plot_ly(economics, x = ~date, y = ~pce), "plot.pdf")
+#'
+#' # svg/webp output or WebGL conversion can be done via RSelenium
+#' if (requireNamespace("RSelenium")) {
+#' rD <- RSelenium::rsDriver(browser = "chrome")
+#' export(
+#' plot_ly(economics, x = ~date, y = ~pce), "plot.svg", rD
+#' )
+#' export(
+#' plot_ly(economics, x = ~date, y = ~pce, z = ~pop), "yay.svg", rD
+#' )
+#' }
+#'
+#' # If you can't get a selenium server running, another option is to
+#' # use Plotly.downloadImage() via htmlwidgets::onRender()...
+#' # Downloading images won't work inside RStudio, but you can set the viewer
+#' # option to NULL to prompt your default web browser
+#' options(viewer = NULL)
+#' plot_ly(economics, x = ~date, y = ~pce, z = ~pop) %>%
+#' htmlwidgets::onRender(
+#' "function(el, x) {
+#' var gd = document.getElementById(el.id);
+#' Plotly.downloadImage(gd, {format: 'png', width: 600, height: 400, filename: 'plot'});
+#' }"
+#' )
+#'}
+export <- function(p = last_plot(), file = "plotly.png", selenium = NULL, ...) {
+ # infer the file type
+ fileType <- tolower(tools::file_ext(file))
+ if (!fileType %in% c('jpeg', 'png', 'webp', 'svg', 'pdf')) {
+ stop("File type ", fileType, " not supported", call. = FALSE)
+ }
+ if (is.webgl(p) && fileType %in% "pdf") {
+ stop(
+ "A personal (or professional) plan is required to export WebGL to pdf:\n",
+ "https://plot.ly/products/cloud/",
+ call. = FALSE
+ )
+ }
+
+ # webshot only support non-webgl jpeg/png/pdf
+ use_webshot <- !is.webgl(p) && fileType %in% c('jpeg', 'png', 'pdf')
+
+ if (!use_webshot) {
+ # download the image when widget is done rendering
+ cmd <- sprintf(
+ "function(el, x) {
+ var gd = document.getElementById(el.id);
+ Plotly.downloadImage(gd, {format: '%s', width: %s, height: %s, filename: '%s'});
+ }",
+ fileType,
+ p$width %||% p$layout$width %||% 800,
+ p$height %||% p$layout$height %||% 600,
+ tools::file_path_sans_ext(file)
+ )
+ p <- htmlwidgets::onRender(p, cmd)
+ }
+
+ # save widget to an HTML file
+ f <- basename(tempfile('plotly', '.', '.html'))
+ on.exit(unlink(f), add = TRUE)
+ html <- htmlwidgets::saveWidget(p, f)
+
+ # phantomjs doesn't support webgl or svg/webp output
+ if (use_webshot) {
+ try_library("webshot", "export")
+ return(webshot::webshot(f, file, ...))
+ }
+
+ if (inherits(selenium, "rsClientServer")) {
+ # TODO: does this work cross-platform?
+ selenium$client$navigate(paste0("file://", normalizePath(f)))
+ } else {
+ stop(
+ "Must provide an object of class 'rsClientServer' to the `selenium` ",
+ "argument to export this plot (see examples section on `help(export)`)",
+ call. = FALSE
+ )
+ }
+ message(
+ sprintf("Success! Check your downloads folder for a file named: '%s'", file)
+ )
+ invisible(file)
+}
diff --git a/R/ggplotly-legacy.R b/R/ggplotly-legacy.R
new file mode 100644
index 0000000..82aa7a0
--- /dev/null
+++ b/R/ggplotly-legacy.R
@@ -0,0 +1,534 @@
+# copy/pasted from View(plotly::gg2list) with packageVersion("plotly") == "4.5.6"
+gg2list_legacy <- function(p, width = NULL, height = NULL, tooltip = "all", layerData = 1,
+ originalData = TRUE, source = "A", ...)
+{
+ # deviceWidth <- width %||% unitConvert(grid::unit(1, "npc"),
+ # "pixels", "width")
+ # deviceHeight <- height %||% unitConvert(grid::unit(1, "npc"),
+ # "pixels", "height")
+ # dev_fun <- if (capabilities("png")) {
+ # grDevices::png
+ # }
+ # else if (capabilities("jpeg")) {
+ # grDevices::jpeg
+ # }
+ # else {
+ # warning("Couldn't find a bitmap device (e.g. png or jpeg).",
+ # "To ensure sizes are converted correctly please",
+ # "compile R to use a bitmap device", call. = FALSE)
+ # grDevices::dev.new
+ # }
+ # tmpPlotFile <- tempfile(fileext = ".png")
+ # dev_fun(tmpPlotFile, width = deviceWidth, height = deviceHeight)
+ plot <- ggfun("plot_clone")(p)
+ if (length(plot$layers) == 0) {
+ plot <- plot + geom_blank()
+ }
+ layers <- plot$layers
+ layer_data <- lapply(layers, function(y) y$layer_data(plot$data))
+ sets <- lapply(layer_data, function(y) attr(y, "set"))
+ scales <- plot$scales
+ by_layer <- function(f) {
+ out <- vector("list", length(data))
+ for (i in seq_along(data)) {
+ out[[i]] <- f(l = layers[[i]], d = data[[i]])
+ }
+ out
+ }
+ layout <- ggfun("create_layout")(plot$facet)
+ data <- layout$setup(layer_data, plot$data, plot$plot_env,
+ plot$coordinates)
+ data <- layout$map(data)
+ groupDomains <- Map(function(x, y) {
+ tryCatch(eval(y$mapping[["group"]] %||% plot$mapping[["group"]],
+ x), error = function(e) NULL)
+ }, data, layers)
+ data <- by_layer(function(l, d) l$compute_aesthetics(d,
+ plot))
+ group_maps <- Map(function(x, y) {
+ tryCatch({
+ x_group <- x[["group"]]
+ names(x_group) <- y
+ x_group <- x_group[!duplicated(x_group)]
+ x_group
+ }, error = function(e) NULL)
+ }, data, groupDomains)
+ data <- lapply(data, ggfun("scales_transform_df"), scales = scales)
+ scale_x <- function() scales$get_scales("x")
+ scale_y <- function() scales$get_scales("y")
+ layout$train_position(data, scale_x(), scale_y())
+ data <- lapply(data, function(d) {
+ if (!is.null(scale_x()) && scale_x()$is_discrete())
+ d$x_plotlyDomain <- d$x
+ if (!is.null(scale_y()) && scale_y()$is_discrete())
+ d$y_plotlyDomain <- d$y
+ d
+ })
+ data <- layout$map_position(data)
+ prestats_data <- data
+ data <- by_layer(function(l, d) l$compute_statistic(d, layout))
+ data <- by_layer(function(l, d) l$map_statistic(d, plot))
+ ggfun("scales_add_missing")(plot, c("x", "y"), plot$plot_env)
+ data <- by_layer(function(l, d) l$compute_geom_1(d))
+ groupDomains <- Map(function(x, y) {
+ tryCatch({
+ names(y)[match(x$group, y)]
+ }, error = function(e) NULL)
+ }, data, group_maps)
+ data <- by_layer(function(l, d) l$compute_position(d, layout))
+ layout$reset_scales()
+ layout$train_position(data, scale_x(), scale_y())
+ data <- layout$map_position(data)
+ npscales <- scales$non_position_scales()
+ if (npscales$n() > 0) {
+ lapply(data, ggfun("scales_train_df"), scales = npscales)
+ for (sc in npscales$scales) {
+ data <- lapply(data, function(d) {
+ if (any(names(d) %in% sc$aesthetics)) {
+ d[paste0(sc$aesthetics, "_plotlyDomain")] <- d[sc$aesthetics]
+ }
+ d
+ })
+ }
+ data <- lapply(data, ggfun("scales_map_df"), scales = npscales)
+ }
+ layout$train_ranges(plot$coordinates)
+ data <- by_layer(function(l, d) l$compute_geom_2(d))
+ data <- by_layer(function(l, d) l$finish_statistics(d))
+ data <- layout$finish_data(data)
+ theme <- ggfun("plot_theme")(plot)
+ elements <- names(which(sapply(theme, inherits, "element")))
+ for (i in elements) {
+ theme[[i]] <- ggplot2::calc_element(i, theme)
+ }
+ pm <- unitConvert(theme$plot.margin, "pixels")
+ gglayout <- list(margin = list(t = pm[[1]], r = pm[[2]],
+ b = pm[[3]], l = pm[[4]]), plot_bgcolor = toRGB(theme$panel.background$fill),
+ paper_bgcolor = toRGB(theme$plot.background$fill), font = text2font(theme$text))
+ if (nchar(plot$labels$title %||% "") > 0) {
+ gglayout$title <- faced(plot$labels$title, theme$plot.title$face)
+ gglayout$titlefont <- text2font(theme$plot.title)
+ gglayout$margin$t <- gglayout$margin$t + gglayout$titlefont$size
+ }
+ gglayout$margin$t <- gglayout$margin$t + 16
+ if (inherits(plot$coordinates, "CoordFlip")) {
+ plot$labels[c("x", "y")] <- plot$labels[c("y", "x")]
+ }
+ nPanels <- nrow(layout$panel_layout)
+ nRows <- max(layout$panel_layout$ROW)
+ nCols <- max(layout$panel_layout$COL)
+ layout$panel_layout$xaxis <- layout$panel_layout$COL
+ layout$panel_layout$yaxis <- layout$panel_layout$ROW
+ layout$panel_layout$xanchor <- nRows
+ layout$panel_layout$yanchor <- 1
+ if (inherits(plot$facet, "FacetWrap")) {
+ if (plot$facet$params$free$x) {
+ layout$panel_layout$xaxis <- layout$panel_layout$PANEL
+ layout$panel_layout$xanchor <- layout$panel_layout$ROW
+ }
+ if (plot$facet$params$free$y) {
+ layout$panel_layout$yaxis <- layout$panel_layout$PANEL
+ layout$panel_layout$yanchor <- layout$panel_layout$COL
+ layout$panel_layout$xanchor <- nPanels
+ }
+ if (plot$facet$params$free$x && plot$facet$params$free$y) {
+ layout$panel_layout$xaxis <- layout$panel_layout$PANEL
+ layout$panel_layout$yaxis <- layout$panel_layout$PANEL
+ layout$panel_layout$xanchor <- layout$panel_layout$PANEL
+ layout$panel_layout$yanchor <- layout$panel_layout$PANEL
+ }
+ }
+ layout$panel_layout$xaxis <- paste0("xaxis", sub("^1$",
+ "", layout$panel_layout$xaxis))
+ layout$panel_layout$yaxis <- paste0("yaxis", sub("^1$",
+ "", layout$panel_layout$yaxis))
+ layout$panel_layout$xanchor <- paste0("y", sub("^1$", "",
+ layout$panel_layout$xanchor))
+ layout$panel_layout$yanchor <- paste0("x", sub("^1$", "",
+ layout$panel_layout$yanchor))
+ layout$panel_layout$x_min <- sapply(layout$panel_ranges,
+ function(z) min(z$x.range))
+ layout$panel_layout$x_max <- sapply(layout$panel_ranges,
+ function(z) max(z$x.range))
+ layout$panel_layout$y_min <- sapply(layout$panel_ranges,
+ function(z) min(z$y.range))
+ layout$panel_layout$y_max <- sapply(layout$panel_ranges,
+ function(z) max(z$y.range))
+ plot$tooltip <- tooltip
+ data <- Map(function(x, y) {
+ tryCatch({
+ x$group_plotlyDomain <- y
+ x
+ }, error = function(e) x)
+ }, data, groupDomains)
+ traces <- layers2traces(data, prestats_data, layout$panel_layout,
+ plot)
+ gglayout <- layers2layout(gglayout, layers, layout$panel_layout)
+ traces <- lapply(traces, function(tr) {
+ tr$hoverinfo <- tr$hoverinfo %||% "text"
+ tr
+ })
+ grps <- sapply(traces, "[[", "legendgroup")
+ traces <- Map(function(x, y) {
+ x$showlegend <- isTRUE(x$showlegend) && y
+ x
+ }, traces, !duplicated(grps))
+ panelMarginX <- unitConvert(theme[["panel.spacing.x"]] %||%
+ theme[["panel.spacing"]], "npc", "width")
+ panelMarginY <- unitConvert(theme[["panel.spacing.y"]] %||%
+ theme[["panel.spacing"]], "npc", "height")
+ if (inherits(plot$facet, "FacetWrap")) {
+ stripSize <- unitConvert(theme[["strip.text.x"]] %||%
+ theme[["strip.text"]], "npc", "height")
+ panelMarginY <- panelMarginY + stripSize
+ if (plot$facet$params$free$x) {
+ axisTicksX <- unitConvert(theme[["axis.ticks.x"]] %||%
+ theme[["axis.ticks"]], "npc", "height")
+ axisTextX <- theme[["axis.text.x"]] %||% theme[["axis.text"]]
+ labz <- unlist(lapply(layout$panel_ranges, "[[",
+ "x.labels"))
+ lab <- labz[which.max(nchar(labz))]
+ panelMarginY <- panelMarginY + axisTicksX + bbox(lab,
+ axisTextX$angle, unitConvert(axisTextX, "npc",
+ "height"))[["height"]]
+ }
+ if (plot$facet$params$free$y) {
+ axisTicksY <- unitConvert(theme[["axis.ticks.y"]] %||%
+ theme[["axis.ticks"]], "npc", "width")
+ axisTextY <- theme[["axis.text.y"]] %||% theme[["axis.text"]]
+ labz <- unlist(lapply(layout$panel_ranges, "[[",
+ "y.labels"))
+ lab <- labz[which.max(nchar(labz))]
+ panelMarginX <- panelMarginX + axisTicksY + bbox(lab,
+ axisTextY$angle, unitConvert(axisTextY, "npc",
+ "width"))[["width"]]
+ }
+ }
+ margins <- c(rep(panelMarginX, 2), rep(panelMarginY, 2))
+ doms <- get_domains(nPanels, nRows, margins)
+ for (i in seq_len(nPanels)) {
+ lay <- layout$panel_layout[i, ]
+ for (xy in c("x", "y")) {
+ theme_el <- function(el) {
+ theme[[paste0(el, ".", xy)]] %||% theme[[el]]
+ }
+ axisTicks <- theme_el("axis.ticks")
+ axisText <- theme_el("axis.text")
+ axisTitle <- theme_el("axis.title")
+ axisLine <- theme_el("axis.line")
+ panelGrid <- theme_el("panel.grid.major")
+ stripText <- theme_el("strip.text")
+ axisName <- lay[, paste0(xy, "axis")]
+ anchor <- lay[, paste0(xy, "anchor")]
+ rng <- layout$panel_ranges[[i]]
+ sc <- if (inherits(plot$coordinates, "CoordFlip")) {
+ scales$get_scales(setdiff(c("x", "y"), xy))
+ }
+ else {
+ scales$get_scales(xy)
+ }
+ type <- if (xy == "x")
+ "height"
+ else "width"
+ axisTitleText <- sc$name %||% plot$labels[[xy]] %||%
+ ""
+ if (is_blank(axisTitle))
+ axisTitleText <- ""
+ axisObj <- list(type = "linear", autorange = FALSE,
+ tickmode = "array", range = rng[[paste0(xy,
+ ".range")]], ticktext = rng[[paste0(xy, ".labels")]],
+ tickvals = rng[[paste0(xy, ".major")]], ticks = if (is_blank(axisTicks)) "" else "outside",
+ tickcolor = toRGB(axisTicks$colour), ticklen = unitConvert(theme$axis.ticks.length,
+ "pixels", type), tickwidth = unitConvert(axisTicks,
+ "pixels", type), showticklabels = !is_blank(axisText),
+ tickfont = text2font(axisText, type), tickangle = -(axisText$angle %||%
+ 0), showline = !is_blank(axisLine), linecolor = toRGB(axisLine$colour),
+ linewidth = unitConvert(axisLine, "pixels",
+ type), showgrid = !is_blank(panelGrid), domain = sort(as.numeric(doms[i,
+ paste0(xy, c("start", "end"))])), gridcolor = toRGB(panelGrid$colour),
+ gridwidth = unitConvert(panelGrid, "pixels",
+ type), zeroline = FALSE, anchor = anchor,
+ title = axisTitleText, titlefont = text2font(axisTitle))
+ if (identical("date", sc$scale_name)) {
+ axisObj$range <- axisObj$range * 86400000
+ if (i == 1) {
+ traces <- lapply(traces, function(z) {
+ z[[xy]] <- z[[xy]] * 86400000
+ z
+ })
+ }
+ }
+ axisObj$tickvals <- scales::rescale(axisObj$tickvals,
+ to = axisObj$range, from = c(0, 1))
+ gglayout[[axisName]] <- axisObj
+ if (i == 1) {
+ axisTickText <- axisObj$ticktext[which.max(nchar(axisObj$ticktext))]
+ side <- if (xy == "x")
+ "b"
+ else "l"
+ gglayout$margin[[side]] <- gglayout$margin[[side]] +
+ axisObj$ticklen + bbox(axisTickText, axisObj$tickangle,
+ axisObj$tickfont$size)[[type]] + bbox(axisTitleText,
+ axisTitle$angle, unitConvert(axisTitle, "pixels",
+ type))[[type]]
+ if (nchar(axisTitleText) > 0) {
+ axisTextSize <- unitConvert(axisText, "npc",
+ type)
+ axisTitleSize <- unitConvert(axisTitle, "npc",
+ type)
+ offset <- (0 - bbox(axisTickText, axisText$angle,
+ axisTextSize)[[type]] - bbox(axisTitleText,
+ axisTitle$angle, axisTitleSize)[[type]]/2 -
+ unitConvert(theme$axis.ticks.length, "npc",
+ type))
+ }
+ if (has_facet(plot)) {
+ stripSize <- unitConvert(stripText, "pixels",
+ type)
+ if (xy == "x") {
+ gglayout$margin$t <- gglayout$margin$t +
+ stripSize
+ }
+ if (xy == "y" && inherits(plot$facet, "FacetGrid")) {
+ gglayout$margin$r <- gglayout$margin$r +
+ stripSize
+ }
+ if (nchar(axisTitleText) > 0) {
+ x <- if (xy == "x")
+ 0.5
+ else offset
+ y <- if (xy == "x")
+ offset
+ else 0.5
+ gglayout$annotations <- c(gglayout$annotations,
+ make_label(faced(axisTitleText, axisTitle$face),
+ x, y, el = axisTitle, xanchor = if (xy ==
+ "x") "center" else "right", yanchor = if (xy ==
+ "x") "top" else "center", annotationType = "axis"))
+ }
+ }
+ }
+ if (has_facet(plot))
+ gglayout[[axisName]]$title <- ""
+ }
+ xdom <- gglayout[[lay[, "xaxis"]]]$domain
+ ydom <- gglayout[[lay[, "yaxis"]]]$domain
+ border <- make_panel_border(xdom, ydom, theme)
+ gglayout$shapes <- c(gglayout$shapes, border)
+ if (has_facet(plot)) {
+ col_vars <- ifelse(inherits(plot$facet, "FacetWrap"),
+ "facets", "cols")
+ col_txt <- paste(plot$facet$params$labeller(lay[names(plot$facet$params[[col_vars]])]),
+ collapse = "<br>")
+ if (is_blank(theme[["strip.text.x"]]))
+ col_txt <- ""
+ if (inherits(plot$facet, "FacetGrid") && lay$ROW !=
+ 1)
+ col_txt <- ""
+ if (nchar(col_txt) > 0) {
+ col_lab <- make_label(col_txt, x = mean(xdom),
+ y = max(ydom), el = theme[["strip.text.x"]] %||%
+ theme[["strip.text"]], xanchor = "center",
+ yanchor = "bottom")
+ gglayout$annotations <- c(gglayout$annotations,
+ col_lab)
+ strip <- make_strip_rect(xdom, ydom, theme,
+ "top")
+ gglayout$shapes <- c(gglayout$shapes, strip)
+ }
+ row_txt <- paste(plot$facet$params$labeller(lay[names(plot$facet$params$rows)]),
+ collapse = "<br>")
+ if (is_blank(theme[["strip.text.y"]]))
+ row_txt <- ""
+ if (inherits(plot$facet, "FacetGrid") && lay$COL !=
+ nCols)
+ row_txt <- ""
+ if (nchar(row_txt) > 0) {
+ row_lab <- make_label(row_txt, x = max(xdom),
+ y = mean(ydom), el = theme[["strip.text.y"]] %||%
+ theme[["strip.text"]], xanchor = "left",
+ yanchor = "middle")
+ gglayout$annotations <- c(gglayout$annotations,
+ row_lab)
+ strip <- make_strip_rect(xdom, ydom, theme,
+ "right")
+ gglayout$shapes <- c(gglayout$shapes, strip)
+ }
+ }
+ }
+ gglayout$showlegend <- sum(unlist(lapply(traces, "[[", "showlegend"))) >=
+ 1
+ gglayout$legend <- list(bgcolor = toRGB(theme$legend.background$fill),
+ bordercolor = toRGB(theme$legend.background$colour),
+ borderwidth = unitConvert(theme$legend.background$size,
+ "pixels", "width"), font = text2font(theme$legend.text))
+ if (npscales$n() == 0 || identical(theme$legend.position,
+ "none")) {
+ gglayout$showlegend <- FALSE
+ }
+ else {
+ theme$legend.box <- theme$legend.box %||% "vertical"
+ theme$legend.key.width <- theme$legend.key.width %||%
+ theme$legend.key.size
+ theme$legend.key.height <- theme$legend.key.height %||%
+ theme$legend.key.size
+ theme$legend.direction <- theme$legend.direction %||%
+ "vertical"
+ if (!identical(theme$legend.direction, "vertical")) {
+ warning("plotly.js does not (yet) support horizontal legend items \n",
+ "You can track progress here: \n", "https://github.com/plotly/plotly.js/issues/53 \n",
+ call. = FALSE)
+ theme$legend.direction <- "vertical"
+ }
+ theme$legend.box.just <- theme$legend.box.just %||%
+ c("center", "center")
+ gdefs <- ggfun("guides_train")(scales, theme, plot$guides,
+ plot$labels)
+ if (length(gdefs) > 0) {
+ gdefs <- ggfun("guides_merge")(gdefs)
+ gdefs <- ggfun("guides_geom")(gdefs, layers, plot$mapping)
+ }
+ colorbar <- compact(lapply(gdefs, gdef2trace, theme,
+ gglayout))
+ nguides <- length(colorbar) + gglayout$showlegend
+ if (nguides >= 2) {
+ gglayout$legend$y <- 1/nguides
+ gglayout$legend$yanchor <- "top"
+ for (i in seq_along(colorbar)) {
+ colorbar[[i]]$marker$colorbar$yanchor <- "top"
+ colorbar[[i]]$marker$colorbar$len <- 1/nguides
+ colorbar[[i]]$marker$colorbar$y <- 1 - (i -
+ 1) * (1/nguides)
+ }
+ }
+ traces <- c(traces, colorbar)
+ if (isTRUE(gglayout$showlegend)) {
+ legendTitles <- compact(lapply(gdefs, function(g) if (inherits(g,
+ "legend"))
+ g$title
+ else NULL))
+ legendTitle <- paste(legendTitles, collapse = "<br>")
+ titleAnnotation <- make_label(legendTitle, x = gglayout$legend$x %||%
+ 1.02, y = gglayout$legend$y %||% 1, theme$legend.title,
+ xanchor = "left", yanchor = "bottom", legendTitle = TRUE)
+ gglayout$annotations <- c(gglayout$annotations,
+ titleAnnotation)
+ gglayout$legend$y <- (gglayout$legend$y %||% 1) -
+ length(legendTitles) * unitConvert(theme$legend.title$size,
+ "npc", "height")
+ }
+ }
+ geoms <- sapply(layers, ggtype, "geom")
+ if (any(idx <- geoms %in% "bar")) {
+ positions <- sapply(layers, ggtype, "position")
+ position <- unique(positions[geoms %in% "bar"])
+ if (length(position) > 1) {
+ warning("plotly doesn't support multiple positions\n",
+ "across geom_bar() layers", call. = FALSE)
+ position <- position[1]
+ }
+ if (position == "identity") {
+ gglayout$barmode <- "overlay"
+ gglayout$legend$traceorder <- "reversed"
+ }
+ else {
+ gglayout$barmode <- "stack"
+ }
+ is_hist <- inherits(plot$scales$get_scales("x"), "ScaleContinuous")
+ if (position == "dodge" || is_hist) {
+ gglayout$bargap <- 0
+ }
+ }
+ if (inherits(plot$coordinates, "CoordFlip")) {
+ for (i in seq_along(traces)) {
+ tr <- traces[[i]]
+ traces[[i]][c("x", "y")] <- tr[c("y", "x")]
+ if (tr$type %in% c("bar", "box"))
+ traces[[i]]$orientation <- "h"
+ if (tr$type == "box")
+ traces[[i]]$hoverinfo <- "x"
+ names(traces[[i]])[grepl("^error_y$", names(tr))] <- "error_x"
+ names(traces[[i]])[grepl("^error_x$", names(tr))] <- "error_y"
+ }
+ }
+ for (xy in c("x", "y")) {
+ type <- if (xy == "x")
+ "width"
+ else "height"
+ err <- if (xy == "x")
+ "error_y"
+ else "error_x"
+ for (i in seq_along(traces)) {
+ e <- traces[[i]][[err]]
+ if (!is.null(e)) {
+ w <- grid::unit(e$width %||% 0, "npc")
+ traces[[i]][[err]]$width <- unitConvert(w, "pixels",
+ type)
+ }
+ }
+ }
+ props <- c("x", "y", "text", "type", "xaxis", "yaxis", "name")
+ hashes <- vapply(traces, function(x) digest::digest(x[names(x) %in%
+ props]), character(1))
+ modes <- vapply(traces, function(x) x$mode %||% "", character(1))
+ nhashes <- length(unique(hashes))
+ if (nhashes < length(traces)) {
+ mergedTraces <- vector("list", nhashes)
+ for (i in unique(hashes)) {
+ idx <- which(hashes %in% i)
+ if (all(modes[idx] %in% c("lines", "markers"))) {
+ mergedTraces[[i]] <- Reduce(modify_list, traces[idx])
+ mergedTraces[[i]]$mode <- "markers+lines"
+ if (any(sapply(traces[idx], "[[", "showlegend"))) {
+ mergedTraces[[i]]$showlegend <- TRUE
+ }
+ }
+ }
+ traces <- mergedTraces
+ }
+ gglayout$hovermode <- "closest"
+ ax <- grep("^[x-y]axis", names(gglayout))
+ for (i in ax) {
+ gglayout[[i]]$hoverformat <- ".2f"
+ }
+ traces <- lapply(compact(traces), function(x) {
+ x$name <- x$name %||% ""
+ x
+ })
+ gglayout$width <- width
+ gglayout$height <- height
+ #grDevices::dev.off()
+ #unlink(tmpPlotFile)
+ l <- list(data = setNames(traces, NULL), layout = compact(gglayout),
+ source = source)
+ l <- rm_asis(l)
+ mappingFormulas <- lapply(layers, function(x) {
+ mappings <- c(x$mapping, if (isTRUE(x$inherit.aes)) plot$mapping)
+ if (originalData) {
+ lapply(mappings, lazyeval::f_new)
+ }
+ else {
+ nms <- names(mappings)
+ setNames(lapply(nms, function(x) lazyeval::f_new(as.symbol(x))),
+ nms)
+ }
+ })
+ return_dat <- if (originalData)
+ layer_data
+ else data
+ return_dat <- Map(function(x, y) {
+ if (is.null(y[["group"]]))
+ return(x)
+ dplyr::group_by_(x, y[["group"]])
+ }, return_dat, mappingFormulas)
+ mappingFormulas <- lapply(mappingFormulas, function(x) x[!grepl("^group$",
+ names(x))])
+ ids <- lapply(seq_along(data), function(x) new_id())
+ l$attrs <- setNames(mappingFormulas, ids)
+ l$attrs <- lapply(l$attrs, function(x) structure(x, class = "plotly_eval"))
+ l$attrs[[1]][["type"]] <- "ggplotly"
+ l$cur_data <- ids[[layerData]]
+ l$visdat <- setNames(lapply(return_dat, function(x) function(y) x),
+ ids)
+ l
+}
diff --git a/R/ggplotly.R b/R/ggplotly.R
new file mode 100644
index 0000000..86e9c4d
--- /dev/null
+++ b/R/ggplotly.R
@@ -0,0 +1,1333 @@
+#' Convert ggplot2 to plotly
+#'
+#' This function converts a [ggplot2::ggplot()] object to a
+#' plotly object.
+#'
+#' @details Conversion of relative sizes depends on the size of the current
+#' graphics device (if no device is open, width/height of a new (off-screen)
+#' device defaults to 640/480). In other words, `height` and
+#' `width` must be specified at runtime to ensure sizing is correct.
+#'
+#' @param p a ggplot object.
+#' @param width Width of the plot in pixels (optional, defaults to automatic sizing).
+#' @param height Height of the plot in pixels (optional, defaults to automatic sizing).
+#' @param tooltip a character vector specifying which aesthetic mappings to show
+#' in the tooltip. The default, "all", means show all the aesthetic mappings
+#' (including the unofficial "text" aesthetic). The order of variables here will
+#' also control the order they appear. For example, use
+#' `tooltip = c("y", "x", "colour")` if you want y first, x second, and
+#' colour last.
+#' @param dynamicTicks should plotly.js dynamically generate axis tick labels?
+#' Dynamic ticks are useful for updating ticks in response to zoom/pan
+#' interactions; however, they can not always reproduce labels as they
+#' would appear in the static ggplot2 image.
+#' @param layerData data from which layer should be returned?
+#' @param originalData should the "original" or "scaled" data be returned?
+#' @param source a character string of length 1. Match the value of this string
+#' with the source argument in [event_data()] to retrieve the
+#' event data corresponding to a specific plot (shiny apps can have multiple plots).
+#' @param ... arguments passed onto methods.
+#' @export
+#' @author Carson Sievert
+#' @references \url{https://plot.ly/ggplot2}
+#' @seealso [plot_ly()]
+#' @examples \dontrun{
+#' # simple example
+#' ggiris <- qplot(Petal.Width, Sepal.Length, data = iris, color = Species)
+#' ggplotly(ggiris)
+#'
+#' data(canada.cities, package = "maps")
+#' viz <- ggplot(canada.cities, aes(long, lat)) +
+#' borders(regions = "canada") +
+#' coord_equal() +
+#' geom_point(aes(text = name, size = pop), colour = "red", alpha = 1/2)
+#' ggplotly(viz, tooltip = c("text", "size"))
+#'
+#'
+#' # highlighting lines
+#' demo("highlight-ggplotly", package = "plotly")
+#'
+#' # client-side linked brushing
+#' library(crosstalk)
+#' d <- SharedData$new(mtcars)
+#' subplot(
+#' qplot(data = d, x = mpg, y = wt),
+#' qplot(data = d, x = mpg, y = vs)
+#' )
+#'
+#' # client-side linked brushing in a scatterplot matrix
+#' SharedData$new(iris) %>%
+#' GGally::ggpairs(aes(colour = Species), columns = 1:4) %>%
+#' ggplotly(tooltip = c("x", "y", "colour"))
+#' }
+#'
+ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
+ tooltip = "all", dynamicTicks = FALSE,
+ layerData = 1, originalData = TRUE, source = "A", ...) {
+ UseMethod("ggplotly", p)
+}
+
+#' @export
+ggplotly.plotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
+ tooltip = "all", dynamicTicks = FALSE,
+ layerData = 1, originalData = TRUE, source = "A", ...) {
+ p
+}
+
+#' @export
+ggplotly.ggmatrix <- function(p = ggplot2::last_plot(), width = NULL,
+ height = NULL, tooltip = "all", dynamicTicks = FALSE,
+ layerData = 1, originalData = TRUE, source = "A", ...) {
+ dots <- list(...)
+ # provide a sensible crosstalk if none is already provided (makes ggnostic() work at least)
+ if (!crosstalk_key() %in% names(p$data)) {
+ p$data[[crosstalk_key()]] <- p$data[[".rownames"]] %||% seq_len(nrow(p$data))
+ attr(p$data, "set") <- dots[["set"]] %||% new_id()
+ }
+ subplotList <- list()
+ for (i in seq_len(p$ncol)) {
+ columnList <- list()
+ for (j in seq_len(p$nrow)) {
+ thisPlot <- p[j, i]
+ if (i == 1) {
+ # should the first column contain axis labels?
+ if (p$showYAxisPlotLabels %||% TRUE) thisPlot <- thisPlot + ylab(p$yAxisLabels[j])
+ } else {
+ # y-axes are never drawn on the interior, and diagonal plots are densities,
+ # so it doesn't make sense to synch zoom actions on y
+ thisPlot <- thisPlot + ylab(NULL) +
+ theme(
+ axis.ticks.y = element_blank(),
+ axis.text.y = element_blank()
+ )
+ }
+ columnList <- c(
+ columnList, list(ggplotly(
+ thisPlot, tooltip = tooltip, dynamicTicks = dynamicTicks,
+ layerData = layerData, originalData = originalData, source = source,
+ width = width, height = height
+ ))
+ )
+ }
+ # conditioned on a column in a ggmatrix, the x-axis should be on the
+ # same scale.
+ s <- subplot(columnList, nrows = p$nrow, margin = 0.01, shareX = TRUE,
+ titleY = TRUE, titleX = TRUE)
+ subplotList <- c(subplotList, list(s))
+ }
+ s <- subplot(subplotList, nrows = 1, margin = 0.01,
+ titleY = TRUE, titleX = TRUE) %>%
+ hide_legend() %>%
+ layout(dragmode = "select")
+ if (nchar(p$title %||% "") > 0) {
+ s <- layout(s, title = p$title)
+ }
+ for (i in seq_along(p$xAxisLabels)) {
+ s$x$layout[[sub("^xaxis1$", "xaxis", paste0("xaxis", i))]]$title <- p$xAxisLabels[[i]]
+ }
+ if (length(p$yAxisLabels)) {
+ s$x$layout$margin$l <- s$x$layout$margin$l + 50
+ }
+
+ config(s)
+}
+
+#' @export
+ggplotly.ggplot <- function(p = ggplot2::last_plot(), width = NULL,
+ height = NULL, tooltip = "all", dynamicTicks = FALSE,
+ layerData = 1, originalData = TRUE, source = "A", ...) {
+ l <- gg2list(p, width = width, height = height, tooltip = tooltip,
+ dynamicTicks = dynamicTicks, layerData = layerData,
+ originalData = originalData, source = source, ...)
+ config(as_widget(l))
+}
+
+#' Convert a ggplot to a list.
+#' @param p ggplot2 plot.
+#' @param width Width of the plot in pixels (optional, defaults to automatic sizing).
+#' @param height Height of the plot in pixels (optional, defaults to automatic sizing).
+#' @param tooltip a character vector specifying which aesthetic tooltips to show in the
+#' tooltip. The default, "all", means show all the aesthetic tooltips
+#' (including the unofficial "text" aesthetic).
+#' @param dynamicTicks accepts the following values: `FALSE`, `TRUE`, `"x"`, or `"y"`.
+#' Dynamic ticks are useful for updating ticks in response to zoom/pan/filter
+#' interactions; however, there is no guarantee they reproduce axis tick text
+#' as they would appear in the static ggplot2 image.
+#' @param layerData data from which layer should be returned?
+#' @param originalData should the "original" or "scaled" data be returned?
+#' @param source a character string of length 1. Match the value of this string
+#' with the source argument in [event_data()] to retrieve the
+#' event data corresponding to a specific plot (shiny apps can have multiple plots).
+#' @param ... currently not used
+#' @return a 'built' plotly object (list with names "data" and "layout").
+#' @export
+gg2list <- function(p, width = NULL, height = NULL,
+ tooltip = "all", dynamicTicks = FALSE,
+ layerData = 1, originalData = TRUE, source = "A", ...) {
+
+ # To convert relative sizes correctly, we use grid::convertHeight(),
+ # which may open a new *screen* device, if none is currently open.
+ # To avoid undesirable side effects, we may need to open a
+ # non-interactive device and close it on exit...
+ # https://github.com/att/rcloud.htmlwidgets/issues/2
+
+ # Note that we never have to open a non-interactive device
+ # in RStudio since it ships with one. Plus, calling dev.size()
+ # adds it to dev.list() & should ensure grid can query the correct device size
+ rStudioDevSize <- if (is_rstudio()) grDevices::dev.size("px")
+
+ if (is.null(grDevices::dev.list())) {
+ dev_fun <- if (system.file(package = "Cairo") != "") {
+ Cairo::Cairo
+ } else if (capabilities("png")) {
+ grDevices::png
+ } else if (capabilities("jpeg")) {
+ grDevices::jpeg
+ } else {
+ stop(
+ "No graphics device is currently open and no cairo or bitmap device is available.\n",
+ "A graphics device is required to convert sizes correctly. You have three options:",
+ " (1) Open a graphics device (with the desired size) using ggplotly()",
+ " (2) install.packages('Cairo')",
+ " (3) compile R to use a bitmap device (png or jpeg)",
+ call. = FALSE
+ )
+ }
+ dev_fun(file = tempfile(), width = width %||% 640, height = height %||% 480)
+ on.exit(grDevices::dev.off(), add = TRUE)
+ }
+
+ # check the value of dynamicTicks
+ dynamicValues <- c(FALSE, TRUE, "x", "y")
+ if (length(setdiff(dynamicTicks, dynamicValues))) {
+ stop(
+ sprintf(
+ "`dynamicValues` accepts the following values: '%s'",
+ paste(dynamicValues, collapse = "', '")
+ ), call. = FALSE
+ )
+ }
+
+ # we currently support ggplot2 >= 2.2.1 (see DESCRIPTION)
+ # there are too many naming changes in 2.2.1.9000 to realistically
+ if (!is_dev_ggplot2()) {
+ message(
+ "We recommend that you use the dev version of ggplot2 with `ggplotly()`\n",
+ "Install it with: `devtools::install_github('hadley/ggplot2')`"
+ )
+ if (!identical(dynamicTicks, FALSE)) {
+ warning(
+ "You need the dev version of ggplot2 to use `dynamicTicks`", call. = FALSE
+ )
+ }
+ return(
+ gg2list_legacy(
+ p, width = width, height = height, tooltip = tooltip,
+ layerData = layerData, originalData = originalData, source = source, ...
+ )
+ )
+ }
+
+ # ------------------------------------------------------------------------
+ # Our internal version of ggplot2::ggplot_build(). Modified from
+ # https://github.com/hadley/ggplot2/blob/0cd0ba/R/plot-build.r#L18-L92
+ # ------------------------------------------------------------------------
+
+ plot <- ggfun("plot_clone")(p)
+ if (length(plot$layers) == 0) {
+ plot <- plot + geom_blank()
+ }
+ layers <- plot$layers
+ layer_data <- lapply(layers, function(y) y$layer_data(plot$data))
+
+ # save crosstalk sets before this attribute gets squashed
+ sets <- lapply(layer_data, function(y) attr(y, "set"))
+
+ scales <- plot$scales
+
+ # Apply function to layer and matching data
+ by_layer <- function(f) {
+ out <- vector("list", length(data))
+ for (i in seq_along(data)) {
+ out[[i]] <- f(l = layers[[i]], d = data[[i]])
+ }
+ out
+ }
+
+ # Initialise panels, add extra data for margins & missing facetting
+ # variables, and add on a PANEL variable to data
+ layout <- ggfun("create_layout")(plot$facet, plot$coordinates)
+ data <- layout$setup(layer_data, plot$data, plot$plot_env)
+
+ # save the domain of the group for display in tooltips
+ groupDomains <- Map(function(x, y) {
+ tryCatch(
+ eval(y$mapping[["group"]] %||% plot$mapping[["group"]], x),
+ error = function(e) NULL
+ )
+ }, data, layers)
+
+ # for simple (StatIdentity) geoms, add crosstalk key to aes mapping
+ # (effectively adding it as a group)
+ # later on, for more complicated geoms (w/ non-trivial summary statistics),
+ # we construct a nested key mapping (within group)
+ layers <- Map(function(x, y) {
+ if (crosstalk_key() %in% names(y) && !"key" %in% names(x[["mapping"]]) &&
+ inherits(x[["stat"]], "StatIdentity")) {
+ x[["mapping"]] <- c(x[["mapping"]], key = as.name(crosstalk_key()))
+ }
+ x
+ }, layers, layer_data)
+
+ # Compute aesthetics to produce data with generalised variable names
+ data <- by_layer(function(l, d) l$compute_aesthetics(d, plot))
+
+ # add frame to group if it exists
+ data <- lapply(data, function(d) {
+ if (!"frame" %in% names(d)) return(d)
+ d$group <- with(d, paste(group, frame, sep = "-"))
+ d
+ })
+
+ # The computed aesthetic codes the groups as integers
+ # Here we build a map each of the integer values to the group label
+ group_maps <- Map(function(x, y) {
+ tryCatch({
+ x_group <- x[["group"]]
+ names(x_group) <- y
+ x_group <- x_group[!duplicated(x_group)]
+ x_group
+ }, error = function(e) NULL
+ )
+ }, data, groupDomains)
+
+ # Before mapping x/y position, save the domain (for discrete scales)
+ # to display in tooltip.
+ data <- lapply(data, function(d) {
+ d[["x_plotlyDomain"]] <- d[["x"]]
+ d[["y_plotlyDomain"]] <- d[["y"]]
+ d
+ })
+
+ # Transform all scales
+ data <- lapply(data, ggfun("scales_transform_df"), scales = scales)
+
+ # Map and train positions so that statistics have access to ranges
+ # and all positions are numeric
+ scale_x <- function() scales$get_scales("x")
+ scale_y <- function() scales$get_scales("y")
+
+ layout$train_position(data, scale_x(), scale_y())
+
+ data <- layout$map_position(data)
+
+ # build a mapping between group and key
+ # if there are multiple keys within a group, the key is a list-column
+ reComputeGroup <- function(x, layer = NULL) {
+ # 1-to-1 link between data & visual marks -- group == key
+ if (inherits(layer$geom, "GeomDotplot")) {
+ x <- split(x, x[["PANEL"]])
+ x <- lapply(x, function(d) {
+ d[["group"]] <- do.call("order", d[c("x", "group")])
+ d
+ })
+ x <- dplyr::bind_rows(x)
+ }
+ if (inherits(layer$geom, "GeomSf")) {
+ x <- split(x, x[["PANEL"]])
+ x <- lapply(x, function(d) {
+ d[["group"]] <- seq_len(nrow(d))
+ d
+ })
+ # I think this is safe?
+ x <- suppressWarnings(dplyr::bind_rows(x))
+ }
+ x
+ }
+
+ nestedKeys <- Map(function(x, y, z) {
+ key <- y[[crosstalk_key()]]
+ if (is.null(key) || inherits(z[["stat"]], "StatIdentity")) return(NULL)
+ x <- reComputeGroup(x, z)
+ tib <- tibble::as_tibble(x[c("PANEL", "group")])
+ tib[["key"]] <- key
+ nested <- tidyr::nest(tib, key, .key = key)
+ # reduce the dimensions of list column elements from 2 to 1
+ nested$key <- lapply(nested$key, function(x) x[[1]])
+ nested
+ }, data, layer_data, layers)
+
+ # for some geoms (e.g. boxplots) plotly.js needs the "pre-statistics" data
+ # we also now provide the option to return one of these two
+ prestats_data <- data
+ data <- by_layer(function(l, d) l$compute_statistic(d, layout))
+ data <- by_layer(function(l, d) l$map_statistic(d, plot))
+
+ # Make sure missing (but required) aesthetics are added
+ ggfun("scales_add_missing")(plot, c("x", "y"), plot$plot_env)
+
+ # Reparameterise geoms from (e.g.) y and width to ymin and ymax
+ data <- by_layer(function(l, d) l$compute_geom_1(d))
+
+ # compute_geom_1 can reorder the rows from `data`, making groupDomains
+ # invalid. We rebuild groupDomains based on the current `data` and the
+ # group map we built before.
+ groupDomains <- Map(function(x, y) {
+ tryCatch({
+ names(y)[match(x$group, y)]
+ }, error = function(e) NULL
+ )
+ }, data, group_maps)
+
+ # there are some geoms (e.g. geom_dotplot()) where attaching the key
+ # before applying the statistic can cause problems, but there is still a
+ # 1-to-1 corresponding between graphical marks and
+
+ # Apply position adjustments
+ data <- by_layer(function(l, d) l$compute_position(d, layout))
+
+ # Reset position scales, then re-train and map. This ensures that facets
+ # have control over the range of a plot: is it generated from what's
+ # displayed, or does it include the range of underlying data
+ layout$reset_scales()
+ layout$train_position(data, scale_x(), scale_y())
+ layout$setup_panel_params()
+ data <- layout$map_position(data)
+
+ # Train and map non-position scales
+ npscales <- scales$non_position_scales()
+ if (npscales$n() > 0) {
+ lapply(data, ggfun("scales_train_df"), scales = npscales)
+ # this for loop is unique to plotly -- it saves the "domain"
+ # of each non-positional scale for display in tooltips
+ for (sc in npscales$scales) {
+ data <- lapply(data, function(d) {
+ # scale may not be relevant for every layer data
+ if (any(names(d) %in% sc$aesthetics)) {
+ d[paste0(sc$aesthetics, "_plotlyDomain")] <- d[sc$aesthetics]
+ }
+ d
+ })
+ }
+ data <- lapply(data, ggfun("scales_map_df"), scales = npscales)
+ }
+
+ # Fill in defaults etc.
+ data <- by_layer(function(l, d) l$compute_geom_2(d))
+
+ # Let layer stat have a final say before rendering
+ data <- by_layer(function(l, d) l$finish_statistics(d))
+
+ # Let Layout modify data before rendering
+ data <- layout$finish_data(data)
+
+ # ------------------------------------------------------------------------
+ # end of ggplot_build()
+ # ------------------------------------------------------------------------
+ # if necessary, attach key
+ data <- Map(function(x, y, z) {
+ if (!length(y)) return(x)
+ x <- reComputeGroup(x, z)
+ # dplyr issue??? https://github.com/tidyverse/dplyr/issues/2701
+ attr(y$group, "n") <- NULL
+ suppressMessages(dplyr::left_join(x, y))
+ }, data, nestedKeys, layers)
+
+ # initiate plotly.js layout with some plot-wide theming stuff
+ theme <- ggfun("plot_theme")(plot)
+ elements <- names(which(sapply(theme, inherits, "element")))
+ for (i in elements) {
+ theme[[i]] <- ggplot2::calc_element(i, theme)
+ }
+ # Translate plot wide theme elements to plotly.js layout
+ pm <- unitConvert(theme$plot.margin, "pixels")
+ gglayout <- list(
+ margin = list(t = pm[[1]], r = pm[[2]], b = pm[[3]], l = pm[[4]]),
+ plot_bgcolor = toRGB(theme$panel.background$fill),
+ paper_bgcolor = toRGB(theme$plot.background$fill),
+ font = text2font(theme$text)
+ )
+ # main plot title
+ if (nchar(plot$labels$title %||% "") > 0) {
+ gglayout$title <- faced(plot$labels$title, theme$plot.title$face)
+ gglayout$titlefont <- text2font(theme$plot.title)
+ gglayout$margin$t <- gglayout$margin$t + gglayout$titlefont$size
+ }
+ # ensure there's enough space for the modebar (this is based on a height of 1em)
+ # https://github.com/plotly/plotly.js/blob/dd1547/src/components/modebar/index.js#L171
+ gglayout$margin$t <- gglayout$margin$t + 16
+
+ # important stuff like layout$panel_params is already flipped, but
+ # plot$scales/plot$labels/data aren't. We flip x/y trace data at the very end
+ # and scales in the axis loop below.
+ if (inherits(plot$coordinates, "CoordFlip")) {
+ plot$labels[c("x", "y")] <- plot$labels[c("y", "x")]
+ }
+
+ # important panel summary stats
+ nPanels <- nrow(layout$layout)
+ nRows <- max(layout$layout$ROW)
+ nCols <- max(layout$layout$COL)
+
+ # panel -> plotly.js axis/anchor info
+ # (assume a grid layout by default)
+ layout$layout$xaxis <- layout$layout$COL
+ layout$layout$yaxis <- layout$layout$ROW
+ layout$layout$xanchor <- nRows
+ layout$layout$yanchor <- 1
+ if (inherits(plot$facet, "FacetWrap")) {
+ if (plot$facet$params$free$x) {
+ layout$layout$xaxis <- layout$layout$PANEL
+ layout$layout$xanchor <- layout$layout$ROW
+ }
+ if (plot$facet$params$free$y) {
+ layout$layout$yaxis <- layout$layout$PANEL
+ layout$layout$yanchor <- layout$layout$COL
+ layout$layout$xanchor <- nPanels
+ }
+ if (plot$facet$params$free$x && plot$facet$params$free$y) {
+ layout$layout$xaxis <- layout$layout$PANEL
+ layout$layout$yaxis <- layout$layout$PANEL
+ layout$layout$xanchor <- layout$layout$PANEL
+ layout$layout$yanchor <- layout$layout$PANEL
+ }
+ }
+ # format the axis/anchor to a format plotly.js respects
+ layout$layout$xaxis <- paste0("xaxis", sub("^1$", "", layout$layout$xaxis))
+ layout$layout$yaxis <- paste0("yaxis", sub("^1$", "", layout$layout$yaxis))
+ layout$layout$xanchor <- paste0("y", sub("^1$", "", layout$layout$xanchor))
+ layout$layout$yanchor <- paste0("x", sub("^1$", "", layout$layout$yanchor))
+ # for some layers2traces computations, we need the range of each panel
+ layout$layout$x_min <- sapply(layout$panel_params, function(z) min(z$x.range %||% z$x_range))
+ layout$layout$x_max <- sapply(layout$panel_params, function(z) max(z$x.range %||% z$x_range))
+ layout$layout$y_min <- sapply(layout$panel_params, function(z) min(z$y.range %||% z$y_range))
+ layout$layout$y_max <- sapply(layout$panel_params, function(z) max(z$y.range %||% z$y_range))
+
+ # layers -> plotly.js traces
+ plot$tooltip <- tooltip
+ data <- Map(function(x, y) {
+ tryCatch({ x$group_plotlyDomain <- y; x }, error = function(e) x)
+ }, data, groupDomains)
+
+ # reattach crosstalk key-set attribute
+ data <- Map(function(x, y) structure(x, set = y), data, sets)
+ traces <- layers2traces(data, prestats_data, layout, plot)
+
+ gglayout <- layers2layout(gglayout, layers, layout$layout)
+
+ # default to just the text in hover info, mainly because of this
+ # https://github.com/plotly/plotly.js/issues/320
+ traces <- lapply(traces, function(tr) {
+ tr$hoverinfo <- tr$hoverinfo %||%"text"
+ tr
+ })
+ # show only one legend entry per legendgroup
+ grps <- sapply(traces, "[[", "legendgroup")
+ traces <- Map(function(x, y) {
+ if (!is.null(x[["frame"]])) return(x)
+ x$showlegend <- isTRUE(x$showlegend) && y
+ x
+ }, traces, !duplicated(grps))
+
+ # ------------------------------------------------------------------------
+ # axis/facet/margin conversion
+ # ------------------------------------------------------------------------
+
+ # panel margins must be computed before panel/axis loops
+ # (in order to use get_domains())
+ panelMarginX <- unitConvert(
+ theme[["panel.spacing.x"]] %||% theme[["panel.spacing"]],
+ "npc", "width"
+ )
+ panelMarginY <- unitConvert(
+ theme[["panel.spacing.y"]] %||% theme[["panel.spacing"]],
+ "npc", "height"
+ )
+ # space for _interior_ facet strips
+ if (inherits(plot$facet, "FacetWrap")) {
+ stripSize <- unitConvert(
+ theme[["strip.text.x"]] %||% theme[["strip.text"]],
+ "npc", "height"
+ )
+ panelMarginY <- panelMarginY + stripSize
+ # space for ticks/text in free scales
+ if (plot$facet$params$free$x) {
+ axisTicksX <- unitConvert(
+ theme[["axis.ticks.x"]] %||% theme[["axis.ticks"]],
+ "npc", "height"
+ )
+ # allocate enough space for the _longest_ text label
+ axisTextX <- theme[["axis.text.x"]] %||% theme[["axis.text"]]
+ labz <- unlist(lapply(layout$panel_params, "[[", "x.labels"))
+ lab <- labz[which.max(nchar(labz))]
+ panelMarginY <- panelMarginY + axisTicksX +
+ bbox(lab, axisTextX$angle, unitConvert(axisTextX, "npc", "height"))[["height"]]
+ }
+ if (plot$facet$params$free$y) {
+ axisTicksY <- unitConvert(
+ theme[["axis.ticks.y"]] %||% theme[["axis.ticks"]],
+ "npc", "width"
+ )
+ # allocate enough space for the _longest_ text label
+ axisTextY <- theme[["axis.text.y"]] %||% theme[["axis.text"]]
+ labz <- unlist(lapply(layout$panel_params, "[[", "y.labels"))
+ lab <- labz[which.max(nchar(labz))]
+ panelMarginX <- panelMarginX + axisTicksY +
+ bbox(lab, axisTextY$angle, unitConvert(axisTextY, "npc", "width"))[["width"]]
+ }
+ }
+ margins <- c(
+ rep(panelMarginX, 2),
+ rep(panelMarginY, 2)
+ )
+ doms <- get_domains(nPanels, nRows, margins)
+
+ for (i in seq_len(nPanels)) {
+ lay <- layout$layout[i, ]
+ for (xy in c("x", "y")) {
+ # find axis specific theme elements that inherit from their parent
+ theme_el <- function(el) {
+ theme[[paste0(el, ".", xy)]] %||% theme[[el]]
+ }
+ axisTicks <- theme_el("axis.ticks")
+ axisText <- theme_el("axis.text")
+ axisTitle <- theme_el("axis.title")
+ axisLine <- theme_el("axis.line")
+ panelGrid <- theme_el("panel.grid.major")
+ stripText <- theme_el("strip.text")
+
+ axisName <- lay[, paste0(xy, "axis")]
+ anchor <- lay[, paste0(xy, "anchor")]
+ rng <- layout$panel_params[[i]]
+
+ # panel_params is quite different for "CoordSf"
+ if ("CoordSf" %in% class(p$coordinates)) {
+ # see CoordSf$render_axis_v
+ direction <- if (xy == "x") "E" else "N"
+ idx <- rng$graticule$type == direction & !is.na(rng$graticule$degree_label)
+ tickData <- rng$graticule[idx, ]
+ # TODO: how to convert a language object to unicode character string?
+ rng[[paste0(xy, ".labels")]] <- as.character(tickData[["degree_label"]])
+ rng[[paste0(xy, ".major")]] <- tickData[[paste0(xy, "_start")]]
+
+ # If it doesn't already exist (for this panel),
+ # generate graticule (as done in, CoordSf$render_bg)
+ isGrill <- vapply(traces, function(tr) {
+ identical(tr$xaxis, lay$xaxis) &&
+ identical(tr$yaxis, lay$yaxis) &&
+ isTRUE(tr$`_isGraticule`)
+ }, logical(1))
+
+ if (sum(isGrill) == 0) {
+ d <- expand(rng$graticule)
+ d$x <- scales::rescale(d$x, rng$x_range, from = c(0, 1))
+ d$y <- scales::rescale(d$y, rng$y_range, from = c(0, 1))
+ params <- list(
+ colour = theme$panel.grid.major$colour,
+ size = theme$panel.grid.major$size,
+ linetype = theme$panel.grid.major$linetype
+ )
+ grill <- geom2trace.GeomPath(d, params)
+ grill$hoverinfo <- "none"
+ grill$showlegend <- FALSE
+ grill$`_isGraticule` <- TRUE
+ grill$xaxis <- lay$xaxis
+ grill$yaxis <- lay$yaxis
+
+ traces <- c(list(grill), traces)
+ }
+
+ # if labels are empty, don't show axis ticks
+ tickExists <- with(rng$graticule, sapply(degree_label, is.language))
+ if (sum(tickExists) == 0) {
+ theme$axis.ticks.length <- 0
+ } else{
+ # convert the special *degree expression in plotmath to HTML entity
+ # TODO: can this be done more generally for all ?
+ rng[[paste0(xy, ".labels")]] <- sub(
+ "\\*\\s+degree[ ]?[\\*]?", "°", rng[[paste0(xy, ".labels")]]
+ )
+ }
+
+ }
+
+ # stuff like layout$panel_params is already flipped, but scales aren't
+ sc <- if (inherits(plot$coordinates, "CoordFlip")) {
+ scales$get_scales(setdiff(c("x", "y"), xy))
+ } else {
+ scales$get_scales(xy)
+ }
+ # type of unit conversion
+ type <- if (xy == "x") "height" else "width"
+ # get axis title
+ axisTitleText <- sc$name %||% plot$labels[[xy]] %||% ""
+ if (is_blank(axisTitle)) axisTitleText <- ""
+
+ # is this axis dynamic?
+ isDynamic <- isTRUE(dynamicTicks) || identical(dynamicTicks, xy)
+ if (isDynamic && !p$coordinates$is_linear()) {
+ warning(
+ "`dynamicTicks` is only supported for linear (i.e., cartesian) coordinates",
+ call. = FALSE
+ )
+ }
+ # determine axis types (note: scale_name may go away someday)
+ # https://github.com/hadley/ggplot2/issues/1312
+ isDate <- isTRUE(sc$scale_name %in% c("date", "datetime"))
+ isDateType <- isDynamic && isDate
+ isDiscrete <- identical(sc$scale_name, "position_d")
+ isDiscreteType <- isDynamic && isDiscrete
+
+ axisObj <- list(
+ # TODO: log type?
+ type = if (isDateType) "date" else if (isDiscreteType) "category" else "linear",
+ autorange = isDynamic,
+ range = rng[[paste0(xy, ".range")]] %||% rng[[paste0(xy, "_range")]],
+ tickmode = if (isDynamic) "auto" else "array",
+ ticktext = rng[[paste0(xy, ".labels")]],
+ tickvals = rng[[paste0(xy, ".major")]],
+ categoryorder = "array",
+ categoryarray = rng[[paste0(xy, ".labels")]],
+ nticks = nrow(rng),
+ ticks = if (is_blank(axisTicks)) "" else "outside",
+ tickcolor = toRGB(axisTicks$colour),
+ ticklen = unitConvert(theme$axis.ticks.length, "pixels", type),
+ tickwidth = unitConvert(axisTicks, "pixels", type),
+ showticklabels = !is_blank(axisText),
+ tickfont = text2font(axisText, type),
+ tickangle = - (axisText$angle %||% 0),
+ showline = !is_blank(axisLine),
+ linecolor = toRGB(axisLine$colour),
+ linewidth = unitConvert(axisLine, "pixels", type),
+ # TODO: always `showgrid=FALSE` and implement our own using traces
+ showgrid = !is_blank(panelGrid) && !"CoordSf" %in% class(p$coordinates),
+ domain = sort(as.numeric(doms[i, paste0(xy, c("start", "end"))])),
+ gridcolor = toRGB(panelGrid$colour),
+ gridwidth = unitConvert(panelGrid, "pixels", type),
+ zeroline = FALSE,
+ anchor = anchor,
+ title = faced(axisTitleText, axisTitle$face),
+ titlefont = text2font(axisTitle)
+ )
+
+ # set scaleanchor/scaleratio if these are fixed coordinates
+ fixed_coords <- c("CoordSf", "CoordFixed", "CoordMap", "CoordQuickmap")
+ if (inherits(p$coordinates, fixed_coords)) {
+ axisObj$scaleanchor <- anchor
+ ratio <- p$coordinates$ratio %||% p$coordinates$aspect(rng) %||% 1
+ axisObj$scaleratio <- if (xy == "y") ratio else 1 / ratio
+ }
+
+ # TODO: should we implement aspect ratios?
+ if (!is.null(theme$aspect.ratio)) {
+ warning(
+ "Aspect ratios aren't yet implemented, but you can manually set",
+ " a suitable height/width", call. = FALSE
+ )
+ }
+
+ # tickvals are currently on 0-1 scale, but we want them on data scale
+ axisObj$tickvals <- scales::rescale(
+ axisObj$tickvals, to = axisObj$range, from = c(0, 1)
+ )
+
+ # inverse transform date data based on tickvals/ticktext
+ invert_date <- function(x, scale) {
+ if (inherits(scale, "ScaleContinuousDatetime")) {
+ as.POSIXct(x, origin = "1970-01-01", tz = scale$timezone)
+ } else {
+ as.Date(x, origin = "1970-01-01", tz = scale$timezone)
+ }
+ }
+
+ if (isDateType) {
+ axisObj$range <- invert_date(axisObj$range, sc)
+ traces <- lapply(traces, function(tr) {
+ tr[[xy]] <- invert_date(tr[[xy]], sc)
+ # TODO: are there other similar cases we need to handle?
+ if (identical("bar", tr$type)) {
+ tr[["width"]] <- invert_date(tr[["width"]], sc)
+ }
+ tr
+ })
+ }
+
+ # inverse transform categorical data based on tickvals/ticktext
+ if (isDiscreteType) {
+ traces <- lapply(traces, function(tr) {
+ # map x/y trace data back to the 'closest' ticktext label
+ # http://r.789695.n4.nabble.com/check-for-nearest-value-in-a-vector-td4369339.html
+ tr[[xy]]<- vapply(tr[[xy]], function(val) {
+ with(axisObj, ticktext[[which.min(abs(tickvals - val))]])
+ }, character(1))
+ tr
+ })
+ if ("dodge" %in% sapply(layers, ggtype, "position")) gglayout$barmode <- "dodge"
+ }
+
+ # attach axis object to the layout
+ gglayout[[axisName]] <- axisObj
+
+ # do some stuff that should be done once for the entire plot
+ if (i == 1) {
+ axisTickText <- axisObj$ticktext[which.max(nchar(axisObj$ticktext))]
+ side <- if (xy == "x") "b" else "l"
+ # account for axis ticks, ticks text, and titles in plot margins
+ # (apparently ggplot2 doesn't support axis.title/axis.text margins)
+ gglayout$margin[[side]] <- gglayout$margin[[side]] + axisObj$ticklen +
+ bbox(axisTickText, axisObj$tickangle, axisObj$tickfont$size)[[type]] +
+ bbox(axisTitleText, axisTitle$angle, unitConvert(axisTitle, "pixels", type))[[type]]
+
+ if (nchar(axisTitleText) > 0) {
+ axisTextSize <- unitConvert(axisText, "npc", type)
+ axisTitleSize <- unitConvert(axisTitle, "npc", type)
+ offset <-
+ (0 -
+ bbox(axisTickText, axisText$angle, axisTextSize)[[type]] -
+ bbox(axisTitleText, axisTitle$angle, axisTitleSize)[[type]] / 2 -
+ unitConvert(theme$axis.ticks.length, "npc", type))
+ }
+
+ # add space for exterior facet strips in `layout.margin`
+
+ if (has_facet(plot)) {
+ stripSize <- unitConvert(stripText, "pixels", type)
+ if (xy == "x") {
+ gglayout$margin$t <- gglayout$margin$t + stripSize
+ }
+ if (xy == "y" && inherits(plot$facet, "FacetGrid")) {
+ gglayout$margin$r <- gglayout$margin$r + stripSize
+ }
+ # facets have multiple axis objects, but only one title for the plot,
+ # so we empty the titles and try to draw the title as an annotation
+ if (nchar(axisTitleText) > 0) {
+ # npc is on a 0-1 scale of the _entire_ device,
+ # but these units _should_ be wrt to the plotting region
+ # multiplying the offset by 2 seems to work, but this is a terrible hack
+ x <- if (xy == "x") 0.5 else offset
+ y <- if (xy == "x") offset else 0.5
+ gglayout$annotations <- c(
+ gglayout$annotations,
+ make_label(
+ faced(axisTitleText, axisTitle$face), x, y, el = axisTitle,
+ xanchor = if (xy == "x") "center" else "right",
+ yanchor = if (xy == "x") "top" else "center",
+ annotationType = "axis"
+ )
+ )
+ }
+ }
+ }
+ if (has_facet(plot)) gglayout[[axisName]]$title <- ""
+ } # end of axis loop
+
+ # theme(panel.border = ) -> plotly rect shape
+ xdom <- gglayout[[lay[, "xaxis"]]]$domain
+ ydom <- gglayout[[lay[, "yaxis"]]]$domain
+ border <- make_panel_border(xdom, ydom, theme)
+ gglayout$shapes <- c(gglayout$shapes, border)
+
+ # facet strips -> plotly annotations
+ if (has_facet(plot)) {
+ col_vars <- ifelse(inherits(plot$facet, "FacetWrap"), "facets", "cols")
+ col_txt <- paste(
+ plot$facet$params$labeller(
+ lay[names(plot$facet$params[[col_vars]])]
+ ), collapse = br()
+ )
+ if (is_blank(theme[["strip.text.x"]])) col_txt <- ""
+ if (inherits(plot$facet, "FacetGrid") && lay$ROW != 1) col_txt <- ""
+ if (nchar(col_txt) > 0) {
+ col_lab <- make_label(
+ col_txt, x = mean(xdom), y = max(ydom),
+ el = theme[["strip.text.x"]] %||% theme[["strip.text"]],
+ xanchor = "center", yanchor = "bottom"
+ )
+ gglayout$annotations <- c(gglayout$annotations, col_lab)
+ strip <- make_strip_rect(xdom, ydom, theme, "top")
+ gglayout$shapes <- c(gglayout$shapes, strip)
+ }
+ row_txt <- paste(
+ plot$facet$params$labeller(
+ lay[names(plot$facet$params$rows)]
+ ), collapse = br()
+ )
+ if (is_blank(theme[["strip.text.y"]])) row_txt <- ""
+ if (inherits(plot$facet, "FacetGrid") && lay$COL != nCols) row_txt <- ""
+ if (nchar(row_txt) > 0) {
+ row_lab <- make_label(
+ row_txt, x = max(xdom), y = mean(ydom),
+ el = theme[["strip.text.y"]] %||% theme[["strip.text"]],
+ xanchor = "left", yanchor = "middle"
+ )
+ gglayout$annotations <- c(gglayout$annotations, row_lab)
+ strip <- make_strip_rect(xdom, ydom, theme, "right")
+ gglayout$shapes <- c(gglayout$shapes, strip)
+ }
+ }
+ } # end of panel loop
+
+
+ # ------------------------------------------------------------------------
+ # guide conversion
+ # Strategy: Obtain and translate the output of ggplot2:::guides_train().
+ # To do so, we borrow some of the body of ggplot2:::guides_build().
+ # ------------------------------------------------------------------------
+ # will there be a legend?
+ gglayout$showlegend <- sum(unlist(lapply(traces, "[[", "showlegend"))) >= 1
+
+ # legend styling
+ gglayout$legend <- list(
+ bgcolor = toRGB(theme$legend.background$fill),
+ bordercolor = toRGB(theme$legend.background$colour),
+ borderwidth = unitConvert(theme$legend.background$size, "pixels", "width"),
+ font = text2font(theme$legend.text)
+ )
+
+ # if theme(legend.position = "none") is used, don't show a legend _or_ guide
+ if (npscales$n() == 0 || identical(theme$legend.position, "none")) {
+ gglayout$showlegend <- FALSE
+ } else {
+ # by default, guide boxes are vertically aligned
+ theme$legend.box <- theme$legend.box %||% "vertical"
+
+ # size of key (also used for bar in colorbar guide)
+ theme$legend.key.width <- theme$legend.key.width %||% theme$legend.key.size
+ theme$legend.key.height <- theme$legend.key.height %||% theme$legend.key.size
+
+ # legend direction must be vertical
+ theme$legend.direction <- theme$legend.direction %||% "vertical"
+ if (!identical(theme$legend.direction, "vertical")) {
+ warning(
+ "plotly.js does not (yet) support horizontal legend items \n",
+ "You can track progress here: \n",
+ "https://github.com/plotly/plotly.js/issues/53 \n",
+ call. = FALSE
+ )
+ theme$legend.direction <- "vertical"
+ }
+
+ # justification of legend boxes
+ theme$legend.box.just <- theme$legend.box.just %||% c("center", "center")
+ # scales -> data for guides
+ gdefs <- ggfun("guides_train")(scales, theme, plot$guides, plot$labels)
+ if (length(gdefs) > 0) {
+ gdefs <- ggfun("guides_merge")(gdefs)
+ gdefs <- ggfun("guides_geom")(gdefs, layers, plot$mapping)
+ }
+
+ # colourbar -> plotly.js colorbar
+ colorbar <- compact(lapply(gdefs, gdef2trace, theme, gglayout))
+ nguides <- length(colorbar) + gglayout$showlegend
+ # If we have 2 or more guides, set x/y positions accordingly
+ if (nguides >= 2) {
+ # place legend at the bottom
+ gglayout$legend$y <- 1 / nguides
+ gglayout$legend$yanchor <- "top"
+ # adjust colorbar position(s)
+ for (i in seq_along(colorbar)) {
+ colorbar[[i]]$marker$colorbar$yanchor <- "top"
+ colorbar[[i]]$marker$colorbar$len <- 1 / nguides
+ colorbar[[i]]$marker$colorbar$y <- 1 - (i - 1) * (1 / nguides)
+ }
+ }
+ traces <- c(traces, colorbar)
+
+ # legend title annotation - https://github.com/plotly/plotly.js/issues/276
+ if (isTRUE(gglayout$showlegend)) {
+ legendTitles <- compact(lapply(gdefs, function(g) if (inherits(g, "legend")) g$title else NULL))
+ legendTitle <- paste(legendTitles, collapse = br())
+ titleAnnotation <- make_label(
+ legendTitle,
+ x = gglayout$legend$x %||% 1.02,
+ y = gglayout$legend$y %||% 1,
+ theme$legend.title,
+ xanchor = "left",
+ yanchor = "bottom",
+ # just so the R client knows this is a title
+ legendTitle = TRUE
+ )
+ gglayout$annotations <- c(gglayout$annotations, titleAnnotation)
+ # adjust the height of the legend to accomodate for the title
+ # this assumes the legend always appears below colorbars
+ gglayout$legend$y <- (gglayout$legend$y %||% 1) -
+ length(legendTitles) * unitConvert(theme$legend.title$size, "npc", "height")
+ }
+ }
+
+ # flip x/y in traces for flipped coordinates
+ # (we've already done appropriate flipping for axis objects)
+ if (inherits(plot$coordinates, "CoordFlip")) {
+ for (i in seq_along(traces)) {
+ tr <- traces[[i]]
+ # flipping logic for bar positioning is in geom2trace.GeomBar
+ if (tr$type != "bar") traces[[i]][c("x", "y")] <- tr[c("y", "x")]
+ if (tr$type %in% "box") {
+ traces[[i]]$orientation <- "h"
+ traces[[i]]$hoverinfo <- "x"
+ }
+ names(traces[[i]])[grepl("^error_y$", names(tr))] <- "error_x"
+ names(traces[[i]])[grepl("^error_x$", names(tr))] <- "error_y"
+ }
+ }
+
+ # Error bar widths in ggplot2 are on the range of the x/y scale,
+ # but plotly wants them in pixels:
+ for (xy in c("x", "y")) {
+ type <- if (xy == "x") "width" else "height"
+ err <- if (xy == "x") "error_y" else "error_x"
+ for (i in seq_along(traces)) {
+ e <- traces[[i]][[err]]
+ if (!is.null(e)) {
+ # TODO: again, "npc" is on device scale...we really want plot scale
+ w <- grid::unit(e$width %||% 0, "npc")
+ traces[[i]][[err]]$width <- unitConvert(w, "pixels", type)
+ }
+ }
+ }
+
+ # try to merge marker/line traces that have the same values for these props
+ props <- c("x", "y", "text", "type", "xaxis", "yaxis", "name")
+ hashes <- vapply(traces, function(x) digest::digest(x[names(x) %in% props]), character(1))
+ modes <- vapply(traces, function(x) x$mode %||% "", character(1))
+ nhashes <- length(unique(hashes))
+ if (nhashes < length(traces)) {
+ mergedTraces <- vector("list", nhashes)
+ for (i in unique(hashes)) {
+ idx <- which(hashes %in% i)
+ mergedTraces[[i]] <- Reduce(modify_list, traces[idx])
+ mergedTraces[[i]]$mode <- paste(
+ unique(unlist(lapply(traces[idx], "[[", "mode"))),
+ collapse = "+"
+ )
+ # show one, show all
+ show <- vapply(traces[idx], function(tr) tr$showlegend %||% TRUE, logical(1))
+ if (any(show)) {
+ mergedTraces[[i]]$showlegend <- TRUE
+ }
+ }
+ traces <- mergedTraces
+ }
+
+ # better layout defaults (TODO: provide a mechanism for templating defaults)
+ gglayout$hovermode <- "closest"
+ ax <- grep("^[x-y]axis", names(gglayout))
+ for (i in ax) {
+ gglayout[[i]]$hoverformat <- ".2f"
+ }
+ # If a trace isn't named, it shouldn't have additional hoverinfo
+ traces <- lapply(compact(traces), function(x) { x$name <- x$name %||% ""; x })
+
+ gglayout$width <- width
+ gglayout$height <- height
+ gglayout$barmode <- gglayout$barmode %||% "relative"
+
+ l <- list(
+ data = setNames(traces, NULL),
+ layout = compact(gglayout),
+ # prevent autosize on doubleClick which clears ggplot2 margins
+ config = list(doubleClick = "reset"),
+ source = source
+ )
+ # strip any existing 'AsIs' list elements of their 'AsIs' status.
+ # this is necessary since ggplot_build(qplot(1:10, fill = I("red")))
+ # returns list element with their 'AsIs' class,
+ # which conflicts with our JSON unboxing strategy.
+ l <- rm_asis(l)
+
+ # start build a plotly object with meta information about the ggplot
+ # first, translate layer mappings -> plotly attrs
+ mappingFormulas <- lapply(layers, function(x) {
+ mappings <- c(x$mapping, if (isTRUE(x$inherit.aes)) plot$mapping)
+ if (originalData) {
+ lapply(mappings, lazyeval::f_new)
+ } else {
+ nms <- names(mappings)
+ setNames(lapply(nms, function(x) lazyeval::f_new(as.name(x))), nms)
+ }
+ })
+
+ return_dat <- if (originalData) layer_data else data
+
+ # translate group aesthetics to data attributes
+ return_dat <- Map(function(x, y) {
+ if (is.null(y[["group"]])) return(x)
+ dplyr::group_by_(x, y[["group"]])
+ }, return_dat, mappingFormulas)
+
+ # don't need to add group as an attribute anymore
+ mappingFormulas <- lapply(mappingFormulas, function(x) x[!grepl("^group$", names(x))])
+
+ ids <- lapply(seq_along(data), function(x) new_id())
+ l$attrs <- setNames(mappingFormulas, ids)
+ l$attrs <- lapply(l$attrs, function(x) structure(x, class = "plotly_eval"))
+ # the build step removes the first attrs if no type exists
+ l$attrs[[1]][["type"]] <- l$data[[1]][["type"]] %||% "scatter"
+
+ l$cur_data <- ids[[layerData]]
+ l$visdat <- setNames(lapply(return_dat, function(x) function(y) x), ids)
+
+ l
+}
+
+
+#-----------------------------------------------------------------------------
+# ggplotly 'utility' functions
+#-----------------------------------------------------------------------------
+
+# convert ggplot2 sizes and grid unit(s) to pixels or normalized point coordinates
+unitConvert <- function(u, to = c("npc", "pixels"), type = c("x", "y", "height", "width")) {
+ u <- verifyUnit(u)
+
+ convert <- switch(
+ type[1],
+ x = grid::convertX,
+ y = grid::convertY,
+ width = grid::convertWidth,
+ height = grid::convertHeight
+ )
+ # convert everything to npc first
+ if (inherits(u, "margin")) {
+ # margins consist of 4 parts: top, right, bottom, and left
+ uh <- grid::convertHeight(u, "npc")
+ uw <- grid::convertWidth(u, "npc")
+ u <- grid::unit(c(uh[1], uw[2], uh[3], uw[4]), "npc")
+ } else {
+ u <- convert(u, "npc")
+ }
+ if (to[1] == "pixels") {
+ if (inherits(u, "margin")) {
+ uh <- mm2pixels(grid::convertHeight(uh, "mm"))
+ uw <- mm2pixels(grid::convertWidth(uw, "mm"))
+ u <- c(uh[1], uw[2], uh[3], uw[4])
+ } else {
+ u <- mm2pixels(convert(u, "mm"))
+ }
+ }
+ as.numeric(u)
+}
+
+# ggplot2 size is in millimeters. plotly is in pixels. To do this correctly,
+# we need to know PPI/DPI of the display. I'm not sure of a decent way to do that
+# from R, but it seems 96 is a reasonable assumption.
+mm2pixels <- function(u) {
+ u <- verifyUnit(u)
+ if (attr(u, "unit") != "mm") {
+ stop("Unit must be in millimeters")
+ }
+ (as.numeric(u) * 96) / 25.4
+}
+
+verifyUnit <- function(u) {
+ # the default unit in ggplot2 is millimeters (unless it's element_text())
+ if (is.null(attr(u, "unit"))) {
+ u <- if (inherits(u, "element")) {
+ grid::unit(u$size %||% 0, "points")
+ } else {
+ grid::unit(u %||% 0, "mm")
+ }
+ }
+ u
+}
+
+# detect a blank theme element
+is_blank <- function(x) {
+ inherits(x, "element_blank") && inherits(x, "element")
+}
+
+# given text, and x/y coordinates on 0-1 scale,
+# convert ggplot2::element_text() to plotly annotation
+make_label <- function(txt = "", x, y, el = ggplot2::element_text(), ...) {
+ if (is_blank(el) || is.null(txt) || nchar(txt) == 0 || length(txt) == 0) {
+ return(NULL)
+ }
+ angle <- el$angle %||% 0
+ list(list(
+ text = txt,
+ x = x,
+ y = y,
+ showarrow = FALSE,
+ # TODO: hjust/vjust?
+ ax = 0,
+ ay = 0,
+ font = text2font(el),
+ xref = "paper",
+ yref = "paper",
+ textangle = -angle,
+ ...
+ ))
+}
+
+has_facet <- function(x) {
+ inherits(x$facet, c("FacetGrid", "FacetWrap"))
+}
+
+#' Estimate bounding box of a rotated string
+#'
+#' @param txt a character string of length 1
+#' @param angle sets the angle of the tick labels with respect to the
+#' horizontal (e.g., `tickangle` of -90 draws the tick labels vertically)
+#' @param size vertical size of a character
+#' @references
+#' https://www.dropbox.com/s/nc6968prgw8ne4w/bbox.pdf?dl=0
+
+bbox <- function(txt = "foo", angle = 0, size = 12) {
+ # assuming the horizontal size of a character is roughly half of the vertical
+ n <- nchar(txt)
+ if (sum(n) == 0) return(list(height = 0, width = 0))
+ w <- size * (nchar(txt) / 2)
+ angle <- abs(angle %||% 0)
+ # do the sensible thing in the majority of cases
+ if (angle == 0) return(list(height = size, width = w))
+ if (angle == 90) return(list(height = w, width = size))
+ # first, compute the hypotenus
+ hyp <- sqrt(size ^ 2 + w ^ 2)
+ list(
+ height = max(hyp * cos(90 - angle), size),
+ width = max(hyp * sin(90 - angle), w)
+ )
+}
+
+# create a plotly font object from ggplot2::element_text()
+text2font <- function(x = ggplot2::element_text(), type = "height") {
+ list(
+ color = toRGB(x$colour),
+ family = x$family,
+ # TODO: what about the size of vertical text?
+ size = unitConvert(grid::unit(x$size %||% 0, "points"), "pixels", type)
+ )
+}
+
+# wrap text in bold/italics according to the text "face"
+faced <- function(txt, face = "plain") {
+ if (is.null(face)) face <- "plain"
+ x <- switch(face,
+ plain = txt,
+ bold = bold(txt),
+ italic = italic(txt),
+ bold.italic = bold(italic(txt))
+ )
+ # if, for some reason, a face we don't support is used, return the text
+ if (is.null(x)) txt else x
+}
+bold <- function(x) paste("<b>", x, "</b>")
+italic <- function(x) paste("<i>", x, "</i>")
+
+# if a vector that has one unique value (ignoring missings), return that value
+uniq <- function(x) {
+ u <- unique(x)
+ if (identical(u, NA) || length(u) == 0) return(u)
+ u <- u[!is.na(u)]
+ if (length(u) == 1) u else x
+}
+
+# theme(strip.background) -> plotly.js rect shape
+make_strip_rect <- function(xdom, ydom, theme, side = "top") {
+ rekt <- rect2shape(theme[["strip.background"]])
+ stripTextX <- theme[["strip.text.x"]] %||% theme[["strip.text"]]
+ xTextSize <- unitConvert(stripTextX$size, "npc", "width")
+ stripTextY <- theme[["strip.text.y"]] %||% theme[["strip.text"]]
+ yTextSize <- unitConvert(stripTextY$size, "npc", "height")
+ if ("right" %in% side) {
+ # x-padding should be accounted for in `layout.margin.r`
+ rekt$x0 <- xdom[2]
+ rekt$x1 <- xdom[2] + xTextSize
+ rekt$y0 <- ydom[1]
+ rekt$y1 <- ydom[2]
+ }
+ if ("top" %in% side) {
+ rekt$x0 <- xdom[1]
+ rekt$x1 <- xdom[2]
+ rekt$y0 <- ydom[2]
+ rekt$y1 <- ydom[2] + yTextSize
+ }
+ list(rekt)
+}
+
+# theme(panel.border) -> plotly.js rect shape
+make_panel_border <- function(xdom, ydom, theme) {
+ rekt <- rect2shape(theme[["panel.border"]])
+ rekt$x0 <- xdom[1]
+ rekt$x1 <- xdom[2]
+ rekt$y0 <- ydom[1]
+ rekt$y1 <- ydom[2]
+ list(rekt)
+}
+
+# element_rect -> plotly.js rect shape
+rect2shape <- function(rekt = ggplot2::element_rect()) {
+ list(
+ type = "rect",
+ fillcolor = toRGB(rekt$fill),
+ line = list(
+ color = toRGB(rekt$colour),
+ width = unitConvert(rekt, "pixels", "width"),
+ linetype = lty2dash(rekt$linetype)
+ ),
+ yref = "paper",
+ xref = "paper"
+ )
+}
+
+is_dev_ggplot2 <- function() {
+ packageVersion("ggplot2") > "2.2.1"
+}
+
+# We need access to internal ggplot2 functions in several places
+# this helps us import functions in a way that R CMD check won't cry about
+ggfun <- function(x) {
+ tryCatch(getFromNamespace(x, "ggplot2"), error = function(e) NULL)
+}
+
+ggtype <- function(x, y = "geom") {
+ sub(y, "", tolower(class(x[[y]])[1]))
+}
+
+# colourbar -> plotly.js colorbar
+gdef2trace <- function(gdef, theme, gglayout) {
+ if (inherits(gdef, "colorbar")) {
+ # sometimes the key has missing values, which we can ignore
+ gdef$key <- gdef$key[!is.na(gdef$key$.value), ]
+ rng <- range(gdef$bar$value)
+ gdef$bar$value <- scales::rescale(gdef$bar$value, from = rng)
+ gdef$key$.value <- scales::rescale(gdef$key$.value, from = rng)
+ list(
+ x = with(gglayout$xaxis, if (identical(tickmode, "auto")) ticktext else tickvals)[[1]],
+ y = with(gglayout$yaxis, if (identical(tickmode, "auto")) ticktext else tickvals)[[1]],
+ # esentially to prevent this getting merged at a later point
+ name = gdef$hash,
+ type = "scatter",
+ mode = "markers",
+ opacity = 0,
+ hoverinfo = "none",
+ showlegend = FALSE,
+ # do everything on a 0-1 scale
+ marker = list(
+ color = c(0, 1),
+ colorscale = setNames(gdef$bar[c("value", "colour")], NULL),
+ colorbar = list(
+ bgcolor = toRGB(theme$legend.background$fill),
+ bordercolor = toRGB(theme$legend.background$colour),
+ borderwidth = unitConvert(
+ theme$legend.background$size, "pixels", "width"
+ ),
+ thickness = unitConvert(
+ theme$legend.key.width, "pixels", "width"
+ ),
+ title = gdef$title,
+ titlefont = text2font(gdef$title.theme %||% theme$legend.title),
+ tickmode = "array",
+ ticktext = gdef$key$.label,
+ tickvals = gdef$key$.value,
+ tickfont = text2font(gdef$label.theme %||% theme$legend.text),
+ ticklen = 2,
+ len = 1/2
+ )
+ )
+ )
+ } else {
+ # if plotly.js gets better support for multiple legends,
+ # that conversion should go here
+ NULL
+ }
+}
diff --git a/R/group2NA.R b/R/group2NA.R
new file mode 100644
index 0000000..9511f96
--- /dev/null
+++ b/R/group2NA.R
@@ -0,0 +1,99 @@
+#' Separate groups with missing values
+#'
+#' This function is used internally by plotly, but may also be useful to some
+#' power users. The details section explains when and why this function is useful.
+#'
+#' @details If a group of scatter traces share the same non-positional characteristics
+#' (i.e., color, fill, etc), it is more efficient to draw them as a single trace
+#' with missing values that separate the groups (instead of multiple traces),
+#' In this case, one should also take care to make sure
+#' \href{https://plot.ly/r/reference/#scatter-connectgaps}{connectgaps}
+#' is set to `FALSE`.
+#'
+#' @param data a data frame.
+#' @param groupNames character vector of grouping variable(s)
+#' @param nested other variables that group should be nested
+#' (i.e., ordered) within.
+#' @param ordered a variable to arrange by (within nested & groupNames). This
+#' is useful primarily for ordering by x
+#' @param retrace.first should the first row of each group be appended to the
+#' last row? This is useful for enclosing polygons with lines.
+#' @export
+#' @return a data.frame with rows ordered by: `nested`,
+#' then `groupNames`, then `ordered`. As long as `groupNames`
+#' contains valid variable names, new rows will also be inserted to separate
+#' the groups.
+#' @examples
+#'
+#' # note the insertion of new rows with missing values
+#' group2NA(mtcars, "vs", "cyl")
+#'
+#' # need to group lines by city somehow!
+#' plot_ly(txhousing, x = ~date, y = ~median) %>% add_lines()
+#'
+#' # instead of using group_by(), you could use group2NA()
+#' tx <- group2NA(txhousing, "city")
+#' plot_ly(tx, x = ~date, y = ~median) %>% add_lines()
+#'
+#' # add_lines() will ensure paths are sorted by x, but this is equivalent
+#' tx <- group2NA(txhousing, "city", ordered = "date")
+#' plot_ly(tx, x = ~date, y = ~median) %>% add_paths()
+#'
+
+group2NA <- function(data, groupNames = "group", nested = NULL, ordered = NULL,
+ retrace.first = inherits(data, "GeomPolygon")) {
+
+ if (NROW(data) == 0) return(data)
+
+ # evaluate this lazy argument now (in case we change class of data)
+ retrace <- force(retrace.first)
+
+ # sanitize variable names (TODO: throw warnings if non-existing vars are referenced?)
+ groupNames <- groupNames[groupNames %in% names(data)]
+ nested <- nested[nested %in% names(data)]
+ ordered <- ordered[ordered %in% names(data)]
+
+ # for restoring class information on exit
+ datClass <- oldClass(data)
+
+ dt <- data.table::as.data.table(data)
+
+ # if group doesn't exist, just order the rows and exit
+ if (!length(groupNames)) {
+ keyVars <- c(nested, ordered)
+ if (length(keyVars)) data.table::setorderv(dt, cols = keyVars)
+ return(structure(dt, class = datClass))
+ }
+
+ # order the rows
+ data.table::setorderv(dt, cols = c(nested, groupNames, ordered))
+
+ # when connectgaps=FALSE, inserting NAs ensures each "group"
+ # will be visually distinct https://plot.ly/r/reference/#scatter-connectgaps
+ # also, retracing is useful for creating polygon(s) via scatter trace(s)
+ keyVars <- c(nested, groupNames)
+ keyNum <- length(keyVars) + 1
+ idx <- if (retrace) {
+ dt[, c(.I, .I[1], NA), by = keyVars][[keyNum]]
+ } else {
+ dt[, c(.I, NA), by = keyVars][[keyNum]]
+ }
+ dt <- dt[idx]
+
+ # remove NAs that unnecessarily seperate nested groups
+ # (at least internally, nested really tracks trace index, meaning we don't need
+ # to seperate them)
+ NAidx <- which(is.na(idx))
+ for (i in seq_along(keyVars)) {
+ dt[[keyVars[[i]]]][NAidx] <- dt[[keyVars[[i]]]][NAidx - 1]
+ }
+ if (length(nested)) {
+ dt <- dt[ dt[, .I[-.N], by = nested][[length(nested) + 1]] ]
+ } else {
+ dt <- dt[-.N]
+ }
+
+ structure(dt, class = datClass)
+}
+
+utils::globalVariables(c(".I", ".N"))
diff --git a/R/helpers.R b/R/helpers.R
new file mode 100644
index 0000000..afa92e1
--- /dev/null
+++ b/R/helpers.R
@@ -0,0 +1,217 @@
+#' Modify the colorbar
+#'
+#' @param p a plotly object
+#' @param ... arguments are documented here
+#' \url{https://plot.ly/r/reference/#scatter-marker-colorbar}.
+#' @param limits numeric vector of length 2. Set the extent of the colorbar scale.
+#' @param which colorbar to modify? Should only be relevant for subplots with
+#' multiple colorbars.
+#' @author Carson Sievert
+#' @export
+#' @examples
+#'
+#' p <- plot_ly(mtcars, x = ~wt, y = ~mpg, color = ~cyl)
+#'
+#' # pass any colorbar attribute --
+#' # https://plot.ly/r/reference/#scatter-marker-colorbar
+#' colorbar(p, len = 0.5)
+#'
+#' # Expand the limits of the colorbar
+#' colorbar(p, limits = c(0, 20))
+#' # values outside the colorbar limits are considered "missing"
+#' colorbar(p, limits = c(5, 6))
+#'
+#' # also works on colorbars generated via a z value
+#' corr <- cor(diamonds[vapply(diamonds, is.numeric, logical(1))])
+#' plot_ly(x = rownames(corr), y = colnames(corr), z = corr) %>%
+#' add_heatmap() %>%
+#' colorbar(limits = c(-1, 1))
+
+colorbar <- function(p, ..., limits = NULL, which = 1) {
+ colorbar_built(plotly_build(p), ..., limits = limits, which = which)
+}
+
+colorbar_built <- function(p, ..., limits = NULL, which = 1) {
+
+ isBar <- vapply(p$x$data, is.colorbar, logical(1))
+ if (sum(isBar) == 0) {
+ warning("Didn't find a colorbar to modify.", call. = FALSE)
+ return(p)
+ }
+
+ indicies <- which(isBar)[which]
+
+ for (i in indicies) {
+
+ tr <- p$x$data[[i]]
+ hasZcolor <- inherits(tr, "zcolor")
+
+ # retrain limits of the colorscale
+ if (!is.null(limits)) {
+ limits <- sort(limits)
+ if (hasZcolor) {
+ z <- p$x$data[[i]][["z"]]
+ if (!is.null(dz <- dim(z))) {
+ z <- c(z)
+ }
+ z[z < limits[1] | limits[2] < z] <- NA
+ if (!is.null(dz)) dim(z) <- dz
+ p$x$data[[i]]$z <- z
+ p$x$data[[i]]$zmin <- limits[1]
+ p$x$data[[i]]$zmax <- limits[2]
+ } else {
+ # since the colorscale is in a different trace, retrain all traces
+ p$x$data <- lapply(p$x$data, function(x) {
+ col <- x$marker[["color"]]
+ x$marker[["color"]][col < limits[1] | limits[2] < col] <- NA
+ x$marker[["cmin"]] <- limits[1]
+ x$marker[["cmax"]] <- limits[2]
+ x
+ })
+ }
+ }
+
+ # pass along ... to the colorbar
+ if (hasZcolor) {
+ p$x$data[[i]]$colorbar <- modify_list(tr$colorbar, list(...))
+ } else {
+ p$x$data[[i]]$marker$colorbar <- modify_list(tr$marker$colorbar, list(...))
+ }
+ }
+
+ p
+}
+
+
+#' Hide guides (legends and colorbars)
+#'
+#' @param p a plotly object.
+#' @export
+#' @seealso [hide_legend()], [hide_colorbar()]
+#'
+
+hide_guides <- function(p) {
+ hide_legend(hide_colorbar(p))
+}
+
+
+#' Hide color bar(s)
+#'
+#' @param p a plotly object.
+#' @export
+#' @seealso [hide_legend()]
+#' @examples
+#'
+#' p <- plot_ly(mtcars, x = ~wt, y = ~cyl, color = ~cyl)
+#' hide_colorbar(p)
+#'
+hide_colorbar <- function(p) {
+ p <- plotly_build(p)
+ for (i in seq_along(p$x$data)) {
+ trace <- p$x$data[[i]]
+ if (has_attr(trace$type, "showscale")) {
+ p$x$data[[i]]$showscale <- FALSE
+ }
+ if (has_attr(trace$type, "marker")) {
+ p$x$data[[i]]$marker$showscale <- FALSE
+ }
+ }
+ p
+}
+
+#' Hide legend
+#'
+#' @param p a plotly object.
+#' @export
+#' @seealso [hide_colorbar()]
+#' @examples
+#'
+#' p <- plot_ly(mtcars, x = ~wt, y = ~cyl, color = ~factor(cyl))
+#' hide_legend(p)
+
+hide_legend <- function(p) {
+ if (ggplot2::is.ggplot(p)) {
+ p <- plotly_build(p)
+ }
+ p$x$.hideLegend <- TRUE
+ p
+}
+
+#' Convert trace types to WebGL
+#'
+#' @param p a plotly or ggplot object.
+#' @export
+#' @examples
+#'
+#' # currently no bargl trace type
+#' toWebGL(qplot(1:10))
+#' toWebGL(qplot(1:10, 1:10))
+#'
+toWebGL <- function(p) {
+ if (ggplot2::is.ggplot(p)) {
+ p <- plotly_build(p)
+ }
+ p$x$.plotlyWebGl <- TRUE
+ p
+}
+
+
+#' Create a complete empty plotly graph.
+#'
+#' Useful when used with [subplot()]
+#'
+#' @param ... arguments passed onto [plot_ly()]
+#'
+#' @export
+plotly_empty <- function(...) {
+ eaxis <- list(
+ showticklabels = FALSE,
+ showgrid = FALSE,
+ zeroline = FALSE
+ )
+ layout(plot_ly(...), xaxis = eaxis, yaxis = eaxis)
+}
+
+
+#' Convert a raster object to a data URI
+#'
+#' Convenient embedding images via [layout()]
+#' \href{images}{https://plot.ly/r/reference/#layout-images}.
+#'
+#' @param r an object coercable to a raster object via [as.raster()]
+#' @param ... arguments passed onto [as.raster()].
+#' @author Carson Sievert
+#' @export
+#' @examples
+#'
+#' # a red gradient (from ?as.raster)
+#' r <- as.raster(matrix(hcl(0, 80, seq(50, 80, 10)), nrow = 4, ncol = 5))
+#' plot(r)
+#'
+#' # embed the raster as an image
+#' plot_ly(x = 1, y = 1) %>%
+#' layout(
+#' images = list(list(
+#' source = raster2uri(r),
+#' xref = "paper",
+#' yref = "paper",
+#' x = 0, y = 0,
+#' sizex = 0.5, sizey = 0.5,
+#' xanchor = "left", yanchor = "bottom"
+#' ))
+#' )
+
+raster2uri <- function(r, ...) {
+ try_library("png", "raster2uri")
+ # should be 4 x n matrix
+ r <- grDevices::as.raster(r, ...)
+ rgbs <- col2rgb(c(r), alpha = T) / 255
+ nr <- dim(r)[1]
+ nc <- dim(r)[2]
+ reds <- matrix(rgbs[1, ], nrow = nr, ncol = nc, byrow = TRUE)
+ greens <- matrix(rgbs[2, ], nrow = nr, ncol = nc, byrow = TRUE)
+ blues <- matrix(rgbs[3, ], nrow = nr, ncol = nc, byrow = TRUE)
+ alphas <- matrix(rgbs[4, ], nrow = nr, ncol = nc, byrow = TRUE)
+ png <- array(c(reds, greens, blues, alphas), dim = c(dim(r), 4))
+ base64enc::dataURI(png::writePNG(png), mime = "image/png")
+}
diff --git a/R/highlight.R b/R/highlight.R
new file mode 100644
index 0000000..d5d4df9
--- /dev/null
+++ b/R/highlight.R
@@ -0,0 +1,215 @@
+#' Query graphical elements in multiple linked views
+#'
+#' This function sets a variety of options for brushing (i.e., highlighting)
+#' multiple plots. These options are primarily designed for linking
+#' multiple plotly graphs, and may not behave as expected when linking
+#' plotly to another htmlwidget package via crosstalk. In some cases,
+#' other htmlwidgets will respect these options, such as persistent selection
+#' in leaflet (see `demo("highlight-leaflet", package = "plotly")`).
+#'
+#' @param p a plotly visualization.
+#' @param on turn on a selection on which event(s)? To disable on events
+#' altogether, use `NULL`. Currently the following are supported:
+#' \itemize{
+#' \item `'plotly_click'`
+#' \item `'plotly_hover'`
+#' \item `'plotly_selected'`: triggered through rectangular
+#' (layout.dragmode = 'select') or lasso (layout.dragmode = 'lasso') brush.
+#' Currently only works for scatter traces with mode 'markers'.
+#' }
+#' @param off turn off a selection on which event(s)? To disable off
+#' events altogether, use `NULL`. Currently the following are supported:
+#' \itemize{
+#' \item `'plotly_doubleclick'`: triggered on a double mouse click while
+#' (layout.dragmode = 'zoom') or (layout.dragmode = 'pan')
+#' \item `'plotly_deselect'`: triggered on a double mouse click while
+#' (layout.dragmode = 'select') or (layout.dragmode = 'lasso')
+#' \item `'plotly_relayout'`: triggered whenever axes are rescaled
+#' (i.e., clicking the home button in the modebar) or whenever the height/width
+#' of the plot changes.
+#' }
+#' @param persistent should selections persist (i.e., accumulate)?
+#' @param dynamic should a widget for changing selection colors be included?
+#' @param color character string of color(s) to use for
+#' highlighting selections. See [toRGB()] for valid color
+#' specifications. If `NULL` (the default), the color of selected marks
+#' are not altered.
+#' @param selectize provide a selectize.js widget for selecting keys? Note that
+#' the label used for this widget derives from the groupName of the SharedData object.
+#' @param defaultValues a vector of values for setting a "default selection".
+#' These values should match the key attribute.
+#' @param opacityDim a number between 0 and 1 used to reduce the
+#' opacity of non-selected traces (by multiplying with the existing opacity).
+#' @param selected attributes of the selection, see [attrs_selected()].
+#' @param ... currently not supported.
+#' @export
+#' @author Carson Sievert
+#' @references \url{https://cpsievert.github.io/plotly_book/linking-views-without-shiny.html}
+#' @seealso [attrs_selected()]
+#' @examples
+#'
+#' # These examples are designed to show you how to highlight/brush a *single*
+#' # view. For examples of multiple linked views, see `demo(package = "plotly")`
+#'
+#'
+#' library(crosstalk)
+#' d <- SharedData$new(txhousing, ~city)
+#' p <- ggplot(d, aes(date, median, group = city)) + geom_line()
+#' gg <- ggplotly(p, tooltip = "city")
+#' highlight(gg, persistent = TRUE, dynamic = TRUE)
+#'
+#' # supply custom colors to the brush
+#' cols <- toRGB(RColorBrewer::brewer.pal(3, "Dark2"), 0.5)
+#' highlight(
+#' gg, on = "plotly_hover", color = cols, persistent = TRUE, dynamic = TRUE
+#' )
+#'
+#' # Use attrs_selected() for complete control over the selection appearance
+#' # note any relevant colors you specify here should override the color argument
+#' s <- attrs_selected(
+#' showlegend = TRUE,
+#' mode = "lines+markers",
+#' marker = list(symbol = "x")
+#' )
+#'
+#' highlight(
+#' layout(gg, showlegend = TRUE),
+#' selected = s, persistent = TRUE
+#' )
+#'
+
+highlight <- function(p, on = "plotly_click", off,
+ persistent = getOption("persistent", FALSE),
+ dynamic = FALSE, color = NULL,
+ selectize = FALSE, defaultValues = NULL,
+ opacityDim = getOption("opacityDim", 0.2),
+ selected = attrs_selected(), ...) {
+
+ # currently ... is not-supported and will catch
+ # some arguments we supported at one point
+ dots <- list(...)
+ if (length(dots)) {
+ warning(
+ "The following arguments are not supported:\n",
+ toString(names(dots)), "\n",
+ "Arguments such as: hoverinfo and showInLegend \n",
+ "have been replaced by selected and other",
+ call. = FALSE
+ )
+ }
+
+ if (opacityDim < 0 || 1 < opacityDim) {
+ stop("opacityDim must be between 0 and 1", call. = FALSE)
+ }
+ if (dynamic && length(color) < 2) {
+ message("Adding more colors to the selection color palette")
+ color <- c(color, RColorBrewer::brewer.pal(4, "Set1"))
+ }
+ if (!dynamic && length(color) > 1) {
+ warning(
+ "Can only use a single color for selections when dynamic=FALSE",
+ call. = FALSE
+ )
+ color <- color[1]
+ }
+
+ # attach HTML dependencies (these libraries are used in the HTMLwidgets.renderValue() method)
+ # TODO: only attach these when keys are present!
+ if (selectize) {
+ p$dependencies <- c(p$dependencies, list(selectizeLib()))
+ }
+ if (dynamic) {
+ p$dependencies <- c(p$dependencies, list(colourPickerLib()))
+ }
+
+
+ # TODO: expose unhover?
+ off_options <- paste0(
+ "plotly_", c("doubleclick", "deselect", "relayout")
+ )
+ if (missing(off)) {
+ off_default <- switch(
+ on %||% "",
+ plotly_selected = "plotly_deselect",
+ plotly_click = "plotly_doubleclick",
+ plotly_hover = "plotly_doubleclick"
+ )
+ off <- default(off_default %||% "plotly_relayout")
+ }
+
+ # main (non-plotly.js) spec passed along to HTMLwidgets.renderValue()
+ p$x$highlight <- list(
+ # NULL may be used to disable on/off events
+ on = if (!is.null(on)) match.arg(on, paste0("plotly_", c("click", "hover", "selected"))),
+ off = if (is.default(off)) off else if (!is.null(off)) match.arg(off, off_options),
+ persistent = persistent,
+ dynamic = dynamic,
+ # TODO: convert to hex...see colourpicker:::formatHEX()
+ color = toRGB(color),
+ selectize = selectize,
+ defaultValues = defaultValues,
+ opacityDim = opacityDim,
+ selected = selected
+ )
+
+ p
+}
+
+#' Specify attributes of selection traces
+#'
+#' By default the name of the selection trace derives from the selected values.
+#'
+#'
+#' @param opacity a number between 0 and 1 specifying the overall opacity of
+#' the selected trace
+#' @param ... other trace attributes attached to the selection trace.
+#' @export
+#' @author Carson Sievert
+
+attrs_selected <- function(opacity = 1, ...) {
+ if (opacity < 0 || 1 < opacity) {
+ stop("opacity must be between 0 and 1", call. = FALSE)
+ }
+
+ args <- list(
+ opacity = opacity
+ )
+
+ # TODO: verify attr names... maybe that should happen in the build step?
+ dots <- list(...)
+
+
+ c(dots, args)
+}
+
+
+# ----------------------------------------------------------------------------
+# Utility functions
+# ----------------------------------------------------------------------------
+
+
+highlight_defaults <- function() {
+ args <- formals(highlight)[-1]
+ # have to evaluate args now that some of them are functions...
+ compact(lapply(args, function(x) tryNULL(eval(x))))
+}
+
+selectizeLib <- function(bootstrap = TRUE) {
+ htmltools::htmlDependency(
+ "selectize", "0.12.0", depPath("selectize"),
+ stylesheet = if (bootstrap) "selectize.bootstrap3.css",
+ script = "selectize.min.js"
+ )
+}
+
+colourPickerLib <- function() {
+ htmltools::htmlDependency(
+ "colourpicker", "1.1", depPath("colourpicker"),
+ stylesheet = "colourpicker.min.css",
+ script = "colourpicker.min.js"
+ )
+}
+
+depPath <- function(...) {
+ system.file('htmlwidgets', 'lib', ..., package = 'plotly')
+}
diff --git a/R/imports.R b/R/imports.R
new file mode 100644
index 0000000..3e184f1
--- /dev/null
+++ b/R/imports.R
@@ -0,0 +1,176 @@
+#' @import ggplot2
+#' @importFrom grDevices col2rgb extendrange dev.list dev.off rgb as.raster
+#' @importFrom graphics layout
+#' @importFrom utils getFromNamespace modifyList data packageVersion browseURL str
+#' @importFrom stats setNames complete.cases quantile is.leaf
+#' @importFrom tidyr unnest
+#' @importFrom viridisLite viridis
+#' @importFrom jsonlite toJSON fromJSON
+#' @importFrom httr GET POST PATCH content config add_headers stop_for_status
+#' @importFrom htmlwidgets createWidget sizingPolicy saveWidget onRender prependContent
+#' @importFrom lazyeval f_eval is_formula all_dots is_lang f_new
+#' @importFrom tibble as_tibble
+#' @importFrom htmltools browsable tagList tags
+#' @importFrom purrr transpose
+#' @importFrom tools file_ext file_path_sans_ext
+#' @importFrom data.table as.data.table setorderv
+NULL
+
+
+#' @importFrom dplyr mutate
+#' @name mutate
+#' @rdname reexports
+#' @export
+dplyr::mutate
+
+#' @importFrom dplyr mutate_
+#' @name mutate_
+#' @rdname reexports
+#' @export
+dplyr::mutate_
+
+#' @importFrom dplyr transmute
+#' @name transmute
+#' @rdname reexports
+#' @export
+dplyr::transmute
+
+#' @importFrom dplyr transmute_
+#' @name transmute_
+#' @rdname reexports
+#' @export
+dplyr::transmute_
+
+#' @importFrom dplyr select
+#' @name select
+#' @rdname reexports
+#' @export
+dplyr::select
+
+#' @importFrom dplyr select_
+#' @name select_
+#' @rdname reexports
+#' @export
+dplyr::select_
+
+#' @importFrom dplyr rename
+#' @name rename
+#' @rdname reexports
+#' @export
+dplyr::rename
+
+#' @importFrom dplyr rename_
+#' @name rename_
+#' @rdname reexports
+#' @export
+dplyr::rename_
+
+#' @importFrom dplyr group_by
+#' @name group_by
+#' @rdname reexports
+#' @export
+dplyr::group_by
+
+#' @importFrom dplyr group_by_
+#' @name group_by_
+#' @rdname reexports
+#' @export
+dplyr::group_by_
+
+#' @importFrom dplyr groups
+#' @name groups
+#' @rdname reexports
+#' @export
+dplyr::groups
+
+#' @importFrom dplyr ungroup
+#' @name ungroup
+#' @rdname reexports
+#' @export
+dplyr::ungroup
+
+#' @importFrom dplyr summarise
+#' @name summarise
+#' @rdname reexports
+#' @export
+dplyr::summarise
+
+#' @importFrom dplyr summarise_
+#' @name summarise_
+#' @rdname reexports
+#' @export
+dplyr::summarise_
+
+#' @importFrom dplyr do
+#' @name do
+#' @rdname reexports
+#' @export
+dplyr::do
+
+#' @importFrom dplyr do_
+#' @name do_
+#' @rdname reexports
+#' @export
+dplyr::do_
+
+#' @importFrom dplyr arrange
+#' @name arrange
+#' @rdname reexports
+#' @export
+dplyr::arrange
+
+#' @importFrom dplyr arrange_
+#' @name arrange_
+#' @rdname reexports
+#' @export
+dplyr::arrange_
+
+#' @importFrom dplyr distinct
+#' @name distinct
+#' @rdname reexports
+#' @export
+dplyr::distinct
+
+#' @importFrom dplyr distinct_
+#' @name distinct_
+#' @rdname reexports
+#' @export
+dplyr::distinct_
+
+#' @importFrom dplyr slice
+#' @name slice
+#' @rdname reexports
+#' @export
+dplyr::slice
+
+#' @importFrom dplyr slice_
+#' @name slice_
+#' @rdname reexports
+#' @export
+dplyr::slice_
+
+#' @importFrom dplyr filter
+#' @name filter
+#' @rdname reexports
+#' @export
+dplyr::filter
+
+#' @importFrom dplyr filter_
+#' @name filter_
+#' @rdname reexports
+#' @export
+dplyr::filter_
+
+# waiting on https://github.com/tidyverse/tidyr/pull/229
+#
+# #' @importFrom tidyr gather
+# #' @name gather
+# #' @rdname reexports
+# #' @export
+# tidyr::gather
+#
+# #' @importFrom tidyr gather_
+# #' @name gather_
+# #' @rdname reexports
+# #' @export
+# tidyr::gather_
diff --git a/R/last_plot.R b/R/last_plot.R
new file mode 100644
index 0000000..26bdbcf
--- /dev/null
+++ b/R/last_plot.R
@@ -0,0 +1,21 @@
+# Same as here, thanks Hadley --
+# https://github.com/hadley/ggplot2/blob/8aa578/R/plot-last.r
+
+.plot_store <- function() {
+ .last_plot <- NULL
+
+ list(
+ get = function() .last_plot,
+ set = function(value) .last_plot <<- value
+ )
+}
+.store <- .plot_store()
+
+# Set last plot created or modified
+set_last_plot <- function(value) .store$set(value)
+
+#' Retrieve the last plot to be modified or created.
+#'
+#' @seealso [ggplot2::last_plot()]
+#' @export
+last_plot <- function() .store$get()
diff --git a/R/layers2layout.R b/R/layers2layout.R
new file mode 100644
index 0000000..cf5afac
--- /dev/null
+++ b/R/layers2layout.R
@@ -0,0 +1,29 @@
+# convert layout-specific geoms/layers
+layers2layout <- function(gglayout, layers, layout) {
+ geoms <- sapply(layers, function(x) class(x[["geom"]])[1])
+ RasterGeom <- which(geoms %in% "GeomRasterAnn")
+ for (i in RasterGeom) {
+ params <- layers[[i]]$geom_params
+ for (j in seq_len(nrow(layout))) {
+ lay <- layout[j, ]
+
+ img <- list(
+ source = raster2uri(params$raster),
+ # TODO: ask plotly.js to implement explicit placement between traces?
+ layer = if (RasterGeom / length(geoms) > 0.5) "above" else "below",
+ xref = sub("axis", "", lay[["xaxis"]]),
+ yref = sub("axis", "", lay[["yaxis"]]),
+ x = params$xmin,
+ xanchor = "left",
+ sizex = with(params, abs(xmax - xmin)),
+ y = params$ymin,
+ yanchor = "bottom",
+ sizey = with(params, abs(ymax - ymin)),
+ sizing = "stretch"
+ )
+ gglayout$images <- c(gglayout$images, list(img))
+ }
+ }
+ # TODO: maybe we could support a subset of grobs in GeomCustomAnn?
+ gglayout
+}
diff --git a/R/layers2traces.R b/R/layers2traces.R
new file mode 100644
index 0000000..3d962ba
--- /dev/null
+++ b/R/layers2traces.R
@@ -0,0 +1,1082 @@
+# layer -> trace conversion
+layers2traces <- function(data, prestats_data, layout, p) {
+ # Attach a "geom class" to each layer of data for method dispatch
+ data <- Map(function(x, y) prefix_class(x, class(y$geom)[1]), data, p$layers)
+
+ # Extract parameters (and "hovertext aesthetics") in each layer
+ params <- Map(function(x, y) {
+ param <- c(
+ y[["geom_params"]], y[["stat_params"]], y[["aes_params"]],
+ position = ggtype(y, "position")
+ )
+
+ # by default, show all user-specified and generated aesthetics in hovertext
+ stat_aes <- y$stat$default_aes
+ map <- c(y$mapping, stat_aes[grepl("^\\.\\.", as.character(stat_aes))])
+ # add on plot-level mappings, if they're inherited
+ if (isTRUE(y$inherit.aes)) map <- c(map, p$mapping)
+ # "hidden" names should be taken verbatim
+ idx <- grepl("^\\.\\.", map) & grepl("\\.\\.$", map)
+ map <- setNames(
+ sub("^\\.\\.", "", sub("\\.\\.$", "", as.character(map))),
+ names(map)
+ )
+ if (!identical(p$tooltip, "all")) {
+ map <- map[names(map) %in% p$tooltip | map %in% p$tooltip]
+ }
+ # throw out positional coordinates if we're hovering on fill
+ if (identical("fills", hover_on(x))) {
+ map <- map[!names(map) %in% c("x", "xmin", "xmax", "y", "ymin", "ymax")]
+ }
+ # disregard geometry mapping in hovertext for GeomSf
+ if ("GeomSf" %in% class(y$geom)) {
+ map <- map[!names(map) %in% "geometry"]
+ }
+ param[["hoverTextAes"]] <- map
+ param
+ }, data, p$layers)
+
+ hoverTextAes <- lapply(params, "[[", "hoverTextAes")
+ # attach a new column (hovertext) to each layer of data
+ # (mapped to the text trace property)
+ data <- Map(function(x, y) {
+ if (nrow(x) == 0) return(x)
+ # make sure the relevant aes exists in the data
+ for (i in seq_along(y)) {
+ aesName <- names(y)[[i]]
+ if (!aesName %in% names(x)) next
+ # TODO: should we be getting the name from scale_*(name) first?
+ varName <- y[[i]]
+ # "automatically" generated group aes is not informative
+ if (identical("group", unique(varName, aesName))) next
+ # add a line break if hovertext already exists
+ if ("hovertext" %in% names(x)) x$hovertext <- paste0(x$hovertext, br())
+ # text aestheic should be taken verbatim (for custom tooltips)
+ prefix <- if (identical(aesName, "text")) "" else paste0(varName, ": ")
+ # look for the domain, if that's not found, provide the range (useful for identity scales)
+ txt <- x[[paste0(aesName, "_plotlyDomain")]] %||% x[[aesName]]
+ suffix <- tryNULL(format(txt, justify = "none")) %||% ""
+ # put the height of the bar in the tooltip
+ if (inherits(x, "GeomBar") && identical(aesName, "y")) {
+ suffix <- format(x[["ymax"]] - x[["ymin"]], justify = "none")
+ }
+ x$hovertext <- paste0(x$hovertext, prefix, suffix)
+ }
+ x$hovertext <- x$hovertext %||% ""
+ x
+ }, data, hoverTextAes)
+
+ # draw legends only for discrete scales
+ discreteScales <- list()
+ for (sc in p$scales$non_position_scales()$scales) {
+ if (sc$is_discrete()) {
+ discreteScales[[sc$aesthetics]] <- sc
+ }
+ }
+ # Convert "high-level" geoms to their "low-level" counterpart
+ # This may involve preprocessing the data, for example:
+ # 1. geom_line() is really geom_path() with data sorted by x
+ # 2. geom_smooth() is really geom_path() + geom_ribbon()
+ datz <- list()
+ paramz <- list()
+ layout <- if (is_dev_ggplot2()) layout else list(layout = layout)
+ for (i in seq_along(data)) {
+ # This has to be done in a loop, since some layers are really two layers,
+ # (and we need to replicate the data/params in those cases)
+ set <- attr(data[[i]], "set")
+ d <- to_basic(data[[i]], prestats_data[[i]], layout, params[[i]], p)
+ d <- structure(d, set = set)
+ if (is.data.frame(d)) d <- list(d)
+ for (j in seq_along(d)) {
+ datz <- c(datz, d[j])
+ paramz <- c(paramz, params[i])
+ }
+ }
+ # now to the actual layer -> trace conversion
+ trace.list <- list()
+ for (i in seq_along(datz)) {
+ d <- datz[[i]]
+ # variables that produce multiple traces and deserve their own legend entries
+ split_legend <- paste0(names(discreteScales), "_plotlyDomain")
+ # add variable that produce multiple traces, but do _not_ deserve entries
+ split_by <- c(split_legend, "PANEL", "frame", split_on(d))
+ # ensure the factor level orders (which determines traces order)
+ # matches the order of the domain values
+ split_vars <- intersect(split_by, names(d))
+ lvls <- unique(d[split_vars])
+ lvls <- lvls[do.call(order, lvls), , drop = FALSE]
+ separator <- new_id()
+ fac <- factor(
+ apply(d[split_vars], 1, paste, collapse = separator),
+ levels = apply(lvls, 1, paste, collapse = separator)
+ )
+ if (all(is.na(fac))) fac <- 1
+ dl <- split(d, fac, drop = TRUE)
+ # list of traces for this layer
+ trs <- Map(geom2trace, dl, paramz[i], list(p))
+ # attach the crosstalk group/set
+ trs <- Map(function(x, y) { x$set <- attr(y, "set"); x}, trs, dl)
+ # if we need a legend, set name/legendgroup/showlegend
+ # note: this allows us to control multiple traces from one legend entry
+ if (any(split_legend %in% names(d))) {
+ nms <- strsplit(names(trs), separator, fixed = TRUE)
+ nms <- vapply(nms, function(x) {
+ y <- unique(x[seq_along(split_legend)])
+ if (length(y) > 1) paste0("(", paste(y, collapse = ","), ")") else y
+ }, character(1))
+ trs <- Map(function(x, y) {
+ x$name <- y
+ x$legendgroup <- y
+ # depending on the geom (e.g. smooth) this may be FALSE already
+ x$showlegend <- x$showlegend %||% TRUE
+ x
+ }, trs, nms)
+ } else {
+ trs <- lapply(trs, function(x) { x$showlegend <- FALSE; x })
+ }
+
+ # each trace is with respect to which axis?
+ for (j in seq_along(trs)) {
+ panel <- unique(dl[[j]]$PANEL)
+ trs[[j]]$xaxis <- sub("axis", "", layout$layout[panel, "xaxis"])
+ trs[[j]]$yaxis <- sub("axis", "", layout$layout[panel, "yaxis"])
+ }
+ trace.list <- c(trace.list, trs)
+ }
+ trace.list
+}
+
+
+#' Convert a geom to a "basic" geom.
+#'
+#' This function makes it possible to convert ggplot2 geoms that
+#' are not included with ggplot2 itself. Users shouldn't need to use
+#' this function. It exists purely to allow other package authors to write
+#' their own conversion method(s).
+#'
+#' @param data the data returned by `ggplot2::ggplot_build()`.
+#' @param prestats_data the data before statistics are computed.
+#' @param layout the panel layout.
+#' @param params parameters for the geom, statistic, and 'constant' aesthetics
+#' @param p a ggplot2 object (the conversion may depend on scales, for instance).
+#' @param ... currently ignored
+#' @export
+to_basic <- function(data, prestats_data, layout, params, p, ...) {
+ UseMethod("to_basic")
+}
+
+#' @export
+to_basic.GeomCol <- function(data, prestats_data, layout, params, p, ...) {
+ prefix_class(data, "GeomBar")
+}
+
+#' @export
+to_basic.GeomViolin <- function(data, prestats_data, layout, params, p, ...) {
+ n <- nrow(data)
+ revData <- data[order(data[["y"]], decreasing = TRUE), ]
+ idx <- !names(data) %in% c("x", "xmin", "xmax")
+ data <- rbind(
+ cbind(x = data[["x"]] - data$violinwidth / 2, data[, idx]),
+ cbind(x = revData[["x"]] + revData$violinwidth / 2, revData[, idx])
+ )
+ if (!is.null(data$hovertext)) data$hovertext <- paste0(data$hovertext, br())
+ data$hovertext <- paste0(
+ data$hovertext, "density: ", format(data$density, justify = "none")
+ )
+ prefix_class(data, c("GeomPolygon", "GeomViolin"))
+}
+
+#' @export
+to_basic.GeomBoxplot <- function(data, prestats_data, layout, params, p, ...) {
+ aez <- names(GeomBoxplot$default_aes)
+ for (i in aez) {
+ prestats_data[[i]] <- NULL
+ }
+ vars <- c("PANEL", "group", "key", aez, grep("_plotlyDomain$", names(data), value = T))
+ prefix_class(
+ merge(prestats_data, data[names(data) %in% vars], by = c("PANEL", "group"), sort = FALSE),
+ "GeomBoxplot"
+ )
+}
+
+#' @export
+to_basic.GeomSmooth <- function(data, prestats_data, layout, params, p, ...) {
+ if (nrow(data) == 0) {
+ return(prefix_class(data, "GeomBlank"))
+ }
+ dat <- prefix_class(data, "GeomPath")
+ # alpha for the path is always 1 (see GeomSmooth$draw_key)
+ dat$alpha <- 1
+ if (!identical(params$se, FALSE)) {
+ dat2 <- prefix_class(ribbon_dat(data), c("GeomPolygon", "GeomSmooth"))
+ dat2$colour <- NULL
+ dat <- list(dat, dat2)
+ }
+ dat
+}
+
+#' @export
+to_basic.GeomRibbon <- function(data, prestats_data, layout, params, p, ...) {
+ prefix_class(ribbon_dat(data), "GeomPolygon")
+}
+
+#' @export
+to_basic.GeomArea <- function(data, prestats_data, layout, params, p, ...) {
+ prefix_class(ribbon_dat(data), "GeomPolygon")
+}
+
+#' @export
+to_basic.GeomDensity <- function(data, prestats_data, layout, params, p, ...) {
+ prefix_class(ribbon_dat(data), "GeomPolygon")
+}
+
+#' @export
+to_basic.GeomLine <- function(data, prestats_data, layout, params, p, ...) {
+ data <- data[order(data[["x"]]), ]
+ prefix_class(data, "GeomPath")
+}
+
+#' @export
+to_basic.GeomStep <- function(data, prestats_data, layout, params, p, ...) {
+ prefix_class(data, "GeomPath")
+}
+
+#' @export
+to_basic.GeomSegment <- function(data, prestats_data, layout, params, p, ...) {
+ # Every row is one segment, we convert to a line with several
+ # groups which can be efficiently drawn by adding NA rows.
+ data$group <- seq_len(nrow(data))
+ others <- data[!names(data) %in% c("x", "y", "xend", "yend")]
+ data <- with(data, {
+ rbind(cbind(x, y, others),
+ cbind(x = xend, y = yend, others))
+ })
+ prefix_class(data, "GeomPath")
+}
+
+#' @export
+to_basic.GeomRect <- function(data, prestats_data, layout, params, p, ...) {
+ data$group <- seq_len(nrow(data))
+ others <- data[!names(data) %in% c("xmin", "ymin", "xmax", "ymax", "y", "x")]
+ dat <- with(data, {
+ rbind(cbind(x = xmin, y = ymin, others),
+ cbind(x = xmin, y = ymax, others),
+ cbind(x = xmax, y = ymax, others),
+ cbind(x = xmax, y = ymin, others))
+ })
+ prefix_class(dat, c("GeomPolygon", "GeomRect"))
+}
+
+#' @export
+to_basic.GeomSf <- function(data, prestats_data, layout, params, p, ...) {
+
+ data <- expand(data)
+
+ # determine the type of simple feature for each row
+ # recode the simple feature with the type of geometry used to render it
+ data[[".plotlySfType"]] <- sapply(data$geometry, function(x) class(x)[2])
+ dat <- dplyr::mutate(
+ data, .plotlySfType = dplyr::recode(.plotlySfType,
+ MULTIPOLYGON = "GeomPolygon",
+ MULTILINESTRING = "GeomLine",
+ MULTIPOINT = "GeomPoint",
+ POLYGON = "GeomPolygon",
+ LINESTRING = "GeomLine",
+ POINT = "GeomPoint"
+ ))
+
+ # return a list of data frames...one for every geometry (a la, GeomSmooth)
+ d <- split(dat, dat[[".plotlySfType"]])
+ for (i in seq_along(d)) {
+ d[[i]] <- prefix_class(d[[i]], names(d)[[i]])
+ }
+ if (length(d) == 1) d[[1]] else d
+}
+utils::globalVariables(c(".plotlySfType"))
+
+#' @export
+to_basic.GeomMap <- function(data, prestats_data, layout, params, p, ...) {
+ common <- intersect(data$map_id, params$map$id)
+ data <- data[data$map_id %in% common, , drop = FALSE]
+ map <- params$map[params$map$id %in% common, , drop = FALSE]
+ # TODO: do we need coord_munch() as in GeomMap$draw_panel()
+ data$id <- data$map_id
+ data$map_id <- NULL
+ data$group <- NULL
+ data <- merge(data, map, by = "id", sort = FALSE)
+ data$group <- interaction(data[names(data) %in% c("PANEL", "group", "id")])
+ prefix_class(data, c("GeomPolygon", "GeomMap"))
+}
+
+#' @export
+to_basic.GeomAnnotationMap <- function(data, prestats_data, layout, params, p, ...) {
+ # TODO: we could/should? reduce this data down to the panel limits, but
+ # probably more effort than it's worth
+ d <- params$map
+
+ # add hovertext
+ hasRegion <- isTRUE(p$tooltip %in% c("all", "region"))
+ hasSubRegion <- isTRUE(p$tooltip %in% c("all", "subregion"))
+ d$hovertext <- d$hovertext %||% paste0(
+ if (hasRegion) d$region, if (hasSubRegion) paste0(br(), d$subregion)
+ )
+ prefix_class(d, c("GeomPolygon", "GeomAnnotationMap"))
+}
+
+#' @export
+to_basic.GeomRaster <- function(data, prestats_data, layout, params, p, ...) {
+ data <- prefix_class(data, "GeomTile")
+ to_basic(data, prestats_data, layout, params)
+}
+
+#' @export
+to_basic.GeomRasterAnn <- function(data, prestats_data, layout, params, p, ...) {
+ # rasters are handled in ggplotly.R since they are layout specific
+ prefix_class(data, "GeomBlank")
+}
+
+#' @export
+to_basic.GeomTile <- function(data, prestats_data, layout, params, p, ...) {
+ # geom2trace.GeomTile is a heatmap, which requires continuous fill
+ if (is.discrete(prestats_data$fill)) {
+ data <- prefix_class(data, "GeomRect")
+ to_basic(data, prestats_data, layout, params, p)
+ } else {
+ data
+ }
+}
+
+#' @export
+to_basic.GeomHex <- function(data, prestats_data, layout, params, p, ...) {
+ # see ggplot2:::hexGrob
+ dx <- resolution(data[["x"]], FALSE)
+ dy <- resolution(data[["y"]], FALSE)/sqrt(3)/2 * 1.15
+ hexC <- hexbin::hexcoords(dx, dy, n = 1)
+ n <- nrow(data)
+ data$size <- ifelse(data$size < 1, data$size ^ (1 / 6), data$size ^ 6)
+ x <- rep.int(hexC[["x"]], n) * rep(data$size, each = 6) + rep(data[["x"]], each = 6)
+ y <- rep.int(hexC[["y"]], n) * rep(data$size, each = 6) + rep(data[["y"]], each = 6)
+ data <- data[rep(seq_len(n), each = 6), ]
+ data[["x"]] <- x
+ data[["y"]] <- y
+ data$group <- rep(seq_len(n), each = 6)
+ prefix_class(data, c("GeomPolygon", "GeomHex"))
+}
+
+#' @export
+to_basic.GeomContour <- function(data, prestats_data, layout, params, p, ...) {
+ if (!"fill" %in% names(data)) data$fill <- NA
+ prefix_class(data, "GeomPath")
+}
+
+#' @export
+to_basic.GeomDensity2d <- function(data, prestats_data, layout, params, p, ...) {
+ if ("hovertext" %in% names(data)) {
+ data$hovertext <- paste0(data$hovertext, br())
+ }
+ data$hovertext <- paste0(
+ data$hovertext, "Level: ", format(data$level, justify = "none")
+ )
+ if (!"fill" %in% names(data)) data$fill <- NA
+ prefix_class(data, "GeomPath")
+}
+
+#' @export
+to_basic.GeomAbline <- function(data, prestats_data, layout, params, p, ...) {
+ # ugh, we can't trust the group here
+ data$group <- interaction(
+ data[!grepl("group", names(data)) & !vapply(data, anyNA, logical(1))]
+ )
+ lay <- tidyr::gather_(layout$layout, "variable", "x", c("x_min", "x_max"))
+ data <- merge(lay[c("PANEL", "x")], data, by = "PANEL")
+ data[["y"]] <- with(data, intercept + slope * x)
+ prefix_class(data, c("GeomHline", "GeomPath"))
+}
+
+#' @export
+to_basic.GeomHline <- function(data, prestats_data, layout, params, p, ...) {
+ # ugh, we can't trust the group here
+ data$group <- do.call(paste,
+ data[!grepl("group", names(data)) & !vapply(data, anyNA, logical(1))]
+ )
+ lay <- tidyr::gather_(layout$layout, "variable", "x", c("x_min", "x_max"))
+ data <- merge(lay[c("PANEL", "x")], data, by = "PANEL")
+ data[["y"]] <- data$yintercept
+ prefix_class(data, c("GeomHline", "GeomPath"))
+}
+
+#' @export
+to_basic.GeomVline <- function(data, prestats_data, layout, params, p, ...) {
+ # ugh, we can't trust the group here
+ data$group <- do.call(paste,
+ data[!grepl("group", names(data)) & !vapply(data, anyNA, logical(1))]
+ )
+ lay <- tidyr::gather_(layout$layout, "variable", "y", c("y_min", "y_max"))
+ data <- merge(lay[c("PANEL", "y")], data, by = "PANEL")
+ data[["x"]] <- data$xintercept
+ prefix_class(data, c("GeomVline", "GeomPath"))
+}
+
+#' @export
+to_basic.GeomJitter <- function(data, prestats_data, layout, params, p, ...) {
+ prefix_class(data, "GeomPoint")
+}
+
+
+#' @export
+to_basic.GeomErrorbar <- function(data, prestats_data, layout, params, p, ...) {
+ # width for ggplot2 means size of the entire bar, on the data scale
+ # (plotly.js wants half, in pixels)
+ data <- merge(data, layout$layout, by = "PANEL", sort = FALSE)
+ data$width <- (data[["xmax"]] - data[["x"]]) /(data[["x_max"]] - data[["x_min"]])
+ data$fill <- NULL
+ prefix_class(data, "GeomErrorbar")
+}
+
+#' @export
+to_basic.GeomErrorbarh <- function(data, prestats_data, layout, params, p, ...) {
+ # height for ggplot2 means size of the entire bar, on the data scale
+ # (plotly.js wants half, in pixels)
+ data <- merge(data, layout$layout, by = "PANEL", sort = FALSE)
+ data$width <- (data[["ymax"]] - data[["y"]]) / (data[["y_max"]] - data[["y_min"]])
+ data$fill <- NULL
+ prefix_class(data, "GeomErrorbarh")
+}
+
+#' @export
+to_basic.GeomLinerange <- function(data, prestats_data, layout, params, p, ...) {
+
+ if (!is.null(data[["y"]])) {
+ data$width <- 0
+ return(prefix_class(data, "GeomErrorbar"))
+ }
+
+ # reshape data so that x/y reflect path data
+ data$group <- seq_len(nrow(data))
+ data <- tidyr::gather_(data, "recodeVariable", "y", c("ymin", "ymax"))
+ data <- data[order(data$group), ]
+ # fix the hovertext (by removing the "irrelevant" aesthetic)
+ recodeMap <- p$mapping[dplyr::recode(data[["recodeVariable"]], "ymax" = "ymin", "ymin" = "ymax")]
+ data$hovertext <- Map(function(x, y) {
+ paste(x[!grepl(y, x)], collapse = br())
+ }, strsplit(data$hovertext, br()), paste0("^", recodeMap, ":"))
+
+ prefix_class(data, "GeomPath")
+}
+
+#' @export
+to_basic.GeomPointrange <- function(data, prestats_data, layout, params, p, ...) {
+ data$width <- 0
+ list(
+ prefix_class(data, "GeomErrorbar"),
+ prefix_class(data, "GeomPoint")
+ )
+}
+
+#' @export
+to_basic.GeomDotplot <- function(data, prestats_data, layout, params, p, ...) {
+ if (identical(params$binaxis, "y")) {
+ dotdia <- params$dotsize * data$binwidth[1]/(layout$layout$y_max - layout$layout$y_min)
+ data$size <- as.numeric(grid::convertHeight(grid::unit(dotdia, "npc"), "mm")) / 2
+ data$x <- (data$countidx - 0.5) * (as.numeric(dotdia) * 6)
+ } else {
+ dotdia <- params$dotsize * data$binwidth[1]/(layout$layout$x_max - layout$layout$x_min)
+ data$size <- as.numeric(grid::convertWidth(grid::unit(dotdia, "npc"), "mm")) / 2
+ # TODO: why times 6?!?!
+ data$y <- (data$countidx - 0.5) * (as.numeric(dotdia) * 6)
+ }
+ prefix_class(data, "GeomPoint")
+}
+
+#' @export
+to_basic.GeomSpoke <- function(data, prestats_data, layout, params, p, ...) {
+ # if radius/angle are a constant, still add them to the hovertext
+ # NOTE: it'd be more accurate, but more complicated, to use the aes mapping
+ for (var in c("radius", "angle")) {
+ if (length(unique(data[[var]])) != 1) next
+ data[["hovertext"]] <- paste0(
+ data[["hovertext"]], br(), var, ": ", format(data[[var]], justify = "none")
+ )
+ }
+ prefix_class(to_basic.GeomSegment(data), "GeomSpoke")
+}
+
+#' @export
+to_basic.GeomCrossbar <- function(data, prestats_data, layout, params, p, ...) {
+ # from GeomCrossbar$draw_panel()
+ middle <- transform(data, x = xmin, xend = xmax, yend = y, size = size * params$fatten, alpha = NA)
+ list(
+ prefix_class(to_basic.GeomRect(data), "GeomCrossbar"),
+ prefix_class(to_basic.GeomSegment(middle), "GeomCrossbar")
+ )
+}
+utils::globalVariables(c("xmin", "xmax", "y", "size"))
+
+#' @export
+to_basic.GeomRug <- function(data, prestats_data, layout, params, p, ...) {
+ # allow the tick length to vary across panels
+ layout <- layout$layout
+ layout$tickval_y <- 0.03 * abs(layout$y_max - layout$y_min)
+ layout$tickval_x <- 0.03 * abs(layout$x_max - layout$x_min)
+ data <- merge(data, layout[c("PANEL", "x_min", "x_max", "y_min", "y_max", "tickval_y", "tickval_x")])
+
+ # see GeomRug$draw_panel()
+ rugs <- list()
+ sides <- params$sides
+ others <- data[!names(data) %in% c("x", "y")]
+ if (!is.null(data[["x"]])) {
+ if (grepl("b", sides)) {
+ rugs$b <- with(
+ data, data.frame(
+ x = x,
+ xend = x,
+ y = y_min,
+ yend = y_min + tickval_y,
+ others
+ )
+ )
+ }
+ if (grepl("t", sides)) {
+ rugs$t <- with(
+ data, data.frame(
+ x = x,
+ xend = x,
+ y = y_max - tickval_y,
+ yend = y_max,
+ others
+ )
+ )
+ }
+ }
+ if (!is.null(data[["y"]])) {
+ if (grepl("l", sides)) {
+ rugs$l <- with(
+ data, data.frame(
+ x = x_min,
+ xend = x_min + tickval_x,
+ y = y,
+ yend = y,
+ others
+ )
+ )
+ }
+ if (grepl("r", sides)) {
+ rugs$r <- with(
+ data, data.frame(
+ x = x_max - tickval_x,
+ xend = x_max,
+ y = y,
+ yend = y,
+ others
+ )
+ )
+ }
+ }
+
+ lapply(rugs, function(d) {
+ prefix_class(to_basic.GeomSegment(d), "GeomRug")
+ })
+}
+
+#' @export
+to_basic.GeomQuantile <- function(data, prestats_data, layout, params, p, ...){
+ dat <- split(data, data$quantile)
+ dat <- lapply(dat, prefix_class, y = "GeomPath")
+ dat
+}
+
+#' @export
+to_basic.default <- function(data, prestats_data, layout, params, p, ...) {
+ data
+}
+
+#' Convert a "basic" geoms to a plotly.js trace.
+#'
+#' This function makes it possible to convert ggplot2 geoms that
+#' are not included with ggplot2 itself. Users shouldn't need to use
+#' this function. It exists purely to allow other package authors to write
+#' their own conversion method(s).
+#'
+#' @param data the data returned by `plotly::to_basic`.
+#' @param params parameters for the geom, statistic, and 'constant' aesthetics
+#' @param p a ggplot2 object (the conversion may depend on scales, for instance).
+#' @export
+geom2trace <- function(data, params, p) {
+ if (nrow(data) == 0) return(geom2trace.GeomBlank(data, params, p))
+ UseMethod("geom2trace")
+}
+
+#' @export
+geom2trace.GeomBlank <- function(data, params, p) {
+ list(visible = FALSE)
+}
+
+#' @export
+geom2trace.GeomPath <- function(data, params, p) {
+ data <- group2NA(data)
+ L <- list(
+ x = data[["x"]],
+ y = data[["y"]],
+ text = uniq(data[["hovertext"]]),
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ type = "scatter",
+ mode = "lines",
+ name = if (inherits(data, "GeomSmooth")) "fitted values",
+ line = list(
+ # TODO: line width array? -- https://github.com/plotly/plotly.js/issues/147
+ width = aes2plotly(data, params, "size")[1],
+ color = toRGB(
+ aes2plotly(data, params, "colour"),
+ aes2plotly(data, params, "alpha")
+ ),
+ dash = aes2plotly(data, params, "linetype")
+ ),
+ hoveron = hover_on(data)
+ )
+ if (inherits(data, "GeomStep")) L$line$shape <- params$direction %||% "hv"
+ compact(L)
+}
+
+#' @export
+geom2trace.GeomPoint <- function(data, params, p) {
+ shape <- aes2plotly(data, params, "shape")
+ color <- aes2plotly(data, params, "colour")
+ isDotPlot <- inherits(data, "GeomDotplot")
+ L <- list(
+ x = data[["x"]],
+ y = data[["y"]],
+ text = if (isDotPlot) data[["key"]] else uniq(data[["hovertext"]]),
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ type = "scatter",
+ mode = "markers",
+ marker = list(
+ autocolorscale = FALSE,
+ color = color,
+ opacity = aes2plotly(data, params, "alpha"),
+ size = aes2plotly(data, params, "size"),
+ symbol = shape,
+ line = list(
+ width = aes2plotly(data, params, "stroke"),
+ color = color
+ )
+ ),
+ hoveron = hover_on(data)
+ )
+ # fill is only relevant for pch %in% 21:25
+ pch <- uniq(data$shape) %||% params$shape %||% GeomPoint$default_aes$shape
+ if (any(idx <- pch %in% 21:25) || any(idx <- !is.null(data[["fill_plotlyDomain"]]))) {
+ L$marker$color[idx] <- aes2plotly(data, params, "fill")[idx]
+ }
+ compact(L)
+}
+
+#' @export
+geom2trace.GeomBar <- function(data, params, p) {
+ # TODO: does position play a role here?
+ #pos <- params$position %||% "stack"
+ flip <- inherits(p$coordinates, "CoordFlip")
+
+ if (!flip) {
+ width <- with(data, xmax - xmin)
+ # TODO: does this cause rounding issues when inverse transforming for dynamicTicks?
+ x <- with(data, (xmax + xmin) / 2)
+ base <- data[["ymin"]]
+ y <- with(data, ymax - ymin)
+ } else {
+ width <- with(data, xmax - xmin)
+ # TODO: does this cause rounding issues when inverse transforming for dynamicTicks?
+ y <- with(data, (xmax + xmin) / 2)
+ base <- data[["ymin"]]
+ x <- with(data, ymax - ymin)
+ }
+
+ compact(list(
+ orientation = if (flip) "h" else "v",
+ width = width,
+ base = base,
+ x = x,
+ y = y,
+ text = uniq(data[["hovertext"]]),
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ type = "bar",
+ marker = list(
+ autocolorscale = FALSE,
+ color = toRGB(
+ aes2plotly(data, params, "fill"),
+ aes2plotly(data, params, "alpha")
+ ),
+ line = list(
+ width = aes2plotly(data, params, "size"),
+ color = aes2plotly(data, params, "colour")
+ )
+ )
+ ))
+}
+
+#' @export
+geom2trace.GeomPolygon <- function(data, params, p) {
+
+ data <- group2NA(data)
+
+ L <- list(
+ x = data[["x"]],
+ y = data[["y"]],
+ text = uniq(data[["hovertext"]]),
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ type = "scatter",
+ mode = "lines",
+ line = list(
+ width = aes2plotly(data, params, "size"),
+ color = toRGB(
+ aes2plotly(data, params, "colour"),
+ aes2plotly(data, params, "alpha")
+ ),
+ dash = aes2plotly(data, params, "linetype")
+ ),
+ fill = "toself",
+ fillcolor = toRGB(
+ aes2plotly(data, params, "fill"),
+ aes2plotly(data, params, "alpha")
+ ),
+ hoveron = hover_on(data)
+ )
+ if (inherits(data, "GeomSmooth")) L$hoverinfo <- "x+y"
+ if (inherits(data, "GeomCrossbar")) L$hoverinfo <- "none"
+ compact(L)
+}
+
+#' @export
+geom2trace.GeomBoxplot <- function(data, params, p) {
+ compact(list(
+ x = data[["x"]],
+ y = data[["y"]],
+ hoverinfo = "y",
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ type = "box",
+ fillcolor = toRGB(
+ aes2plotly(data, params, "fill"),
+ aes2plotly(data, params, "alpha")
+ ),
+ # marker styling must inherit from GeomPoint$default_aes
+ # https://github.com/hadley/ggplot2/blob/ab42c2ca81458b0cf78e3ba47ed5db21f4d0fc30/NEWS#L73-L77
+ marker = list(
+ opacity = GeomPoint$default_aes$alpha,
+ outliercolor = toRGB(GeomPoint$default_aes$colour),
+ line = list(
+ width = mm2pixels(GeomPoint$default_aes$stroke),
+ color = toRGB(GeomPoint$default_aes$colour)
+ ),
+ size = mm2pixels(GeomPoint$default_aes$size)
+ ),
+ line = list(
+ color = aes2plotly(data, params, "colour"),
+ width = aes2plotly(data, params, "size")
+ )
+ ))
+}
+
+
+#' @export
+geom2trace.GeomText <- function(data, params, p) {
+ compact(list(
+ x = data[["x"]],
+ y = data[["y"]],
+ text = data[["label"]],
+ hovertext = data[["hovertext"]],
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ textfont = list(
+ # TODO: how to translate fontface/family?
+ size = aes2plotly(data, params, "size"),
+ color = toRGB(
+ aes2plotly(data, params, "colour"),
+ aes2plotly(data, params, "alpha")
+ )
+ ),
+ type = "scatter",
+ mode = "text",
+ hoveron = hover_on(data)
+ ))
+}
+
+#' @export
+geom2trace.GeomTile <- function(data, params, p) {
+ x <- sort(unique(data[["x"]]))
+ y <- sort(unique(data[["y"]]))
+ # make sure we're dealing with a complete grid
+ g <- expand.grid(x = x, y = y)
+ g$order <- seq_len(nrow(g))
+ g <- merge(g, data, by = c("x", "y"), all.x = TRUE)
+ g <- g[order(g$order), ]
+ # put fill domain on 0-1 scale for colorscale purposes
+ g$fill_plotlyDomain <- scales::rescale(g$fill_plotlyDomain)
+ # create the colorscale
+ colScale <- unique(g[, c("fill_plotlyDomain", "fill")])
+ # colorscale goes crazy if there are NAs
+ colScale <- colScale[stats::complete.cases(colScale), ]
+ colScale <- colScale[order(colScale$fill_plotlyDomain), ]
+ compact(list(
+ x = x,
+ y = y,
+ z = matrix(g$fill_plotlyDomain, nrow = length(y), ncol = length(x), byrow = TRUE),
+ text = matrix(g$hovertext, nrow = length(y), ncol = length(x), byrow = TRUE),
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ colorscale = setNames(colScale, NULL),
+ type = "heatmap",
+ showscale = FALSE,
+ autocolorscale = FALSE
+ ))
+}
+
+#' @export
+geom2trace.GeomErrorbar <- function(data, params, p) {
+ make_error(data, params, "y")
+}
+
+#' @export
+geom2trace.GeomErrorbarh <- function(data, params, p) {
+ make_error(data, params, "x")
+}
+
+#' @export
+geom2trace.default <- function(data, params, p) {
+ warning(
+ "geom_", class(data)[1], "() has yet to be implemented in plotly.\n",
+ " If you'd like to see this geom implemented,\n",
+ " Please open an issue with your example code at\n",
+ " https://github.com/ropensci/plotly/issues"
+ )
+ list()
+}
+
+# ---------------------------------------------------------------------------
+# Utility functions
+# --------------------------------------------------------------------------
+
+# given a geom, should we split on any continuous variables?
+# this is necessary for some geoms, for example, polygons
+# since plotly.js can't draw two polygons with different fill in a single trace
+split_on <- function(dat) {
+ lookup <- list(
+ GeomHline = c("linetype", "colour", "size"),
+ GeomVline = c("linetype", "colour", "size"),
+ GeomAbline = c("linetype", "colour", "size"),
+ GeomPath = c("fill", "colour", "size"),
+ GeomPolygon = c("fill", "colour", "size"),
+ GeomBar = "fill",
+ GeomBoxplot = c("colour", "fill", "size"),
+ GeomErrorbar = "colour",
+ GeomErrorbarh = "colour",
+ GeomText = "colour"
+ )
+ # try to split on the domain (for sensible trace ordering)
+ for (i in names(lookup)) {
+ domainName <- paste0(lookup[[i]], "_plotlyDomain")
+ idx <- domainName %in% names(dat)
+ lookup[[i]][idx] <- domainName[idx]
+ }
+ # search all the classes for relevant splits (moving from specific->generic)
+ splits <- NULL
+ for (i in class(dat)) {
+ splits <- splits %||% lookup[[i]]
+ }
+ # if hovering on fill, we need to split on hovertext
+ if (identical(hover_on(dat), "fills")) {
+ splits <- c(splits, "hovertext")
+ }
+ # make sure the variable is in the data, and is non-constant
+ splits <- splits[splits %in% names(dat)]
+ # is there more than one unique value for this aes split in the data?
+ for (i in splits) {
+ if (length(unique(dat[, i])) < 2) {
+ splits <- setdiff(splits, i)
+ }
+ }
+ splits
+}
+
+# given a geom, are we hovering over points or fill?
+hover_on <- function(data) {
+ if (inherits(data, c("GeomHex", "GeomRect", "GeomMap", "GeomMosaic", "GeomAnnotationMap")) ||
+ # is this a "basic" polygon?
+ identical("GeomPolygon", grep("^Geom", class(data), value = T))) {
+ "fills"
+ } else {
+ "points"
+ }
+}
+
+# make trace with errorbars
+make_error <- function(data, params, xy = "x") {
+ color <- aes2plotly(data, params, "colour")
+ e <- list(
+ x = data[["x"]],
+ y = data[["y"]],
+ text = uniq(data[["hovertext"]]),
+ key = data[["key"]],
+ frame = data[["frame"]],
+ ids = data[["ids"]],
+ type = "scatter",
+ mode = "lines",
+ opacity = aes2plotly(data, params, "alpha"),
+ line = list(color = "transparent")
+ )
+ e[[paste0("error_", xy)]] <- list(
+ array = data[[paste0(xy, "max")]] - data[[xy]],
+ arrayminus = data[[xy]] - data[[paste0(xy, "min")]],
+ type = "data",
+ width = data$width[1] / 2,
+ symmetric = FALSE,
+ color = color
+ )
+ compact(e)
+}
+
+# function to transform geom_ribbon data into format plotly likes
+# (note this function is also used for geom_smooth)
+ribbon_dat <- function(dat) {
+ n <- nrow(dat)
+ o <- order(dat[["x"]])
+ o2 <- order(dat[["x"]], decreasing = TRUE)
+ used <- c("x", "ymin", "ymax", "y")
+ not_used <- setdiff(names(dat), used)
+ # top-half of ribbon
+ tmp <- dat[o, ]
+ others <- tmp[not_used]
+ dat1 <- cbind(x = tmp[["x"]], y = tmp[["ymin"]], others)
+ dat1[n+1, ] <- data.frame(x = tmp[["x"]][n], y = tmp[["ymin"]][n], others[n, ])
+ # bottom-half of ribbon
+ tmp2 <- dat[o2, ]
+ others2 <- tmp2[not_used]
+ dat2 <- cbind(x = tmp2[["x"]], y = tmp2[["ymax"]], others2)
+ structure(rbind(dat1, dat2), class = oldClass(dat))
+}
+
+aes2plotly <- function(data, params, aes = "size") {
+ geom <- class(data)[1]
+ vals <- uniq(data[[aes]]) %||% params[[aes]] %||%
+ ggfun(geom)$default_aes[[aes]] %||% NA
+ converter <- switch(
+ aes,
+ size = mm2pixels,
+ stroke = mm2pixels,
+ colour = toRGB,
+ fill = toRGB,
+ linetype = lty2dash,
+ shape = pch2symbol,
+ alpha = function(x) { x[is.na(x)] <- 1; x },
+ width = function(x) { x / 2},
+ height = function(x) { x / 2}
+ )
+ if (is.null(converter)) {
+ warning("A converter for ", aes, " wasn't found. \n",
+ "Please report this issue to: \n",
+ "https://github.com/ropensci/plotly/issues/new", call. = FALSE)
+ converter <- identity
+ }
+ converter(vals)
+}
+
+# Convert R pch point codes to plotly "symbol" codes.
+pch2symbol <- function(x) {
+ lookup <- list(
+ "0" = "square-open",
+ "1" = "circle-open",
+ "2" = "triangle-up-open",
+ "3" = "cross-thin-open",
+ "4" = "x-thin-open",
+ "5" = "diamond-open",
+ "6" = "triangle-down-open",
+ "7" = "square-x-open",
+ "8" = "asterisk-open",
+ "9" = "diamond-x-open",
+ "10" = "circle-cross-open",
+ "11" = "hexagram-open",
+ "12" = "square-cross-open",
+ "13" = "circle-x-open",
+ "14" = "square-open-dot",
+ "15" = "square",
+ "16" = "circle",
+ "17" = "triangle-up",
+ "18" = "diamond",
+ "19" = "circle",
+ "20" = "circle",
+ "21" = "circle",
+ "22" = "square",
+ "23" = "diamond",
+ "24" = "triangle-up",
+ "25" = "triangle-down",
+ "32" = "circle",
+ "35" = "hash-open",
+ "42" = "asterisk-open",
+ "43" = "cross-thin-open",
+ "45" = "line-ew-open",
+ "47" = "line-ne-open",
+ "48" = "circle-open",
+ "79" = "circle-open",
+ "88" = "x-thin-open",
+ "92" = "line-nw-open",
+ "95" = "line-ew-open",
+ "111" = "circle-open",
+ "o" = "circle-open",
+ "O" = "circle-open",
+ "+" = "cross-thin-open"
+ )
+ x <- as.character(x)
+ idx <- x %in% names(lookup)
+ if (any(idx)) {
+ x[idx] <- lookup[x[idx]]
+ }
+ as.character(x)
+}
+
+# Convert R lty line type codes to plotly "dash" codes.
+lty2dash <- function(x) {
+ lookup <- list(
+ "0" = "none",
+ "1" = "solid",
+ "2" = "dash",
+ "3" = "dot",
+ "4" = "dashdot",
+ "5" = "longdash",
+ "6" = "longdashdot",
+ "blank" = "none",
+ "solid" = "solid",
+ "dashed" = "dash",
+ "dotted" = "dot",
+ "dotdash" = "dashdot",
+ "longdash" = "longdash",
+ "twodash" = "longdashdot",
+ "22" = "dash",
+ "42" = "dot",
+ "44" = "dashdot",
+ "13" = "longdash",
+ "1343" = "longdashdot",
+ "73" = "dash",
+ "2262" = "dashdot" ,
+ "12223242" = "dashdot" ,
+ "F282" = "dash",
+ "F4448444" = "dash",
+ "224282F2" = "dash",
+ "F1" = "dash"
+ )
+ x <- as.character(x)
+ idx <- x %in% names(lookup)
+ if (any(idx)) {
+ x[idx] <- lookup[x[idx]]
+ }
+ as.character(x)
+}
diff --git a/R/layout.R b/R/layout.R
new file mode 100644
index 0000000..5873104
--- /dev/null
+++ b/R/layout.R
@@ -0,0 +1,118 @@
+#' Modify the layout of a plotly visualization
+#'
+#' @param p A plotly object.
+#' @param ... Arguments to the layout object. For documentation,
+#' see \url{https://plot.ly/r/reference/#Layout_and_layout_style_objects}
+#' @param data A data frame to associate with this layout (optional). If not
+#' provided, arguments are evaluated using the data frame in [plot_ly()].
+#' @author Carson Sievert
+#' @export
+layout <- function(p, ..., data = NULL) {
+ UseMethod("layout")
+}
+
+#' @export
+layout.matrix <- function(p, ..., data = NULL) {
+ # workaround for the popular graphics::layout() function
+ # https://github.com/ropensci/plotly/issues/464
+ graphics::layout(p, ...)
+}
+
+#' @export
+layout.shiny.tag.list <- function(p, ..., data = NULL) {
+ idx <- which(vapply(p, is.plotly, logical(1)))
+ for (i in idx) {
+ p[[i]] <- layout.plotly(p[[i]], ..., data = NULL)
+ }
+ p
+}
+
+#' @export
+layout.plotly <- function(p, ..., data = NULL) {
+ p <- add_data(p, data)
+ attrs <- list(...)
+ if (!is.null(attrs[["height"]]) || !is.null(attrs[["width"]])) {
+ warning("Specifying width/height in layout() is now deprecated.\n",
+ "Please specify in ggplotly() or plot_ly()", call. = FALSE)
+ }
+ # similar to add_trace()
+ p$x$layoutAttrs <- c(
+ p$x$layoutAttrs %||% list(),
+ setNames(list(attrs), p$x$cur_data)
+ )
+ p
+}
+
+#' Add a range slider to the x-axis
+#'
+#' @param p plotly object.
+#' @param start a start date/value.
+#' @param end an end date/value.
+#' @param ... these arguments are documented here
+#' \url{https://plot.ly/r/reference/#layout-xaxis-rangeslider}
+#' @export
+#' @author Carson Sievert
+#' @examples
+#'
+#' plot_ly(x = time(USAccDeaths), y = USAccDeaths) %>%
+#' add_lines() %>%
+#' rangeslider()
+#'
+#' d <- tibble::tibble(
+#' time = seq(as.Date("2016-01-01"), as.Date("2016-08-31"), by = "days"),
+#' y = rnorm(seq_along(time))
+#' )
+#'
+#' plot_ly(d, x = ~time, y = ~y) %>%
+#' add_lines() %>%
+#' rangeslider(d$time[5], d$time[50])
+#'
+#'
+rangeslider <- function(p, start = NULL, end = NULL, ...) {
+ if (sum(grepl("^xaxis", names(p$x$layout))) > 1) {
+ stop("Can only add a rangeslider to a plot with one x-axis", call. = FALSE)
+ }
+
+ p$x$layout$xaxis$range <- c(
+ to_milliseconds(start),
+ to_milliseconds(end)
+ )
+
+ p$x$layout$xaxis$rangeslider <- list(visible = TRUE, ...)
+ p
+}
+
+
+#' Set the default configuration for plotly
+#'
+#' @param p a plotly object
+#' @param ... these arguments are documented at
+#' \url{https://github.com/plotly/plotly.js/blob/master/src/plot_api/plot_config.js}
+#' @param collaborate include the collaborate mode bar button (unique to the R pkg)?
+#' @param cloud include the send data to cloud button?
+#' @author Carson Sievert
+#' @export
+#' @examples
+#'
+#' config(plot_ly(), displaylogo = FALSE, collaborate = FALSE)
+#'
+
+config <- function(p, ..., collaborate = TRUE, cloud = FALSE) {
+
+ p$x$config <- modify_list(p$x$config, list(...))
+
+ nms <- sapply(p$x$config[["modeBarButtonsToAdd"]], "[[", "name")
+ hasCollab <- sharingButton()[["name"]] %in% nms
+
+ if (collaborate && !hasCollab) {
+ nAdd <- length(p$x$config[["modeBarButtonsToAdd"]])
+ p$x$config[["modeBarButtonsToAdd"]][[nAdd + 1]] <- sharingButton()
+ }
+ if (!collaborate) {
+ p$x$config[["modeBarButtonsToAdd"]][nms %in% sharingButton()[["name"]]] <- NULL
+ }
+
+ p$x$config$cloud <- cloud
+
+ p
+}
diff --git a/R/modeBarButtons.R b/R/modeBarButtons.R
new file mode 100644
index 0000000..f2482d3
--- /dev/null
+++ b/R/modeBarButtons.R
@@ -0,0 +1,23 @@
+sharingButton <- function() {
+ url <- 'https://cpsievert.github.io/plotly_book/plot-ly-for-collaboration.html'
+ list(
+ name = "Collaborate",
+ icon = list(
+ width = 1000,
+ ascent = 500,
+ descent = -50,
+ path = sharingPath
+ ),
+ click = htmlwidgets::JS(sprintf(
+ "function(gd) {
+ // is this being viewed in RStudio?
+ if (location.search == '?viewer_pane=1') {
+ alert('To learn about plotly for collaboration, visit:\\n %s');
+ } else {
+ window.open('%s', '_blank');
+ }
+ }", url, url))
+ )
+}
+
+sharingPath <- 'M487 375c7-10 9-23 5-36l-79-259c-3-12-11-23-22-31-11-8-22-12-35-12l-263 0c-15 0-29 5-43 15-13 10-23 23-28 37-5 13-5 25-1 37 0 0 0 3 1 7 1 5 1 8 1 11 0 2 0 4-1 6 0 3-1 5-1 6 1 2 2 4 3 6 1 2 2 4 4 6 2 3 4 5 5 7 5 7 9 16 13 26 4 10 7 19 9 26 0 2 0 5 0 9-1 4-1 6 0 8 0 2 2 5 4 8 3 3 5 5 5 7 4 6 8 15 12 26 4 11 7 19 7 26 1 1 0 4 0 9-1 4-1 7 0 8 1 2 3 5 6 8 4 4 6 6 6 7 4 5 8 13 13 24 4 11 7 20 7 28 1 1 0 4 0 7-1 3-1 6-1 7 0 2 1 4 3 6 1 1 3 4 5 6 2 3 3 5 5 6 1 2 3 5 4 9 2 3 3 7 5 [...]
diff --git a/R/pipe.R b/R/pipe.R
new file mode 100644
index 0000000..3c13a22
--- /dev/null
+++ b/R/pipe.R
@@ -0,0 +1,4 @@
+
+#' @importFrom magrittr %>%
+#' @export
+magrittr::`%>%`
diff --git a/R/plotly.R b/R/plotly.R
new file mode 100644
index 0000000..fd28ced
--- /dev/null
+++ b/R/plotly.R
@@ -0,0 +1,414 @@
+#' Initiate a plotly visualization
+#'
+#' Transform data into a plotly visualization.
+#'
+#' There are a number of "visual properties" that aren't included in the official
+#' Reference section (see below).
+#'
+#' @param data A data frame (optional) or [crosstalk::SharedData] object.
+#' @param ... These arguments are documented at \url{https://plot.ly/r/reference/}
+#' Note that acceptable arguments depend on the value of `type`.
+#' @param type A character string describing the type of trace.
+#' @param color A formula containing a name or expression.
+#' Values are scaled and mapped to color codes based on the value of
+#' `colors` and `alpha`. To avoid scaling, wrap with [I()],
+#' and provide value(s) that can be converted to rgb color codes by
+#' [grDevices::col2rgb()].
+#' @param colors Either a colorbrewer2.org palette name (e.g. "YlOrRd" or "Blues"),
+#' or a vector of colors to interpolate in hexadecimal "#RRGGBB" format,
+#' or a color interpolation function like `colorRamp()`.
+#' @param alpha A number between 0 and 1 specifying the alpha channel applied to color.
+#' @param symbol A formula containing a name or expression.
+#' Values are scaled and mapped to symbols based on the value of `symbols`.
+#' To avoid scaling, wrap with [I()], and provide valid
+#' [pch()] values and/or valid plotly symbol(s) as a string
+#' @param symbols A character vector of symbol types.
+#' Either valid \link{pch} or plotly symbol codes may be supplied.
+#' @param linetype A formula containing a name or expression.
+#' Values are scaled and mapped to linetypes based on the value of
+#' `linetypes`. To avoid scaling, wrap with [I()].
+#' @param linetypes A character vector of line types.
+#' Either valid \link{par} (lty) or plotly dash codes may be supplied.
+#' @param size A formula containing a name or expression yielding a numeric vector.
+#' Values are scaled according to the range specified in `sizes`.
+#' @param sizes A numeric vector of length 2 used to scale sizes to pixels.
+#' @param split A formula containing a name or expression. Similar to
+#' [group_by()], but ensures at least one trace for each unique
+#' value. This replaces the functionality of the (now deprecated)
+#' `group` argument.
+#' @param frame A formula containing a name or expression. The resulting value
+#' is used to split data into frames, and then animated.
+#' @param width Width in pixels (optional, defaults to automatic sizing).
+#' @param height Height in pixels (optional, defaults to automatic sizing).
+#' @param source a character string of length 1. Match the value of this string
+#' with the source argument in [event_data()] to retrieve the
+#' event data corresponding to a specific plot (shiny apps can have multiple plots).
+#' @author Carson Sievert
+#' @seealso \itemize{
+#' \item For initializing a plotly-geo object: [plot_geo()].
+#' \item For initializing a plotly-mapbox object: [plot_mapbox()].
+#' \item For translating a ggplot2 object to a plotly object: [ggplotly()].
+#' \item For modifying any plotly object: [layout()], [add_trace()], [style()]
+#' \item
+#' }
+#' @export
+#' @examples
+#' \dontrun{
+#'
+#' # plot_ly() tries to create a sensible plot based on the information you
+#' # give it. If you don't provide a trace type, plot_ly() will infer one.
+#' plot_ly(economics, x = ~pop)
+#' plot_ly(economics, x = ~date, y = ~pop)
+#' # plot_ly() doesn't require data frame(s), which allows one to take
+#' # advantage of trace type(s) designed specifically for numeric matrices
+#' plot_ly(z = ~volcano)
+#' plot_ly(z = ~volcano, type = "surface")
+#'
+#' # plotly has a functional interface: every plotly function takes a plotly
+#' # object as it's first input argument and returns a modified plotly object
+#' add_lines(plot_ly(economics, x = ~date, y = ~unemploy/pop))
+#'
+#' # To make code more readable, plotly imports the pipe operator from magrittr
+#' economics %>% plot_ly(x = ~date, y = ~unemploy/pop) %>% add_lines()
+#'
+#' # Attributes defined via plot_ly() set 'global' attributes that
+#' # are carried onto subsequent traces, but those may be over-written
+#' plot_ly(economics, x = ~date, color = I("black")) %>%
+#' add_lines(y = ~uempmed) %>%
+#' add_lines(y = ~psavert, color = I("red"))
+#'
+#' # Attributes are documented in the figure reference -> https://plot.ly/r/reference
+#' # You might notice plot_ly() has named arguments that aren't in this figure
+#' # reference. These arguments make it easier to map abstract data values to
+#' # visual attributes.
+#' p <- plot_ly(iris, x = ~Sepal.Width, y = ~Sepal.Length)
+#' add_markers(p, color = ~Petal.Length, size = ~Petal.Length)
+#' add_markers(p, color = ~Species)
+#' add_markers(p, color = ~Species, colors = "Set1")
+#' add_markers(p, symbol = ~Species)
+#' add_paths(p, linetype = ~Species)
+#'
+#' }
+#'
+plot_ly <- function(data = data.frame(), ..., type = NULL,
+ color, colors = NULL, alpha = 1, symbol, symbols = NULL,
+ size, sizes = c(10, 100), linetype, linetypes = NULL,
+ split, frame, width = NULL, height = NULL, source = "A") {
+
+ if (!is.data.frame(data) && !crosstalk::is.SharedData(data)) {
+ stop("First argument, `data`, must be a data frame or shared data.", call. = FALSE)
+ }
+
+ # "native" plotly arguments
+ attrs <- list(...)
+
+ # warn about old arguments that are no longer supported
+ for (i in c("filename", "fileopt", "world_readable")) {
+ if (is.null(attrs[[i]])) next
+ warning("Ignoring ", i, ". Use `plotly_POST()` if you want to post figures to plotly.")
+ attrs[[i]] <- NULL
+ }
+ if (!is.null(attrs[["group"]])) {
+ warning(
+ "The group argument has been deprecated. Use `group_by()` or split instead.\n",
+ "See `help('plotly_data')` for examples"
+ )
+ attrs[["group"]] <- NULL
+ }
+ if (!is.null(attrs[["inherit"]])) {
+ warning("The inherit argument has been deprecated.")
+ attrs[["inherit"]] <- NULL
+ }
+
+ # tack on variable mappings
+ attrs$color <- if (!missing(color)) color
+ attrs$symbol <- if (!missing(symbol)) symbol
+ attrs$linetype <- if (!missing(linetype)) linetype
+ attrs$size <- if (!missing(size)) size
+ attrs$split <- if (!missing(split)) split
+ attrs$frame <- if (!missing(frame)) frame
+
+ # tack on scale ranges
+ attrs$colors <- colors
+ attrs$alpha <- alpha
+ attrs$symbols <- symbols
+ attrs$linetypes <- linetypes
+ attrs$sizes <- sizes
+ attrs$type <- type
+
+ # id for tracking attribute mappings and finding the most current data
+ id <- new_id()
+ # avoid weird naming clashes
+ plotlyVisDat <- data
+ p <- list(
+ visdat = setNames(list(function() plotlyVisDat), id),
+ cur_data = id,
+ attrs = setNames(list(attrs), id),
+ # we always deal with a _list_ of traces and _list_ of layouts
+ # since they can each have different data
+ layout = list(
+ width = width,
+ height = height,
+ # sane margin defaults (mainly for RStudio)
+ margin = list(b = 40, l = 60, t = 25, r = 10)
+ ),
+ source = source
+ )
+ # ensure the collab button is shown (and the save/edit button is hidden) by default
+ config(as_widget(p))
+}
+
+
+#' Initiate a plotly-mapbox object
+#'
+#' Use this function instead of [plot_ly()] to initialize
+#' a plotly-mapbox object. This enforces the entire plot so use
+#' the scattermapbox trace type, and enables higher level geometries
+#' like [add_polygons()] to work
+#'
+#' @param data A data frame (optional).
+#' @param ... arguments passed along to [plot_ly()]. They should be
+#' valid scattermapbox attributes - \url{https://plot.ly/r/reference/#scattermapbox}.
+#' Note that x/y can also be used in place of lat/lon.
+#' @export
+#' @author Carson Sievert
+#' @seealso [plot_ly()], [plot_geo()], [ggplotly()]
+#'
+#' @examples \dontrun{
+#'
+#' map_data("world", "canada") %>%
+#' group_by(group) %>%
+#' plot_mapbox(x = ~long, y = ~lat) %>%
+#' add_polygons() %>%
+#' layout(
+#' mapbox = list(
+#' center = list(lat = ~median(lat), lon = ~median(long))
+#' )
+#' )
+#' }
+#'
+plot_mapbox <- function(data = data.frame(), ...) {
+ p <- config(plot_ly(data, ...), mapboxAccessToken = mapbox_token())
+ # not only do we use this for is_mapbox(), but also setting the layout attr
+ # https://plot.ly/r/reference/#layout-mapbox
+ p$x$layout$mapType <- "mapbox"
+ geo2cartesian(p)
+}
+
+#' Initiate a plotly-geo object
+#'
+#' Use this function instead of [plot_ly()] to initialize
+#' a plotly-geo object. This enforces the entire plot so use
+#' the scattergeo trace type, and enables higher level geometries
+#' like [add_polygons()] to work
+#'
+#' @param data A data frame (optional).
+#' @param ... arguments passed along to [plot_ly()].
+#' @export
+#' @author Carson Sievert
+#' @seealso [plot_ly()], [plot_mapbox()], [ggplotly()]
+#' @examples
+#'
+#' map_data("world", "canada") %>%
+#' group_by(group) %>%
+#' plot_geo(x = ~long, y = ~lat) %>%
+#' add_markers(size = I(1))
+#'
+plot_geo <- function(data = data.frame(), ...) {
+ p <- plot_ly(data, ...)
+ # not only do we use this for is_geo(), but also setting the layout attr
+ # https://plot.ly/r/reference/#layout-geo
+ p$x$layout$mapType <- "geo"
+ geo2cartesian(p)
+}
+
+
+#' Plot an interactive dendrogram
+#'
+#' This function takes advantage of nested key selections to implement an
+#' interactive dendrogram. Selecting a node selects all the labels (i.e. leafs)
+#' under that node.
+#'
+#' @param d a dendrogram object
+#' @param set defines a crosstalk group
+#' @param xmin minimum of the range of the x-scale
+#' @param width width
+#' @param height height
+#' @param ... arguments supplied to [subplot()]
+#' @export
+#' @author Carson Sievert
+#' @seealso [plot_ly()], [plot_mapbox()], [ggplotly()]
+#' @examples
+#'
+#' hc <- hclust(dist(USArrests), "ave")
+#' dend1 <- as.dendrogram(hc)
+#' plot_dendro(dend1, height = 600) %>%
+#' hide_legend() %>%
+#' highlight(persistent = TRUE, dynamic = TRUE)
+#'
+
+plot_dendro <- function(d, set = "A", xmin = -50, height = 500, width = 500, ...) {
+ # get x/y locations of every node in the tree
+ allXY <- get_xy(d)
+ # get non-zero heights so we can split on them and find the relevant labels
+ non0 <- allXY[["y"]][allXY[["y"]] > 0]
+ # splitting on the minimum height would generate all terminal nodes anyway
+ split <- non0[min(non0) < non0]
+ # label is a list-column since non-zero heights have multiple labels
+ # for now, we just have access to terminal node labels
+ labs <- labels(d)
+ allXY$label <- vector("list", nrow(allXY))
+ allXY$label[[1]] <- labs
+ allXY$label[allXY$y == 0] <- labs
+
+ # collect all the *unique* non-trivial nodes
+ nodes <- list()
+ for (i in split) {
+ dsub <- cut(d, i)$lower
+ for (j in seq_along(dsub)) {
+ s <- dsub[[j]]
+ if (is.leaf(s)) next
+ if (any(vapply(nodes, function(x) identical(x, s), logical(1)))) next
+ nodes[[length(nodes) + 1]] <- s
+ }
+ }
+
+ heights <- sapply(nodes, function(x) attr(x, "height"))
+ labs <- lapply(nodes, labels)
+
+ # NOTE: this won't support nodes that have the same height
+ # but that isn't possible, right?
+ for (i in seq_along(heights)) {
+ allXY$label[[which(allXY$y == heights[i])]] <- labs[[i]]
+ }
+
+ tidy_segments <- dendextend::as.ggdend(d)$segments
+
+ allTXT <- allXY[allXY$y == 0, ]
+
+ blank_axis <- list(
+ title = "",
+ showticklabels = FALSE,
+ zeroline = FALSE
+ )
+
+ allXY$members <- sapply(allXY$label, length)
+ allTXT$label <- as.character(allTXT$label)
+
+ allXY %>%
+ plot_ly(x = ~y, y = ~x, color = I("black"), hoverinfo = "none",
+ height = height, width = width) %>%
+ add_segments(
+ data = tidy_segments, xend = ~yend, yend = ~xend, showlegend = FALSE
+ ) %>%
+ add_markers(
+ data = allXY[allXY$y > 0, ], key = ~label, set = set, name = "nodes",
+ text = ~paste0("members: ", members), hoverinfo = "text"
+ ) %>%
+ add_text(
+ data = allTXT, x = 0, y = ~x, text = ~label, key = ~label, set = set,
+ textposition = "middle left", name = "labels"
+ ) %>%
+ layout(
+ dragmode = "select",
+ xaxis = c(blank_axis, list(range = c(xmin, extendrange(allXY[["y"]])[2]))),
+ yaxis = c(blank_axis, list(range = extendrange(allXY[["x"]])))
+ )
+}
+
+get_xy <- function(node) {
+ setNames(
+ tibble::as_tibble(dendextend::get_nodes_xy(node)),
+ c("x", "y")
+ )
+}
+
+
+
+#' Convert a list to a plotly htmlwidget object
+#'
+#' @param x a plotly object.
+#' @param ... other options passed onto `htmlwidgets::createWidget`
+#' @export
+#' @examples
+#'
+#' trace <- list(x = 1, y = 1)
+#' obj <- list(data = list(trace), layout = list(title = "my plot"))
+#' as_widget(obj)
+#'
+
+as_widget <- function(x, ...) {
+ if (inherits(x, "htmlwidget")) return(x)
+ # add plotly class mainly for printing method
+ # customize the JSON serializer (for htmlwidgets)
+ attr(x, 'TOJSON_FUNC') <- to_JSON
+ w <- htmlwidgets::createWidget(
+ name = "plotly",
+ x = x,
+ width = x$layout$width,
+ height = x$layout$height,
+ sizingPolicy = htmlwidgets::sizingPolicy(
+ browser.fill = TRUE,
+ defaultWidth = '100%',
+ defaultHeight = 400
+ ),
+ preRenderHook = plotly_build,
+ dependencies = c(
+ list(typedArrayPolyfill()),
+ crosstalk::crosstalkLibs(),
+ list(plotlyMainBundle())
+ )
+ )
+ # set an ID to avoid the rmarkdown warning ('.Random.seed' is not an integer vector but of type 'NULL', so ignored)
+ # note this will throw a warning in shiny, but it is at least less obtrusive
+ w$elementId <- w$elementId %||% new_id()
+ w
+}
+
+typedArrayPolyfill <- function() {
+ htmltools::htmlDependency(
+ "typedarray", "0.1",
+ src = depPath("typedarray"),
+ script = "typedarray.min.js"
+ )
+}
+
+# TODO: suggest a plotlyBundles package that has trace-level bundles
+# and bundle size at print time.
+plotlyMainBundle <- function() {
+ htmltools::htmlDependency(
+ "plotlyjs", "1.29.2",
+ src = depPath("plotlyjs"),
+ script = "plotly-latest.min.js",
+ stylesheet = "plotly-htmlwidgets.css"
+ )
+}
+
+#' Remove TypedArray polyfill
+#'
+#' By default, plotly.js' TypedArray polyfill is included as a dependency, so
+#' printing "just works" in any context. Many users won't need this polyfill,
+#' so this function may be used to remove it and thus reduce the size of the page.
+#'
+#' @details The polyfill seems to be only relevant for those rendering plots
+#' via phantomjs and RStudio on some Windows platforms.
+#'
+#' @param p a plotly object
+#' @export
+#' @examples
+#'
+#' \dontrun{
+#' p1 <- plot_ly()
+#' p2 <- remove_typedarray_polyfill(p1)
+#' t1 <- tempfile(fileext = ".html")
+#' htmlwidgets::saveWidget(p1, t1)
+#' file.info(t1)$size
+#' htmlwidgets::saveWidget(p2, t1)
+#' file.info(t1)$size
+#' }
+
+remove_typedarray_polyfill <- function(p) {
+ isTA <- vapply(p$dependencies, function(x) identical(x[["name"]], "typedarray"), logical(1))
+ p$dependencies <- p$dependencies[!isTA]
+ p
+}
diff --git a/R/plotly_IMAGE.R b/R/plotly_IMAGE.R
new file mode 100644
index 0000000..dd3f2b2
--- /dev/null
+++ b/R/plotly_IMAGE.R
@@ -0,0 +1,44 @@
+#' Create a static image
+#'
+#' The images endpoint turns a plot (which may be given in multiple forms)
+#' into an image of the desired format.
+#'
+#' @param x either a plotly object or a list.
+#' @param width Image width in pixels
+#' @param height Image height in pixels
+#' @param format The desired image format 'png', 'jpeg', 'svg', 'pdf', 'eps', or 'webp'
+#' @param scale Both png and jpeg formats will be scaled beyond the specified width and height by this number.
+#' @param out_file A filename for writing the image to a file.
+#' @param ... arguments passed onto `httr::POST`
+#' @export
+#' @examples \dontrun{
+#' p <- plot_ly(x = 1:10)
+#' Png <- plotly_IMAGE(p, out_file = "plotly-test-image.png")
+#' Jpeg <- plotly_IMAGE(p, format = "jpeg", out_file = "plotly-test-image.jpeg")
+#' Svg <- plotly_IMAGE(p, format = "svg", out_file = "plotly-test-image.svg")
+#' Pdf <- plotly_IMAGE(p, format = "pdf", out_file = "plotly-test-image.pdf")
+#' }
+#'
+
+plotly_IMAGE <- function(x, width = 1000, height = 500, format = "png",
+ scale = 1, out_file, ...) {
+ x <- plotly_build(x)[["x"]]
+
+ bod <- list(
+ figure = x[c("data", "layout")],
+ width = width,
+ height = height,
+ format = format,
+ scale = scale,
+ encoded = FALSE,
+ filename = Sys.time()
+ )
+ base_url <- file.path(get_domain("api"), "v2", "images")
+ resp <- httr::POST(
+ base_url, body = to_JSON(bod), api_headers(), api_auth(),
+ if (!missing(out_file)) httr::write_disk(out_file, overwrite = TRUE),
+ ...
+ )
+ con <- process(append_class(resp, "api_image"))
+ invisible(con)
+}
diff --git a/R/plotly_build.R b/R/plotly_build.R
new file mode 100644
index 0000000..8bbc979
--- /dev/null
+++ b/R/plotly_build.R
@@ -0,0 +1,878 @@
+#' 'Build' (i.e., evaluate) a plotly object
+#'
+#' This generic function creates the list object sent to plotly.js
+#' for rendering. Using this function can be useful for overriding defaults
+#' provided by `ggplotly`/`plot_ly` or for debugging rendering
+#' errors.
+#'
+#' @param p a ggplot object, or a plotly object, or a list.
+#' @param registerFrames should a frame trace attribute be interpreted as frames in an animation?
+#' @export
+#' @examples
+#'
+#' p <- plot_ly(economics, x = ~date, y = ~pce)
+#' # the unevaluated plotly object
+#' str(p)
+#' # the evaluated data
+#' str(plotly_build(p)$x$data)
+#'
+plotly_build <- function(p, registerFrames = TRUE) {
+ UseMethod("plotly_build")
+}
+
+#' @export
+plotly_build.list <- function(p, registerFrames = TRUE) {
+ plotly_build(as_widget(p))
+}
+
+#' @export
+plotly_build.gg <- function(p, registerFrames = TRUE) {
+ # note: since preRenderHook = plotly_build in as_widget(),
+ # plotly_build.plotly() will be called on gg objects as well
+ plotly_build(ggplotly(p))
+}
+
+#' @export
+plotly_build.plotly <- function(p, registerFrames = TRUE) {
+
+ # make this plot retrievable
+ set_last_plot(p)
+
+ layouts <- Map(function(x, y) {
+
+ d <- plotly_data(p, y)
+ x <- rapply(x, eval_attr, data = d, how = "list")
+
+ # if an annotation attribute is an array, expand into multiple annotations
+ nAnnotations <- max(lengths(x$annotations) %||% 0)
+ if (!is.null(names(x$annotations))) {
+ # font is the only list object, so store it, and attach after transposing
+ font <- x$annotations[["font"]]
+ x$annotations <- purrr::transpose(lapply(x$annotations, function(x) {
+ as.list(rep(x, length.out = nAnnotations))
+ }))
+ for (i in seq_len(nAnnotations)) {
+ x$annotations[[i]][["font"]] <- font
+ }
+ }
+
+ x[lengths(x) > 0]
+
+ }, p$x$layoutAttrs, names2(p$x$layoutAttrs))
+
+ # get rid of the data -> layout mapping
+ p$x$layoutAttrs <- NULL
+
+ # accumulate, rather than override, annotations.
+ annotations <- Reduce(c, c(
+ list(p$x$layout$annotations),
+ setNames(compact(lapply(layouts, "[[", "annotations")), NULL)
+ ))
+
+ # merge layouts into a single layout (more recent layouts will override older ones)
+ p$x$layout <- modify_list(p$x$layout, Reduce(modify_list, layouts))
+ p$x$layout$annotations <- annotations
+
+ # keep frame mapping for populating layout.slider.currentvalue in animations
+ frameMapping <- unique(unlist(
+ lapply(p$x$attrs, function(x) deparse2(x[["frame"]])),
+ use.names = FALSE
+ ))
+ if (length(frameMapping) > 1) {
+ warning("Only one `frame` variable is allowed", call. = FALSE)
+ }
+
+ # Attributes should be NULL if none exist (rather than an empty list)
+ if (length(p$x$attrs) == 0) p$x$attrs <- NULL
+
+ # If type was not specified in plot_ly(), it doesn't create a trace unless
+ # there are no other traces
+ if (is.null(p$x$attrs[[1]][["type"]]) && length(p$x$attrs) > 1) {
+ p$x$attrs[[1]] <- NULL
+ }
+
+ # have the attributes already been evaluated?
+ is.evaled <- function(x) inherits(x, "plotly_eval")
+ attrsToEval <- p$x$attrs[!vapply(p$x$attrs, is.evaled, logical(1))]
+
+ # trace type checking and renaming for plot objects
+ if (is_mapbox(p) || is_geo(p)) {
+ p <- geo2cartesian(p)
+ attrsToEval <- lapply(attrsToEval, function(tr) {
+ if (!grepl("scatter|choropleth", tr[["type"]] %||% "scatter")) {
+ stop("Cant add a '", tr[["type"]], "' trace to a map object", call. = FALSE)
+ }
+ if (is_mapbox(p)) tr[["type"]] <- "scattermapbox"
+ if (is_geo(p)) {
+ tr[["type"]] <- if (!is.null(tr[["z"]])) "choropleth" else "scattergeo"
+ }
+ tr
+ })
+ }
+
+ dats <- Map(function(x, y) {
+
+ # perform the evaluation
+ dat <- plotly_data(p, y)
+ trace <- structure(
+ rapply(x, eval_attr, data = dat, how = "list"),
+ class = oldClass(x)
+ )
+
+ # attach crosstalk info, if necessary
+ if (crosstalk_key() %in% names(dat)) {
+ trace[["key"]] <- trace[["key"]] %||% dat[[crosstalk_key()]]
+ trace[["set"]] <- trace[["set"]] %||% attr(dat, "set")
+ }
+
+ # if appropriate, tack on a group index
+ grps <- tryCatch(
+ as.character(dplyr::groups(dat)),
+ error = function(e) character(0)
+ )
+ if (length(grps) && any(lengths(trace) == NROW(dat))) {
+ trace[[".plotlyGroupIndex"]] <- interaction(dat[, grps, drop = F])
+ }
+
+ # determine trace type (if not specified, can depend on the # of data points)
+ # note that this should also determine a sensible mode, if appropriate
+ trace <- verify_type(trace)
+ # verify orientation of boxes/bars
+ trace <- verify_orientation(trace)
+
+ # add sensible axis names to layout
+ for (i in c("x", "y", "z")) {
+ nm <- paste0(i, "axis")
+ idx <- which(names(trace) %in% i)
+ if (length(idx) == 1) {
+ title <- deparse2(x[[idx]])
+ if (is3d(trace$type) || i == "z") {
+ p$x$layout$scene[[nm]]$title <<- p$x$layout$scene[[nm]]$title %||% title
+ } else {
+ p$x$layout[[nm]]$title <<- p$x$layout[[nm]]$title %||% title
+ }
+ }
+ }
+
+ if (inherits(trace, c("plotly_surface", "plotly_contour"))) {
+ # TODO: generate matrix for users?
+ # (1) if z is vector, and x/y are null throw error
+ # (2) if x/y/z are vectors and length(x) * length(y) == length(z), convert z to matrix
+ if (!is.matrix(trace[["z"]]) || !is.numeric(trace[["z"]])) {
+ stop("`z` must be a numeric matrix", call. = FALSE)
+ }
+ }
+
+ # collect non-positional scales, plotly.js data_arrays, and "special"
+ # array attributes for "data training"
+ Attrs <- Schema$traces[[trace[["type"]]]]$attributes
+ isArray <- vapply(Attrs, function(x) {
+ tryFALSE(identical(x[["valType"]], "data_array"))
+ }, logical(1))
+ arrayOk <- vapply(Attrs, function(x) tryNULL(x[["arrayOk"]]) %||% FALSE, logical(1))
+ # "non-tidy" traces allow x/y of different lengths, so ignore those
+ dataArrayAttrs <- if (is_tidy(trace)) names(Attrs)[isArray | arrayOk]
+ allAttrs <- c(
+ dataArrayAttrs, special_attrs(trace), npscales(), "frame",
+ # for some reason, text isn't listed as a data array in some traces
+ # I'm looking at you scattergeo...
+ ".plotlyGroupIndex", "text", "key"
+ )
+ tr <- trace[names(trace) %in% allAttrs]
+ # TODO: does it make sense to "train" matrices/2D-tables (e.g. z)?
+ tr <- tr[vapply(tr, function(x) is.null(dim(x)) && is.atomic(x), logical(1))]
+ builtData <- tibble::as_tibble(tr)
+ # avoid clobbering I() (i.e., variables that shouldn't be scaled)
+ for (i in seq_along(tr)) {
+ if (inherits(tr[[i]], "AsIs")) builtData[[i]] <- I(builtData[[i]])
+ }
+
+ if (NROW(builtData) > 0) {
+ # Build the index used to split one "trace" into multiple traces
+ isAsIs <- vapply(builtData, function(x) inherits(x, "AsIs"), logical(1))
+ isDiscrete <- vapply(builtData, is.discrete, logical(1))
+ # note: can only have one linetype per trace
+ isSplit <- names(builtData) %in% c("split", "linetype", "frame") |
+ !isAsIs & isDiscrete & names(builtData) %in% c("symbol", "color")
+ if (any(isSplit)) {
+ paste2 <- function(x, y) if (identical(x, y)) x else paste(x, y, sep = br())
+ splitVars <- builtData[isSplit]
+ traceIndex <- Reduce(paste2, splitVars)
+ if (!is.null(trace$name)) {
+ traceIndex <- paste2(traceIndex, trace$name)
+ }
+ builtData[[".plotlyTraceIndex"]] <- traceIndex
+ # in registerFrames() we need to strip the frame from .plotlyTraceIndex
+ # so keep track of which variable it is...
+ trace$frameOrder <- which(names(splitVars) %in% "frame")
+ }
+ # Build the index used to determine grouping (later on, NAs are inserted
+ # via group2NA() to create the groups). This is done in 3 parts:
+ # 1. Sort data by the trace index since groups are nested within traces.
+ # 2. Translate missing values on positional scales to a grouping variable.
+ # If grouping isn't relevant for this trace, a warning is thrown since
+ # NAs are removed.
+ # 3. The grouping from (2) and any groups detected via dplyr::groups()
+ # are combined into a single grouping variable, .plotlyGroupIndex
+ builtData <- arrange_safe(builtData, ".plotlyTraceIndex")
+ isComplete <- complete.cases(builtData[names(builtData) %in% c("x", "y", "z")])
+ # warn about missing values if groups aren't relevant for this trace type
+ if (any(!isComplete) && !has_group(trace)) {
+ warning("Ignoring ", sum(!isComplete), " observations", call. = FALSE)
+ }
+ builtData[[".plotlyMissingIndex"]] <- cumsum(!isComplete)
+ builtData <- builtData[isComplete, ]
+ if (length(grps) && has_group(trace) && isTRUE(trace[["connectgaps"]])) {
+ stop(
+ "Can't use connectgaps=TRUE when data has group(s).", call. = FALSE
+ )
+ }
+ builtData[[".plotlyGroupIndex"]] <- interaction(
+ builtData[[".plotlyGroupIndex"]] %||% "",
+ builtData[[".plotlyMissingIndex"]]
+ )
+ builtData <- arrange_safe(builtData,
+ c(".plotlyTraceIndex", ".plotlyGroupIndex",
+ if (inherits(trace, "plotly_line")) "x")
+ )
+ builtData <- train_data(builtData, trace)
+ trace[[".plotlyVariableMapping"]] <- names(builtData)
+
+ # copy over to the trace data
+ for (i in names(builtData)) {
+ trace[[i]] <- builtData[[i]]
+ }
+ }
+
+ # TODO: provide a better way to clean up "high-level" attrs
+ trace[c("ymin", "ymax", "yend", "xend")] <- NULL
+ trace[lengths(trace) > 0]
+
+ }, attrsToEval, names2(attrsToEval))
+
+ p$x$attrs <- lapply(p$x$attrs, function(x) structure(x, class = "plotly_eval"))
+
+ # traceify by the interaction of discrete variables
+ traces <- list()
+ for (i in seq_along(dats)) {
+ d <- dats[[i]]
+ scaleAttrs <- names(d) %in% paste0(npscales(), "s")
+ traces <- c(traces, traceify(d[!scaleAttrs], d$.plotlyTraceIndex))
+ if (i == 1) traces[[1]] <- c(traces[[1]], d[scaleAttrs])
+ }
+
+ # insert NAs to differentiate groups
+ traces <- lapply(traces, function(x) {
+ d <- data.frame(x[names(x) %in% x$.plotlyVariableMapping], stringsAsFactors = FALSE)
+ d <- group2NA(
+ d, if (has_group(x)) ".plotlyGroupIndex",
+ ordered = if (inherits(x, "plotly_line")) "x",
+ retrace.first = inherits(x, "plotly_polygon")
+ )
+ for (i in x$.plotlyVariableMapping) {
+ # try to reduce the amount of data we have to send for non-positional scales
+ x[[i]] <- structure(
+ if (i %in% npscales()) uniq(d[[i]]) else d[[i]],
+ class = oldClass(x[[i]])
+ )
+ }
+ x
+ })
+ # "transforms" of (i.e., apply scaling to) special arguments
+ # IMPORTANT: scales are applied at the plot-level!!
+ colorTitle <- unlist(lapply(p$x$attrs, function(x) {
+ deparse2(x[["color"]] %||% x[["z"]])
+ }))
+ traces <- map_color(traces, title = paste(colorTitle, collapse = br()))
+ traces <- map_size(traces)
+ traces <- map_symbol(traces)
+ traces <- map_linetype(traces)
+
+ for (i in seq_along(traces)) {
+ # remove special mapping attributes
+ mappingAttrs <- c(
+ "alpha", npscales(), paste0(npscales(), "s"),
+ ".plotlyGroupIndex", ".plotlyMissingIndex",
+ ".plotlyTraceIndex", ".plotlyVariableMapping"
+ )
+ for (j in mappingAttrs) {
+ traces[[i]][[j]] <- NULL
+ }
+ }
+
+ # .crossTalkKey -> key
+ traces <- lapply(traces, function(x) {
+ setNames(x, sub(crosstalk_key(), "key", names(x), fixed = TRUE))
+ })
+
+ # it's possible that the plot object already has some traces
+ # (like figures pulled from a plotly server)
+ p$x$data <- setNames(c(p$x$data, traces), NULL)
+
+ # supply linked highlighting options/features
+ p <- supply_highlight_attrs(p)
+
+ # supply trace anchor and domain information
+ p <- supply_defaults(p)
+
+ # attribute naming corrections for "geo-like" traces
+ p$x$data <- lapply(p$x$data, function(tr) {
+ if (isTRUE(tr[["type"]] %in% c("scattermapbox", "scattergeo"))) {
+ tr[["lat"]] <- tr[["lat"]] %||% tr[["y"]]
+ tr[["lon"]] <- tr[["lon"]] %||% tr[["x"]]
+ tr[c("x", "y")] <- NULL
+ }
+ tr
+ })
+
+ # polar charts don't like null width/height keys
+ if (is.null(p$x$layout[["height"]])) p$x$layout[["height"]] <- NULL
+ if (is.null(p$x$layout[["width"]])) p$x$layout[["width"]] <- NULL
+
+ # ensure we get the order of categories correct
+ # (plotly.js uses the order in which categories appear by default)
+ p <- populate_categorical_axes(p)
+ # translate '\n' to '<br />' in text strings
+ p <- translate_linebreaks(p)
+ # if it makes sense, add markers/lines/text to mode
+ p <- verify_mode(p)
+ # annotations & shapes must be an array of objects
+ # TODO: should we add anything else to this?
+ p <- verify_arrays(p)
+ # set a sensible hovermode if it hasn't been specified already
+ p <- verify_hovermode(p)
+ # try to convert to webgl if toWebGl was used
+ p <- verify_webgl(p)
+ # crosstalk dynamically adds traces, meaning that a legend could be dynamically
+ # added, which is confusing. So here we populate a sensible default.
+ p <- verify_showlegend(p)
+
+ # NOTE: this needs to occur *before* registering frames so simple/nested key
+ # flags get passed onto frame data.
+ p <- verify_key_type(p)
+
+ if (registerFrames) {
+ p <- registerFrames(p, frameMapping = frameMapping)
+ }
+
+ p <- verify_guides(p)
+
+ # verify plot attributes are legal according to the plotly.js spec
+ p <- verify_attr_names(p)
+ # box up 'data_array' attributes where appropriate
+ p <- verify_attr_spec(p)
+
+ # make sure plots don't get sent out of the network (for enterprise)
+ p$x$base_url <- get_domain()
+ p
+}
+
+# ----------------------------------------------------------------
+# Functions used solely within plotly_build
+# ----------------------------------------------------------------
+
+registerFrames <- function(p, frameMapping = NULL) {
+ # ensure one frame value per trace, and if its missing, insert NA
+ p$x$data <- lapply(p$x$data, function(tr) {
+ tr[["frame"]] <- tr[["frame"]][[1]] %||% NA
+ tr
+ })
+
+ # the ordering of this object determines the ordering of the frames
+ frameAttrs <- unlist(lapply(p$x$data, "[[", "frame"))
+ # NOTE: getLevels() should drop NAs
+ frameNames <- getLevels(frameAttrs)
+ p$x$data <- lapply(p$x$data, function(tr) { tr$frame <- as.character(tr$frame); tr })
+
+ # remove frames from the trace names
+ for (i in seq_along(p$x$data)) {
+ tr <- p$x$data[[i]]
+ if (length(tr[["name"]]) != 1) next
+ nms <- strsplit(as.character(tr[["name"]]), br())[[1]]
+ idx <- setdiff(seq_along(nms), tr$frameOrder %||% 0)
+ p$x$data[[i]]$name <- if (length(idx)) paste(nms[idx], collapse = br()) else NULL
+ p$x$data[[i]]$frameOrder <- NULL
+ }
+
+ # exit in trivial cases
+ nFrames <- length(frameNames)
+ if (nFrames < 2) return(p)
+
+ # --------------------------------------------------------------------------
+ # set a "global" range of x/y (TODO: handle multiple axes?)
+ # --------------------------------------------------------------------------
+
+ x <- unlist(lapply(p$x$data, function(x) x[["x"]]))
+ if (is.numeric(x)) {
+ rng <- range(x, na.rm = TRUE)
+ if (identical(p$x$layout$xaxis$type, "log")) {
+ rng <- log10(rng)
+ rng[is.nan(rng)] <- 0
+ }
+ p$x$layout$xaxis$range <- p$x$layout$xaxis$range %||% extendrange(rng)
+ }
+ y <- unlist(lapply(p$x$data, function(x) x[["y"]]))
+ if (is.numeric(y)) {
+ rng <- range(y, na.rm = TRUE)
+ if (identical(p$x$layout$yaxis$type, "log")) {
+ rng <- log10(rng)
+ rng[is.nan(rng)] <- 0
+ }
+ p$x$layout$yaxis$range <- p$x$layout$yaxis$range %||% extendrange(rng)
+ }
+
+ # --------------------------------------------------------------------------
+ # Similar to setting a global x/y range, we need a "global trace range"
+ #
+ # implementation details via @rreusser: frames specify *state changes*,
+ # so if frame 1 has 3 traces, and frame 2 has 2 traces,
+ # we need to explicity supply 3 traces
+ # in both frames, but make 1 invisible in frame 2. For example,
+ # http://codepen.io/cpsievert/pen/gmXVWe
+ # For that reason, every frame (including the "initial" frame) has the
+ # max # of traces and "missing traces" are not visible (i.e., `visible=false`)
+ # --------------------------------------------------------------------------
+
+ # remember, at this point, frame has been removed from the trace name
+ frameTraceNames <- unique(unlist(lapply(p$x$data[!is.na(frameAttrs)], "[[", "name")))
+ for (i in seq_along(frameNames)) {
+ nm <- frameNames[[i]]
+ d <- p$x$data[sapply(p$x$data, "[[", "frame") %in% nm]
+
+ # ensure, the frames API knows what is visible/invisible
+ d <- lapply(d, function(tr) { tr$visible <- tr$visible %||% TRUE; tr })
+
+ # if this frame is missing a trace name, supply an invisible one
+ traceNamesMissing <- setdiff(frameTraceNames, sapply(d, "[[", "name"))
+ for (j in traceNamesMissing) {
+ idx <- vapply(p$x$data, function(tr) isTRUE(tr[["name"]] == j), logical(1))
+ idx <- which(idx)[[1]]
+ invisible <- modify_list(p$x$data[[idx]], list(visible = FALSE))
+ d <- c(d, list(invisible))
+ }
+ p$x$frames[[i]] <- list(
+ name = as.character(format(nm)),
+ data = lapply(d, function(tr) {
+ spec <- Schema$traces[[tr$type %||% "scatter"]]$attributes
+ verify_attr(tr, spec)
+ })
+ )
+ }
+
+ # ensure the plot knows about the "global trace range"
+ firstFrame <- vapply(p$x$data, function(tr) isTRUE(tr[["frame"]] %in% frameNames[[1]]), logical(1))
+ p$x$data[firstFrame] <- p$x$frames[[1]]$data
+
+ # remove frame traces
+ idx <- vapply(p$x$data, function(tr) isTRUE(tr[["frame"]] %in% frameNames[-1]), logical(1))
+ p$x$data[idx] <- NULL
+
+ # this works since we now have a global trace range
+ p$x$frames <- lapply(p$x$frames, function(f) {
+ f$traces <- i(which(!is.na(sapply(p$x$data, "[[", "frame"))) - 1)
+ f
+ })
+
+ # retrain color defaults
+ p$x$data <- retrain_color_defaults(p$x$data)
+ p$x$frames <- lapply(p$x$frames, function(f) {
+ f$data <- retrain_color_defaults(
+ f$data, colorDefaults = traceColorDefaults()[f$traces + 1]
+ )
+ f
+ })
+
+ # populate layout.sliders.currentvalue with a sensible default
+ defaultvalue <- if (length(frameMapping) == 1) {
+ list(
+ prefix = paste0(frameMapping, ": "),
+ xanchor = 'right',
+ font = list(
+ size = 16,
+ color = toRGB("gray80")
+ )
+ )
+ } else NULL
+
+ # supply animation option defaults (a la, highlight_defaults())
+ p$animation <- p$animation %||% animation_opts_defaults()
+
+ # if all the frame trace data are scatter traces, set a default of redraw=F
+ types <- unique(unlist(lapply(p$x$frames, function(f) {
+ vapply(f$data, function(tr) tr$type %||% "scatter", character(1))
+ })))
+ if (identical(types, "scatter") && is.default(p$animation$frame$redraw)) {
+ p$animation$frame$redraw <- default(FALSE)
+ }
+
+ # _always_ display an animation button and slider by default
+ animation_button_supply(
+ animation_slider_supply(p, currentvalue = defaultvalue)
+ )
+}
+
+
+train_data <- function(data, trace) {
+ if (inherits(trace, "plotly_ribbon")) {
+ data <- ribbon_dat(data)
+ }
+ if (inherits(trace, "plotly_segment")) {
+ # TODO: this could be faster, more efficient
+ data$.plotlyGroupIndex <- seq_len(NROW(data))
+ idx <- rep(seq_len(NROW(data)), each = 2)
+ dat <- as.data.frame(data[!grepl("^xend$|^yend", names(data))])
+ dat <- dat[idx, ]
+ idx2 <- seq.int(2, NROW(dat), by = 2)
+ dat[idx2, "x"] <- data[["xend"]]
+ dat[idx2, "y"] <- data[["yend"]]
+ data <- dplyr::group_by_(dat, ".plotlyGroupIndex", add = TRUE)
+ }
+ # TODO: a lot more geoms!!!
+ data
+}
+
+
+map_size <- function(traces) {
+ sizeList <- lapply(traces, "[[", "size")
+ nSizes <- lengths(sizeList)
+ # if no "top-level" color is present, return traces untouched
+ if (all(nSizes == 0)) {
+ return(traces)
+ }
+ allSize <- unlist(compact(sizeList))
+ if (!is.null(allSize) && is.discrete(allSize)) {
+ stop("Size must be mapped to a numeric variable\n",
+ "symbols only make sense for discrete variables", call. = FALSE)
+ }
+ sizeRange <- range(allSize, na.rm = TRUE)
+
+ types <- unlist(lapply(traces, function(tr) tr$type %||% "scatter"))
+ modes <- unlist(lapply(traces, function(tr) tr$mode %||% "lines"))
+ hasMarker <- has_marker(types, modes)
+ hasLine <- has_line(types, modes)
+ hasText <- has_text(types, modes)
+
+ for (i in which(nSizes > 0)) {
+ s <- sizeList[[i]]
+ isConstant <- inherits(s, "AsIs")
+ sizeI <- if (isConstant) {
+ structure(s, class = setdiff(class(s), "AsIs"))
+ } else {
+ scales::rescale(s, from = sizeRange, to = traces[[1]]$sizes)
+ }
+ if (hasMarker[[i]]) {
+ traces[[i]]$marker <- modify_list(
+ list(size = sizeI, sizemode = "area"),
+ traces[[i]]$marker
+ )
+ }
+ if (hasLine[[i]]) {
+ if (!isConstant || length(sizeI) > 1) {
+ warning(
+ "plotly.js doesn't yet support line.width arrays, track this issue for progress\n",
+ " https://github.com/plotly/plotly.js/issues/147",
+ call. = FALSE
+ )
+ }
+ traces[[i]]$line <- modify_list(
+ list(width = sizeI[1]),
+ traces[[i]]$line
+ )
+ }
+ if (hasText[[i]]) {
+ if (!isConstant || length(sizeI) > 1) {
+ warning(
+ "plotly.js doesn't yet support textfont.size arrays",
+ call. = FALSE
+ )
+ }
+ traces[[i]]$textfont <- modify_list(
+ list(size = sizeI[1]),
+ traces[[i]]$textfont
+ )
+ }
+ }
+ traces
+}
+
+# appends a new (empty) trace to generate (plot-wide) colorbar/colorscale
+map_color <- function(traces, title = "", na.color = "transparent") {
+ color <- lapply(traces, function(x) {
+ x[["color"]] %||% if (grepl("histogram2d", x[["type"]])) c(0, 1) else if (has_attr(x[["type"]], "colorscale")) x[["z"]] else NULL
+ })
+ isConstant <- vapply(color, function(x) inherits(x, "AsIs") || is.null(x), logical(1))
+ isNumeric <- vapply(color, is.numeric, logical(1)) & !isConstant
+ isDiscrete <- vapply(color, is.discrete, logical(1)) & !isConstant
+ if (any(isNumeric & isDiscrete)) {
+ stop("Can't have both discrete and numeric color mappings", call. = FALSE)
+ }
+ # color/colorscale/colorbar attribute placement depends on trace type and marker mode
+ types <- unlist(lapply(traces, function(tr) tr$type %||% "scatter"))
+ modes <- unlist(lapply(traces, function(tr) tr$mode %||% "lines"))
+ hasMarker <- has_marker(types, modes)
+ hasLine <- has_line(types, modes)
+ hasText <- has_text(types, modes)
+ hasZ <- has_attr(types, "colorscale") &
+ any(vapply(traces, function(tr) {
+ !is.null(tr[["z"]]) || grepl("histogram2d", tr[["type"]])
+ }, logical(1)))
+
+ colorDefaults <- traceColorDefaults()
+ for (i in which(isConstant)) {
+ # https://github.com/plotly/plotly.js/blob/c83735/src/plots/plots.js#L58
+ idx <- i %% length(colorDefaults)
+ if (idx == 0) idx <- 10
+ col <- color[[i]] %||% colorDefaults[[idx]]
+ alpha <- traces[[i]]$alpha %||% 1
+ rgb <- toRGB(col, alpha)
+ # we need some way to identify pre-specified defaults in subplot to retrain them
+ if (is.null(color[[i]])) attr(rgb, "defaultAlpha") <- alpha
+ obj <- if (hasLine[[i]]) "line" else if (hasMarker[[i]]) "marker" else if (hasText[[i]]) "textfont"
+ if (is.null(obj)) next
+ traces[[i]][[obj]] <- modify_list(list(color = rgb), traces[[i]][[obj]])
+ traces[[i]][[obj]] <- modify_list(list(fillcolor = rgb), traces[[i]][[obj]])
+ }
+
+ if (any(isNumeric)) {
+ palette <- traces[[1]][["colors"]] %||% viridisLite::viridis(10)
+ # TODO: use ggstat::frange() when it's on CRAN?
+ allColor <- unlist(color[isNumeric])
+ rng <- range(allColor, na.rm = TRUE)
+ colScale <- scales::col_numeric(palette, rng, na.color = na.color)
+ # generate the colorscale to be shared across traces
+ vals <- if (diff(rng) > 0) {
+ as.numeric(stats::quantile(allColor, probs = seq(0, 1, length.out = 25), na.rm = TRUE))
+ } else {
+ c(0, 1)
+ }
+ colorScale <- matrix(
+ c(scales::rescale(vals), toRGB(colScale(vals), traces[[1]]$alpha %||% 1)),
+ ncol = 2
+ )
+ colorObj <- list(
+ colorbar = Reduce(modify_list, lapply(traces, function(x) x$marker[["colorbar"]])) %||%
+ list(title = as.character(title), ticklen = 2),
+ cmin = rng[1],
+ cmax = rng[2],
+ colorscale = colorScale,
+ showscale = FALSE
+ )
+ for (i in which(isNumeric)) {
+ if (hasZ[[i]]) {
+ colorObj[c("cmin", "cmax")] <- NULL
+ colorObj[["showscale"]] <- TRUE
+ traces[[i]] <- modify_list(colorObj, traces[[i]])
+ traces[[i]]$colorscale <- as_df(traces[[i]]$colorscale)
+ # sigh, contour colorscale doesn't support alpha
+ if (grepl("contour", traces[[i]][["type"]])) {
+ traces[[i]]$colorscale[, 2] <- strip_alpha(traces[[i]]$colorscale[, 2])
+ }
+ traces[[i]] <- structure(traces[[i]], class = c("plotly_colorbar", "zcolor"))
+ next
+ }
+ colorObj$color <- color[[i]]
+ if (hasLine[[i]]) {
+ if (types[[i]] %in% c("scatter", "scattergl")) {
+ warning("Numeric color variables cannot (yet) be mapped to lines.\n",
+ " when the trace type is 'scatter' or 'scattergl'.\n", call. = FALSE)
+ traces[[i]]$mode <- paste0(traces[[i]]$mode, "+markers")
+ hasMarker[[i]] <- TRUE
+ } else {
+ # scatter3d supports data arrays for color
+ traces[[i]][["line"]] <- modify_list(colorObj, traces[[i]][["line"]])
+ traces[[i]]$marker$colorscale <- as_df(traces[[i]]$marker$colorscale)
+ }
+ }
+ if (hasMarker[[i]]) {
+ traces[[i]][["marker"]] <- modify_list(colorObj, traces[[i]][["marker"]])
+ traces[[i]]$marker$colorscale <- as_df(traces[[i]]$marker$colorscale)
+ }
+ if (hasText[[i]]) {
+ warning("Numeric color variables cannot (yet) be mapped to text.\n",
+ "Feel free to make a feature request \n",
+ "https://github.com/plotly/plotly.js", call. = FALSE)
+ }
+ }
+ if (any(hasZ)) return(traces)
+ # add an "empty" trace with the colorbar
+ colorObj$color <- rng
+ colorObj$showscale <- TRUE
+ colorBarTrace <- list(
+ x = range(unlist(lapply(traces, "[[", "x")), na.rm = TRUE),
+ y = range(unlist(lapply(traces, "[[", "y")), na.rm = TRUE),
+ type = if (any(types %in% glTypes())) "scattergl" else "scatter",
+ mode = "markers",
+ opacity = 0,
+ hoverinfo = "none",
+ showlegend = FALSE,
+ marker = colorObj
+ )
+ # yay for consistency plotly.js
+ if ("scatter3d" %in% types) {
+ colorBarTrace$type <- "scatter3d"
+ colorBarTrace$z <- range(unlist(lapply(traces, "[[", "z")), na.rm = TRUE)
+ }
+ if (length(type <- intersect(c("scattergeo", "scattermapbox"), types))) {
+ colorBarTrace$type <- type
+ colorBarTrace$lat <- colorBarTrace$y
+ colorBarTrace$lon <- colorBarTrace$x
+ colorBarTrace[["x"]] <- NULL
+ colorBarTrace[["y"]] <- NULL
+ }
+ traces[[length(traces) + 1]] <- structure(colorBarTrace, class = "plotly_colorbar")
+ }
+
+ if (any(isDiscrete)) {
+ # unlist() does _not_ preserve order factors
+ isOrdered <- all(vapply(color[isDiscrete], is.ordered, logical(1)))
+ allColor <- unlist(color[isDiscrete])
+ lvls <- getLevels(allColor)
+ N <- length(lvls)
+ pal <- traces[[1]][["colors"]] %||%
+ if (isOrdered) viridisLite::viridis(N) else RColorBrewer::brewer.pal(N, "Set2")
+ colScale <- scales::col_factor(pal, levels = names(pal) %||% lvls, na.color = na.color)
+ for (i in which(isDiscrete)) {
+ rgb <- toRGB(colScale(as.character(color[[i]])), traces[[i]]$alpha %||% 1)
+ obj <- if (hasLine[[i]]) "line" else if (hasMarker[[i]]) "marker" else if (hasText[[i]]) "textfont"
+ traces[[i]][[obj]] <- modify_list(list(color = rgb), traces[[i]][[obj]])
+ # match the plotly.js default of half transparency in the fill color
+ traces[[i]][[obj]] <- modify_list(list(fillcolor = toRGB(rgb, 0.5)), traces[[i]][[obj]])
+ }
+ }
+
+ # marker.line.color (stroke) inherits from marker.color (color)
+ # TODO: allow users to control via a `stroke`` argument
+ # to make consistent, in "filled polygons", color -> fillcolor, stroke -> line.color
+ for (i in seq_along(color)) {
+ if (!is.null(traces[[i]]$marker$color)) {
+ traces[[i]]$marker$line$color <- traces[[i]]$marker$line$color %||% "transparent"
+ for (j in c("error_x", "error_y")) {
+ if (!is.null(traces[[i]][[j]])) {
+ traces[[i]][[j]][["color"]] <- traces[[i]][[j]][["color"]] %||%
+ traces[[i]]$marker[["color"]]
+ }
+ }
+ }
+ }
+
+ traces
+}
+
+map_symbol <- function(traces) {
+ symbolList <- lapply(traces, "[[", "symbol")
+ nSymbols <- lengths(symbolList)
+ # if no "top-level" symbol is present, return traces untouched
+ if (all(nSymbols == 0)) {
+ return(traces)
+ }
+ symbol <- unlist(compact(symbolList))
+ lvls <- getLevels(symbol)
+ # get a sensible default palette (also throws warnings)
+ pal <- setNames(scales::shape_pal()(length(lvls)), lvls)
+ pal <- supplyUserPalette(pal, traces[[1]][["symbols"]])
+
+ validSymbols <- as.character(Schema$traces$scatter$attributes$marker$symbol$values)
+
+ for (i in which(nSymbols > 0)) {
+ s <- symbolList[[i]]
+ symbols <- pch2symbol(if (inherits(s, "AsIs")) s else as.character(pal[as.character(s)]))
+ illegalSymbols <- setdiff(symbols, validSymbols)
+ if (length(illegalSymbols)) {
+ warning(
+ "The following are not valid symbol codes:\n'",
+ paste(illegalSymbols, collapse = "', '"), "'\n",
+ "Valid symbols include:\n'",
+ paste(validSymbols, collapse = "', '"), call. = FALSE
+ )
+ }
+ traces[[i]][["marker"]] <- modify_list(
+ list(symbol = symbols), traces[[i]][["marker"]]
+ )
+ # ensure the mode is set so that the symbol is relevant
+ if (!grepl("markers", traces[[i]]$mode %||% "")) {
+ message("Adding markers to mode; otherwise symbol would have no effect.")
+ traces[[i]]$mode <- paste0(traces[[i]]$mode, "+markers")
+ }
+ }
+ traces
+}
+
+map_linetype <- function(traces) {
+ linetypeList <- lapply(traces, "[[", "linetype")
+ nLinetypes <- lengths(linetypeList)
+ # if no "top-level" linetype is present, return traces untouched
+ if (all(nLinetypes == 0)) {
+ return(traces)
+ }
+ linetype <- unlist(compact(linetypeList))
+ lvls <- getLevels(linetype)
+ # get a sensible default palette
+ pal <- setNames(scales::linetype_pal()(length(lvls)), lvls)
+ pal <- supplyUserPalette(pal, traces[[1]][["linetypes"]])
+
+ validLinetypes <- as.character(Schema$traces$scatter$attributes$line$dash$values)
+ if (length(pal) > length(validLinetypes)) {
+ warning("plotly.js only supports 6 different linetypes", call. = FALSE)
+ }
+
+ for (i in which(nLinetypes > 0)) {
+ l <- linetypeList[[i]]
+ dashes <- lty2dash(if (inherits(l, "AsIs")) l else as.character(pal[as.character(l)]))
+ illegalLinetypes <- setdiff(dashes, validLinetypes)
+ if (length(illegalLinetypes)) {
+ warning(
+ "The following are not valid linetype codes:\n'",
+ paste(illegalLinetypes, collapse = "', '"), "'\n",
+ "Valid linetypes include:\n'",
+ paste(validLinetypes, collapse = "', '"), "'", call. = FALSE
+ )
+ }
+ traces[[i]][["line"]] <- modify_list(
+ list(dash = dashes), traces[[i]][["line"]]
+ )
+ # ensure the mode is set so that the linetype is relevant
+ if (!grepl("lines", traces[[i]]$mode %||% "")) {
+ message("Adding lines to mode; otherwise linetype would have no effect.")
+ traces[[i]][["mode"]] <- paste0(traces[[i]][["mode"]], "+lines")
+ }
+ }
+ traces
+}
+
+
+# break up a single trace into multiple traces according to values stored
+# a particular key name
+traceify <- function(dat, x = NULL) {
+ if (length(x) == 0) return(list(dat))
+ lvls <- if (is.factor(x)) levels(x) else unique(x)
+ lvls <- lvls[lvls %in% x]
+ # the order of lvls determines the order in which traces are drawn
+ # for ordered factors at least, it makes sense to draw the highest level first
+ # since that _should_ be the darkest color in a sequential pallette
+ if (is.ordered(x)) lvls <- rev(lvls)
+ n <- length(x)
+ # recursively search for a non-list of appropriate length (if it is, subset it)
+ recurse <- function(z, n, idx) {
+ if (is.list(z)) lapply(z, recurse, n, idx) else if (length(z) == n) z[idx] else z
+ }
+ new_dat <- list()
+ for (j in seq_along(lvls)) {
+ new_dat[[j]] <- lapply(dat, function(y) recurse(y, n, x %in% lvls[j]))
+ new_dat[[j]]$name <- lvls[j]
+ }
+ return(new_dat)
+}
+
+
+eval_attr <- function(x, data = NULL) {
+ if (lazyeval::is_formula(x)) lazyeval::f_eval(x, data) else x
+}
+
+# overwrite defaults with the user defined palette
+supplyUserPalette <- function(default, user) {
+ for (i in seq_along(user)) {
+ idx <- names(user)[i] %||% i
+ default[idx] <- user[i]
+ }
+ default
+}
diff --git a/R/plotly_data.R b/R/plotly_data.R
new file mode 100644
index 0000000..dc1f279
--- /dev/null
+++ b/R/plotly_data.R
@@ -0,0 +1,253 @@
+#' Obtain data associated with a plotly graph
+#'
+#' `plotly_data()` returns data associated with
+#' a plotly visualization (if there are multiple data frames, by default,
+#' it returns the most recent one).
+#'
+#' @param p a plotly visualization
+#' @param id a character string or number referencing an "attribute layer".
+#'
+#' @param .data a plotly visualization
+#' @param x a plotly visualization
+#' @param ... stuff passed onto the relevant method
+#' @param add By default, when add = FALSE, group_by will override existing groups.
+#' To instead add to the existing groups, use add = TRUE
+#' @param .dots Used to work around non-standard evaluation. See vignette("nse") for details
+#'
+#' @name plotly_data
+#' @export
+#' @examples
+#'
+#' # use group_by() to define groups of visual markings
+#' p <- txhousing %>%
+#' group_by(city) %>%
+#' plot_ly(x = ~date, y = ~sales)
+#' p
+#'
+#' # plotly objects preserve data groupings
+#' groups(p)
+#' plotly_data(p)
+#'
+#' # dplyr verbs operate on plotly objects as if they were data frames
+#' p <- economics %>%
+#' plot_ly(x = ~date, y = ~unemploy / pop) %>%
+#' add_lines() %>%
+#' mutate(rate = unemploy / pop) %>%
+#' filter(rate == max(rate))
+#' plotly_data(p)
+#' add_markers(p)
+#' layout(p, annotations = list(x = ~date, y = ~rate, text = "peak"))
+#'
+#' # use group_by() + do() + subplot() for trellis displays
+#' d <- group_by(mpg, drv)
+#' plots <- do(d, p = plot_ly(., x = ~cty, name = ~drv))
+#' subplot(plots[["p"]], nrows = 3, shareX = TRUE)
+#'
+#' # arrange displays by their mean
+#' means <- summarise(d, mn = mean(cty, na.rm = TRUE))
+#' means %>%
+#' dplyr::left_join(plots) %>%
+#' arrange(mn) %>%
+#' subplot(nrows = NROW(.), shareX = TRUE)
+#'
+#' # more dplyr verbs applied to plotly objects
+#' p <- mtcars %>%
+#' plot_ly(x = ~wt, y = ~mpg, name = "scatter trace") %>%
+#' add_markers()
+#' p %>% slice(1) %>% plotly_data()
+#' p %>% slice(1) %>% add_markers(name = "first observation")
+#' p %>% filter(cyl == 4) %>% plotly_data()
+#' p %>% filter(cyl == 4) %>% add_markers(name = "four cylinders")
+#'
+#'
+plotly_data <- function(p, id = p$x$cur_data) {
+ if (!is.plotly(p)) {
+ stop("This function can only retrieve data from plotly objects.")
+ }
+ f <- p$x$visdat[[id]]
+ # if data has been specified, f should be a closure that, when called,
+ # returns data
+ if (is.null(f)) return(f)
+ if (!is.function(f)) stop("Expected a closure", call. = FALSE)
+ dat <- f()
+ if (crosstalk::is.SharedData(dat)) {
+ key <- dat$key()
+ set <- dat$groupName()
+ dat <- dat$origData()
+ dat[[crosstalk_key()]] <- key
+ # not allowed for list-columns!
+ #dat <- dplyr::group_by_(dat, crosstalk_key(), add = TRUE)
+ dat <- structure(dat, set = set)
+ }
+ if (is.data.frame(dat)) tibble::as_tibble(dat) else dat
+}
+
+#' @rdname plotly_data
+#' @export
+groups.plotly <- function(x) {
+ dplyr::groups(plotly_data(x))
+}
+
+#' @rdname plotly_data
+#' @export
+ungroup.plotly <- function(x, ...) {
+ d <- dplyr::ungroup(plotly_data(x))
+ add_data(x, d)
+}
+
+#' @rdname plotly_data
+#' @export
+group_by_.plotly <- function(.data, ..., .dots, add = FALSE) {
+ d <- plotly_data(.data)
+ d2 <- dplyr::group_by_(d, .dots = lazyeval::all_dots(.dots, ...), add = add)
+ # add crosstalk key as a group (to enable examples like demos/highlight-pipeline.R)
+ if (crosstalk_key() %in% names(d)) {
+ d2 <- dplyr::group_by_(d2, crosstalk_key(), add = TRUE)
+ }
+ add_data(.data, d2)
+}
+
+#' @rdname plotly_data
+#' @export
+summarise_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::summarise_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+#' @rdname plotly_data
+#' @export
+mutate_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ dotz <- lazyeval::all_dots(.dots, ...)
+ # '.' in a pipeline should really reference the data!!
+ lapply(dotz, function(x) { assign(".", d, x$env) })
+ set <- attr(d, "set")
+ d <- dplyr::mutate_(d, .dots = dotz)
+ add_data(.data, structure(d, set = set))
+}
+
+#' @rdname plotly_data
+#' @export
+do_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ dotz <- lazyeval::all_dots(.dots, ...)
+ # '.' in a pipeline should really reference the data!!
+ lapply(dotz, function(x) { assign(".", d, x$env) })
+ set <- attr(d, "set")
+ d <- dplyr::do_(d, .dots = dotz)
+ add_data(.data, structure(d, set = set))
+}
+
+#' @rdname plotly_data
+#' @export
+arrange_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::arrange_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+#' @rdname plotly_data
+#' @export
+select_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::select_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+#' @rdname plotly_data
+#' @export
+filter_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::filter_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+#' @rdname plotly_data
+#' @export
+distinct_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::distinct_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+#' @rdname plotly_data
+#' @export
+slice_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::slice_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+#' @rdname plotly_data
+#' @export
+rename_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::rename_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+#' @rdname plotly_data
+#' @export
+transmute_.plotly <- function(.data, ..., .dots) {
+ d <- plotly_data(.data)
+ d <- dplyr::transmute_(d, .dots = lazyeval::all_dots(.dots, ...))
+ add_data(.data, d)
+}
+
+# ---------------------------------------------------------------------------
+# tidyr methods
+# waiting on https://github.com/tidyverse/tidyr/pull/229
+# ---------------------------------------------------------------------------
+
+# #' @rdname plotly_data
+# #' @export
+# gather_.plotly <- function(data, key_col, value_col, gather_cols, na.rm = FALSE,
+# convert = FALSE, factor_key = FALSE) {
+# d <- plotly_data(data)
+# set <- attr(d, "set")
+# d <- tidyr::gather_(
+# d, key_col = key_col, value_col = value_col, gather_cols = gather_cols,
+# na.rm = na.rm, convert = convert, factor_key = factor_key
+# )
+# add_data(data, structure(d, set = set))
+# }
+#
+# #' @importFrom dplyr select_vars
+# #' @rdname plotly_data
+# #' @export
+# gather_vars.plotly <- function(data, key_col, value_col, ...) {
+# d <- plotly_data(data)
+# if (n_dots(...) == 0) {
+# setdiff(colnames(d), c(key_col, value_col))
+# } else {
+# unname(dplyr::select_vars(colnames(d), ...))
+# }
+# }
+#
+# n_dots <- function(...) nargs()
+
+
+# ---------------------------------------------------------------------------
+# miscellanous methods
+# ---------------------------------------------------------------------------
+
+# Avoid errors when passing a shared data to ggplot2
+# qplot(data = crosstalk::SharedData$new(mtcars), mpg, wt)
+
+#' @export
+fortify.SharedData <- function(model, data, ...) {
+ key <- model$key()
+ set <- model$groupName()
+ data <- model$origData()
+ # need a consistent name so we know how to access it ggplotly()
+ data[[crosstalk_key()]] <- key
+ structure(data, set = set)
+}
+
+# yes, you can feed a plotly object into ggplot %^)
+#' @export
+ggplot.plotly <- function(data, mapping = aes(), ...,
+ environment = parent.frame()) {
+ ggplot(plotly_data(data), mapping = mapping, ..., environment = environment)
+}
diff --git a/R/plotly_example.R b/R/plotly_example.R
new file mode 100644
index 0000000..8ccd62d
--- /dev/null
+++ b/R/plotly_example.R
@@ -0,0 +1,52 @@
+#' Run a plotly example(s)
+#'
+#' Provides a unified interface for running demos, shiny apps, and Rmd documents
+#' which are bundled with the package.
+#'
+#' @param type the type of example
+#' @param name the name of the example (valid names depend on `type`).
+#' @param ... arguments passed onto the suitable method.
+#' @export
+#' @author Carson Sievert
+
+plotly_example <- function(type = c("demo", "shiny", "rmd"), name, ...) {
+ if (missing(name)) {
+ stop("Must provide an example name", call. = FALSE)
+ }
+
+ type <- match.arg(type, type)
+
+ if (type == "demo") {
+ utils::demo(topic = name, package = "plotly")
+ }
+
+ # check to make sure the example exists
+ exampleDir <- system.file("examples", type, package = "plotly")
+ nms <- basename(list.dirs(exampleDir, recursive = FALSE))
+ if (!isTRUE(name %in% nms)) {
+ stop(
+ sprintf(
+ "Couldn't find that %s example. Valid examples include: '%s'",
+ type, paste(nms, collapse = "', '")
+ ),
+ .call = FALSE
+ )
+ }
+
+ finalDir <- system.file("examples", type, name, package = "plotly")
+
+ if (type == "shiny") {
+ try_library("shiny", "plotly_example")
+ getFromNamespace("runApp", asNamespace("shiny"))(finalDir, ...)
+ }
+
+ if (type == "rmd") {
+ try_library("rmarkdown", "plotly_example")
+ input <- file.path(finalDir, "index.Rmd")
+ output <- tempfile(fileext = ".html")
+ getFromNamespace("render", asNamespace("rmarkdown"))(input, output_file = output, ...)
+ browseURL(output)
+ }
+
+ invisible(NULL)
+}
diff --git a/R/print.R b/R/print.R
new file mode 100644
index 0000000..b75224c
--- /dev/null
+++ b/R/print.R
@@ -0,0 +1,104 @@
+#' Print method for a 'generic' API response
+#'
+#' @param x a list.
+#' @param ... additional arguments (currently ignored)
+#' @export
+print.api <- function(x, ...) {
+ cat("<Response from plot.ly>\n")
+ utils::str(x)
+ invisible(x)
+}
+
+#' Print a plot on plotly's platform
+#'
+#' @param x a plotly figure object
+#' @param ... additional arguments (currently ignored)
+#' @export
+print.api_plot <- function(x, ...) {
+ utils::browseURL(api_plot_url(x))
+ invisible(x)
+}
+
+#' Print a plotly grid object
+#'
+#' @param x a plotly grid object
+#' @param ... additional arguments (currently ignored)
+#' @export
+print.api_grid <- function(x, ...) {
+ utils::browseURL(api_grid_url(x))
+ invisible(x)
+}
+
+#' Print a plotly grid object
+#'
+#' @param x a plotly grid object
+#' @param ... additional arguments (currently ignored)
+#' @export
+print.api_grid_local <- function(x, ...) {
+ res <- tryCatch(tibble::as_tibble(x$preview), error = function(e) x$preview)
+ print(res)
+ invisible(x)
+}
+
+#' Embed a plotly figure as an iframe in a knitr doc
+#'
+#' @param x a plotly figure object
+#' @param options knitr options.
+#' @param ... placeholder.
+#' @export
+#' @references https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd
+knit_print.api_plot <- function(x, options, ...) {
+ iframe <- plotly_iframe(
+ api_plot_url(x, embed = TRUE),
+ options[["width"]] %||% "800",
+ options[["height"]] %||% "600",
+ url_ext = ""
+ )
+ knitr::asis_output(iframe)
+}
+
+#' Embed a plotly grid as an iframe in a knitr doc
+#'
+#' @param x a plotly figure object
+#' @param options knitr options.
+#' @param ... placeholder.
+#' @export
+#' @references https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd
+knit_print.api_grid <- function(x, options, ...) {
+ iframe <- plotly_iframe(
+ api_grid_url(x, embed = TRUE),
+ options[["width"]] %||% "800",
+ options[["height"]] %||% "600",
+ url_ext = ""
+ )
+ knitr::asis_output(iframe)
+}
+
+
+#' Embed a plotly grid as an iframe in a knitr doc
+#'
+#' @param x a plotly figure object
+#' @param options knitr options.
+#' @param ... placeholder.
+#' @export
+#' @references https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd
+knit_print.api_grid_local <- function(x, options, ...) {
+ print(tibble::as_tibble(x$preview))
+}
+
+
+api_plot_url <- function(x, embed = FALSE) {
+ url <- if (embed) x$embed_url else x$web_url
+ secret <- x$share_key_enabled %||% FALSE
+ if (secret) paste0(url, "?share_key=", x$share_key) else url
+}
+
+api_grid_url <- function(x, embed = FALSE) {
+ secret <- x$share_key_enabled %||% FALSE
+ if (embed) {
+ paste0(x$embed_url, if (secret) paste0("?share_key=", x$share_key))
+ } else {
+ # encourage people to use the create platform
+ paste0("https://plot.ly/create/?fid=", x$fid, if (secret) paste0("&share_key=", x$share_key))
+ }
+}
diff --git a/R/process.R b/R/process.R
new file mode 100644
index 0000000..bd987b0
--- /dev/null
+++ b/R/process.R
@@ -0,0 +1,55 @@
+# ----------------------------------------------------------------------------
+# Methods for processing API responses
+# ----------------------------------------------------------------------------
+
+process <- function(resp) {
+ UseMethod("process")
+}
+
+process.default <- function(resp) {
+ json_content(relay_error(resp))
+}
+
+process.api_plot <- function(resp) {
+ json_content(relay_error(resp))
+}
+
+process.api_image <- function(resp) {
+ relay_error(resp)
+ type <- resp[["headers"]][["content-type"]]
+ # httr (should) know to call png::readPNG() which returns raster array
+ tryCatch(
+ httr::content(resp, as = "parsed", type = type),
+ error = function(e) httr::content(resp, as = "raw", type = type)
+ )
+}
+
+# the default for httr::content() doesn't simplify vectors apparently...
+json_content <- function(resp) {
+ if (length(resp$content) == 0) return(list())
+ from_JSON(
+ httr::content(
+ resp,
+ as = "text",
+ type = resp[["headers"]][["content-type"]],
+ encoding = "UTF-8"
+ )
+ )
+}
+
+relay_error <- function(resp) {
+ if (!httr::http_error(resp)) {
+ return(resp)
+ }
+ con <- httr::content(resp)
+ # if we can't relay the plotly server error messages, return the response
+ if (!"errors" %in% names(con)) {
+ return(resp)
+ }
+ msgs <- lapply(con$errors, "[[", "message")
+ stop(
+ httr::http_status(resp)[["message"]], "\n\t",
+ paste(msgs, collapse = "\n\t"),
+ call. = FALSE
+ )
+}
diff --git a/R/proxy.R b/R/proxy.R
new file mode 100644
index 0000000..f962fa9
--- /dev/null
+++ b/R/proxy.R
@@ -0,0 +1,110 @@
+#' Modify a plotly object inside a shiny app
+#'
+#' @param outputId single-element character vector indicating the output ID
+#' map to modify (if invoked from a Shiny module, the namespace will be added
+#' automatically)
+#' @param session the Shiny session object to which the map belongs; usually the
+#' default value will suffice.
+#' @param deferUntilFlush indicates whether actions performed against this
+#' instance should be carried out right away, or whether they should be held
+#' until after the next time all of the outputs are updated.
+#' @rdname plotlyProxy
+#' @export
+#' @examples
+#'
+#'
+#' if (require("shiny") && interactive()) {
+#' plotly_example("shiny", "proxy_relayout")
+#' plotly_example("shiny", "proxy_mapbox")
+#' }
+#'
+plotlyProxy <- function(outputId, session = shiny::getDefaultReactiveDomain(),
+ deferUntilFlush = TRUE) {
+
+ # implementation very similar to leaflet::leafletProxy & DT:dataTableProxy
+ if (is.null(session)) {
+ stop("plotlyProxy must be called from the server function of a Shiny app")
+ }
+
+ if (!is.null(session$ns) && nzchar(session$ns(NULL)) &&
+ # TODO: require a recent version of R and use startsWith()?
+ substring(outputId, 1, nchar(session$ns(""))) != session$ns("")) {
+ outputId <- session$ns(outputId)
+ }
+ structure(
+ list(
+ session = session,
+ id = outputId,
+ deferUntilFlush = deferUntilFlush
+ # TODO: is there actually a use-case for this?
+ #x = structure(list(), leafletData = data),
+ #dependencies = NULL
+ ),
+ class = "plotly_proxy"
+ )
+}
+
+
+# ----------------------------------------------------------------------
+# TODO: implement some higher-level functions, say `plotlyProxyLayout()`,
+# `plotlyProxyAddTraces()`, `plotlyProxyStyle()`, that pick the right
+# method, except formula/data mappings, and possibly some argument checking
+# ----------------------------------------------------------------------
+
+
+#' @param p a plotly proxy object (created with `plotlyProxy`)
+#' @param method a plotlyjs method to invoke. For a list of options,
+#' visit the \href{https://plot.ly/javascript/plotlyjs-function-reference}{plotlyjs function reference}
+#' @param ... unnamed arguments passed onto the plotly.js method
+#' @rdname plotlyProxy
+#' @export
+plotlyProxyInvoke <- function(p, method, ...) {
+
+ if (!is.proxy(p))
+ stop("p must be a proxy object. See `help(plotlyProxy)`", call. = FALSE)
+
+ if (missing(method))
+ stop(
+ "Must provide a plotly.js method (as a character string of length 1).\n",
+ sprintf("Valid options include: '%s'",
+ paste(plotlyjs_methods(), collapse = "', '")),
+ call. = FALSE
+ )
+
+ method <- match.arg(method, plotlyjs_methods())
+
+ msg <- list(
+ id = p$id,
+ method = method,
+ # TODO: can we leverage the plotly_build() infrastructure in a smart way?
+ # args = evalFormula(list(...), data)
+ args = list(...)
+ )
+
+ if (isTRUE(p$deferUntilFlushed)) {
+
+ p$session$onFlushed(function() {
+ p$session$sendCustomMessage("plotly-calls", msg)
+ }, once = TRUE)
+
+ } else {
+
+ p$session$sendCustomMessage("plotly-calls", msg)
+
+ }
+
+ p
+}
+
+
+plotlyjs_methods <- function() {
+ c(
+ "restyle", "relayout", "update", "addTraces", "deleteTraces", "moveTraces",
+ "extendTraces", "prependTraces", "purge", "toImage", "downloadImage", "animate"
+ )
+}
+
+
+is.proxy <- function(x) {
+ inherits(x, "plotly_proxy")
+}
diff --git a/R/sf.R b/R/sf.R
new file mode 100644
index 0000000..68ae7d4
--- /dev/null
+++ b/R/sf.R
@@ -0,0 +1,141 @@
+# take a "tidy" sf data frame and "expand it" so every row represents a point
+# rather than geometry/geometries
+expand <- function(data) {
+ xs <- lapply(data$geometry, get_x)
+ ys <- lapply(data$geometry, get_y)
+ ns <- lapply(data$geometry, get_l)
+ ids <- lapply(ns, function(x) rep(seq_len(length(x)), x))
+ dats <- Map(function(x, y, z, w) {
+ data.frame(x, y, group = paste(z, w, sep = "-"), `.plotlyMergeID` = w, stringsAsFactors = FALSE)
+ }, xs, ys, ids, seq_along(data$geometry))
+ # merge this "expanded" geometry data back with original data
+ ids <- seq_len(nrow(data))
+ data[[".plotlyMergeID"]] <- ids
+ data[["group"]] <- NULL
+ dplyr::left_join(dplyr::bind_rows(dats), data, by = ".plotlyMergeID")
+}
+
+# ------------------------------------------------------------------
+# these helper functions are adapted from methods(st_as_grob)
+# see, for example, getS3method("st_as_grob", "MULTIPOLYGON")
+# ------------------------------------------------------------------
+
+#' Obtain x coordinates of sf geometry/geometries
+#'
+#' Exported for internal reasons. Not intended for general use.
+#'
+#' @param g an sf geometry
+#' @export
+get_x <- function(g) {
+ UseMethod("get_x")
+}
+
+#' Obtain y coordinates of sf geometry/geometries
+#'
+#' Exported for internal reasons. Not intended for general use.
+#'
+#' @param g an sf geometry
+#' @export
+get_y <- function(g) {
+ UseMethod("get_y")
+}
+
+#' Obtain number of points comprising a geometry
+#'
+#' Exported for internal reasons. Not intended for general use.
+#'
+#' @param g an sf geometry
+#' @export
+get_l <- function(g) {
+ UseMethod("get_l")
+}
+
+#' @export
+get_x.MULTIPOLYGON <- function(g) {
+ unlist(sapply(g, function(v) sapply(v, function(z) z[, 1])))
+}
+
+#' @export
+get_y.MULTIPOLYGON <- function(g) {
+ unlist(sapply(g, function(v) sapply(v, function(z) z[, 2])))
+}
+
+#' @export
+get_l.MULTIPOLYGON <- function(g) {
+ unlist(sapply(g, function(v) sapply(v, nrow)))
+}
+
+#' @export
+get_x.POLYGON <- function(g) {
+ unlist(sapply(g, function(y) y[, 1]))
+}
+
+#' @export
+get_y.POLYGON <- function(g) {
+ unlist(sapply(g, function(y) y[, 2]))
+}
+
+#' @export
+get_l.POLYGON <- function(g) {
+ sapply(g, nrow)
+}
+
+#' @export
+get_x.MULTILINESTRING <- function(g) {
+ unlist(sapply(g, function(y) y[, 1]))
+}
+
+#' @export
+get_y.MULTILINESTRING <- function(g) {
+ unlist(sapply(g, function(y) y[, 2]))
+}
+
+#' @export
+get_l.MULTILINESTRING <- function(g) {
+ sapply(g, nrow)
+}
+
+#' @export
+get_x.LINESTRING <- function(g) {
+ g[, 1]
+}
+
+#' @export
+get_y.LINESTRING <- function(g) {
+ g[, 2]
+}
+
+#' @export
+get_l.LINESTRING <- function(g) {
+ nrow(g)
+}
+
+#' @export
+get_x.MULTIPOINT <- function(g) {
+ g[, 1]
+}
+
+#' @export
+get_y.MULTIPOINT <- function(g) {
+ g[, 2]
+}
+
+#' @export
+get_l.MULTIPOINT <- function(g) {
+ nrow(g)
+}
+
+#' @export
+get_x.POINT <- function(g) {
+ g[1]
+}
+
+#' @export
+get_y.POINT <- function(g) {
+ g[2]
+}
+
+#' @export
+get_l.POINT <- function(g) {
+ nrow(g)
+}
diff --git a/R/shiny.R b/R/shiny.R
new file mode 100644
index 0000000..6f32259
--- /dev/null
+++ b/R/shiny.R
@@ -0,0 +1,71 @@
+#' Shiny bindings for plotly
+#'
+#' Output and render functions for using plotly within Shiny
+#' applications and interactive Rmd documents.
+#'
+#' @param outputId output variable to read from
+#' @param width,height Must be a valid CSS unit (like \code{"100\%"},
+#' `"400px"`, `"auto"`) or a number, which will be coerced to a
+#' string and have `"px"` appended.
+#' @param inline use an inline (`span()`) or block container
+#' (`div()`) for the output
+#' @param expr An expression that generates a plotly
+#' @param env The environment in which to evaluate `expr`.
+#' @param quoted Is `expr` a quoted expression (with `quote()`)? This
+#' is useful if you want to save an expression in a variable.
+#'
+#' @importFrom htmlwidgets shinyWidgetOutput
+#' @importFrom htmlwidgets shinyRenderWidget
+#' @name plotly-shiny
+#'
+#' @export
+plotlyOutput <- function(outputId, width = "100%", height = "400px",
+ inline = FALSE) {
+ htmlwidgets::shinyWidgetOutput(
+ outputId = outputId,
+ name = "plotly",
+ width = width,
+ height = height,
+ inline = inline,
+ package = "plotly"
+ )
+}
+
+#' @rdname plotly-shiny
+#' @export
+renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
+ if (!quoted) { expr <- substitute(expr) } # force quoted
+ # this makes it possible to pass a ggplot2 object to renderPlotly()
+ # https://github.com/ramnathv/htmlwidgets/issues/166#issuecomment-153000306
+ expr <- as.call(list(call("::", quote("plotly"), quote("ggplotly")), expr))
+ shinyRenderWidget(expr, plotlyOutput, env, quoted = TRUE)
+}
+
+
+#' Access plotly user input event data in shiny
+#'
+#' This function must be called within a reactive shiny context.
+#'
+#' @param event The type of plotly event. Currently 'plotly_hover',
+#' 'plotly_click', 'plotly_selected', and 'plotly_relayout' are supported.
+#' @param source a character string of length 1. Match the value of this string
+#' with the source argument in [plot_ly()] to retrieve the
+#' event data corresponding to a specific plot (shiny apps can have multiple plots).
+#' @param session a shiny session object (the default should almost always be used).
+#' @export
+#' @author Carson Sievert
+#' @examples \dontrun{
+#' plotly_example("shiny", "event_data")
+#' }
+
+event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected",
+ "plotly_relayout"), source = "A",
+ session = shiny::getDefaultReactiveDomain()) {
+ if (is.null(session)) {
+ stop("No reactive domain detected. This function can only be called \n",
+ "from within a reactive shiny context.")
+ }
+ src <- sprintf(".clientValue-%s-%s", event[1], source)
+ val <- session$rootScope()$input[[src]]
+ if (is.null(val)) val else jsonlite::fromJSON(val)
+}
diff --git a/R/signup.R b/R/signup.R
new file mode 100644
index 0000000..1f9472b
--- /dev/null
+++ b/R/signup.R
@@ -0,0 +1,58 @@
+
+#' Create a new plotly account.
+#'
+#' A sign up interface to plotly through the R Console.
+#'
+#' @param username Desired username.
+#' @param email Desired email.
+#' @param save If request is successful, should the username & API key be
+#' automatically stored as an environment variable in a .Rprofile?
+#'
+#' @return
+#' \itemize{
+#' \item api_key key to use with the api
+#' \item tmp_pw temporary password to access your plotly account
+#' }
+#' @references https://plot.ly/rest/
+#' @export
+#' @examples \dontrun{
+#' # You need a plotly username and API key to communicate with the plotly API.
+#'
+#' # If you don't already have an API key, you can obtain one with a valid
+#' # username and email via signup().
+#' s <- signup('anna.lyst', 'anna.lyst@@plot.ly')
+#'
+#' # If you already have a username and API key, please create the following
+#' # environment variables:
+#' Sys.setenv("plotly_username" = "me")
+#' Sys.setenv("plotly_api_key" = "mykey")
+#' # You can also change the default domain if you have a plotly server.
+#' Sys.setenv("plotly_domain" = "http://mydomain.com")
+#'
+#' # If you want to automatically load these environment variables when you
+#' # start R, you can put them inside your ~/.Rprofile
+#' # (see help(.Rprofile) for more details)
+#'
+#' }
+signup <- function(username, email, save = TRUE) {
+ if (missing(username)) username <- verify("username")
+ if (missing(email)) stop("Must specify a valid email")
+ # construct body of message to plotly server
+ bod <- list(
+ un = username,
+ email = email,
+ platform = "R",
+ version = as.character(packageVersion("plotly"))
+ )
+ base_url <- file.path(get_domain(), "apimkacct")
+ resp <- httr::POST(base_url, body = bod)
+ con <- process(append_class(resp, "signup"))
+ if (save) {
+ # store API key as an environment variable in .Rprofile
+ cat_profile("username", con$un)
+ cat_profile("api_key", con$api_key)
+ }
+ Sys.setenv("plotly_username" = con$un)
+ Sys.setenv("plotly_api_key" = con$api_key)
+ invisible(con)
+}
diff --git a/R/style.R b/R/style.R
new file mode 100644
index 0000000..427017b
--- /dev/null
+++ b/R/style.R
@@ -0,0 +1,34 @@
+
+#' Modify trace(s)
+#'
+#' Modify trace(s) of an existing plotly visualization. Useful when used in
+#' conjunction with [get_figure()].
+#'
+#' @param p A plotly visualization.
+#' @param ... Visual properties.
+#' @param traces numeric vector. Which traces should be modified? By default,
+#' attributes place in `...` will be applied to every trace.
+#' @seealso [api_download_plot()]
+#' @author Carson Sievert
+#' @export
+#' @examples
+#'
+#' p <- qplot(data = mtcars, wt, mpg, geom = c("point", "smooth"))
+#' # keep the hover info for points, but remove it for the line/ribbon
+#' style(p, hoverinfo = "none", traces = c(2, 3))
+#'
+style <- function(p, ..., traces = NULL) {
+ p <- plotly_build(p)
+ nTraces <- length(p$x$data)
+ traces <- traces %||% seq_len(nTraces)
+ idx <- traces > nTraces
+ traces <- traces[!idx]
+ if (any(idx)) warning("You've referenced non-existent traces", call. = FALSE)
+ argz <- list(...)
+ for (i in traces) {
+ for (j in names(argz)) {
+ p$x$data[[i]][[j]] <- argz[[j]]
+ }
+ }
+ p
+}
diff --git a/R/subplots.R b/R/subplots.R
new file mode 100644
index 0000000..b855af0
--- /dev/null
+++ b/R/subplots.R
@@ -0,0 +1,425 @@
+#' View multiple plots in a single view
+#'
+#' @param ... One of the following
+#' \itemize{
+#' \item any number of plotly/ggplot2 objects.
+#' \item a list of plotly/ggplot2 objects.
+#' \item a tibble with one list-column of plotly/ggplot2 objects.
+#' }
+#' @param nrows number of rows for laying out plots in a grid-like structure.
+#' Only used if no domain is already specified.
+#' @param widths relative width of each column on a 0-1 scale. By default all
+#' columns have an equal relative width.
+#' @param heights relative height of each row on a 0-1 scale. By default all
+#' rows have an equal relative height.
+#' @param margin either a single value or four values (all between 0 and 1).
+#' If four values are provided, the first is used as the left margin, the second
+#' is used as the right margin, the third is used as the top margin, and the
+#' fourth is used as the bottom margin.
+#' If a single value is provided, it will be used as all four margins.
+#' @param shareX should the x-axis be shared amongst the subplots?
+#' @param shareY should the y-axis be shared amongst the subplots?
+#' @param titleX should x-axis titles be retained?
+#' @param titleY should y-axis titles be retained?
+#' @param which_layout adopt the layout of which plot? If the default value of
+#' "merge" is used, layout options found later in the sequence of plots will
+#' override options found earlier in the sequence. This argument also accepts a
+#' numeric vector specifying which plots to consider when merging.
+#' @return A plotly object
+#' @export
+#' @author Carson Sievert
+#' @examples
+#'
+#' # pass any number of plotly objects to subplot()
+#' p1 <- plot_ly(economics, x = ~date, y = ~uempmed)
+#' p2 <- plot_ly(economics, x = ~date, y = ~unemploy)
+#' subplot(p1, p2, p1, p2, nrows = 2, margin = 0.05)
+#'
+#' #' # anchor multiple traces on the same legend entry
+#' p1 <- add_lines(p1, color = I("black"), name = "1st", legendgroup = "1st")
+#' p2 <- add_lines(p2, color = I("red"), name = "2nd", legendgroup = "2nd")
+#'
+#' subplot(
+#' p1, style(p1, showlegend = FALSE),
+#' p2, style(p2, showlegend = FALSE),
+#' nrows = 2, margin = 0.05
+#' )
+#'
+#' # or pass a list
+#' economics_long %>%
+#' split(.$variable) %>%
+#' lapply(function(d) plot_ly(d, x = ~date, y = ~value)) %>%
+#' subplot(nrows = NROW(.), shareX = TRUE)
+#'
+#' # or pass a tibble with a list-column of plotly objects
+#' economics_long %>%
+#' group_by(variable) %>%
+#' do(p = plot_ly(., x = ~date, y = ~value)) %>%
+#' subplot(nrows = NROW(.), shareX = TRUE)
+#'
+#' # learn more at https://cpsievert.github.io/plotly_book/subplot.html
+#'
+
+subplot <- function(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02,
+ shareX = FALSE, shareY = FALSE, titleX = shareX,
+ titleY = shareY, which_layout = "merge") {
+
+
+ plots <- dots2plots(...)
+
+ # some plotly functions call plotly_build()...subplot() doesn't like that
+ for (i in seq_along(plots)) {
+ if (!is.null(plots[[i]][["frames"]])) {
+ warning(
+ sprintf("`subplot()` detected plot #%s was 'pre-built' and already has registered\n", i),
+ "animation frames. This can cause problems and may happen by calling a \n",
+ "function like `animation_opts()` or `highlight()` (which returns a 'built' plot)\n",
+ "_before_ `subplot()`. Consider using such functions _after_ `subplot()`.",
+ call. = FALSE
+ )
+ }
+ }
+
+ # build all the plots without registering frames
+ plotz <- lapply(plots, function(d) plotly_build(d, registerFrames = FALSE)[["x"]])
+
+ # Are any traces referencing "axislike" layout attributes that are missing?
+ # If so, move those traces to a "new plot", and inherit layout attributes,
+ # which makes this sort of thing possible:
+ # https://plot.ly/r/map-subplots-and-small-multiples/
+ plots <- list()
+ for (i in seq_along(plotz)) {
+ p <- plots[[i]] <- plotz[[i]]
+ layoutAttrs <- c(names(p$layout), c("mapbox", "geo", "xaxis", "yaxis"))
+ xTraceAttrs <- sub("^x", "xaxis", sapply(p$data, function(tr) tr[["subplot"]] %||% tr[["geo"]] %||% tr[["xaxis"]] %||% "x"))
+ yTraceAttrs <- sub("^y", "yaxis", sapply(p$data, function(tr) tr[["subplot"]] %||% tr[["geo"]] %||% tr[["yaxis"]] %||% "y"))
+ missingAttrs <- setdiff(c(xTraceAttrs, yTraceAttrs), layoutAttrs)
+ # move to next iteration if trace references are complete
+ if (!length(missingAttrs)) next
+ # remove each "missing" trace from this plot
+ missingTraces <- xTraceAttrs %in% missingAttrs | yTraceAttrs %in% missingAttrs
+ plots[[i]]$data[missingTraces] <- NULL
+ # move traces with "similar missingness" to a new plot
+ for (j in missingAttrs) {
+ newPlot <- list(
+ data = p$data[xTraceAttrs %in% j | yTraceAttrs %in% j],
+ layout = p$layout
+ )
+ # reset the anchors
+ newPlot$data <- lapply(newPlot$data, function(tr) {
+ for (k in c("mapbox", "geo", "xaxis", "yaxis")) {
+ tr[[k]] <- sub("[0-9]+", "", tr[[k]]) %||% NULL
+ }
+ tr
+ })
+ plots <- c(plots, list(newPlot))
+ }
+ }
+
+ # grab main plot objects
+ traces <- lapply(plots, "[[", "data")
+ layouts <- lapply(plots, "[[", "layout")
+ shapes <- lapply(layouts, "[[", "shapes")
+ annotations <- lapply(layouts, function(x) {
+ # keep non axis title annotations (for rescaling)
+ axes <- vapply(x$annotations, function(a) identical(a$annotationType, "axis"), logical(1))
+ x$annotations[!axes]
+ })
+ # collect axis objects (note a _single_ geo/mapbox object counts a both an x and y)
+ xAxes <- lapply(layouts, function(lay) {
+ keys <- grep("^geo|^mapbox|^xaxis", names(lay), value = TRUE) %||% "xaxis"
+ for (k in keys) {
+ dom <- lay[[k]]$domain %||% c(0, 1)
+ if ("x" %in% names(dom)) dom <- dom[["x"]]
+ }
+ lay[keys]
+ })
+ yAxes <- lapply(layouts, function(lay) {
+ keys <- grep("^geo|^mapbox|^yaxis", names(lay), value = TRUE) %||% "yaxis"
+ for (k in keys) {
+ dom <- lay[[k]]$domain %||% c(0, 1)
+ if ("y" %in% names(dom)) dom <- dom[["y"]]
+ }
+ lay[keys]
+ })
+ if (!titleX) {
+ xAxes <- lapply(xAxes, function(ax) lapply(ax, function(y) { y$title <- NULL; y }))
+ }
+ if (!titleY) {
+ yAxes <- lapply(yAxes, function(ax) lapply(ax, function(y) { y$title <- NULL; y }))
+ }
+ # number of x/y axes per plot
+ xAxisN <- vapply(xAxes, length, numeric(1))
+ yAxisN <- vapply(yAxes, length, numeric(1))
+ # old -> new axis name dictionary
+ ncols <- ceiling(length(plots) / nrows)
+ xAxisID <- seq_len(sum(xAxisN))
+ if (shareX) {
+ if (length(unique(xAxisN)) > 1) {
+ warning("Must have a consistent number of axes per 'subplot' to share them.")
+ } else {
+ xAxisID <- rep(rep(seq_len(ncols * unique(xAxisN)), length.out = length(plots)), unique(xAxisN))
+ }
+ }
+ yAxisID <- seq_len(sum(yAxisN))
+ if (shareY) {
+ if (length(unique(yAxisN)) > 1) {
+ warning("Must have a consistent number of axes per 'subplot' to share them.")
+ } else {
+ yAxisID <- rep(rep(seq_len(nrows * unique(xAxisN)), each = ncols, length.out = length(plots)), unique(yAxisN))
+ }
+ }
+ # current "axis" names
+ xCurrentNames <- unlist(lapply(xAxes, names))
+ yCurrentNames <- unlist(lapply(yAxes, names))
+ xNewNames <- paste0(
+ sub("[0-9]+$", "", xCurrentNames),
+ sub("^1$", "", xAxisID)
+ )
+ yNewNames <- paste0(
+ sub("[0-9]+$", "", yCurrentNames),
+ sub("^1$", "", yAxisID)
+ )
+ xAxisMap <- setNames(xCurrentNames, xNewNames)
+ yAxisMap <- setNames(yCurrentNames, yNewNames)
+ # split the map by plot ID
+ xAxisMap <- split(xAxisMap, rep(seq_along(plots), xAxisN))
+ yAxisMap <- split(yAxisMap, rep(seq_along(plots), yAxisN))
+ # domains of each subplot
+ domainInfo <- get_domains(
+ length(plots), nrows, margin, widths = widths, heights = heights
+ )
+ for (i in seq_along(plots)) {
+ # map axis object names
+ xMap <- xAxisMap[[i]]
+ yMap <- yAxisMap[[i]]
+ xAxes[[i]] <- setNames(xAxes[[i]], names(xMap))
+ yAxes[[i]] <- setNames(yAxes[[i]], names(yMap))
+ # for cartesian, bump corresponding axis anchor
+ for (j in seq_along(xAxes[[i]])) {
+ if (grepl("^geo|^mapbox", names(xAxes[[i]][j]))) next
+ map <- yMap[yMap %in% sub("y", "yaxis", xAxes[[i]][[j]]$anchor %||% "y")]
+ xAxes[[i]][[j]]$anchor <- sub("axis", "", names(map))
+ }
+ for (j in seq_along(yAxes[[i]])) {
+ if (grepl("^geo|^mapbox", names(yAxes[[i]][j]))) next
+ map <- xMap[xMap %in% sub("x", "xaxis", yAxes[[i]][[j]]$anchor %||% "x")]
+ yAxes[[i]][[j]]$anchor <- sub("axis", "", names(map))
+ }
+ # map trace xaxis/yaxis/geo attributes
+ for (key in c("geo", "subplot", "xaxis", "yaxis")) {
+ oldAnchors <- unlist(lapply(traces[[i]], "[[", key))
+ if (!length(oldAnchors)) next
+ axisMap <- if (key == "yaxis") yMap else xMap
+ axisMap <- setNames(sub("axis", "", axisMap), sub("axis", "", names(axisMap)))
+ newAnchors <- names(axisMap)[match(oldAnchors, axisMap)]
+ traces[[i]] <- Map(function(tr, a) { tr[[key]] <- a; tr }, traces[[i]], newAnchors)
+ }
+ # rescale domains according to the tabular layout
+ xDom <- as.numeric(domainInfo[i, c("xstart", "xend")])
+ yDom <- as.numeric(domainInfo[i, c("yend", "ystart")])
+ reScale <- function(old, new) {
+ sort(scales::rescale(
+ old %||% c(0, 1), new, from = c(0, 1)
+ ))
+ }
+ xAxes[[i]] <- lapply(xAxes[[i]], function(ax) {
+ if (all(c("x", "y") %in% names(ax$domain))) {
+ # geo domains are different from cartesian
+ ax$domain$x <- reScale(ax$domain$x, xDom)
+ ax$domain$y <- reScale(ax$domain$y, yDom)
+ } else {
+ ax$domain <- reScale(ax$domain, xDom)
+ }
+ ax
+ })
+ yAxes[[i]] <- lapply(yAxes[[i]], function(ax) {
+ if (all(c("x", "y") %in% names(ax$domain))) {
+ # geo domains are different from cartesian
+ ax$domain$x <- reScale(ax$domain$x, xDom)
+ ax$domain$y <- reScale(ax$domain$y, yDom)
+ } else {
+ ax$domain <- reScale(ax$domain, yDom)
+ }
+ ax
+ })
+ }
+
+ p <- list(
+ data = Reduce(c, traces),
+ layout = Reduce(modify_list, c(xAxes, rev(yAxes)))
+ )
+ # retrain default coloring
+ p$data <- retrain_color_defaults(p$data)
+
+ # reposition shapes and annotations
+ annotations <- Map(reposition, annotations, split(domainInfo, seq_along(plots)))
+ shapes <- Map(reposition, shapes, split(domainInfo, seq_along(plots)))
+ p$layout$annotations <- Reduce(c, annotations)
+ p$layout$shapes <- Reduce(c, shapes)
+ # merge non-axis layout stuff
+ layouts <- lapply(layouts, function(x) {
+ x[!grepl("^[x-y]axis|^geo|^mapbox|annotations|shapes", names(x))] %||% list()
+ })
+ if (which_layout != "merge") {
+ if (!is.numeric(which_layout)) warning("which_layout must be numeric")
+ if (!all(idx <- which_layout %in% seq_along(plots))) {
+ warning("which_layout is referencing non-existant layouts")
+ which_layout <- which_layout[idx]
+ }
+ layouts <- layouts[which_layout]
+ }
+ p$attrs <- Reduce(c, lapply(plots, "[[", "attrs"))
+ p$layout <- modify_list(p$layout, Reduce(modify_list, layouts))
+ p$source <- ensure_one(plots, "source")
+ p$config <- ensure_one(plots, "config")
+ p$highlight <- ensure_one(plots, "highlight")
+ p$subplot <- TRUE
+ as_widget(p)
+}
+
+# ----------------------------------------------------------------
+# Functions used solely within subplot()
+# ----------------------------------------------------------------
+
+# take a "collection" of plots and
+dots2plots <- function(...) {
+ dotz <- list(...)
+
+ # if ... is a list (or a tibble), list(...) is a (length 1) list
+ # containing a list of plotly objects
+ if (length(dotz) == 1 && is.list(dotz[[1]]) && !is.plotly(dotz[[1]])) {
+ dotz <- dotz[[1]]
+ }
+
+ if (tibble::is_tibble(dotz)) {
+ # if dots is a tibble, search for one column with a list of plotly objects
+ idx <- which(vapply(dotz, function(x) is.plotly(x[[1]]), logical(1)))
+ if (length(idx) != 1) {
+ stop(
+ "If you supply a tibble to subplot(), \n",
+ "it must have _one_ column with a list of plotly objects",
+ call. = FALSE
+ )
+ }
+ dotz <- dotz[[idx]]
+ }
+
+ dotz
+}
+
+
+
+# helper function that warns if more than one plot-level attribute
+# has been specified in a list of plots (and returning that attribute)
+ensure_one <- function(plots, attr) {
+ attrs <- lapply(plots, "[", attr)
+ for (i in seq_along(attrs)) {
+ if (!identical(attrs[[1]], attrs[[i]])) {
+ warning("Can only have one: ", attr, call. = FALSE)
+ break
+ }
+ }
+ attrs[[length(attrs)]][[1]]
+}
+
+
+get_domains <- function(nplots = 1, nrows = 1, margins = 0.01,
+ widths = NULL, heights = NULL) {
+ if (length(margins) == 1) margins <- rep(margins, 4)
+ if (length(margins) != 4) stop("margins must be length 1 or 4", call. = FALSE)
+ ncols <- ceiling(nplots / nrows)
+ widths <- widths %||% rep(1 / ncols, ncols)
+ heights <- heights %||% rep(1 / nrows, nrows)
+ if (length(widths) != ncols) {
+ stop("The length of the widths argument must be equal ",
+ "to the number of columns", call. = FALSE)
+ }
+ if (length(heights) != nrows) {
+ stop("The length of the heights argument is ", length(heights),
+ ", but the number of rows is ", nrows, call. = FALSE)
+ }
+ if (any(widths < 0) | any(heights < 0)) {
+ stop("The widths and heights arguments must contain positive values")
+ }
+ if (sum(widths) > 1 | sum(heights) > 1) {
+ stop("The sum of the widths and heights arguments must be less than 1")
+ }
+
+ widths <- cumsum(c(0, widths))
+ heights <- cumsum(c(0, heights))
+ # 'center' these values if there is still room left
+ widths <- widths + (1 - max(widths)) / 2
+ heights <- heights + (1 - max(heights)) / 2
+
+ xs <- vector("list", ncols)
+ for (i in seq_len(ncols)) {
+ xs[[i]] <- c(
+ xstart = widths[i] + if (i == 1) 0 else margins[1],
+ xend = widths[i + 1] - if (i == ncols) 0 else margins[2]
+ )
+ }
+ xz <- rep_len(xs, nplots)
+
+ ys <- vector("list", nrows)
+ for (i in seq_len(nplots)) {
+ j <- ceiling(i / ncols)
+ ys[[i]] <- c(
+ ystart = 1 - (heights[j]) - if (j == 1) 0 else margins[3],
+ yend = 1 - (heights[j + 1]) + if (j == nrows) 0 else margins[4]
+ )
+ }
+ list2df(Map(c, xz, ys))
+}
+
+list2df <- function(x, nms) {
+ #stopifnot(length(unique(sapply(x, length))) == 1)
+ m <- if (length(x) == 1) t(x[[1]]) else Reduce(rbind, x)
+ row.names(m) <- NULL
+ df <- data.frame(m)
+ if (!missing(nms)) setNames(df, nms) else df
+}
+
+# translate x/y positions according to domain objects
+# (useful mostly for repositioning annotations/shapes in subplots)
+reposition <- function(obj, domains) {
+ # we need x and y in order to rescale them!
+ for (i in seq_along(obj)) {
+ o <- obj[[i]]
+ # TODO: this implementation currently assumes xref/yref == "paper"
+ # should we support references to axis objects as well?
+ for (j in c("x", "x0", "x1")) {
+ if (is.numeric(o[[j]])) {
+ obj[[i]][[j]] <- scales::rescale(
+ o[[j]], as.numeric(domains[c("xstart", "xend")]), from = c(0, 1)
+ )
+ }
+ }
+ for (j in c("y", "y0", "y1")) {
+ if (is.numeric(o[[j]])) {
+ obj[[i]][[j]] <- scales::rescale(
+ o[[j]], as.numeric(domains[c("yend", "ystart")]), from = c(0, 1)
+ )
+ }
+ }
+ }
+ obj
+}
+
+
+retrain_color_defaults <- function(traces, colorDefaults = traceColorDefaults()) {
+ for (i in seq_along(traces)) {
+ # https://github.com/plotly/plotly.js/blob/c83735/src/plots/plots.js#L58
+ idx <- i %% length(colorDefaults)
+ if (idx == 0) idx <- length(colorDefaults)
+ newDefault <- colorDefaults[[idx]]
+ for (j in c("marker", "line", "text")) {
+ obj <- traces[[i]][[j]]
+ if (!"color" %in% names(obj)) next
+ alpha <- attr(obj[["color"]], "defaultAlpha")
+ if (is.null(alpha)) next
+ traces[[i]][[j]][["color"]] <- toRGB(colorDefaults[[idx]], alpha)
+ }
+ }
+ traces
+}
diff --git a/R/sysdata.rda b/R/sysdata.rda
new file mode 100644
index 0000000..57bd4de
Binary files /dev/null and b/R/sysdata.rda differ
diff --git a/R/toRGB.R b/R/toRGB.R
new file mode 100644
index 0000000..14aeb67
--- /dev/null
+++ b/R/toRGB.R
@@ -0,0 +1,104 @@
+#' Convert R colours to RGBA hexadecimal colour values
+#' @param x see the `col` argument in `col2rgb` for valid specifications
+#' @param alpha alpha channel on 0-1 scale
+#' @return hexadecimal colour value (if is.na(x), return "transparent" for compatibility with Plotly)
+#' @export
+#' @seealso [showRGB()]
+#' @examples
+#'
+#' toRGB("steelblue")
+#' # [1] "rgba(70,130,180,1)"
+#'
+#' m <- list(
+#' color = toRGB("red"),
+#' line = list(
+#' color = toRGB("black"),
+#' width = 19
+#' )
+#' )
+#'
+#' plot_ly(x = 1, y = 1, marker = m)
+#'
+toRGB <- function(x, alpha = 1) {
+ if (length(x) == 0) return(x)
+ if (any(x %in% "transparent")) return(x)
+ # allow x/alpha vector(s), but only sensible cases...
+ if (length(x) != length(alpha)) {
+ # try to reduce alpha to length 1 (in case x is of length 1)
+ alpha <- uniq(alpha)
+ if (length(x) != length(alpha) && length(alpha) != 1) {
+ stop(
+ "alpha must be of length 1 or the same length as x",
+ call. = FALSE
+ )
+ }
+ }
+ # add alpha to already converted "rgb(x,y,z)" codes
+ idx <- grepl("^rgba\\(", x) & alpha <= 1 & 0 <= alpha
+ if (any(idx)) {
+ x[idx] <- rgb2hex(x[idx])
+ }
+ # for some reason ggplot2 has "NA" in some place (instead of NA)
+ if (is.character(x)) {
+ x[x == "NA"] <- NA
+ }
+ # as of ggplot2 version 1.1, an NA alpha is treated as though it's 1
+ alpha[is.na(alpha)] <- 1
+ rgb_matrix <- grDevices::col2rgb(x, alpha = TRUE)
+ # multiply the existing alpha with specified alpha (both on 0-1 scale)
+ rgb_matrix["alpha", ] <- alpha * scales::rescale(
+ rgb_matrix["alpha", ], from = c(0, 255)
+ )
+ rgb_matrix["alpha", ] <- round(rgb_matrix["alpha", ], 4)
+ rgba <- sprintf("rgba(%s)", apply(rgb_matrix, 2, paste, collapse = ","))
+
+ rgba[is.na(x)] <- "transparent"
+ rgba
+}
+
+
+#' View colors already formatted by toRGB()
+#'
+#' Useful for viewing colors after they've been converted to plotly.js'
+#' color format -- "rgba(255, 255, 255, 1)"
+#'
+#' @param x character string specifying color(s).
+#' @param ... arguments passed along to `scales::show_col`.
+#' @export
+#' @author Carson Sievert
+#' @examples
+#'
+#' showRGB(toRGB(colors()), labels = FALSE)
+showRGB <- function(x, ...) {
+ if (!all(grepl("^rgba", x))) stop("All values must begin with rgba")
+ cols <- lapply(strsplit(x, ","), function(y) {
+ vals <- as.numeric(gsub("rgba\\(|\\)", "", y))
+ vals[1:3] <- vals[1:3] / 255
+ setNames(as.list(vals), c("red", "green", "blue", "alpha"))
+ })
+ rgbs <- sapply(cols, function(x) do.call(grDevices::rgb, x))
+ scales::show_col(rgbs, ...)
+}
+
+# take a 'plotly color' and produce a hex code
+rgb2hex <- function(string = "rgba(255,255,255,1)") {
+ vals <- sub("rgba\\(", "", sub("\\)", "", string))
+ valz <- strsplit(vals, ",")
+ sapply(valz, function(x) {
+ x <- setNames(as.numeric(x), c("red", "green", "blue", "alpha"))
+ x[["alpha"]] <- x[["alpha"]] * 255
+ do.call(grDevices::rgb, c(x, list(maxColorValue = 255)))
+ })
+}
+
+#convert rgba() to rgb()
+strip_alpha <- function(string = "rgba(255,255,255,1)", warn = TRUE) {
+ if (length(string) == 0) return(string)
+ if (warn) {
+ if (any(!grepl(",1\\)$", string))) {
+ warning("Removing an alpha value lower than 1")
+ }
+ }
+ string <- sub(",1\\)$", ")", string)
+ sub("^rgba", "rgb", string)
+}
diff --git a/R/utils.R b/R/utils.R
new file mode 100644
index 0000000..265f18b
--- /dev/null
+++ b/R/utils.R
@@ -0,0 +1,930 @@
+is.plotly <- function(x) {
+ inherits(x, "plotly")
+}
+
+is.formula <- function(f) {
+ inherits(f, "formula")
+}
+
+is.colorbar <- function(tr) {
+ inherits(tr, "plotly_colorbar")
+}
+
+is.evaled <- function(p) {
+ all(vapply(p$x$attrs, function(attr) inherits(attr, "plotly_eval"), logical(1)))
+}
+
+is.webgl <- function(p) {
+ if (!is.evaled(p)) p <- plotly_build(p)
+ types <- vapply(p$x$data, function(tr) tr[["type"]] %||% "scatter", character(1))
+ any(types %in% glTypes())
+}
+
+glTypes <- function() {
+ c(
+ "scattergl", "scatter3d", "mesh3d", "heatmapgl", "pointcloud", "parcoords",
+ "surface"
+ )
+}
+
+# just like ggplot2:::is.discrete()
+is.discrete <- function(x) {
+ is.factor(x) || is.character(x) || is.logical(x)
+}
+
+"%||%" <- function(x, y) {
+ if (length(x) > 0 || is_blank(x)) x else y
+}
+
+# kind of like %||%, but only respects user-defined defaults
+# (instead of defaults provided in the build step)
+"%|D|%" <- function(x, y) {
+ if (!is.default(x)) x %||% y else y
+}
+
+# standard way to specify a line break
+br <- function() "<br />"
+
+is.default <- function(x) {
+ inherits(x, "plotly_default")
+}
+
+default <- function(x) {
+ structure(x, class = "plotly_default")
+}
+
+compact <- function(x) {
+ Filter(Negate(is.null), x)
+}
+
+modify_list <- function(x, y, ...) {
+ modifyList(x %||% list(), y %||% list(), ...)
+}
+
+# convert a vector of dates/date-times to milliseconds
+to_milliseconds <- function(x) {
+ if (inherits(x, "Date")) return(as.numeric(x) * 86400000)
+ if (inherits(x, "POSIXt")) return(as.numeric(x) * 1000)
+ # throw warning?
+ x
+}
+
+# apply a function to x, retaining class and "special" plotly attributes
+retain <- function(x, f = identity) {
+ y <- structure(f(x), class = oldClass(x))
+ attrs <- attributes(x)
+ # TODO: do we set any other "special" attributes internally
+ # (grepping "structure(" suggests no)
+ attrs <- attrs[names(attrs) %in% c("defaultAlpha", "apiSrc")]
+ if (length(attrs)) {
+ attributes(y) <- attrs
+ }
+ y
+}
+
+deparse2 <- function(x) {
+ if (is.null(x) || !is.language(x)) return(NULL)
+ sub("^~", "", paste(deparse(x, 500L), collapse = ""))
+}
+
+new_id <- function() {
+ basename(tempfile(""))
+}
+
+names2 <- function(x) {
+ names(x) %||% rep("", length(x))
+}
+
+getLevels <- function(x) {
+ if (is.factor(x)) levels(x) else sort(unique(x))
+}
+
+tryNULL <- function(expr) tryCatch(expr, error = function(e) NULL)
+
+# Don't attempt to do "tidy" data training on these trace types
+is_tidy <- function(trace) {
+ type <- trace[["type"]] %||% "scatter"
+ !type %in% c(
+ "mesh3d", "heatmap", "histogram2d",
+ "histogram2dcontour", "contour", "surface"
+ )
+}
+
+# is grouping relevant for this geometry? (e.g., grouping doesn't effect a scatterplot)
+has_group <- function(trace) {
+ inherits(trace, paste0("plotly_", c("segment", "path", "line", "polygon"))) ||
+ (grepl("scatter", trace[["type"]]) && grepl("lines", trace[["mode"]]))
+}
+
+# currently implemented non-positional scales in plot_ly()
+npscales <- function() {
+ c("color", "symbol", "linetype", "size", "split")
+}
+
+# copied from https://github.com/plotly/plotly.js/blob/master/src/components/color/attributes.js
+traceColorDefaults <- function() {
+ c('#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
+ '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf')
+}
+
+# column name for crosstalk key
+# TODO: make this more unique?
+crosstalk_key <- function() ".crossTalkKey"
+
+# modifyList turns elements that are data.frames into lists
+# which changes the behavior of toJSON
+as_df <- function(x) {
+ if (is.null(x) || is.matrix(x)) return(x)
+ if (is.list(x) && !is.data.frame(x)) {
+ setNames(as.data.frame(x), NULL)
+ }
+}
+
+# arrange data if the vars exist, don't throw error if they don't
+arrange_safe <- function(data, vars) {
+ vars <- vars[vars %in% names(data)]
+ if (length(vars)) dplyr::arrange_(data, .dots = vars) else data
+}
+
+is_mapbox <- function(p) {
+ identical(p$x$layout[["mapType"]], "mapbox")
+}
+
+is_geo <- function(p) {
+ identical(p$x$layout[["mapType"]], "geo")
+}
+
+is_type <- function(p, type) {
+ types <- vapply(p$x$data, function(tr) tr[["type"]] %||% "scatter", character(1))
+ all(types %in% type)
+}
+
+# Replace elements of a nested list
+#
+# @param x a named list
+# @param indicies a vector of indices.
+# A 1D list may be used to specify both numeric and non-numeric inidices
+# @param val the value used to
+# @examples
+#
+# x <- list(a = 1)
+# # equivalent to `x$a <- 2`
+# re_place(x, "a", 2)
+#
+# y <- list(a = list(list(b = 2)))
+#
+# # equivalent to `y$a[[1]]$b <- 2`
+# y <- re_place(y, list("a", 1, "b"), 3)
+# y
+
+re_place <- function(x, indicies = 1, val) {
+
+ expr <- call("[[", quote(x), indicies[[1]])
+ if (length(indicies) == 1) {
+ eval(call("<-", expr, val))
+ return(x)
+ }
+
+ for (i in seq(2, length(indicies))) {
+ expr <- call("[[", expr, indicies[[i]])
+ }
+
+ eval(call("<-", expr, val))
+ x
+}
+
+
+# retrive mapbox token if one is set; otherwise, throw error
+mapbox_token <- function() {
+ token <- Sys.getenv("MAPBOX_TOKEN", NA)
+ if (is.na(token)) {
+ stop(
+ "No mapbox access token found. Obtain a token here\n",
+ "https://www.mapbox.com/help/create-api-access-token/\n",
+ "Once you have a token, assign it to an environment variable \n",
+ "named 'MAPBOX_TOKEN', for example,\n",
+ "Sys.setenv('MAPBOX_TOKEN' = 'secret token')", call. = FALSE
+ )
+ }
+ token
+}
+
+# rename attrs (unevaluated arguments) from geo locations (lat/lon) to cartesian
+geo2cartesian <- function(p) {
+ p$x$attrs <- lapply(p$x$attrs, function(tr) {
+ tr[["x"]] <- tr[["x"]] %||% tr[["lat"]]
+ tr[["y"]] <- tr[["y"]] %||% tr[["lon"]]
+ tr
+ })
+ p
+}
+
+is_subplot <- function(p) {
+ isTRUE(p$x$subplot)
+}
+
+supply_defaults <- function(p) {
+ # no need to supply defaults for subplots
+ if (is_subplot(p)) return(p)
+ # supply trace anchor defaults
+ anchors <- if (is_geo(p)) c("geo" = "geo") else if (is_mapbox(p)) c("subplot" = "mapbox") else c("xaxis" = "x", "yaxis" = "y")
+
+ p$x$data <- lapply(p$x$data, function(tr) {
+ for (i in seq_along(anchors)) {
+ key <- names(anchors)[[i]]
+ if (!has_attr(tr[["type"]] %||% "scatter", key)) next
+ tr[[key]] <- sub("^y1$", "y", sub("^x1$", "x", tr[[key]][1])) %||% anchors[[i]]
+ }
+ tr
+ })
+ # hack to avoid https://github.com/ropensci/plotly/issues/945
+ if (is_type(p, "parcoords")) p$x$layout$margin$t <- NULL
+
+ # supply domain defaults
+ geoDomain <- list(x = c(0, 1), y = c(0, 1))
+ if (is_geo(p) || is_mapbox(p)) {
+ p$x$layout[grepl("^[x-y]axis", names(p$x$layout))] <- NULL
+ p$x$layout[[p$x$layout$mapType]] <- modify_list(
+ list(domain = geoDomain), p$x$layout[[p$x$layout$mapType]]
+ )
+ } else {
+ axes <- if (is_type(p, "scatterternary")) {
+ c("aaxis", "baxis", "caxis")
+ } else if (is_type(p, "pie") || is_type(p, "parcoords") || is_type(p, "sankey")) {
+ NULL
+ } else {
+ c("xaxis", "yaxis")
+ }
+ for (axis in axes) {
+ p$x$layout[[axis]] <- modify_list(
+ list(domain = c(0, 1)), p$x$layout[[axis]]
+ )
+ }
+ }
+ p
+}
+
+supply_highlight_attrs <- function(p) {
+ # set "global" options via crosstalk variable
+ p$x$highlight <- p$x$highlight %||% highlight_defaults()
+ p <- htmlwidgets::onRender(
+ p, sprintf(
+ "function(el, x) { var ctConfig = crosstalk.var('plotlyCrosstalkOpts').set(%s); }",
+ to_JSON(p$x$highlight)
+ )
+ )
+
+ # defaults are now populated, allowing us to populate some other
+ # attributes such as the selectize widget definition
+ sets <- unlist(lapply(p$x$data, "[[", "set"))
+ keys <- setNames(lapply(p$x$data, "[[", "key"), sets)
+ p$x$highlight$ctGroups <- i(unique(sets))
+
+ # TODO: throw warning if we don't detect valid keys?
+ hasKeys <- FALSE
+ for (i in p$x$highlight$ctGroups) {
+ k <- unique(unlist(keys[names(keys) %in% i], use.names = FALSE))
+ if (is.null(k)) next
+ k <- k[!is.null(k)]
+ hasKeys <- TRUE
+
+ # include one selectize dropdown per "valid" SharedData layer
+ if (isTRUE(p$x$highlight$selectize)) {
+ p$x$selectize[[new_id()]] <- list(
+ items = data.frame(value = k, label = k), group = i
+ )
+ }
+
+ # set default values via crosstalk api
+ vals <- p$x$highlight$defaultValues[p$x$highlight$defaultValues %in% k]
+ if (length(vals)) {
+ p <- htmlwidgets::onRender(
+ p, sprintf(
+ "function(el, x) { crosstalk.group('%s').var('selection').set(%s) }",
+ i, jsonlite::toJSON(vals, auto_unbox = FALSE)
+ )
+ )
+ }
+ }
+
+ # add HTML dependencies, set a sensible dragmode default, & throw messages
+ if (hasKeys) {
+ p$x$layout$dragmode <- p$x$layout$dragmode %|D|%
+ default(switch(p$x$highlight$on %||% "", plotly_selected = "select") %||% "zoom")
+ if (is.default(p$x$highlight$off)) {
+ message(
+ sprintf(
+ "Setting the `off` event (i.e., '%s') to match the `on` event (i.e., '%s'). You can change this default via the `highlight()` function.",
+ p$x$highlight$off, p$x$highlight$on
+ )
+ )
+ }
+ }
+
+ p
+}
+
+
+# make sure plot attributes adhere to the plotly.js schema
+verify_attr_names <- function(p) {
+ # some layout attributes (e.g., [x-y]axis can have trailing numbers)
+ attrs_name_check(
+ sub("[0-9]+$", "", names(p$x$layout)),
+ c(names(Schema$layout$layoutAttributes), c("barmode", "bargap", "mapType")),
+ "layout"
+ )
+ for (tr in seq_along(p$x$data)) {
+ thisTrace <- p$x$data[[tr]]
+ attrSpec <- Schema$traces[[thisTrace$type %||% "scatter"]]$attributes
+ # make sure attribute names are valid
+ attrs_name_check(
+ names(thisTrace),
+ c(names(attrSpec), "key", "set", "frame", "transforms", "_isNestedKey", "_isSimpleKey", "_isGraticule"),
+ thisTrace$type
+ )
+ }
+ invisible(p)
+}
+
+
+
+# ensure both the layout and trace attributes adhere to the plot schema
+verify_attr_spec <- function(p) {
+ if (!is.null(p$x$layout)) {
+ layoutNames <- names(p$x$layout)
+ layoutNew <- verify_attr(
+ setNames(p$x$layout, sub("[0-9]+$", "", layoutNames)),
+ Schema$layout$layoutAttributes
+ )
+ p$x$layout <- setNames(layoutNew, layoutNames)
+ }
+ for (tr in seq_along(p$x$data)) {
+ thisTrace <- p$x$data[[tr]]
+ validAttrs <- Schema$traces[[thisTrace$type %||% "scatter"]]$attributes
+ p$x$data[[tr]] <- verify_attr(thisTrace, validAttrs)
+ # prevent these objects from sending null keys
+ p$x$data[[tr]][["xaxis"]] <- p$x$data[[tr]][["xaxis"]] %||% NULL
+ p$x$data[[tr]][["yaxis"]] <- p$x$data[[tr]][["yaxis"]] %||% NULL
+ }
+
+ p
+}
+
+verify_attr <- function(proposed, schema) {
+ for (attr in names(proposed)) {
+ attrSchema <- schema[[attr]]
+ # if schema is missing (i.e., this is an un-official attr), move along
+ if (is.null(attrSchema)) next
+
+ valType <- tryNULL(attrSchema[["valType"]]) %||% ""
+ role <- tryNULL(attrSchema[["role"]]) %||% ""
+ arrayOK <- tryNULL(attrSchema[["arrayOk"]]) %||% FALSE
+ isDataArray <- identical(valType, "data_array")
+
+ # where applicable, reduce single valued vectors to a constant
+ # (while preserving attributes)
+ if (!isDataArray && !arrayOK && !identical(role, "object")) {
+ proposed[[attr]] <- retain(proposed[[attr]], unique)
+ }
+
+ # ensure data_arrays of length 1 are boxed up by to_JSON()
+ if (isDataArray) {
+ proposed[[attr]] <- i(proposed[[attr]])
+ }
+
+ # tag 'src-able' attributes (needed for api_create())
+ isSrcAble <- !is.null(schema[[paste0(attr, "src")]]) && length(proposed[[attr]]) > 1
+ if (isDataArray || isSrcAble) {
+ proposed[[attr]] <- structure(proposed[[attr]], apiSrc = TRUE)
+ }
+
+ # do the same for "sub-attributes"
+ # TODO: should this be done recursively?
+ if (identical(role, "object")) {
+ for (attr2 in names(proposed[[attr]])) {
+ if (is.null(attrSchema[[attr2]])) next
+
+ valType2 <- tryNULL(attrSchema[[attr2]][["valType"]]) %||% ""
+ role2 <- tryNULL(attrSchema[[attr2]][["role"]]) %||% ""
+ arrayOK2 <- tryNULL(attrSchema[[attr2]][["arrayOk"]]) %||% FALSE
+ isDataArray2 <- identical(valType2, "data_array")
+
+ if (!isDataArray2 && !arrayOK2 && !identical(role2, "object")) {
+ proposed[[attr]][[attr2]] <- retain(proposed[[attr]][[attr2]], unique)
+ }
+
+ # ensure data_arrays of length 1 are boxed up by to_JSON()
+ if (isDataArray2) {
+ proposed[[attr]][[attr2]] <- i(proposed[[attr]][[attr2]])
+ }
+
+ # tag 'src-able' attributes (needed for api_create())
+ isSrcAble2 <- !is.null(schema[[attr]][[paste0(attr2, "src")]]) &&
+ length(proposed[[attr]][[attr2]]) > 1
+ if (isDataArray2 || isSrcAble2) {
+ proposed[[attr]][[attr2]] <- structure(
+ proposed[[attr]][[attr2]], apiSrc = TRUE
+ )
+ }
+
+ }
+ }
+ }
+ proposed
+}
+
+attrs_name_check <- function(proposedAttrs, validAttrs, type = "scatter") {
+ illegalAttrs <- setdiff(proposedAttrs, validAttrs)
+ if (length(illegalAttrs)) {
+ warning("'", type, "' objects don't have these attributes: '",
+ paste(illegalAttrs, collapse = "', '"), "'\n",
+ "Valid attributes include:\n'",
+ paste(validAttrs, collapse = "', '"), "'\n",
+ call. = FALSE)
+ }
+ invisible(proposedAttrs)
+}
+
+# make sure trace type is valid
+# TODO: add an argument to verify trace properties are valid (https://github.com/ropensci/plotly/issues/540)
+verify_type <- function(trace) {
+ if (is.null(trace$type)) {
+ attrs <- names(trace)
+ attrLengths <- lengths(trace)
+ trace$type <- if (all(c("x", "y", "z") %in% attrs)) {
+ if (all(c("i", "j", "k") %in% attrs)) "mesh3d" else "scatter3d"
+ } else if (all(c("x", "y") %in% attrs)) {
+ xNumeric <- !is.discrete(trace[["x"]])
+ yNumeric <- !is.discrete(trace[["y"]])
+ if (xNumeric && yNumeric) {
+ if (any(attrLengths) > 15000) "scattergl" else "scatter"
+ } else if (xNumeric || yNumeric) {
+ "bar"
+ } else "histogram2d"
+ } else if ("y" %in% attrs || "x" %in% attrs) {
+ "histogram"
+ } else if ("z" %in% attrs) {
+ "heatmap"
+ } else {
+ warning("No trace type specified and no positional attributes specified",
+ call. = FALSE)
+ "scatter"
+ }
+ relay_type(trace$type)
+ }
+ if (!is.character(trace$type) || length(trace$type) != 1) {
+ stop("The trace type must be a character vector of length 1.\n",
+ call. = FALSE)
+ }
+ if (!trace$type %in% names(Schema$traces)) {
+ stop("Trace type must be one of the following: \n",
+ "'", paste(names(Schema$traces), collapse = "', '"), "'",
+ call. = FALSE)
+ }
+ # if scatter/scatter3d/scattergl, default to a scatterplot
+ if (grepl("scatter", trace$type) && is.null(trace$mode)) {
+ message(
+ "No ", trace$type, " mode specifed:\n",
+ " Setting the mode to markers\n",
+ " Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode"
+ )
+ trace$mode <- "markers"
+ }
+ trace
+}
+
+relay_type <- function(type) {
+ message(
+ "No trace type specified:\n",
+ " Based on info supplied, a '", type, "' trace seems appropriate.\n",
+ " Read more about this trace type -> https://plot.ly/r/reference/#", type
+ )
+ type
+}
+
+# Searches a list for character strings and translates R linebreaks to HTML
+# linebreaks (i.e., '\n' -> '<br />'). JavaScript function definitions created
+# via `htmlwidgets::JS()` are ignored
+translate_linebreaks <- function(p) {
+ recurse <- function(a) {
+ typ <- typeof(a)
+ if (typ == "list") {
+ # retain the class of list elements
+ # which important for many things, such as colorbars
+ a[] <- lapply(a, recurse)
+ } else if (typ == "character" && !inherits(a, "JS_EVAL")) {
+ attrs <- attributes(a)
+ a <- gsub("\n", br(), a, fixed = TRUE)
+ attributes(a) <- attrs
+ }
+ a
+ }
+ p$x[] <- lapply(p$x, recurse)
+ p
+}
+
+verify_orientation <- function(trace) {
+ xNumeric <- !is.discrete(trace[["x"]]) && !is.null(trace[["x"]] %||% NULL)
+ yNumeric <- !is.discrete(trace[["y"]]) && !is.null(trace[["y"]] %||% NULL)
+ if (xNumeric && !yNumeric) {
+ if (any(c("bar", "box") %in% trace[["type"]])) {
+ trace$orientation <- "h"
+ }
+ }
+ if (yNumeric && "histogram" %in% trace[["type"]]) {
+ trace$orientation <- "h"
+ }
+ trace
+}
+
+verify_mode <- function(p) {
+ for (tr in seq_along(p$x$data)) {
+ trace <- p$x$data[[tr]]
+ if (grepl("scatter", trace$type %||% "scatter")) {
+ if (!is.null(trace$marker) && !grepl("markers", trace$mode %||% "")) {
+ message(
+ "A marker object has been specified, but markers is not in the mode\n",
+ "Adding markers to the mode..."
+ )
+ p$x$data[[tr]]$mode <- paste0(p$x$data[[tr]]$mode, "+markers")
+ }
+ if (!is.null(trace$line) && !grepl("lines", trace$mode %||% "")) {
+ message(
+ "A line object has been specified, but lines is not in the mode\n",
+ "Adding lines to the mode..."
+ )
+ p$x$data[[tr]]$mode <- paste0(p$x$data[[tr]]$mode, "+lines")
+ }
+ if (!is.null(trace$textfont) && !grepl("text", trace$mode %||% "")) {
+ warning(
+ "A textfont object has been specified, but text is not in the mode\n",
+ "Adding text to the mode..."
+ )
+ p$x$data[[tr]]$mode <- paste0(p$x$data[[tr]]$mode, "+text")
+ }
+ }
+ }
+ p
+}
+
+# populate categorical axes using categoryorder="array" & categoryarray=[]
+populate_categorical_axes <- function(p) {
+ axes <- p$x$layout[grepl("^xaxis|^yaxis", names(p$x$layout))] %||%
+ list(xaxis = NULL, yaxis = NULL)
+ for (i in seq_along(axes)) {
+ axis <- axes[[i]]
+ axisName <- names(axes)[[i]]
+ axisType <- substr(axisName, 0, 1)
+ # ggplotly() populates these attributes...don't want to clobber that
+ if (!is.null(axis$ticktext) || !is.null(axis$tickvals)) next
+ # collect all the data that goes on this axis
+ d <- lapply(p$x$data, "[[", axisType)
+ isOnThisAxis <- function(tr) {
+ is.null(tr[["geo"]]) && sub("axis", "", axisName) %in%
+ (tr[[sub("[0-9]+", "", axisName)]] %||% axisType) &&
+ # avoid reordering matrices (see #863)
+ !is.matrix(tr[["z"]])
+ }
+ d <- d[vapply(p$x$data, isOnThisAxis, logical(1))]
+ if (length(d) == 0) next
+ isDiscrete <- vapply(d, is.discrete, logical(1))
+ if (0 < sum(isDiscrete) & sum(isDiscrete) < length(d)) {
+ warning(
+ "Can't display both discrete & non-discrete data on same axis",
+ call. = FALSE
+ )
+ next
+ }
+ if (sum(isDiscrete) == 0) next
+ categories <- lapply(d, getLevels)
+ categories <- unique(unlist(categories))
+ if (any(!vapply(d, is.factor, logical(1)))) categories <- sort(categories)
+ p$x$layout[[axisName]]$type <-
+ p$x$layout[[axisName]]$type %||% "category"
+ p$x$layout[[axisName]]$categoryorder <-
+ p$x$layout[[axisName]]$categoryorder %||% "array"
+ p$x$layout[[axisName]]$categoryarray <-
+ p$x$layout[[axisName]]$categoryarray %||% categories
+ }
+ p
+}
+
+verify_arrays <- function(p) {
+ for (i in c("annotations", "shapes", "images")) {
+ thing <- p$x$layout[[i]]
+ if (is.list(thing) && !is.null(names(thing))) {
+ p$x$layout[[i]] <- list(thing)
+ }
+ }
+ p
+}
+
+verify_hovermode <- function(p) {
+ if (!is.null(p$x$layout$hovermode)) {
+ return(p)
+ }
+ types <- unlist(lapply(p$x$data, function(tr) tr$type %||% "scatter"))
+ modes <- unlist(lapply(p$x$data, function(tr) tr$mode %||% "lines"))
+ if (any(grepl("markers", modes) & types == "scatter") ||
+ any(c("plotly_hover", "plotly_click") %in% p$x$highlight$on)) {
+ p$x$layout$hovermode <- "closest"
+ }
+ p
+}
+
+verify_key_type <- function(p) {
+ keys <- lapply(p$x$data, "[[", "key")
+ for (i in seq_along(keys)) {
+ k <- keys[[i]]
+ if (is.null(k)) next
+ # does it *ever* make sense to have a missing key value?
+ uk <- uniq(k)
+ if (length(uk) == 1) {
+ # i.e., the key for this trace has one value. In this case,
+ # we don't have iterate through the entire key, so instead,
+ # we provide a flag to inform client side logic to match the _entire_
+ # trace if this one key value is a match
+ p$x$data[[i]]$key <- uk[[1]]
+ p$x$data[[i]]$`_isSimpleKey` <- TRUE
+ p$x$data[[i]]$`_isNestedKey` <- FALSE
+ }
+ p$x$data[[i]]$`_isNestedKey` <- p$x$data[[i]]$`_isNestedKey` %||% !lazyeval::is_atomic(k)
+ # key values should always be strings
+ if (p$x$data[[i]]$`_isNestedKey`) {
+ p$x$data[[i]]$key <- lapply(p$x$data[[i]]$key, function(x) I(as.character(x)))
+ p$x$data[[i]]$key <- setNames(p$x$data[[i]]$key, NULL)
+ } else {
+ p$x$data[[i]]$key <- I(as.character(p$x$data[[i]]$key))
+ }
+ }
+ p
+}
+
+verify_webgl <- function(p) {
+ # see toWebGL
+ if (!isTRUE(p$x$.plotlyWebGl)) {
+ return(p)
+ }
+ types <- sapply(p$x$data, function(x) x[["type"]][1] %||% "scatter")
+ idx <- paste0(types, "gl") %in% names(Schema$traces)
+ if (any(!idx)) {
+ warning(
+ "The following traces don't have a WebGL equivalent: ",
+ paste(which(!idx), collapse = ", ")
+ )
+ }
+ for (i in which(idx)) {
+ p$x$data[[i]]$type <- paste0(p$x$data[[i]]$type, "gl")
+ }
+ p
+}
+
+verify_showlegend <- function(p) {
+ # this attribute should be set in hide_legend()
+ # it ensures that "legend titles" go away in addition to showlegend = FALSE
+ if (isTRUE(p$x$.hideLegend)) {
+ ann <- p$x$layout$annotations
+ is_title <- vapply(ann, function(x) isTRUE(x$legendTitle), logical(1))
+ p$x$layout$annotations <- ann[!is_title]
+ p$x$layout$showlegend <- FALSE
+ }
+ show <- vapply(p$x$data, function(x) x$showlegend %||% TRUE, logical(1))
+ # respect only _user-specified_ defaults
+ p$x$layout$showlegend <- p$x$layout$showlegend %|D|%
+ default(sum(show) > 1 || isTRUE(p$x$highlight$showInLegend))
+ p
+}
+
+verify_guides <- function(p) {
+
+ # since colorbars are implemented as "invisible" traces, prevent a "trivial" legend
+ if (has_colorbar(p) && has_legend(p) && length(p$x$data) <= 2) {
+ p$x$layout$showlegend <- default(FALSE)
+ }
+
+ isVisibleBar <- function(tr) {
+ is.colorbar(tr) && isTRUE(tr$showscale %||% TRUE)
+ }
+ isBar <- vapply(p$x$data, isVisibleBar, logical(1))
+ nGuides <- sum(isBar) + has_legend(p)
+
+ if (nGuides > 1) {
+
+ # place legend at bottom since its scrolly
+ p$x$layout$legend <- modify_list(
+ list(y = 1 - ((nGuides - 1) / nGuides), yanchor = "top"),
+ p$x$layout$legend
+ )
+
+ idx <- which(isBar)
+ for (i in seq_along(idx)) {
+ p <- colorbar_built(
+ p, which = i, len = 1 / nGuides, y = 1 - ((i - 1) / nGuides),
+ lenmode = "fraction", yanchor = "top"
+ )
+ }
+
+ }
+
+ p
+}
+
+has_marker <- function(types, modes) {
+ is_scatter <- grepl("scatter", types)
+ ifelse(is_scatter, grepl("marker", modes), has_attr(types, "marker"))
+}
+
+has_line <- function(types, modes) {
+ is_scatter <- grepl("scatter", types)
+ ifelse(is_scatter, grepl("line", modes), has_attr(types, "line"))
+}
+
+has_text <- function(types, modes) {
+ is_scatter <- grepl("scatter", types)
+ ifelse(is_scatter, grepl("text", modes), has_attr(types, "textfont"))
+}
+
+has_attr <- function(types, attr = "marker") {
+ if (length(attr) != 1) stop("attr must be of length 1")
+ vapply(types, function(x) attr %in% names(Schema$traces[[x]]$attributes), logical(1))
+}
+
+has_legend <- function(p) {
+ showLegend <- function(tr) {
+ tr$showlegend %||% TRUE
+ }
+ any(vapply(p$x$data, showLegend, logical(1))) &&
+ isTRUE(p$x$layout$showlegend %|D|% TRUE)
+}
+
+has_colorbar <- function(p) {
+ isVisibleBar <- function(tr) {
+ is.colorbar(tr) && isTRUE(tr$showscale %||% TRUE)
+ }
+ any(vapply(p$x$data, isVisibleBar, logical(1)))
+}
+
+# is a given trace type 3d?
+is3d <- function(type = NULL) {
+ type <- type %||% "scatter"
+ type %in% c("mesh3d", "scatter3d", "surface")
+}
+
+# Check for credentials/configuration and throw warnings where appropriate
+verify <- function(what = "username", warn = TRUE) {
+ val <- grab(what)
+ if (val == "" && warn) {
+ switch(what,
+ username = warning("You need a plotly username. See help(signup, package = 'plotly')", call. = FALSE),
+ api_key = warning("You need an api_key. See help(signup, package = 'plotly')", call. = FALSE))
+ warning("Couldn't find ", what, call. = FALSE)
+ }
+ as.character(val)
+}
+
+# Check whether a certain credential/configuration exists.
+grab <- function(what = "username") {
+ who <- paste0("plotly_", what)
+ val <- Sys.getenv(who, "")
+ # If the environment variable doesn't exist, try reading hidden files that may
+ # have been created using other languages or earlier versions of this package
+ if (val == "") {
+ PLOTLY_DIR <- file.path(normalizePath("~", mustWork = TRUE), ".plotly")
+ CREDENTIALS_FILE <- file.path(PLOTLY_DIR, ".credentials")
+ CONFIG_FILE <- file.path(PLOTLY_DIR, ".config")
+ # note: try_file can be 'succesful', yet return NULL
+ val2 <- try_file(CREDENTIALS_FILE, what)
+ val <- if (length(nchar(val2)) == 0) try_file(CONFIG_FILE, what) else val2
+ val <- val %||% ""
+ }
+ # return true if value is non-trivial
+ setNames(val, who)
+}
+
+# try to grab an object key from a JSON file (returns empty string on error)
+try_file <- function(f, what) {
+ tryCatch(jsonlite::fromJSON(f)[[what]], error = function(e) NULL)
+}
+
+# preferred defaults for toJSON mapping
+to_JSON <- function(x, ...) {
+ jsonlite::toJSON(x, digits = 50, auto_unbox = TRUE, force = TRUE,
+ null = "null", na = "null", ...)
+}
+
+# preferred defaults for toJSON mapping
+from_JSON <- function(x, ...) {
+ jsonlite::fromJSON(x, simplifyDataFrame = FALSE, simplifyMatrix = FALSE, ...)
+}
+
+i <- function(x) {
+ if (is.null(x)) {
+ return(NULL)
+ } else if (length(x) == 1) {
+ return(I(x))
+ } else{
+ return(x)
+ }
+}
+
+rm_asis <- function(x) {
+ # jsonlite converts NULL to {} and NA to null (plotly prefers null to {})
+ # https://github.com/jeroenooms/jsonlite/issues/29
+ if (is.null(x)) return(NA)
+ if (is.data.frame(x)) return(x)
+ if (is.list(x)) lapply(x, rm_asis)
+ # strip any existing 'AsIs' list elements of their 'AsIs' status.
+ # this is necessary since ggplot_build(qplot(1:10, fill = I("red")))
+ # returns list element with their 'AsIs' class,
+ # which conflicts with our JSON unboxing strategy.
+ else if (inherits(x, "AsIs")) class(x) <- setdiff(class(x), "AsIs")
+ else x
+}
+
+
+# add a class to an object only if it is new, and keep any existing classes of
+# that object
+append_class <- function(x, y) {
+ structure(x, class = unique(c(class(x), y)))
+}
+prefix_class <- function(x, y) {
+ structure(x, class = unique(c(y, class(x))))
+}
+replace_class <- function(x, new, old) {
+ class(x) <- sub(old, new, class(x))
+ x
+}
+remove_class <- function(x, y) {
+ oldClass(x) <- setdiff(oldClass(x), y)
+ x
+}
+
+# TODO: what are some other common configuration options we want to support??
+get_domain <- function(type = "") {
+ if (type == "api") {
+ # new onprem instances don't have an https://api-thiscompany.plot.ly
+ # but https://thiscompany.plot.ly seems to just work in that case...
+ Sys.getenv("plotly_api_domain", Sys.getenv("plotly_domain", "https://api.plot.ly"))
+ } else {
+ Sys.getenv("plotly_domain", "https://plot.ly")
+ }
+}
+
+# plotly's special keyword arguments in POST body
+get_kwargs <- function() {
+ c("filename", "fileopt", "style", "traces", "layout", "frames", "world_readable")
+}
+
+# "common" POST header fields
+api_headers <- function() {
+ v <- as.character(packageVersion("plotly"))
+ httr::add_headers(
+ plotly_version = v,
+ `Plotly-Client-Platform` = paste("R", v),
+ `Content-Type` = "application/json",
+ Accept = "*/*"
+ )
+}
+
+api_auth <- function() {
+ httr::authenticate(
+ verify("username"),
+ verify("api_key")
+ )
+}
+
+
+# try to write environment variables to an .Rprofile
+cat_profile <- function(key, value, path = "~") {
+ r_profile <- file.path(normalizePath(path, mustWork = TRUE),
+ ".Rprofile")
+ snippet <- sprintf('\nSys.setenv("plotly_%s" = "%s")', key, value)
+ if (!file.exists(r_profile)) {
+ message("Creating", r_profile)
+ r_profile_con <- file(r_profile)
+ }
+ if (file.access(r_profile, 2) != 0) {
+ stop("R doesn't have permission to write to this file: ", path, "\n",
+ "You should consider putting this in an .Rprofile ", "\n",
+ "(or sourcing it when you use plotly): ", snippet)
+ }
+ if (file.access(r_profile, 4) != 0) {
+ stop("R doesn't have permission to read this file: ", path)
+ }
+ message("Adding plotly_", key, " environment variable to ", r_profile)
+ cat(snippet, file = r_profile, append = TRUE)
+}
+
+
+# check that suggested packages are installed
+try_library <- function(pkg, fun = NULL) {
+ if (system.file(package = pkg) != "") {
+ return(invisible())
+ }
+ stop("Package `", pkg, "` required", if (!is.null(fun)) paste0(" for `", fun, "`"), ".\n",
+ "Please install and try again.", call. = FALSE)
+}
+
+is_rstudio <- function() {
+ identical(Sys.getenv("RSTUDIO", NA), "1")
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..36408bd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,90 @@
+[](https://travis-ci.org/ropensci/plotly)
+
+# plotly
+
+An R package for creating interactive web graphics via the open source JavaScript graphing library [plotly.js](https://github.com/plotly/plotly.js).
+
+## Installation
+
+Install from CRAN:
+
+```r
+install.packages("plotly")
+```
+
+Or install the latest development version (on GitHub) via devtools:
+
+```r
+devtools::install_github("ropensci/plotly")
+```
+
+**NOTE:** The CRAN version of **plotly** is designed to work with the CRAN version of **ggplot2**, but at least for the time being, we recommend using the development versions of both **plotly** and **ggplot2** (`devtools::install_github("hadley/ggplot2")`).
+
+## Getting started
+
+### Web-based ggplot2 graphics
+
+If you use [ggplot2](https://github.com/hadley/ggplot2), `ggplotly()` converts your static plots to an interactive web-based version!
+
+```r
+library(plotly)
+g <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
+ stat_density_2d(aes(fill = ..level..), geom = "polygon") +
+ xlim(1, 6) + ylim(40, 100)
+ggplotly(g)
+```
+
+
+
+By default, `ggplotly()` tries to replicate the static ggplot2 version exactly (before any interaction occurs), but sometimes you need greater control over the interactive behavior. The `ggplotly()` function itself has some convenient "high-level" arguments, such as `dynamicTicks`, which tells plotly.js to dynamically recompute axes, when appropriate. The `style()` function also comes in handy for _modifying_ the underlying [traces attributes](https://plot.ly/r/reference/#scatter-hoveron [...]
+
+```r
+gg <- ggplotly(g, dynamicTicks = "y")
+style(gg, hoveron = "points", hoverinfo = "x+y+text", hoverlabel = list(bgcolor = "white"))
+```
+
+
+
+Moreover, since `ggplotly()` returns a plotly object, you can apply essentially any function from the R package on that object. Some useful ones include `layout()` (for [customizing the layout](https://cpsievert.github.io/plotly_book/extending-ggplotly.html#customizing-the-layout)), `add_traces()` (and its higher-level `add_*()` siblings, for example `add_polygons()`, for [adding new traces/data](https://cpsievert.github.io/plotly_book/extending-ggplotly.html#leveraging-statistical-outpu [...]
+
+The `ggplotly()` function will also respect some "unofficial" **ggplot2** aesthetics, namely `text` (for [customizing the tooltip](https://cpsievert.github.io/plotly_book/a-case-study-of-housing-sales-in-texas.html#fig:ggsubplot)), `frame` (for [creating animations](https://cpsievert.github.io/plotly_book/key-frame-animations.html)), and `ids` (for ensuring sensible smooth transitions).
+
+### Using plotly without ggplot2
+
+The `plot_ly()` function provides a more direct interface to plotly.js so you can leverage more specialized chart types (e.g., [parallel coordinates](https://plot.ly/r/parallel-coordinates-plot/) or [maps](https://plot.ly/r/maps/)) or even some visualization that the ggplot2 API won't ever support (e.g., surface, [mesh](https://plot.ly/r/3d-mesh/), [trisurf](https://plot.ly/r/trisurf/), or sankey diagrams). The [cheatsheet](https://images.plot.ly/plotly-documentation/images/r_cheat_sheet [...]
+
+```r
+plot_ly(z = ~volcano, type = "surface")
+```
+
+
+
+
+## Crosstalk support
+
+The R package has special support for linking/highlighting/filtering views that is not (yet) available outside of the R package. This functionality is built upon the [**crosstalk** package](https://rstudio.github.io/crosstalk/), which distinguishes between two event classes: *select* and *filter*. The **plotly** package interprets these classes in the following way:
+
+1. **Select**: add new "selection" trace(s) (i.e., graphical marks) and *dim* the other traces. Some people refer to this as "brushing" or "highlighting".
+2. **Filter**: retain "selection" trace(s), but *remove* other traces, and update the layout accordingly. Some people refer to this as "crossfilter" or "drill-down".
+
+The following gif helps to demonstrate the difference -- see [here](http://rpubs.com/cpsievert/275511) for the code used to generate it.
+
+
+
+Like other [crosstalk enabled widgets](https://rstudio.github.io/crosstalk/widgets.html), **plotly** responds to filter events, but you can't (yet) emit a filter event via direct manipulation of a plotly graph. Unlike (some) other crosstalk enabled widgets, **plotly** has advanced support for select (a much more broad class than filter) events, like [persistent/dynamic brushing](https://cpsievert.github.io/plotly_book/linking-views-without-shiny.html#transient-versus-persistent-selection [...]
+
+To date, [this slide deck](https://cpsievert.github.io/plotcon17/workshop/day2) is the most comprehensive, yet somewhat thorough, walk-through of this framework, but the [linking views without shiny](https://cpsievert.github.io/plotly_book/linking-views-without-shiny.html) chapter of the [plotly book](https://cpsievert.github.io/plotly_book/) provides even more background. There are also numerous demos shipped with the package which provide nice examples (list all the demos via `demo(pac [...]
+
+**plotly**'s **crosstalk** functionality aims to provide tools for interactively exploring subsets of your data with a fixed definition from data to plot. If you need more flexibility, you can always embed **crosstalk** plots within a larger **shiny** app, or even [access and respond to any plotly event within shiny](https://cpsievert.github.io/plotly_book/linking-views-with-shiny.html), but adding **shiny** into the equation comes with a cost -- the result is no longer standalone HTML ( [...]
+
+## Learn more
+
+We have lots of examples on <https://plot.ly/r/> and <https://plot.ly/ggplot2/>, but a more comprehensive review is also available at <https://cpsievert.github.io/plotly_book/>. I also have a number of [slide decks](http://cpsievert.github.io/talks/) that also have useful demos.
+
+## Contributing
+
+Please read through our [contributing guidelines](https://github.com/ropensci/plotly/blob/master/CONTRIBUTING.md). Included are directions for opening issues, asking questions, contributing changes to plotly, and our code of conduct.
+
+---
+
+
diff --git a/data/hobbs.rda b/data/hobbs.rda
new file mode 100644
index 0000000..d849444
Binary files /dev/null and b/data/hobbs.rda differ
diff --git a/data/mic.rda b/data/mic.rda
new file mode 100644
index 0000000..7df2dd7
Binary files /dev/null and b/data/mic.rda differ
diff --git a/data/wind.rda b/data/wind.rda
new file mode 100644
index 0000000..039a8f5
Binary files /dev/null and b/data/wind.rda differ
diff --git a/demo/00Index b/demo/00Index
new file mode 100644
index 0000000..3c6f514
--- /dev/null
+++ b/demo/00Index
@@ -0,0 +1,14 @@
+crosstalk-highlight-intro An introduction to linked views
+crosstalk-highlight-subplot Basic scatterplot brushing with subplot()
+crosstalk-highlight-ggplotly Linking ggplot2 plots
+crosstalk-highlight-leaflet Linking to leaflet
+crosstalk-highlight-ggpairs Brushing a scatterplot matrix via ggpairs
+crosstalk-highlight-pipeline Aggregating SharedData
+crosstalk-highlight-binned-target Let plotly.js dynamically bin the raw data
+crosstalk-highlight-epl An example of linked facets
+crosstalk-filter-lines Using crosstalk's filter_select() to filter lines
+crosstalk-filter-dynamic-axis Using crosstalk's filter_select() to dynamically change the y-axis
+animation-tour-basic A basic example of a grand tour
+animation-tour-USArrests Linking a dendrogram to a tour
+rotate Using htmlwidgets::onRender() to rotate the camera of a 3D graph
+ternary A basic ternary plot
diff --git a/demo/animation-tour-USArrests.R b/demo/animation-tour-USArrests.R
new file mode 100644
index 0000000..6e05540
--- /dev/null
+++ b/demo/animation-tour-USArrests.R
@@ -0,0 +1,75 @@
+# adapted from https://github.com/rstudio/ggvis/blob/master/demo/tourr.r
+library(tourr)
+library(plotly)
+
+mat <- rescale(USArrests[, 1:4])
+tour <- new_tour(mat, grand_tour(), NULL)
+
+tour_dat <- function(step_size) {
+ step <- tour(step_size)
+ proj <- center(mat %*% step$proj)
+ data.frame(x = proj[,1], y = proj[,2], state = rownames(mat))
+}
+
+proj_dat <- function(step_size) {
+ step <- tour(step_size)
+ data.frame(
+ x = step$proj[,1], y = step$proj[,2], state = colnames(mat)
+ )
+}
+
+steps <- c(0, rep(1/15, 200))
+stepz <- cumsum(steps)
+
+# tidy version of tour data
+tour_dats <- lapply(steps, tour_dat)
+tour_datz <- Map(function(x, y) cbind(x, step = y), tour_dats, stepz)
+tour_dat <- dplyr::bind_rows(tour_datz)
+
+# tidy version of tour projection data
+proj_dats <- lapply(steps, proj_dat)
+proj_datz <- Map(function(x, y) cbind(x, step = y), proj_dats, stepz)
+proj_dat <- dplyr::bind_rows(proj_datz)
+
+
+ax <- list(
+ title = "", showticklabels = FALSE,
+ zeroline = FALSE, showgrid = FALSE,
+ range = c(-1.1, 1.1)
+)
+
+# for nicely formatted slider labels
+options(digits = 3)
+
+tour_dat <- crosstalk::SharedData$new(tour_dat, ~state, group = "A")
+
+tour <- proj_dat %>%
+ plot_ly(x = ~x, y = ~y, frame = ~step, color = I("black")) %>%
+ add_segments(xend = 0, yend = 0, color = I("gray80")) %>%
+ add_text(text = ~state) %>%
+ add_markers(data = tour_dat, text = ~state, hoverinfo = "text") %>%
+ layout(xaxis = ax, yaxis = ax)
+
+dend <- USArrests %>%
+ dist() %>%
+ hclust() %>%
+ as.dendrogram() %>%
+ plot_dendro(set = "A", xmin = -100, height = 900, width = 1100)
+
+USArrests$state <- rownames(USArrests)
+USArrests$abb <- setNames(state.abb, state.name)[USArrests$state]
+
+map <- plot_geo(USArrests, color = I("black")) %>%
+ add_trace(locations = ~abb, locationmode = "USA-states",
+ key = ~state, set = "A") %>%
+ layout(geo = list(
+ scope = 'usa',
+ projection = list(type = 'albers usa'),
+ lakecolor = toRGB('white')
+ ))
+
+subplot(tour, map, nrows = 2, margin = 0) %>%
+ subplot(dend, shareY = FALSE, margin = 0) %>%
+ hide_legend() %>%
+ animation_opts(33, redraw = FALSE) %>%
+ highlight(persistent = TRUE, dynamic = TRUE)
diff --git a/demo/animation-tour-basic.R b/demo/animation-tour-basic.R
new file mode 100644
index 0000000..b56a313
--- /dev/null
+++ b/demo/animation-tour-basic.R
@@ -0,0 +1,52 @@
+# adapted from https://github.com/rstudio/ggvis/blob/master/demo/tourr.r
+library(tourr)
+library(plotly)
+
+mat <- rescale(as.matrix(flea[1:6]))
+tour <- new_tour(mat, grand_tour(), NULL)
+
+tour_dat <- function(step_size) {
+ step <- tour(step_size)
+ proj <- center(mat %*% step$proj)
+ data.frame(x = proj[,1], y = proj[,2],
+ species = flea$species)
+}
+
+proj_dat <- function(step_size) {
+ step <- tour(step_size)
+ data.frame(
+ x = step$proj[,1], y = step$proj[,2], measure = colnames(mat)
+ )
+}
+
+steps <- c(0, rep(1/15, 50))
+stepz <- cumsum(steps)
+
+# tidy version of tour data
+tour_dats <- lapply(steps, tour_dat)
+tour_datz <- Map(function(x, y) cbind(x, step = y), tour_dats, stepz)
+tour_dat <- dplyr::bind_rows(tour_datz)
+
+# tidy version of tour projection data
+proj_dats <- lapply(steps, proj_dat)
+proj_datz <- Map(function(x, y) cbind(x, step = y), proj_dats, stepz)
+proj_dat <- dplyr::bind_rows(proj_datz)
+
+
+ax <- list(
+ title = "",
+ range = c(-1, 1),
+ zeroline = FALSE
+)
+
+# for nicely formatted slider labels
+options(digits = 2)
+
+proj_dat %>%
+ plot_ly(x = ~x, y = ~y, frame = ~step, color = I("gray80")) %>%
+ add_segments(xend = 0, yend = 0) %>%
+ add_text(text = ~measure) %>%
+ add_markers(color = ~species, data = tour_dat) %>%
+ hide_legend() %>%
+ layout(xaxis = ax, yaxis = ax) %>%
+ animation_opts(33, redraw = FALSE)
diff --git a/demo/crosstalk-filter-dynamic-axis.R b/demo/crosstalk-filter-dynamic-axis.R
new file mode 100644
index 0000000..9091300
--- /dev/null
+++ b/demo/crosstalk-filter-dynamic-axis.R
@@ -0,0 +1,15 @@
+library(plotly)
+library(tidyr)
+library(crosstalk)
+
+m <- gather(mtcars, variable, value, -vs)
+msd <- SharedData$new(m, ~variable)
+gg <- ggplot(msd, aes(factor(vs), value)) +
+ geom_jitter(alpha = 0.3)
+
+bscols(
+ widths = c(11, 6, 6),
+ filter_select("id", "Select a variable", msd, ~variable, multiple = FALSE),
+ ggplotly(gg, dynamicTicks = "y") %>% layout(margin = list(l = 30)),
+ plot_ly(msd, x = ~jitter(vs), y = ~value) %>% add_markers(alpha = 0.3)
+)
diff --git a/demo/crosstalk-filter-lines.R b/demo/crosstalk-filter-lines.R
new file mode 100644
index 0000000..032b185
--- /dev/null
+++ b/demo/crosstalk-filter-lines.R
@@ -0,0 +1,14 @@
+library(crosstalk)
+library(ggplot2)
+library(gapminder)
+library(plotly)
+
+sd <- SharedData$new(gapminder)
+
+g <- ggplot(sd, aes(year, lifeExp, color = country, group = country)) +
+ geom_line()
+
+bscols(widths = c(12, 12),
+ filter_select("country", "Country:", sd, ~ country),
+ ggplotly(g)
+)
diff --git a/demo/crosstalk-highlight-binned-target.R b/demo/crosstalk-highlight-binned-target.R
new file mode 100644
index 0000000..1b67c1c
--- /dev/null
+++ b/demo/crosstalk-highlight-binned-target.R
@@ -0,0 +1,65 @@
+# These examples demonstrate ways to display binned/aggregated selections
+library(crosstalk)
+library(plotly)
+
+d <- SharedData$new(mtcars)
+scatterplot <- plot_ly(d, x = ~mpg, y = ~disp) %>%
+ add_markers(color = I("black")) %>%
+ highlight("plotly_selected")
+
+# add_histogram() does both continuous _and_ discrete binning in the browser,
+# allowing us to perform aggregations on the fly, without
+p <- subplot(
+ plot_ly(d, x = ~factor(cyl)) %>% add_histogram(color = I("black")),
+ scatterplot
+)
+
+# Crosstalk selections are actually additional traces, and, by default,
+# plotly.js will try to dodge bars placed under the same category
+layout(p, barmode = "overlay") %>%
+ highlight(
+ "plotly_selected",
+ selected = attrs_selected(showlegend = FALSE)
+ )
+
+# same idea, but now with a boxplot
+p <- plot_ly(d, y = ~disp, color = I("black")) %>% add_boxplot(name = "overall")
+subplot(p, scatterplot, shareY = TRUE) %>%
+ highlight(
+ "plotly_selected",
+ selected = attrs_selected(name = "selection")
+ )
+
+
+
+tx <- SharedData$new(txhousing, ~city)
+p1 <- ggplot(tx, aes(date, median, group = city)) + geom_line() + xlab(NULL)
+gg1 <- ggplotly(p1, tooltip = c("city", "date", "median"))
+p2 <- plot_ly(tx, x = ~median, color = I("black")) %>%
+ add_histogram(histnorm = "probability density")
+
+subplot(gg1, p2, titleX = TRUE, titleY = TRUE) %>%
+ layout(barmode = "overlay") %>%
+ highlight(
+ dynamic = TRUE, persistent = TRUE,
+ selected = attrs_selected(opacity = 0.3)
+ )
+
+
+
+
+d <- SharedData$new(mpg)
+dots <- plot_ly(d, color = ~class, x = ~displ, y = ~cyl)
+boxs <- plot_ly(d, color = ~class, x = ~class, y = ~cty) %>% add_boxplot()
+bars <- plot_ly(d, x = ~class, color = ~class)
+
+subplot(dots, boxs) %>%
+ subplot(bars, nrows = 2) %>%
+ layout(
+ dragmode = "select",
+ barmode = "overlay",
+ showlegend = FALSE
+ ) %>%
+ highlight("plotly_selected")
+
+
diff --git a/demo/crosstalk-highlight-epl.R b/demo/crosstalk-highlight-epl.R
new file mode 100644
index 0000000..55d7272
--- /dev/null
+++ b/demo/crosstalk-highlight-epl.R
@@ -0,0 +1,30 @@
+library(engsoccerdata)
+library(dplyr)
+library(tidyr)
+library(plotly)
+library(crosstalk)
+
+# shape data into desired format
+dat <- england %>%
+ gather(location, team, home, visitor) %>%
+ # focus on tier 1 teams that are still playing in 2015
+ filter(team %in% maketable_eng(england, 2015, 1)[["team"]]) %>%
+ mutate(
+ pts = ifelse(location == "home" & goaldif > 0, 3,
+ ifelse(location == "away" & goaldif < 0, 3, 1))
+ ) %>%
+ arrange(Date) %>%
+ group_by(Season, team) %>%
+ mutate(gameno = row_number(), cumpts = cumsum(pts))
+
+sd <- SharedData$new(dat, ~Season, "Select a season")
+
+p <- ggplot(sd, aes(x = gameno, y = cumpts)) +
+ geom_line(aes(color = Season, group = Season), alpha = 0.5) +
+ facet_wrap(~ team) + ggtitle("English Premier League Performance (1888-2015)") +
+ xlab("Game in Season") + ylab("Cumulative Points")
+
+gg <- ggplotly(p, tooltip = "colour")
+
+highlight(gg, opacityDim = 0.05, selectize = TRUE)
+
diff --git a/demo/crosstalk-highlight-ggpairs.R b/demo/crosstalk-highlight-ggpairs.R
new file mode 100644
index 0000000..623da96
--- /dev/null
+++ b/demo/crosstalk-highlight-ggpairs.R
@@ -0,0 +1,4 @@
+library(GGally)
+d <- SharedData$new(iris)
+p <- GGally::ggpairs(d, aes(colour = Species), columns = 1:5)
+ggplotly(p) %>% highlight("plotly_selected")
diff --git a/demo/crosstalk-highlight-ggplotly.R b/demo/crosstalk-highlight-ggplotly.R
new file mode 100644
index 0000000..5c15177
--- /dev/null
+++ b/demo/crosstalk-highlight-ggplotly.R
@@ -0,0 +1,35 @@
+library(plotly)
+library(crosstalk)
+
+d <- SharedData$new(txhousing, ~city)
+p <- ggplot(d, aes(date, median, group = city)) + geom_line()
+ggplotly(p, tooltip = "city") %>%
+ highlight(on = "plotly_hover", color = "red")
+
+
+# crosstalk keys are automatically added to the group aesthetic...
+# if you want to avoid adding the key to group for a layer,
+# use the original data
+sd <- SharedData$new(txhousing, ~city)
+p <- ggplot(sd, aes(month, median)) +
+ geom_line(aes(group = city)) +
+ geom_smooth(data = txhousing, method = "gam") +
+ facet_wrap(~ year)
+ggplotly(p) %>%
+ highlight(on = "plotly_click", color = "red")
+
+# perhaps a more useful example
+sd <- SharedData$new(txhousing, ~year)
+p <- ggplot(sd, aes(month, median)) +
+ geom_line(aes(group = year)) +
+ geom_smooth(data = txhousing, method = "gam") +
+ facet_wrap(~ city)
+ggplotly(p, height = 800, width = 1600) %>%
+ highlight(on = "plotly_click", color = "red")
+
+# TODO: why doesn't this work?
+sd <- SharedData$new(mtcars, ~cyl)
+p <- ggplot(sd, aes(factor(vs))) +
+ geom_bar() + facet_wrap(~am)
+ggplotly(p) %>%
+ highlight(on = "plotly_click", color = "red")
diff --git a/demo/crosstalk-highlight-intro.R b/demo/crosstalk-highlight-intro.R
new file mode 100644
index 0000000..d803b00
--- /dev/null
+++ b/demo/crosstalk-highlight-intro.R
@@ -0,0 +1,66 @@
+library(plotly)
+library(crosstalk)
+
+nPatients <- 50
+nVisits <- 10
+
+df <- data.frame(
+ perc = rnorm(n = nPatients * nVisits, mean = 50, sd = 10),
+ patient = rep(seq(nPatients), each = nVisits),
+ visit = rep(seq(nVisits), nPatients)
+)
+
+# delare the patient variable as the "unit of interest"
+sd <- SharedData$new(df, ~patient)
+
+p <- plot_ly(sd, x = ~visit, y = ~perc, color = I("black"),
+ text = ~paste("Patient:", patient)) %>%
+ group_by(patient) %>%
+ add_trace(mode = "markers+lines") %>%
+ highlight("plotly_selected")
+
+# Since crosstalk's SharedData object was supplied to plot_ly() with a key of
+# patient, it knows to highlight any lines/markers matching the selected patient(s).
+# By default, the "on trigger" is "plotly_click", but we've changed that to
+# "plotly_selected", which corresponds to click and drag mouse events.
+# Plotly provides two types of drag modes that will trigger a "plotly_selected"
+# event: "lasso" and "select". You can change the dragmode interactively via
+# the modebar and/or set the default dragmode via `layout()`.
+layout(p, dragmode = "lasso")
+
+# Other interaction types, beyond click and drag interactions, can also select
+# value(s) of a SharedData key and are specified via the highlight() function.
+# The first argument, `on`, sets the interaction type used to add values to the
+# selection set. The second argument, `off`, sets the interaction required to
+# clear the selection set and return to the original view. By default, a
+# "plotly_relayout" event will clear the selection set. This event is triggered
+# by clicking on the home icon in the mode bar, or double-clicking on the plot
+# when in a zoom or pan dragmode. Some other sensible events for clearing the
+# selection set are "plotly_deselect" and "plotly_doubleclick". Both events are
+# triggered with a double click, but are dependant upon the current dragmode
+# ("plotly_deselect" is triggered when in select/lasso dragmode and
+# "plotly_doubleclick" when in zoom/pan dragmode).
+p %>%
+ highlight(on = "plotly_hover", off = "plotly_doubleclick") %>%
+ layout(dragmode = "zoom")
+
+# By default, all selections are transient, meaning prior selections are
+# removed from the selection set before new selections are added. To prevent
+# prior selections from being removed, simply set the persistent argument to
+# `TRUE`.
+highlight(p, on = "plotly_hover", persistent = TRUE)
+
+# Sometimes its useful to compare two or more different selection sets.
+# For example, how do patients with a high response on visit 1 compare to those
+# with a low response? To make this sort of comparison, we can alter the color
+# in multiple persistent selections. By setting the dynamic argument to `TRUE`
+# a colourpicker htmlwidget (@colourpicker) will appear just above the plotly
+# visualization. At any given time, the value of this widget controls the
+# color of new selection(s).
+highlight(p, on = "plotly_hover", persistent = TRUE, dynamic = TRUE)
+
+# By default, the colourpicker widget uses colors from the "Set1"
+# colour brewer palette (@RColorBrewer), but any set of valid R colors can
+# be supplied to the color argument.
+colors <- RColorBrewer::brewer.pal(4, "Dark2")
+highlight(p, on = "plotly_hover", color = colors, dynamic = TRUE, persistent = TRUE)
diff --git a/demo/crosstalk-highlight-leaflet.R b/demo/crosstalk-highlight-leaflet.R
new file mode 100644
index 0000000..07a50e3
--- /dev/null
+++ b/demo/crosstalk-highlight-leaflet.R
@@ -0,0 +1,22 @@
+library(plotly)
+# devtools::install_github("rstudio/leaflet#346")
+library(leaflet)
+library(crosstalk)
+library(htmltools)
+
+# leaflet should respect these "global" highlight() options
+options(
+ opacityDim = 1, persistent = TRUE
+)
+
+sd <- SharedData$new(quakes)
+
+p <- plot_ly(sd, x = ~depth, y = ~mag) %>%
+ add_markers(alpha = 0.5) %>%
+ highlight("plotly_selected", dynamic = TRUE)
+
+map <- leaflet(sd) %>%
+ addTiles() %>%
+ addCircles()
+
+bscols(p, map)
diff --git a/demo/crosstalk-highlight-pipeline.R b/demo/crosstalk-highlight-pipeline.R
new file mode 100644
index 0000000..3ceb6de
--- /dev/null
+++ b/demo/crosstalk-highlight-pipeline.R
@@ -0,0 +1,27 @@
+library(plotly)
+library(crosstalk)
+
+sd <- SharedData$new(txhousing, ~city)
+
+base <- plot_ly(sd, color = I("black")) %>%
+ group_by(city)
+
+p1 <- base %>%
+ summarise(has = sum(is.na(median))) %>%
+ filter(has > 0) %>%
+ arrange(has) %>%
+ add_bars(x = ~has, y = ~factor(city, levels = city), hoverinfo = "x") %>%
+ layout(
+ barmode = "overlay",
+ xaxis = list(title = "Number of months missing"),
+ yaxis = list(title = "")
+ )
+
+p2 <- base %>%
+ add_lines(x = ~date, y = ~median, alpha = 0.3) %>%
+ layout(xaxis = list(title = ""))
+
+ subplot(p1, p2, titleX = TRUE, widths = c(0.3, 0.7)) %>%
+ layout(margin = list(l = 120)) %>%
+ hide_legend() %>%
+ highlight(color = "red")
diff --git a/demo/crosstalk-highlight-subplot.R b/demo/crosstalk-highlight-subplot.R
new file mode 100644
index 0000000..97b26f0
--- /dev/null
+++ b/demo/crosstalk-highlight-subplot.R
@@ -0,0 +1,10 @@
+library(plotly)
+library(crosstalk)
+
+d <- SharedData$new(mtcars)
+s <- subplot(
+ qplot(data = d, x = mpg, y = wt),
+ qplot(data = d, x = mpg, y = vs)
+)
+
+highlight(s, "plotly_selected")
diff --git a/demo/rotate.R b/demo/rotate.R
new file mode 100644
index 0000000..61c5702
--- /dev/null
+++ b/demo/rotate.R
@@ -0,0 +1,53 @@
+library(plotly)
+library(htmlwidgets)
+
+# Thanks to Etienne Tetreault-Pinard
+# http://codepen.io/etpinard/pen/jmvyxV?editors=0010
+
+plot_ly(z = list(list(1,2,3), list(3,2,1)), type = "surface") %>%
+ onRender(
+ "function(el, x) {
+
+ var gd = document.getElementById(el.id);
+
+ var cnt = 0;
+
+ function run() {
+ rotate('scene', Math.PI / 180);
+ requestAnimationFrame(run);
+ }
+ run();
+
+ function rotate(id, angle) {
+ var scene = gd._fullLayout[id]._scene;
+ var camera = scene.getCamera();
+
+ var rtz = xyz2rtz(camera.eye);
+
+ rtz.t += angle;
+
+ camera.eye = rtz2xyz(rtz);
+
+ scene.setCamera(camera);
+ }
+
+ // Math.atan2 will rotate the full 360, but it doesn't render for some reason?
+ function xyz2rtz(xyz) {
+ return {
+ r: Math.sqrt(xyz.x * xyz.x + xyz.y * xyz.y),
+ t: Math.atan(xyz.y / xyz.x),
+ z: xyz.z
+ };
+ }
+
+ function rtz2xyz(rtz) {
+ return {
+ x: rtz.r * Math.cos(rtz.t),
+ y: rtz.r * Math.sin(rtz.t),
+ z: rtz.z
+ };
+ }
+
+ }"
+ )
+
diff --git a/demo/ternary.R b/demo/ternary.R
new file mode 100644
index 0000000..e2778a1
--- /dev/null
+++ b/demo/ternary.R
@@ -0,0 +1,58 @@
+# An improvement on https://www.r-bloggers.com/ternary-plots-in-r-using-plotly/
+
+library(plotly)
+
+# acquire data
+ds <- jsonlite::fromJSON(
+ "https://gist.githubusercontent.com/davenquinn/988167471993bc2ece29/raw/f38d9cb3dd86e315e237fde5d65e185c39c931c2/data.json"
+)
+df <- dplyr::bind_rows(ds, .id = "id")
+
+# reusable function for creating annotation object
+label <- function(txt) {
+ list(
+ text = txt,
+ x = 0.1, y = 1,
+ ax = 0, ay = 0,
+ xref = "paper", yref = "paper",
+ align = "center",
+ font = list(family = "serif", size = 15, color = "white"),
+ bgcolor = "#b3b3b3", bordercolor = "black", borderwidth = 2
+ )
+}
+
+# reusable function for axis formatting
+axis <- function(txt) {
+ list(
+ title = txt, tickformat = ".0%", tickfont = list(size = 10)
+ )
+}
+
+ternaryAxes <- list(
+ aaxis = axis("Clay"),
+ baxis = axis("Sand"),
+ caxis = axis("Silt")
+)
+
+plot_ly(
+ df, a = ~clay, b = ~sand, c = ~silt, color = I("black"), type = "scatterternary"
+) %>%
+ layout(
+ annotations = label("Ternary Markers"), ternary = ternaryAxes
+ )
+
+plot_ly(
+ df, a = ~clay, b = ~sand, c = ~silt, color = I("black"), type = "scatterternary",
+ split = ~id, mode = "lines"
+) %>%
+ layout(
+ annotations = label("Ternary Lines"), ternary = ternaryAxes
+ )
+
+plot_ly(
+ df, a = ~clay, b = ~sand, c = ~silt, color = ~id, type = "scatterternary",
+ fill = "toself", mode = "lines",
+) %>%
+ layout(
+ annotations = label("Ternary Contour"), ternary = ternaryAxes
+ )
diff --git a/inst/build-push-comment.R b/inst/build-push-comment.R
new file mode 100644
index 0000000..b38ea96
--- /dev/null
+++ b/inst/build-push-comment.R
@@ -0,0 +1,73 @@
+# -----------------------------------------------------------------------
+# Travis does two types of builds:
+#
+# (1) A so-called "push". This essentially does a checkout on the most
+# recent commit of the pull request, but *doesn't* merge with master.
+# In this case, $TRAVIS_PULL_REQUEST = "false"
+# (2) A so-called "pr" (pull request). This *simulates* a merge with master.
+# In this case, $TRAVIS_PULL_REQUEST contains the pull request number.
+# And $TRAVIS_COMMIT is a SHA1 hash that won't match the HEAD of master or
+# the branch we're testing.
+#
+# Since it makes more sense to visually compared what we'd see *after* we
+# merge with master, we don't do anything here if it's a push build.
+#
+# For more info on Travis CI environment variables, see
+# http://docs.travis-ci.com/user/ci-environment/#Environment-variables
+# -----------------------------------------------------------------------
+library("httr")
+library("testthat")
+
+# if this is a Travis pull request, make a comment on the pull request
+comment <- grepl("^[0-9]+$", Sys.getenv("TRAVIS_PULL_REQUEST"))
+
+if (comment) {
+ # get the links to differences, if any
+ this_hash <- substr(Sys.getenv("TRAVIS_COMMIT"), 1, 7)
+ this_dir <- normalizePath(this_hash, mustWork = T)
+ links <- sprintf(
+ "http://cpsievert.github.io/plotly-test-table/%s/%s",
+ this_hash, dir(this_dir)
+ )
+ # construct the message
+ build_link <- file.path('https://travis-ci.org/ropensci/plotly/builds',
+ Sys.getenv("TRAVIS_BUILD_ID"))
+ msg <- sprintf(
+ "> The message below was automatically generated after build %s \n\n Detected %s differences -> \n\n %s",
+ build_link, length(links), paste(links, collapse = "\n")
+ )
+ # gistr is a good reference for talking to the github API via httr
+ # https://github.com/ropensci/gistr/blob/master/R/zzz.R
+ base <- 'https://api.github.com/repos/ropensci/plotly/'
+ header <- add_headers(
+ `User-Agent` = "plotly",
+ `Accept` = 'application/vnd.github.v3+json',
+ `Authorization` = paste0("token ", Sys.getenv("GITHUB_PAT"))
+ )
+ commentz <- sprintf(
+ paste0(base, 'issues/%s/comments'),
+ Sys.getenv("TRAVIS_PULL_REQUEST")
+ )
+ res <- GET(commentz, header)
+ warn_for_status(res)
+ old_body <- unlist(lapply(content(res), "[", "body"))
+ # only post a comment if this hash doesn't appear in any of the comments
+ # (needed since Travis sometimes randomly re-builds stuff)
+ if (!any(grepl(this_hash, old_body))) {
+ json <- jsonlite::toJSON(list(body = msg), auto_unbox = TRUE)
+ POST(url = commentz, header, body = json, encode = "json")
+ } else {
+ message("Link already posted")
+ }
+ # update plotly-test-table
+ system("git status")
+ system("git add *")
+ system(sprintf("git commit -q -m 'Pushed from %s'", build_link))
+ # This post explains how this works -- http://rmflight.github.io/posts/2014/11/travis_ci_gh_pages.html
+ repo <- sprintf(
+ "https://%s@github.com/cpsievert/plotly-test-table.git",
+ Sys.getenv("GITHUB_PAT")
+ )
+ system(paste("git pull", repo, "gh-pages"))
+ system(paste("git push -q", repo, "gh-pages"))
+}
diff --git a/inst/docs.R b/inst/docs.R
new file mode 100644
index 0000000..413a810
--- /dev/null
+++ b/inst/docs.R
@@ -0,0 +1,1160 @@
+# install the new/experimental plotly R package
+# devtools::install_github("ropensci/plotly at carson-dsl")
+
+################################################################################
+# Basic Charts (https://plot.ly/r/#basic-charts)
+################################################################################
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/3d-line-plots/
+# ----------------------------------------------------------------------
+
+# initiate a 100 x 3 matrix filled with zeros
+m <- matrix(numeric(300), ncol = 3)
+# simulate a 3D random-walk
+for (i in 2:100) m[i, ] <- m[i-1, ] + rnorm(3)
+# collect everything in a data-frame
+df <- setNames(
+ data.frame(m, seq(1, 100)),
+ c("x", "y", "z", "time")
+)
+
+# create the plotly
+library(plotly)
+plot_ly(df, x = x, y = y, z = z, color = time, type = "scatter3d")
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/3d-scatter-plots/
+# ----------------------------------------------------------------------
+
+# variance-covariance matrix for a multivariate normal distribution
+s <- matrix(c(1, .5, .5,
+ .5, 1, .5,
+ .5, .5, 1), ncol = 3)
+# use the mvtnorm package to sample 200 observations
+obs <- mvtnorm::rmvnorm(200, sigma = s)
+# collect everything in a data-frame
+df <- setNames(data.frame(obs), c("x", "y", "z"))
+
+library(plotly)
+plot_ly(df, x = x, y = y, z = z, type = "scatter3d", mode = "markers")
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/3d-surface-plots/
+# ----------------------------------------------------------------------
+
+library(plotly)
+# volcano is a numeric matrix that ships with R
+plot_ly(z = volcano, type = "surface")
+
+# 2D kernel density estimation
+kd <- with(MASS::geyser, MASS::kde2d(duration, waiting, n = 50))
+with(kd, plot_ly(x = x, y = y, z = z, type = "surface"))
+
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/filled-area-plots/
+# ----------------------------------------------------------------------
+
+library(plotly)
+p <- plot_ly(x = c(1, 2, 3, 4), y = c(0, 2, 3, 5), fill = "tozeroy")
+add_trace(p, x = c(1, 2, 3, 4), y = c(3, 5, 1, 7), fill = "tonexty")
+
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/bar-charts/
+# ----------------------------------------------------------------------
+
+library(plotly)
+p <- plot_ly(
+ x = c("giraffes", "orangutans", "monkeys"),
+ y = c(20, 14, 23),
+ name = "SF Zoo",
+ type = "bar"
+)
+p
+
+p2 <- add_trace(
+ p,
+ x = c("giraffes", "orangutans", "monkeys"),
+ y = c(12, 18, 29),
+ name = "LA Zoo"
+)
+p2
+
+layout(p2, barmode = "stack")
+
+## customizing colors
+
+library(dplyr)
+ggplot2::diamonds %>% count(cut) %>%
+ plot_ly(x = cut, y = n, type = "bar", marker = list(color = toRGB("black")))
+
+# mapping a color variable
+ggplot2::diamonds %>% count(cut, clarity) %>%
+ plot_ly(x = cut, y = n, type = "bar", color = clarity)
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/box-plots/
+# ----------------------------------------------------------------------
+
+library(plotly)
+#' basic boxplot
+plot_ly(y = rnorm(50), type = "box") %>%
+ add_trace(y = rnorm(50, 1))
+#' adding jittered points
+plot_ly(y = rnorm(50), type = "box", boxpoints = "all", jitter = 0.3,
+ pointpos = -1.8)
+#' several box plots
+plot_ly(ggplot2::diamonds, y = price, color = cut, type = "box")
+
+#' grouped box plots
+plot_ly(ggplot2::diamonds, x = cut, y = price, color = clarity, type = "box") %>%
+ layout(boxmode = "group")
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/bubble-charts/
+# ----------------------------------------------------------------------
+
+# IMO, this page should be a part of this page -> https://plot.ly/r/line-and-scatter/
+library(plotly)
+d <- diamonds[sample(nrow(diamonds), 1000), ]
+# note how size is automatically scaled and added as hover text
+plot_ly(d, x = carat, y = price, size = carat, mode = "markers")
+
+plot_ly(d, x = carat, y = price, text = paste("Clarity: ", clarity),
+ mode = "markers", color = carat, size = carat, opacity = carat)
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/contour-plots/
+# ----------------------------------------------------------------------
+
+#' Basic contour
+library(plotly)
+plot_ly(z = volcano, type = "contour")
+
+#' Advanced
+x <- rnorm(200)
+y <- rnorm(200)
+p1 <- plot_ly(x = x, type = "histogram", showlegend = FALSE)
+p2 <- plot_ly(x = x, y = y, type = "histogram2dcontour")
+p3 <- plot_ly(y = y, type = "histogram", showlegend = FALSE)
+a1 <- list(domain = c(0, .85))
+a2 <- list(domain = c(.85, 1))
+hide <- list(title = "", showticklabels = FALSE)
+subplot(
+ layout(p1, xaxis = c(a1, hide), yaxis = a2),
+ layout(p2, xaxis = a1, yaxis = a1),
+ layout(p3, xaxis = a2, yaxis = c(a1, hide))
+)
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/error-bars/
+# ----------------------------------------------------------------------
+
+library(dplyr)
+library(plotly)
+
+p <- ggplot2::mpg %>% group_by(class) %>%
+ summarise(mn = mean(hwy), sd = 1.96 * sd(hwy)) %>%
+ arrange(desc(mn)) %>%
+ plot_ly(x = class, y = mn, error_y = list(value = sd),
+ mode = "markers", name = "Highway") %>%
+ layout(yaxis = list(title = "Miles Per Gallon"))
+p
+
+df2 <- mpg %>% group_by(class) %>%
+ summarise(mn = mean(cty), sd = 1.96 * sd(cty))
+
+add_trace(p, y = mn, error_y = list(value = sd),
+ name = "City", data = df2)
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/heatmaps/
+# ----------------------------------------------------------------------
+
+library(plotly)
+plot_ly(z = volcano, type = "heatmap")
+
+
+#' categorical x/y axis
+m <- matrix(rnorm(9), nrow = 3, ncol = 3)
+plot_ly(z = m, x = c("a", "b", "c"), y = c("d", "e", "f"), type = "heatmap")
+
+#' Sequential Colorscales (Hot)
+plot_ly(z = volcano, colorscale = "Hot", type = "heatmap")
+
+#' Sequential Colorscales (Greys)
+plot_ly(z = volcano, colorscale = "Greys", type = "heatmap")
+
+#' Sequential Colorscales (Greens)
+plot_ly(z = volcano, colorscale = "Greens", type = "heatmap")
+
+#' Custom colorscale via scales package
+vals <- unique(scales::rescale(c(volcano)))
+o <- order(vals, decreasing = FALSE)
+cols <- scales::col_numeric("Blues", domain = NULL)(vals)
+colz <- setNames(data.frame(vals[o], cols[o]), NULL)
+plot_ly(z = volcano, colorscale = colz, type = "heatmap")
+
+library(viridis)
+plot_ly(z = volcano, colors = viridis(256), type = "heatmap")
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/2D-Histogram/
+# ----------------------------------------------------------------------
+
+library(plotly)
+s <- matrix(c(1, -.75, -.75, 1), ncol = 2)
+obs <- mvtnorm::rmvnorm(500, sigma = s)
+plot_ly(x = obs[,1], y = obs[,2], type = "histogram2d")
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/histograms/
+# ----------------------------------------------------------------------
+
+#' Basic histogram
+plot_ly(x = rnorm(50), type = "histogram")
+
+#' Vertical histogram
+plot_ly(y = rnorm(50), type = "histogram")
+
+#' Overlayed histograms
+plot_ly(x = rnorm(500), opacity = 0.6, type = "histogram") %>%
+ add_trace(x = rnorm(500))
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/line-and-scatter/
+# ----------------------------------------------------------------------
+
+#' Simple scatterplot
+plot_ly(data = iris, x = Sepal.Length, y = Petal.Length, mode = "markers")
+
+#' Scatterplot with qualitative colorscale
+plot_ly(data = iris, x = Sepal.Length, y = Petal.Length, color = Species,
+ mode = "markers")
+
+#' colors argument accepts colorbrewer2.org palette names
+plot_ly(data = iris, x = Sepal.Length, y = Petal.Length, color = Species,
+ colors = "Set1", mode = "markers")
+#' By default, colors will 'span the gamut'
+# scales::show_col(RColorBrewer::brewer.pal("Set1"))
+
+#' If you want finer control over the color scheme, you can pass
+#' RGB or hex color codes directly to colors
+pal <- RColorBrewer::brewer.pal(nlevels(iris$Species), "Set1")
+plot_ly(data = iris, x = Sepal.Length, y = Petal.Length, color = Species,
+ colors = pal, mode = "markers")
+
+#' Scatterplot with sequential colorscale
+plot_ly(data = iris, x = Sepal.Length, y = Petal.Length, color = Petal.Width, mode = "markers")
+
+#' Basic time-series (line) plot with loess smooth
+plot_ly(economics, x = date, y = uempmed, name = "unemployment")
+add_trace(y = fitted(loess(uempmed ~ as.numeric(date))))
+
+#' Density plot
+dens <- with(diamonds, tapply(price, INDEX = cut, density))
+df <- data.frame(
+ x = unlist(lapply(dens, "[[", "x")),
+ y = unlist(lapply(dens, "[[", "y")),
+ cut = rep(names(dens), each = length(dens[[1]]$x))
+)
+plot_ly(df, x = x, y = y, color = cut)
+
+#' Different line interpolation options
+x <- 1:5
+y <- c(1, 3, 2, 3, 1)
+plot_ly(x = x, y = y, name = "linear", line = list(shape = "linear")) %>%
+ add_trace(y = y + 5, name = "spline", line = list(shape = "spline")) %>%
+ add_trace(y = y + 10, name = "vhv", line = list(shape = "vhv")) %>%
+ add_trace(y = y + 15, name = "hvh", line = list(shape = "hvh")) %>%
+ add_trace(y = y + 20, name = "vh", line = list(shape = "vh")) %>%
+ add_trace(y = y + 25, name = "hv", line = list(shape = "hv"))
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/log-plot/
+# ----------------------------------------------------------------------
+
+d <- diamonds[sample(nrow(diamonds), 1000), ]
+
+#' Without log scales
+(p <- plot_ly(d, x = carat, y = price, mode = "markers"))
+
+#' With log scales
+layout(p, xaxis = list(type = "log", autorange = T),
+ yaxis = list(type = "log", autorange = T))
+
+# ---------------------------------------------------------------------
+# https://plot.ly/r/graphing-multiple-chart-types/
+# ----------------------------------------------------------------------
+
+#' Scatterplot with loess smoother
+
+library(plotly)
+mtcars <- mtcars[order(mtcars$disp), ]
+p <- plot_ly(mtcars, x = disp, y = mpg, mode = "markers",
+ text = rownames(mtcars), showlegend = FALSE)
+add_trace(p, y = fitted(loess(mpg ~ disp)), mode = "lines",
+ name = "loess smoother", showlegend = TRUE)
+
+#' Scatterplot with loess smoother and it's uncertaincy estimates
+m <- loess(mpg ~ disp, data = mtcars)
+f <- with(predict(m, se = TRUE), data.frame(fit, se.fit))
+
+l <- list(
+ color = toRGB("gray90", alpha = 0.3),
+ fillcolor = toRGB("gray90", alpha = 0.3)
+)
+
+p %>%
+ add_trace(p, data = f, y = fit, mode = "lines") %>%
+ add_trace(p, data = f, y = fit + 1.96 * se.fit, mode = "lines",
+ fill = "tonexty", line = l) %>%
+ add_trace(p, data = f, y = fit - 1.96 * se.fit, mode = "lines",
+ fill = "tonexty", line = l)
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/polar-chart/
+# ----------------------------------------------------------------------
+
+p <- plot_ly(plotly::mic, r = r, t = t, color = nms, mode = "lines")
+layout(p, title = "Mic Patterns", orientation = -90)
+
+p <- plot_ly(plotly::hobbs, r = r, t = t, color = nms, opacity = 0.7, mode = "markers")
+layout(p, title = "Hobbs-Pearson Trials", plot_bgcolor = toRGB("grey90"))
+
+p <- plot_ly(plotly::wind, r = r, t = t, color = nms, type = "area")
+layout(p, radialaxis = list(ticksuffix = "%"), orientation = 270)
+
+# ----------------------------------------------------------------------
+# https://plot.ly/r/time-series/
+# ----------------------------------------------------------------------
+
+#' POSIXlt date/time class
+now_lt <- as.POSIXlt(Sys.time(), tz = "GMT")
+tm <- seq(0, 600, by = 10)
+x <- now_lt - tm
+y <- rnorm(length(x))
+plot_ly(x = x, y = y, text = paste(tm, "seconds from now in GMT"))
+
+#' POSIXct date/time class
+now_ct <- as.POSIXct(Sys.time())
+tm <- seq(0, 600, by = 10)
+x <- now_ct - tm
+y <- rnorm(length(x))
+plot_ly(x = x, y = y, text = paste(tm, "seconds from now in", Sys.timezone()))
+
+#' Dates
+today <- Sys.Date()
+tm <- seq(0, 600, by = 10)
+x <- today - tm
+y <- rnorm(length(x))
+plot_ly(x = x, y = y, text = paste(tm, "days from today"))
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/choropleth-maps/ (new)
+# ----------------------------------------------------------------------------
+
+#' World Choropleth Map
+df <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/2011_us_ag_exports.csv")
+df$hover <- with(df, paste(state, '<br>', "Beef", beef, "Dairy", dairy, "<br>",
+ "Fruits", total.fruits, "Veggies", total.veggies,
+ "<br>", "Wheat", wheat, "Corn", corn))
+# give state boundaries a white border
+l <- list(
+ color = toRGB("white"),
+ width = 2
+)
+# specify some map projection/options
+g <- list(
+ scope = 'usa',
+ projection = list(type = 'albers usa'),
+ showlakes = TRUE,
+ lakecolor = toRGB('white')
+)
+
+plot_ly(df, z = total.exports, text = hover, locations = code, type = 'choropleth',
+ locationmode = 'USA-states', color = total.exports, colors = 'Purples',
+ marker = list(line = l), colorbar = list(title = "Millions USD")) %>%
+ layout(title = '2011 US Agriculture Exports by State<br>(Hover for breakdown)', geo = g)
+
+#' World Choropleth Map
+df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_world_gdp_with_codes.csv')
+
+# light grey boundaries
+l <- list(
+ color = toRGB("grey"),
+ width = 0.5
+)
+
+# specify map projection/options
+g <- list(
+ showframe = FALSE,
+ showcoastlines = FALSE,
+ projection = list(type = 'Mercator')
+)
+
+plot_ly(df, z = GDP..BILLIONS., text = COUNTRY, locations = CODE, type = 'choropleth',
+ color = GDP..BILLIONS., colors = 'Blues', marker = list(line = l),
+ colorbar = list(tickprefix = '$', title = 'GDP Billions US$')) %>%
+ layout(title = '2014 Global GDP<br>Source:<a href="https://www.cia.gov/library/publications/the-world-factbook/fields/2195.html">CIA World Factbook</a>',
+ geo = g)
+
+#' Choropleth Inset Map
+df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_ebola.csv')
+# restrict from June to September
+df <- subset(df, Month %in% 6:9)
+# ordered factor variable with month abbreviations
+df$abbrev <- ordered(month.abb[df$Month], levels = month.abb[6:9])
+# September totals
+df9 <- subset(df, Month == 9)
+
+# common plot options
+g <- list(
+ scope = 'africa',
+ showframe = F,
+ showland = T,
+ landcolor = toRGB("grey90")
+)
+
+g1 <- c(
+ g,
+ resolution = 50,
+ showcoastlines = T,
+ countrycolor = toRGB("white"),
+ coastlinecolor = toRGB("white"),
+ projection = list(type = 'Mercator'),
+ list(lonaxis = list(range = c(-15, -5))),
+ list(lataxis = list(range = c(0, 12))),
+ list(domain = list(x = c(0, 1), y = c(0, 1)))
+)
+
+g2 <- c(
+ g,
+ showcountries = F,
+ bgcolor = toRGB("white", alpha = 0),
+ list(domain = list(x = c(0, .6), y = c(0, .6)))
+)
+
+
+plot_ly(df, type = 'scattergeo', mode = 'markers', locations = Country,
+ locationmode = 'country names', text = paste(Value, "cases"),
+ color = as.ordered(abbrev), marker = list(size = Value/50)) %>%
+ add_trace(type = 'scattergeo', mode = 'text', geo = 'geo2', showlegend = F,
+ lon = 21.0936, lat = 7.1881, text = 'Africa') %>%
+ add_trace(type = 'choropleth', locations = Country, locationmode = 'country names',
+ z = Month, colors = "black", showscale = F, geo = 'geo2', data = df9) %>%
+ layout(title = 'Ebola cases reported by month in West Africa 2014<br> Source: <a href="https://data.hdx.rwlabs.org/dataset/rowca-ebola-cases">HDX</a>',
+ geo = g1, geo2 = g2)
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/lines-on-maps/ (new)
+# ----------------------------------------------------------------------------
+
+#' US Flight Paths Map
+
+# airport locations
+air <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')
+# flights between airports
+flights <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv')
+flights$id <- seq_len(nrow(flights))
+
+# map projection
+geo <- list(
+ scope = 'north america',
+ projection = list(type = 'azimuthal equal area'),
+ showland = TRUE,
+ landcolor = toRGB("gray95"),
+ countrycolor = toRGB("gray80")
+)
+
+plot_ly(air, lon = long, lat = lat, text = airport, type = 'scattergeo',
+ locationmode = 'USA-states', marker = list(size = 2, color = 'red')) %>%
+ add_trace(lon = list(start_lon, end_lon), lat = list(start_lat, end_lat),
+ group = id, opacity = cnt/max(cnt), data = flights,
+ mode = 'lines', line = list(width = 1, color = 'red'),
+ type = 'scattergeo', locationmode = 'USA-states') %>%
+ layout(title = 'Feb. 2011 American Airline flight paths<br>(Hover for airport names)',
+ geo = geo, showlegend = FALSE)
+
+#' London to NYC Great Circle
+library(plotly)
+plot_ly(lat = c(40.7127, 51.5072), lon = c(-74.0059, 0.1275), type = 'scattergeo',
+ mode = 'lines', line = list(width = 2, color = 'blue')) %>%
+ layout(
+ title = 'London to NYC Great Circle',
+ showlegend = FALSE,
+ geo = list(
+ resolution = 50,
+ showland = TRUE,
+ showlakes = TRUE,
+ landcolor = toRGB("grey80"),
+ countrycolor = toRGB("grey80"),
+ lakecolor = toRGB("white"),
+ projection = list(type = "equirectangular"),
+ coastlinewidth = 2,
+ lataxis = list(
+ range = c(20, 60),
+ showgrid = TRUE,
+ tickmode = "linear",
+ dtick = 10
+ ),
+ lonaxis = list(
+ range = c(-100, 20),
+ showgrid = TRUE,
+ tickmode = "linear",
+ dtick = 20
+ )
+ )
+ )
+
+#' Contour lines on globe
+library(plotly)
+df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/globe_contours.csv')
+df$id <- seq_len(nrow(df))
+
+library(tidyr)
+d <- df %>%
+ gather(key, value, -id) %>%
+ separate(key, c("l", "line"), "\\.") %>%
+ spread(l, value)
+
+p <- plot_ly(type = 'scattergeo', mode = 'lines',
+ line = list(width = 2, color = 'violet'))
+
+for (i in unique(d$line))
+ p <- add_trace(p, lat = lat, lon = lon, data = subset(d, line == i))
+
+geo <- list(
+ showland = TRUE,
+ showlakes = TRUE,
+ showcountries = TRUE,
+ showocean = TRUE,
+ countrywidth = 0.5,
+ landcolor = toRGB("grey90"),
+ lakecolor = toRGB("white"),
+ oceancolor = toRGB("white"),
+ projection = list(
+ type = 'orthographic',
+ rotation = list(
+ lon = -100,
+ lat = 40,
+ roll = 0
+ )
+ ),
+ lonaxis = list(
+ showgrid = TRUE,
+ gridcolor = toRGB("gray40"),
+ gridwidth = 0.5
+ ),
+ lataxis = list(
+ showgrid = TRUE,
+ gridcolor = toRGB("gray40"),
+ gridwidth = 0.5
+ )
+)
+
+layout(p, showlegend = FALSE, geo = geo,
+ title = 'Contour lines over globe<br>(Click and drag to rotate)')
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/scatter-plots-on-maps/ (new)
+# ----------------------------------------------------------------------------
+
+#' US Airports Map
+library(plotly)
+df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')
+df$hover <- with(df, paste(airport, city, state, "Arrivals: ", cnt))
+
+# marker styling
+m <- list(
+ colorbar = list(title = "Incoming flights February 2011"),
+ size = 8, opacity = 0.8, symbol = 'square'
+)
+
+# geo styling
+g <- list(
+ scope = 'usa',
+ projection = list(type = 'albers usa'),
+ showland = TRUE,
+ landcolor = toRGB("gray95"),
+ subunitcolor = toRGB("gray85"),
+ countrycolor = toRGB("gray85"),
+ countrywidth = 0.5,
+ subunitwidth = 0.5
+)
+
+plot_ly(df, lat = lat, lon = long, text = hover, color = cnt,
+ type = 'scattergeo', locationmode = 'USA-states', mode = 'markers',
+ marker = m) %>%
+ layout(title = 'Most trafficked US airports<br>(Hover for airport)', geo = g)
+
+#' North American Precipitation Map
+library(plotly)
+df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2015_06_30_precipitation.csv')
+df$hover <- paste(df$Globvalue, "inches")
+
+# change default color scale title
+m <- list(colorbar = list(title = "Total Inches"))
+
+# geo styling
+g <- list(
+ scope = 'north america',
+ showland = TRUE,
+ landcolor = toRGB("grey83"),
+ subunitcolor = toRGB("white"),
+ countrycolor = toRGB("white"),
+ showlakes = TRUE,
+ lakecolor = toRGB("white"),
+ showsubunits = TRUE,
+ showcountries = TRUE,
+ resolution = 50,
+ projection = list(
+ type = 'conic conformal',
+ rotation = list(
+ lon = -100
+ )
+ ),
+ lonaxis = list(
+ showgrid = TRUE,
+ gridwidth = 0.5,
+ range = c(-140, -55),
+ dtick = 5
+ ),
+ lataxis = list(
+ showgrid = TRUE,
+ gridwidth = 0.5,
+ range = c(20, 60),
+ dtick = 5
+ )
+)
+
+plot_ly(df, lat = Lat, lon = Lon, text = hover, color = Globvalue,
+ type = 'scattergeo', marker = m) %>%
+ layout(title = 'US Precipitation 06-30-2015<br>Source: NOAA', geo = g)
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/bubble-maps/ (new)
+# ----------------------------------------------------------------------------
+
+#' United States Bubble Map
+library(plotly)
+df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_us_cities.csv')
+df$hover <- paste(df$name, "Population", df$pop/1e6, " million")
+
+df$q <- with(df, cut(pop, quantile(pop)))
+levels(df$q) <- paste(c("1st", "2nd", "3rd", "4th", "5th"), "Quantile")
+df$q <- as.ordered(df$q)
+
+g <- list(
+ scope = 'usa',
+ projection = list(type = 'albers usa'),
+ showland = TRUE,
+ landcolor = toRGB("gray85"),
+ subunitwidth = 1,
+ countrywidth = 1,
+ subunitcolor = toRGB("white"),
+ countrycolor = toRGB("white")
+)
+
+plot_ly(df, lon = lon, lat = lat, text = hover,
+ marker = list(size = sqrt(pop/10000) + 1),
+ color = q, type = 'scattergeo', locationmode = 'USA-states') %>%
+ layout(title = '2014 US city populations<br>(Click legend to toggle)', geo = g)
+
+#' Ebola Cases in West Africa
+
+# see 'Choropleth Inset Map' example
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/map-subplots-and-small-multiples/ (new)
+# ----------------------------------------------------------------------------
+
+#' US map small multiples
+library(plotly)
+df <- read.csv('https://raw.githubusercontent.com/plotly/datasets/master/1962_2006_walmart_store_openings.csv')
+
+# common map properties
+g <- list(
+ scope = 'usa',
+ showland = T,
+ landcolor = toRGB("gray90"),
+ showcountries = F,
+ subunitcolor = toRGB("white")
+)
+
+# year text labels
+yrs <- unique(df$YEAR)
+id <- seq_along(yrs)
+df2 <- data.frame(
+ YEAR = yrs,
+ id = id
+)
+
+# id for anchoring traces on different plots
+df$id <- as.integer(factor(df$YEAR))
+
+p <- plot_ly(df, type = 'scattergeo', lon = LON, lat = LAT, group = YEAR,
+ geo = paste0("geo", id), showlegend = F,
+ marker = list(color = toRGB("blue"), opacity = 0.5)) %>%
+ add_trace(lon = -78, lat = 47, mode = 'text', group = YEAR,
+ geo = paste0("geo", id), text = YEAR, data = df2) %>%
+ layout(title = 'New Walmart Stores per year 1962-2006<br> Source: <a href="http://www.econ.umn.edu/~holmes/data/WalMart/index.html">University of Minnesota</a>',
+ geo = g,
+ autosize = F,
+ width = 1000,
+ height = 900,
+ hovermode = F)
+
+subplot(p, nrows = 9)
+
+################################################################################
+# Multiple Axes, Subplots, and Insets (https://plot.ly/r/#multiple-axes-subplots-and-insets)
+################################################################################
+
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/subplots/
+# ----------------------------------------------------------------------------
+
+#' Basic subplot
+library(plotly)
+subplot(
+ plot_ly(economics, x = date, y = uempmed),
+ plot_ly(economics, x = date, y = unemploy),
+ margin = 0.05
+) %>% layout(showlegend = FALSE)
+
+#' Sharing an axis
+subplot(
+ plot_ly(economics, x = date, y = uempmed),
+ plot_ly(economics, x = date, y = unemploy),
+ margin = 0.03,
+ nrows = 2,
+ # fyi, share doesn't work (yet)
+ share = "x"
+) %>% layout(showlegend = FALSE)
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/multiple-axes/
+# ----------------------------------------------------------------------------
+
+library(plotly)
+ay <- list(
+ tickfont = list(color = "red"),
+ overlaying = "y",
+ side = "right"
+)
+plot_ly(x = 1:3, y = 10*(1:3), name = "slope of 10") %>%
+ add_trace(x = 2:4, y = 1:3, name = "slope of 1", yaxis = "y2") %>%
+ layout(title = "Double Y Axis", yaxis2 = ay)
+
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/insets/
+# ----------------------------------------------------------------------------
+
+p1 <- plot_ly(x = c(1, 2, 3), y = c(4, 3, 2))
+p2 <- plot_ly(x = c(20, 30, 40), y = c(30, 40, 50)) %>%
+ layout(xaxis = list(domain = c(0.6, 0.95)),
+ yaxis = list(domain = c(0.6, 0.95)))
+subplot(p1, p2)
+
+
+################################################################################
+# Layout Options
+################################################################################
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/setting-graph-size/
+# ----------------------------------------------------------------------------
+
+library(plotly)
+m = list(
+ l = 50,
+ r = 50,
+ b = 100,
+ t = 100,
+ pad = 4
+)
+plot_ly(x = seq(0, 8), y = seq(0, 8)) %>%
+ layout(autosize = F, width = 500, height = 500, margin = m)
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/legend/
+# ----------------------------------------------------------------------------
+
+#' Legend Names
+library(plotly)
+p <- plot_ly(x = seq(0, 8), y = rnorm(8), name = "Blue Trace") %>%
+ add_trace(y = rnorm(8), name = "Orange Trace")
+p
+
+#' Hiding the Legend
+p %>% layout(showlegend = FALSE)
+
+#' Positioning the Legend
+p %>% layout(legend = list(x = 0.5, y = 0))
+
+#' Styling the Legend
+f <- list(
+ family = "sans-serif",
+ size = 12,
+ color = "#000"
+)
+l <- list(
+ font = f,
+ bgcolor = "#E2E2E2",
+ bordercolor = "#FFFFFF",
+ borderwidth = 2
+)
+p %>% layout(legend = l)
+
+#' Hiding Legend Entries
+plot_ly(x = seq(0, 8), y = rnorm(8), showlegend = FALSE) %>%
+ add_trace(y = rnorm(8), name = "Orange Trace", showlegend = TRUE)
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/LaTeX/
+# ----------------------------------------------------------------------------
+
+library(plotly)
+plot_ly(x = c(1, 2, 3, 4), y = c(1, 4, 9, 16),
+ name = "$\\alpha_{1c} = 352 \\pm 11 \\text{ km s}^{-1}$") %>%
+ add_trace(x = c(1, 2, 3, 4), y = c(0.5, 2, 4.5, 8),
+ name = "$\\beta_{1c} = 25 \\pm 11 \\text{ km s}^{-1}$") %>%
+ layout(xaxis = list(title = "$\\sqrt{(n_\\text{c}(t|{T_\\text{early}}))}$"),
+ yaxis = list(title = "$d, r \\text{ (solar radius)}$"))
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/figure-labels/
+# ----------------------------------------------------------------------------
+
+# NOTE: title and link of this page could be improvded
+
+library(plotly)
+f <- list(
+ family = "Courier New, monospace",
+ size = 18,
+ color = "#7f7f7f"
+)
+x <- list(
+ title = "x Axis",
+ titlefont = f
+)
+y <- list(
+ title = "y Axis",
+ titlefont = f
+)
+plot_ly(x = rnorm(10), y = rnorm(10), mode = "markers") %>%
+ layout(xaxis = x, yaxis = y)
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/font/
+# ----------------------------------------------------------------------------
+
+library(plotly)
+f <- list(
+ family = "Courier New, monospace",
+ size = 18,
+ color = "#7f7f7f"
+)
+plot_ly(x = 0:8, y = 0:8) %>%
+ layout(title = "Global Font", font = f)
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/axes/
+# ----------------------------------------------------------------------------
+
+#' Style Axes Ticks and Placement
+
+library(plotly)
+a <- list(
+ autotick = FALSE,
+ ticks = "outside",
+ tick0 = 0,
+ dtick = 0.25,
+ ticklen = 5,
+ tickwidth = 2,
+ tickcolor = toRGB("blue")
+)
+s <- seq(1, 4, by = 0.25)
+plot_ly(x = s, y = s) %>%
+ layout(xaxis = a, yaxis = a)
+
+#' Style Axes Title and Ticks Labels
+
+library(plotly)
+f1 <- list(
+ family = "Arial, sans-serif",
+ size = 18,
+ color = "lightgrey"
+)
+f2 <- list(
+ family = "Old Standard TT, serif",
+ size = 14,
+ color = "black"
+)
+a <- list(
+ title = "AXIS TITLE",
+ titlefont = f1,
+ showticklabels = TRUE,
+ tickangle = 45,
+ tickfont = f2,
+ exponentformat = "e",
+ showexponent = "All"
+)
+
+s <- seq(0, 8)
+plot_ly(x = s, y = s) %>%
+ add_trace(y = rev(s)) %>%
+ layout(xaxis = a, yaxis = a, showlegend = FALSE)
+
+#' Style Axes and the Zero-Line
+
+library(plotly)
+ax <- list(
+ zeroline = TRUE,
+ showline = TRUE,
+ mirror = "ticks",
+ gridcolor = toRGB("gray50"),
+ gridwidth = 2,
+ zerolinecolor = toRGB("red"),
+ zerolinewidth = 4,
+ linecolor = toRGB("black"),
+ linewidth = 6
+)
+s <- seq(-1, 4)
+plot_ly(x = s, y = s) %>%
+ layout(xaxis = ax, yaxis = ax)
+
+#' Hide Axes Title, Lines, Ticks, and Labels
+
+library(plotly)
+ax <- list(
+ title = "",
+ zeroline = FALSE,
+ showline = FALSE,
+ showticklabels = FALSE,
+ showgrid = FALSE
+)
+
+plot_ly(x = c(1, 2), y = c(1, 2)) %>%
+ layout(xaxis = ax, yaxis = ax)
+
+#' Reversed Axes
+
+library(plotly)
+plot_ly(x = c(1, 2), y = c(1, 2)) %>%
+ layout(xaxis = list(autorange = "reversed"))
+
+#' Logarithmic Axes
+
+library(plotly)
+s <- seq(1, 8)
+plot_ly(x = s, y = exp(s), name = "exponential") %>%
+ add_trace(y = s, name = "linear") %>%
+ layout(yaxis = list(type = "log"))
+
+#' Rangemode
+
+library(plotly)
+plot_ly(x = seq(2, 6, by = 2), y = seq(-3, 3, by = 3)) %>%
+ layout(
+ xaxis = list(rangemode = "tozero"),
+ yaxis = list(rangemode = "nonnegative")
+ )
+
+#' Manual ranges
+
+library(plotly)
+s <- seq(1, 8)
+plot_ly(x = s, y = s) %>%
+ add_trace(y = rev(s)) %>%
+ layout(
+ xaxis = list(range = c(2, 5)),
+ yaxis = list(range = c(2, 5))
+ )
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/text-and-annotations/
+# ----------------------------------------------------------------------------
+
+#' Text mode
+plot_ly(mtcars, x = wt, y = mpg, text = rownames(mtcars), mode = "text")
+
+#' Styling text
+t <- list(
+ family = "sans serif",
+ size = 18,
+ color = toRGB("grey50")
+)
+plot_ly(mtcars, x = wt, y = mpg, text = rownames(mtcars), mode = "markers+text",
+ textfont = t, textposition = "top middle")
+
+#' Show custom text on hover
+plot_ly(mtcars, x = wt, y = mpg, text = rownames(mtcars), mode = "markers")
+
+#' Single Annotation
+
+m <- mtcars[which.max(mtcars$mpg), ]
+
+a <- list(
+ x = m$wt,
+ y = m$mpg,
+ text = rownames(m),
+ xref = "x",
+ yref = "y",
+ showarrow = TRUE,
+ arrowhead = 7,
+ ax = 20,
+ ay = -40
+)
+
+plot_ly(mtcars, x = wt, y = mpg, mode = "markers") %>%
+ layout(annotations = a)
+
+#' Styling and Coloring Annotations
+
+m <- mtcars[which.max(mtcars$mpg), ]
+
+f <- list(
+ family = "sans serif",
+ size = 18,
+ color = toRGB("white")
+)
+
+a <- list(
+ textposition = "top right",
+ font = f,
+ x = m$wt,
+ y = m$mpg,
+ text = rownames(m),
+ xref = "x",
+ yref = "y",
+ ax = 20,
+ ay = -40,
+ align = "center",
+ arrowhead = 2,
+ arrowsize = 1,
+ arrowwidth = 2,
+ arrowcolor = toRGB("lightblue"),
+ bordercolor = toRGB("gray50"),
+ borderwidth = 2,
+ borderpad = 4,
+ bgcolor = toRGB("blue"),
+ opacity = 0.8
+)
+
+plot_ly(mtcars, x = wt, y = mpg, mode = "markers") %>%
+ layout(annotations = a)
+
+#' Many Annotations
+
+a <- list()
+for (i in seq_len(nrow(mtcars))) {
+ m <- mtcars[i, ]
+ a[[i]] <- list(
+ x = m$wt,
+ y = m$mpg,
+ text = rownames(m),
+ xref = "x",
+ yref = "y",
+ showarrow = TRUE,
+ arrowhead = 7,
+ ax = 20,
+ ay = -40
+ )
+}
+# could be improved with label dodging, but how to measure text height/width?
+plot_ly() %>% layout(annotations = a)
+
+
+################################################################################
+# File Settings
+################################################################################
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/privacy/
+# ----------------------------------------------------------------------------
+
+library(plotly)
+
+#' public
+plot_ly(x = c(0, 2, 4), y = c(0, 4, 2))
+
+#' private
+plot_ly(x = c(0, 2, 4), y = c(0, 4, 2), world_readable = FALSE)
+
+# another option is to "build" the plot, then tack on these properties
+p <- plot_ly(x = c(0, 2, 4), y = c(0, 4, 2))
+p <- plotly_build(p)
+p$world_readable <- FALSE
+p
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/file-options/
+# ----------------------------------------------------------------------------
+
+library(plotly)
+
+# By default, everytime you print a plotly object, it will create a new file
+plot_ly(x = c(1, 2), y = c(1, 2), filename = "myPlot")
+
+# There are a couple ways to prevent a new file from being created
+# (1) Use get_figure() to obtain a figure object.
+fig <- get_figure("cpsievert", "559")
+# Then modify and print that object (this will only work if you have proper credentials)
+layout(fig, title = paste("Modified on ", Sys.time()))
+
+# (2) If you know the filename attached to the plot you want to modify,
+# place that name in filename and specify fileopt to overwrite that file
+plot_ly(x = c(1, 2), y = c(1, 2), filename = "myPlot", fileopt = "overwrite")
+
+# NOTE: filenames that contain "/" be treated as a Plotly directory and will be saved to your Plotly account in a folder tree. For example, to save your graphs to the folder my-graphs:
+
+################################################################################
+# Get Requests, Static Image Export, and Interactive Embedding (https://plot.ly/r/#get-requests-static-image-export)
+################################################################################
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/embedding-plotly-graphs-in-HTML/
+# ----------------------------------------------------------------------------
+
+# Maybe copy/paste relevant bits from this vignette? ->
+# https://github.com/ropensci/plotly/blob/carson-dsl/vignettes/intro.Rmd
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/shiny-tutorial/
+# ----------------------------------------------------------------------------
+
+# Maybe link to an updated version of this blog post?
+# http://moderndata.plot.ly/dashboards-in-r-with-shiny-plotly/
+
+# If we want, we could copy/paste source from this folder ->
+# https://github.com/ropensci/plotly/tree/carson-dsl/inst/examples
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/get-requests/
+# ----------------------------------------------------------------------------
+
+fig <- get_figure("cpsievert", "559")
+layout(fig, title = paste("Modified on ", Sys.time()))
+
+################################################################################
+# Miscellaneous
+################################################################################
+
+# ----------------------------------------------------------------------------
+# https://plot.ly/r/static-image-export/ (currently no R page)
+# ----------------------------------------------------------------------------
+
+# Use the curl package to download a static image of any publicly viewable figure
+library(curl)
+curl_download("https://plot.ly/~cpsievert/1000.png", "image.png")
+curl_download("https://plot.ly/~cpsievert/1000.pdf", "image.pdf")
+
+# you can also download the underlying SVG
+
+curl_download("https://plot.ly/~cpsievert/1000.svg", "image.svg")
diff --git a/inst/examples/rmd/flexdashboard/index.Rmd b/inst/examples/rmd/flexdashboard/index.Rmd
new file mode 100644
index 0000000..9a3ece7
--- /dev/null
+++ b/inst/examples/rmd/flexdashboard/index.Rmd
@@ -0,0 +1,88 @@
+---
+title: "Flex Dashboard"
+output:
+ flexdashboard::flex_dashboard:
+ orientation: rows
+---
+
+
+```{r setup, include=FALSE}
+library(plotly)
+library(maps)
+knitr::opts_chunk$set(message = FALSE)
+```
+
+Rows {data-height:600}
+------------------------------------------------------------------------------
+
+### Chart A
+
+```{r}
+# This example modifies code from Hadley Wickham -- https://gist.github.com/hadley/233134
+# It also uses data from Nathan Yau's flowingdata site -- http://flowingdata.com/
+unemp <- read.csv("http://datasets.flowingdata.com/unemployment09.csv")
+names(unemp) <- c("id", "state_fips", "county_fips", "name", "year",
+ "?", "?", "?", "rate")
+unemp$county <- tolower(gsub(" County, [A-Z]{2}", "", unemp$name))
+unemp$state <- gsub("^.*([A-Z]{2}).*$", "\\1", unemp$name)
+county_df <- map_data("county")
+names(county_df) <- c("long", "lat", "group", "order", "state_name", "county")
+county_df$state <- state.abb[match(county_df$state_name, tolower(state.name))]
+county_df$state_name <- NULL
+state_df <- map_data("state")
+choropleth <- merge(county_df, unemp, by = c("state", "county"))
+choropleth <- choropleth[order(choropleth$order), ]
+choropleth$rate_d <- cut(choropleth$rate, breaks = c(seq(0, 10, by = 2), 35))
+
+# provide a custom tooltip to plotly with the county name and actual rate
+choropleth$text <- with(choropleth, paste0("County: ", name, "<br>Rate: ", rate))
+p <- ggplot(choropleth, aes(long, lat, group = group)) +
+ geom_polygon(aes(fill = rate_d, text = text),
+ colour = alpha("white", 1/2), size = 0.2) +
+ geom_polygon(data = state_df, colour = "white", fill = NA) +
+ scale_fill_brewer(palette = "PuRd") + theme_void()
+# just show the text aesthetic in the tooltip
+ggplotly(p, tooltip = "text")
+```
+
+### Chart B
+
+```{r}
+crimes <- data.frame(state = tolower(rownames(USArrests)), USArrests)
+crimesm <- tidyr::gather(crimes, variable, value, -state)
+states_map <- map_data("state")
+g <- ggplot(crimesm, aes(map_id = state)) +
+ geom_map(aes(fill = value), map = states_map) +
+ expand_limits(x = states_map$long, y = states_map$lat) +
+ facet_wrap( ~ variable) + theme_void()
+ggplotly(g)
+```
+
+Rows {data-height:400}
+------------------------------------------------------------------------------
+
+
+### Chart C
+
+```{r}
+m <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
+ stat_density_2d() + xlim(0.5, 6) + ylim(40, 110)
+ggplotly(m)
+```
+
+### Chart D
+
+```{r}
+m <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
+ stat_density_2d(aes(fill = ..level..), geom = "polygon") +
+ xlim(0.5, 6) + ylim(40, 110)
+ggplotly(m)
+```
+
+
+### Chart E
+
+```{r}
+m <- ggplot(faithful, aes(x = eruptions, y = waiting)) + geom_hex()
+ggplotly(m)
+```
diff --git a/inst/examples/rmd/onRenderHover/index.Rmd b/inst/examples/rmd/onRenderHover/index.Rmd
new file mode 100644
index 0000000..493586a
--- /dev/null
+++ b/inst/examples/rmd/onRenderHover/index.Rmd
@@ -0,0 +1,50 @@
+---
+title: "Using plotly with onRender"
+author: "Carson Sievert"
+date: "`r Sys.Date()`"
+output: html_document
+---
+
+```{r, message = FALSE, warning = FALSE}
+library(plotly)
+library(htmlwidgets)
+
+set.seed(1056)
+
+nPatients <- 50
+nVisits <- 10
+
+df <- data.frame(
+ fev1_perc = rnorm(n = nPatients * nVisits, mean = 100, sd = 10),
+ uin = rep(seq(nPatients), each = nVisits),
+ visit = rep(seq(nVisits), nPatients)
+)
+c1 <- list(color = toRGB("steelblue", 0.5))
+c2 <- list(color = toRGB("orange", 0.5))
+
+# The color mapping is used only to generate multiple traces
+# (which makes it easier to highlight lines on the JS side)
+df %>%
+ plot_ly(
+ x = ~visit, y = ~fev1_perc, split = ~factor(uin), marker = c1, line = c2
+ ) %>%
+ layout(hovermode = "closest", showlegend = FALSE) %>%
+ onRender('
+ function(el, x) {
+ var graphDiv = document.getElementById(el.id);
+ // reduce the opacity of every trace except for the hover one
+ el.on("plotly_hover", function(e) {
+ var traces = [];
+ for (var i = 0; i < x.data.length; i++) {
+ if (i !== e.points[0].curveNumber) traces.push(i);
+ }
+ Plotly.restyle(graphDiv, "opacity", 0.2, traces);
+ })
+ el.on("plotly_unhover", function(e) {
+ var traces = [];
+ for (var i = 0; i < x.data.length; i++) traces.push(i);
+ Plotly.restyle(graphDiv, "opacity", 1, traces);
+ })
+ }
+ ')
+```
diff --git a/inst/examples/rmd/printing/index.Rmd b/inst/examples/rmd/printing/index.Rmd
new file mode 100644
index 0000000..e1e9f3c
--- /dev/null
+++ b/inst/examples/rmd/printing/index.Rmd
@@ -0,0 +1,85 @@
+---
+title: "Printing plotly objects in a knitr/rmarkdown doc"
+output: html_document
+---
+
+```{r, echo = FALSE}
+knitr::opts_chunk$set(
+ message = FALSE,
+ fig.width = 10,
+ fig.height = 4,
+ comment = "#>",
+ collapse = TRUE
+)
+```
+
+Printing anything created via the R package **plotly** should "just work" in a knitr/rmarkdown document -- including representations of things on your plotly account. However, the default print method that the package provides may not work for your purposes, so this document is designed to help you go beyond those defaults. This is especially useful for objects representing "remote files".
+
+## Remote files
+
+For example, if you create a plot or grid (via `api_create()`), then print the result, you get an HTML iframe pointing to that object on your account.
+
+```{r}
+library(plotly)
+d <- api_create(mtcars)
+d
+```
+
+
+
+As it turns out, the `api_create()` function returns a bunch of metadata about that file on your account. A nice way to inspect that information is to leverage the `jsonedit()` function from the **listviewer** package.
+
+```{r}
+listviewer::jsonedit(d)
+```
+
+Storing this information is a good idea since now you can [modify the "remote file"](https://api.plot.ly/v2/files#partial_update) at a later point. Let's rename the file using the "low-level" `api()` interface.
+
+```{r}
+nm <- paste(sample(LETTERS, 20), collapse = "-")
+d2 <- api(
+ file.path("files", d$fid), "PATCH", list(filename = nm)
+)
+identical(d2$filename, nm)
+```
+
+The `api_create()` function also understands how to "upload" ggplot2/plotly objects to the web platform. Printing in this case will again produce an HTML iframe pointing to the plot as it appears on the platform.
+
+
+```{r}
+p <- api_create(qplot(1:10))
+p
+```
+
+The metadata returned for a plot is structured very much like a grid (i.e., what we saw previously), but now we can leverage some attributes unique to a plot, such as image urls.
+
+```{r}
+library(htmltools)
+tags$img(src = p$image_urls$default)
+```
+
+## Downloading files
+
+You can also download a plot or a grid already hosted on plotly's web platform (assuming they're public, or you have the proper crendentials). When you download a plot, it is converted to an htmlwidget, meaning that when you print it, the plot will render entirely locally (i.e., you don't need internet access for it to render).
+
+```{r}
+p <- api_download_plot(200, "cpsievert")
+layout(
+ p, title ="An htmlwidget version of <a href='https://plot.ly/~cpsievert/200'>this</a> plot"
+)
+```
+
+There is no guarantee a plotly grid will _always_ map back to an R data frame, so `api_download_grid()` returns the abstract list representation of the data, but will _try to_ convert it to a data frame when printing.
+
+```{r}
+g <- api_download_grid(14681, "cpsievert")
+g
+```
+
+```{r}
+# note how the actual data is inside the 'preview' element
+listviewer::jsonedit(g)
+```
+
+
+
diff --git a/inst/examples/shiny/DT/app.R b/inst/examples/shiny/DT/app.R
new file mode 100644
index 0000000..ecfd72c
--- /dev/null
+++ b/inst/examples/shiny/DT/app.R
@@ -0,0 +1,39 @@
+library(plotly)
+library(DT)
+library(shiny)
+library(crosstalk)
+
+m <- mtcars[, c("mpg", "wt", "disp")] %>%
+ tibble::rownames_to_column()
+
+ui <- fluidPage(
+ plotlyOutput("plots"),
+ DT::dataTableOutput("table")
+)
+
+server <- function(input, output) {
+
+ d <- SharedData$new(m, ~rowname)
+
+ output$plots <- renderPlotly({
+ subplot(
+ qplot(data = d, x = mpg, y = wt),
+ qplot(data = d, x = mpg, y = disp),
+ titleX = T, titleY = T, margin = 0.03
+ ) %>% highlight("plotly_selected")
+ })
+
+ output$table <- DT::renderDataTable({
+ m2 <- m[d$selection(),]
+ dt <- DT::datatable(m)
+ if (NROW(m2) == 0) {
+ dt
+ } else {
+ DT::formatStyle(dt, "rowname", target = "row",
+ color = DT::styleEqual(m2$rowname, rep("white", length(m2$rowname))),
+ backgroundColor = DT::styleEqual(m2$rowname, rep("black", length(m2$rowname))))
+ }
+ })
+}
+
+shinyApp(ui = ui, server = server)
diff --git a/inst/examples/shiny/Diamonds/server.R b/inst/examples/shiny/Diamonds/server.R
new file mode 100644
index 0000000..73c61c6
--- /dev/null
+++ b/inst/examples/shiny/Diamonds/server.R
@@ -0,0 +1,27 @@
+library(shiny)
+library(plotly)
+data(diamonds, package = "ggplot2")
+
+shinyServer(function(input, output, session) {
+
+ #add reactive data information. Dataset = built in diamonds data
+ dataset <- reactive({
+ diamonds[sample(nrow(diamonds), input$sampleSize),]
+ })
+
+ output$trendPlot <- renderPlotly({
+ # build graph with ggplot syntax
+ p <- ggplot(dataset(), aes_string(x = input$x, y = input$y)) +
+ geom_point()
+
+ # if color is specified, add it as an aesthetic
+ if (input$color != 'None') p <- p + aes_string(color=input$color)
+
+ # if at least one facet column/row is specified, add it
+ facets <- paste(input$facet_row, '~', input$facet_col)
+ if (facets != '. ~ .') p <- p + facet_grid(facets)
+ # return the ggplot object and renderPlotly() will know how to handle it
+ p
+ })
+
+})
diff --git a/inst/examples/shiny/Diamonds/ui.R b/inst/examples/shiny/Diamonds/ui.R
new file mode 100644
index 0000000..d5d1887
--- /dev/null
+++ b/inst/examples/shiny/Diamonds/ui.R
@@ -0,0 +1,28 @@
+library(shiny)
+library(plotly)
+data(diamonds, package = "ggplot2")
+nms <- names(diamonds)
+
+shinyUI(pageWithSidebar(
+
+ headerPanel("Diamonds Explorer"),
+
+ sidebarPanel(
+
+ sliderInput('sampleSize', 'Sample Size', min = 1, max = nrow(diamonds),
+ value = 1000, step = 500, round = 0),
+
+ selectInput('x', 'X', choices = nms, selected = "carat"),
+ selectInput('y', 'Y', choices = nms, selected = "price"),
+ selectInput('color', 'Color', choices = c('None', nms), selected = "clarity"),
+
+ selectInput('facet_row', 'Facet Row', c(None = '.', nms), selected = "clarity"),
+ selectInput('facet_col', 'Facet Column', c(None = '.', nms)),
+ sliderInput('plotHeight', 'Height of plot (in pixels)',
+ min = 100, max = 2000, value = 1000)
+ ),
+
+ mainPanel(
+ plotlyOutput('trendPlot', height = "800px")
+ )
+))
diff --git a/inst/examples/shiny/Movies/server.R b/inst/examples/shiny/Movies/server.R
new file mode 100644
index 0000000..3faa590
--- /dev/null
+++ b/inst/examples/shiny/Movies/server.R
@@ -0,0 +1,22 @@
+library(shiny)
+library(plotly)
+library(ggplot2movies) # movies is no longer contained within ggplot2
+
+minx <- min(movies$rating)
+maxx <- max(movies$rating)
+
+shinyServer(function(input, output) {
+
+ output$trendPlot <- renderPlotly({
+ # size of the bins depend on the input 'bins'
+ size <- (maxx - minx) / input$bins
+
+ # a simple histogram of movie ratings
+ p <- plot_ly(movies, x = ~rating, autobinx = F, type = "histogram",
+ xbins = list(start = minx, end = maxx, size = size))
+ # style the xaxis
+ layout(p, xaxis = list(title = "Ratings", range = c(minx, maxx), autorange = F,
+ autotick = F, tick0 = minx, dtick = size))
+ })
+})
+
diff --git a/inst/examples/shiny/Movies/ui.R b/inst/examples/shiny/Movies/ui.R
new file mode 100644
index 0000000..92307e3
--- /dev/null
+++ b/inst/examples/shiny/Movies/ui.R
@@ -0,0 +1,12 @@
+library(shiny)
+library(plotly)
+
+shinyUI(fluidPage(
+ titlePanel("Movie Ratings!"),
+ sidebarPanel(
+ sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 10)
+ ),
+ mainPanel(
+ plotlyOutput("trendPlot")
+ )
+))
diff --git a/inst/examples/shiny/UN_Advanced/Data/UN_IdealPoints.csv b/inst/examples/shiny/UN_Advanced/Data/UN_IdealPoints.csv
new file mode 100644
index 0000000..ae29041
--- /dev/null
+++ b/inst/examples/shiny/UN_Advanced/Data/UN_IdealPoints.csv
@@ -0,0 +1,9121 @@
+Year,ID,Name,Ideal.point
+1946,USA,United States of America,1.714
+1946,CAN,Canada,1.849
+1946,CUB,Cuba,1.214
+1946,HAI,Haiti,0.627
+1946,DOM,Dominican Republic,1.554
+1946,MEX,Mexico,0.894
+1946,GUA,Guatemala,0.448
+1946,HON,Honduras,1.218
+1946,SAL,El Salvador,1.98
+1946,NIC,Nicaragua,2.086
+1946,COS,Costa Rica,2.044
+1946,PAN,Panama,1.236
+1946,COL,Colombia,0.309
+1946,VEN,Venezuela,0.651
+1946,ECU,Ecuador,0.975
+1946,PER,Peru,1.879
+1946,BRA,Brazil,1.574
+1946,BOL,Bolivia,1.376
+1946,PAR,Paraguay,2.142
+1946,CHL,Chile,0.846
+1946,ARG,Argentina,1.841
+1946,URU,Uruguay,1.468
+1946,UKG,United Kingdom,2.158
+1946,NTH,Netherlands,2.011
+1946,BEL,Belgium,1.732
+1946,LUX,Luxembourg,1.819
+1946,FRN,France,0.894
+1946,POL,Poland,-1.606
+1946,CZE,Czechoslovakia,-0.796
+1946,YUG,Yugoslavia,-2.303
+1946,GRC,Greece,2.021
+1946,RUS,Russia,-2.296
+1946,UKR,Ukraine,-2.368
+1946,BLR,Belarus,-2.324
+1946,SWD,Sweden,1.262
+1946,NOR,Norway,0.966
+1946,DEN,Denmark,1.231
+1946,ICE,Iceland,1.209
+1946,LBR,Liberia,-0.192
+1946,ETH,Ethiopia,0.441
+1946,SAF,South Africa,1.834
+1946,IRN,Iran,0.757
+1946,TUR,Turkey,1.619
+1946,IRQ,Iraq,0.703
+1946,EGY,Egypt,0.729
+1946,SYR,Syria,0.825
+1946,LEB,Lebanon,1.009
+1946,SAU,Saudi Arabia,0.756
+1946,AFG,Afghanistan,0.669
+1946,TAW,Taiwan,0.741
+1946,IND,India,-0.043
+1946,PHI,Philippines,0.93
+1946,AUL,Australia,1.527
+1946,NEW,New Zealand,1.621
+1947,USA,United States of America,1.813
+1947,CAN,Canada,1.987
+1947,CUB,Cuba,1.059
+1947,HAI,Haiti,0.448
+1947,DOM,Dominican Republic,1.722
+1947,MEX,Mexico,0.735
+1947,GUA,Guatemala,0.341
+1947,HON,Honduras,1.047
+1947,SAL,El Salvador,1.903
+1947,NIC,Nicaragua,2.202
+1947,COS,Costa Rica,2.133
+1947,PAN,Panama,1.013
+1947,COL,Colombia,0.723
+1947,VEN,Venezuela,0.834
+1947,ECU,Ecuador,1.251
+1947,PER,Peru,1.699
+1947,BRA,Brazil,1.565
+1947,BOL,Bolivia,1.439
+1947,PAR,Paraguay,2.177
+1947,CHL,Chile,1.106
+1947,ARG,Argentina,1.966
+1947,URU,Uruguay,1.472
+1947,UKG,United Kingdom,2.119
+1947,NTH,Netherlands,1.928
+1947,BEL,Belgium,1.822
+1947,LUX,Luxembourg,1.765
+1947,FRN,France,1.157
+1947,POL,Poland,-1.667
+1947,CZE,Czechoslovakia,-1.103
+1947,YUG,Yugoslavia,-2.282
+1947,GRC,Greece,2.099
+1947,RUS,Russia,-2.264
+1947,UKR,Ukraine,-2.325
+1947,BLR,Belarus,-2.279
+1947,SWD,Sweden,1.274
+1947,NOR,Norway,1.168
+1947,DEN,Denmark,1.315
+1947,ICE,Iceland,1.273
+1947,LBR,Liberia,0.293
+1947,ETH,Ethiopia,0.066
+1947,SAF,South Africa,1.96
+1947,IRN,Iran,0.517
+1947,TUR,Turkey,1.383
+1947,IRQ,Iraq,0.533
+1947,EGY,Egypt,0.507
+1947,SYR,Syria,0.484
+1947,LEB,Lebanon,0.584
+1947,SAU,Saudi Arabia,0.466
+1947,YAR,Yemen Arab Republic,0.354
+1947,AFG,Afghanistan,0.372
+1947,TAW,Taiwan,0.653
+1947,IND,India,-0.225
+1947,PAK,Pakistan,0.355
+1947,THI,Thailand,0.778
+1947,PHI,Philippines,0.711
+1947,AUL,Australia,1.738
+1947,NEW,New Zealand,1.581
+1948,USA,United States of America,1.936
+1948,CAN,Canada,1.911
+1948,CUB,Cuba,0.877
+1948,HAI,Haiti,0.66
+1948,DOM,Dominican Republic,1.639
+1948,MEX,Mexico,0.778
+1948,GUA,Guatemala,0.38
+1948,HON,Honduras,1.305
+1948,SAL,El Salvador,1.49
+1948,NIC,Nicaragua,1.98
+1948,COS,Costa Rica,1.122
+1948,PAN,Panama,1.31
+1948,COL,Colombia,1.019
+1948,VEN,Venezuela,0.669
+1948,ECU,Ecuador,1.002
+1948,PER,Peru,1.636
+1948,BRA,Brazil,1.351
+1948,BOL,Bolivia,1.474
+1948,PAR,Paraguay,1.602
+1948,CHL,Chile,1.209
+1948,ARG,Argentina,0.933
+1948,URU,Uruguay,1.031
+1948,UKG,United Kingdom,2.074
+1948,NTH,Netherlands,2.104
+1948,BEL,Belgium,1.852
+1948,LUX,Luxembourg,1.955
+1948,FRN,France,1.17
+1948,POL,Poland,-2.085
+1948,CZE,Czechoslovakia,-1.92
+1948,YUG,Yugoslavia,-2.323
+1948,GRC,Greece,1.972
+1948,RUS,Russia,-2.401
+1948,UKR,Ukraine,-2.438
+1948,BLR,Belarus,-2.394
+1948,SWD,Sweden,1.365
+1948,NOR,Norway,1.585
+1948,DEN,Denmark,1.611
+1948,ICE,Iceland,1.872
+1948,LBR,Liberia,0.743
+1948,ETH,Ethiopia,0.661
+1948,SAF,South Africa,1.443
+1948,IRN,Iran,0.424
+1948,TUR,Turkey,1.497
+1948,IRQ,Iraq,0.248
+1948,EGY,Egypt,0.684
+1948,SYR,Syria,0.322
+1948,LEB,Lebanon,0.666
+1948,ISR,Israel,0.458
+1948,SAU,Saudi Arabia,0.421
+1948,YAR,Yemen Arab Republic,0.267
+1948,AFG,Afghanistan,0.125
+1948,TAW,Taiwan,0.828
+1948,IND,India,0.239
+1948,PAK,Pakistan,0.231
+1948,MYA,Myanmar,0.212
+1948,THI,Thailand,0.905
+1948,PHI,Philippines,0.617
+1948,AUL,Australia,1.507
+1948,NEW,New Zealand,1.381
+1949,USA,United States of America,1.877
+1949,CAN,Canada,1.672
+1949,CUB,Cuba,0.8
+1949,HAI,Haiti,0.623
+1949,DOM,Dominican Republic,1.329
+1949,MEX,Mexico,0.589
+1949,GUA,Guatemala,0.453
+1949,HON,Honduras,1.175
+1949,SAL,El Salvador,1.229
+1949,NIC,Nicaragua,1.419
+1949,COS,Costa Rica,1.145
+1949,PAN,Panama,1.383
+1949,COL,Colombia,1.126
+1949,VEN,Venezuela,0.746
+1949,ECU,Ecuador,0.769
+1949,PER,Peru,1.346
+1949,BRA,Brazil,0.911
+1949,BOL,Bolivia,1.375
+1949,PAR,Paraguay,1.517
+1949,CHL,Chile,1.297
+1949,ARG,Argentina,1.028
+1949,URU,Uruguay,0.875
+1949,UKG,United Kingdom,2.039
+1949,NTH,Netherlands,2.294
+1949,BEL,Belgium,2.299
+1949,LUX,Luxembourg,2.08
+1949,FRN,France,1.504
+1949,POL,Poland,-2.279
+1949,CZE,Czechoslovakia,-2.163
+1949,YUG,Yugoslavia,-1.733
+1949,GRC,Greece,1.745
+1949,RUS,Russia,-2.501
+1949,UKR,Ukraine,-2.521
+1949,BLR,Belarus,-2.503
+1949,SWD,Sweden,1.432
+1949,NOR,Norway,1.709
+1949,DEN,Denmark,1.849
+1949,ICE,Iceland,1.7
+1949,LBR,Liberia,0.665
+1949,ETH,Ethiopia,0.499
+1949,SAF,South Africa,1.938
+1949,IRN,Iran,0.474
+1949,TUR,Turkey,1.649
+1949,IRQ,Iraq,0.26
+1949,EGY,Egypt,0.326
+1949,SYR,Syria,0.246
+1949,LEB,Lebanon,0.405
+1949,ISR,Israel,0.609
+1949,SAU,Saudi Arabia,0.22
+1949,YAR,Yemen Arab Republic,-0.001
+1949,AFG,Afghanistan,-0.05
+1949,TAW,Taiwan,0.969
+1949,IND,India,0.203
+1949,PAK,Pakistan,0.354
+1949,MYA,Myanmar,0.434
+1949,THI,Thailand,0.923
+1949,PHI,Philippines,0.53
+1949,AUL,Australia,1.504
+1949,NEW,New Zealand,1.477
+1950,USA,United States of America,1.811
+1950,CAN,Canada,1.812
+1950,CUB,Cuba,0.667
+1950,HAI,Haiti,0.837
+1950,DOM,Dominican Republic,1.462
+1950,MEX,Mexico,0.498
+1950,GUA,Guatemala,0.62
+1950,HON,Honduras,1.223
+1950,SAL,El Salvador,0.995
+1950,NIC,Nicaragua,1.38
+1950,COS,Costa Rica,1.44
+1950,PAN,Panama,1.262
+1950,COL,Colombia,1.217
+1950,VEN,Venezuela,0.95
+1950,ECU,Ecuador,0.909
+1950,PER,Peru,1.489
+1950,BRA,Brazil,1.084
+1950,BOL,Bolivia,1.409
+1950,PAR,Paraguay,1.505
+1950,CHL,Chile,1.176
+1950,ARG,Argentina,0.84
+1950,URU,Uruguay,0.63
+1950,UKG,United Kingdom,1.574
+1950,NTH,Netherlands,2.234
+1950,BEL,Belgium,2.355
+1950,LUX,Luxembourg,2.183
+1950,FRN,France,1.492
+1950,POL,Poland,-2.421
+1950,CZE,Czechoslovakia,-2.33
+1950,YUG,Yugoslavia,-1.095
+1950,GRC,Greece,1.786
+1950,RUS,Russia,-2.569
+1950,UKR,Ukraine,-2.581
+1950,BLR,Belarus,-2.553
+1950,SWD,Sweden,1.411
+1950,NOR,Norway,1.508
+1950,DEN,Denmark,1.595
+1950,ICE,Iceland,1.568
+1950,LBR,Liberia,0.556
+1950,ETH,Ethiopia,0.684
+1950,SAF,South Africa,2.17
+1950,IRN,Iran,0.41
+1950,TUR,Turkey,1.523
+1950,IRQ,Iraq,0.311
+1950,EGY,Egypt,-0.076
+1950,SYR,Syria,-0.121
+1950,LEB,Lebanon,0.251
+1950,ISR,Israel,0.623
+1950,SAU,Saudi Arabia,-0.026
+1950,YAR,Yemen Arab Republic,-0.13
+1950,AFG,Afghanistan,-0.183
+1950,TAW,Taiwan,0.984
+1950,IND,India,-0.069
+1950,PAK,Pakistan,0.303
+1950,MYA,Myanmar,0.049
+1950,THI,Thailand,1.17
+1950,PHI,Philippines,0.603
+1950,INS,Indonesia,-0.303
+1950,AUL,Australia,1.466
+1950,NEW,New Zealand,1.65
+1951,USA,United States of America,1.828
+1951,CAN,Canada,1.916
+1951,CUB,Cuba,0.745
+1951,HAI,Haiti,0.764
+1951,DOM,Dominican Republic,1.114
+1951,MEX,Mexico,0.329
+1951,GUA,Guatemala,0.317
+1951,HON,Honduras,1.184
+1951,SAL,El Salvador,0.88
+1951,NIC,Nicaragua,1.526
+1951,COS,Costa Rica,1.559
+1951,PAN,Panama,1.149
+1951,COL,Colombia,1.065
+1951,VEN,Venezuela,1.099
+1951,ECU,Ecuador,0.667
+1951,PER,Peru,1.432
+1951,BRA,Brazil,1.186
+1951,BOL,Bolivia,1.176
+1951,PAR,Paraguay,1.436
+1951,CHL,Chile,0.844
+1951,ARG,Argentina,0.848
+1951,URU,Uruguay,0.783
+1951,UKG,United Kingdom,1.832
+1951,NTH,Netherlands,2.294
+1951,BEL,Belgium,2.319
+1951,LUX,Luxembourg,2.242
+1951,FRN,France,1.652
+1951,POL,Poland,-2.43
+1951,CZE,Czechoslovakia,-2.364
+1951,YUG,Yugoslavia,-0.838
+1951,GRC,Greece,1.48
+1951,RUS,Russia,-2.56
+1951,UKR,Ukraine,-2.57
+1951,BLR,Belarus,-2.561
+1951,SWD,Sweden,1.536
+1951,NOR,Norway,1.488
+1951,DEN,Denmark,1.576
+1951,ICE,Iceland,1.479
+1951,LBR,Liberia,0.494
+1951,ETH,Ethiopia,0.433
+1951,SAF,South Africa,2.17
+1951,IRN,Iran,0.28
+1951,TUR,Turkey,1.578
+1951,IRQ,Iraq,0.289
+1951,EGY,Egypt,-0.068
+1951,SYR,Syria,-0.104
+1951,LEB,Lebanon,0.315
+1951,ISR,Israel,0.915
+1951,SAU,Saudi Arabia,-0.022
+1951,YAR,Yemen Arab Republic,-0.26
+1951,AFG,Afghanistan,-0.163
+1951,TAW,Taiwan,1.122
+1951,IND,India,0.271
+1951,PAK,Pakistan,0.211
+1951,MYA,Myanmar,-0.106
+1951,THI,Thailand,1.084
+1951,PHI,Philippines,0.52
+1951,INS,Indonesia,-0.414
+1951,AUL,Australia,1.771
+1951,NEW,New Zealand,1.869
+1952,USA,United States of America,1.905
+1952,CAN,Canada,1.989
+1952,CUB,Cuba,0.984
+1952,HAI,Haiti,0.608
+1952,DOM,Dominican Republic,1.25
+1952,MEX,Mexico,0.471
+1952,GUA,Guatemala,0.122
+1952,HON,Honduras,0.673
+1952,SAL,El Salvador,0.548
+1952,NIC,Nicaragua,1.35
+1952,COS,Costa Rica,1.02
+1952,PAN,Panama,0.958
+1952,COL,Colombia,1.195
+1952,VEN,Venezuela,1.104
+1952,ECU,Ecuador,0.866
+1952,PER,Peru,1.411
+1952,BRA,Brazil,0.986
+1952,BOL,Bolivia,0.558
+1952,PAR,Paraguay,1.321
+1952,CHL,Chile,0.654
+1952,ARG,Argentina,0.736
+1952,URU,Uruguay,0.879
+1952,UKG,United Kingdom,2.106
+1952,NTH,Netherlands,2.192
+1952,BEL,Belgium,2.259
+1952,LUX,Luxembourg,2.189
+1952,FRN,France,2.096
+1952,POL,Poland,-2.41
+1952,CZE,Czechoslovakia,-2.418
+1952,YUG,Yugoslavia,-0.05
+1952,GRC,Greece,1.443
+1952,RUS,Russia,-2.512
+1952,UKR,Ukraine,-2.475
+1952,BLR,Belarus,-2.498
+1952,SWD,Sweden,1.442
+1952,NOR,Norway,1.684
+1952,DEN,Denmark,1.681
+1952,ICE,Iceland,1.72
+1952,LBR,Liberia,0.303
+1952,ETH,Ethiopia,0.298
+1952,SAF,South Africa,2.543
+1952,IRN,Iran,-0.167
+1952,TUR,Turkey,1.635
+1952,IRQ,Iraq,0.208
+1952,EGY,Egypt,-0.259
+1952,SYR,Syria,-0.285
+1952,LEB,Lebanon,0.445
+1952,ISR,Israel,1.016
+1952,SAU,Saudi Arabia,-0.381
+1952,YAR,Yemen Arab Republic,-0.376
+1952,AFG,Afghanistan,-0.377
+1952,TAW,Taiwan,0.962
+1952,IND,India,-0.169
+1952,PAK,Pakistan,-0.04
+1952,MYA,Myanmar,-0.341
+1952,THI,Thailand,1.013
+1952,PHI,Philippines,0.515
+1952,INS,Indonesia,-0.549
+1952,AUL,Australia,2.082
+1952,NEW,New Zealand,2.126
+1953,USA,United States of America,1.693
+1953,CAN,Canada,1.745
+1953,CUB,Cuba,1.044
+1953,HAI,Haiti,0.819
+1953,DOM,Dominican Republic,1.528
+1953,MEX,Mexico,0.319
+1953,GUA,Guatemala,0.091
+1953,HON,Honduras,0.969
+1953,SAL,El Salvador,0.747
+1953,NIC,Nicaragua,1.303
+1953,COS,Costa Rica,1.086
+1953,PAN,Panama,1.333
+1953,COL,Colombia,1.673
+1953,VEN,Venezuela,1.068
+1953,ECU,Ecuador,1.018
+1953,PER,Peru,1.326
+1953,BRA,Brazil,1.015
+1953,BOL,Bolivia,0.404
+1953,PAR,Paraguay,1.314
+1953,CHL,Chile,0.952
+1953,ARG,Argentina,0.852
+1953,URU,Uruguay,0.586
+1953,UKG,United Kingdom,2.193
+1953,NTH,Netherlands,2.197
+1953,BEL,Belgium,2.359
+1953,LUX,Luxembourg,2.324
+1953,FRN,France,2.201
+1953,POL,Poland,-2.393
+1953,CZE,Czechoslovakia,-2.407
+1953,YUG,Yugoslavia,-0.138
+1953,GRC,Greece,1.372
+1953,RUS,Russia,-2.496
+1953,UKR,Ukraine,-2.467
+1953,BLR,Belarus,-2.489
+1953,SWD,Sweden,1.26
+1953,NOR,Norway,1.304
+1953,DEN,Denmark,1.342
+1953,ICE,Iceland,1.148
+1953,LBR,Liberia,0.246
+1953,ETH,Ethiopia,0.281
+1953,SAF,South Africa,2.65
+1953,IRN,Iran,-0.12
+1953,TUR,Turkey,1.607
+1953,IRQ,Iraq,0.033
+1953,EGY,Egypt,-0.32
+1953,SYR,Syria,-0.326
+1953,LEB,Lebanon,0.258
+1953,ISR,Israel,1.104
+1953,SAU,Saudi Arabia,-0.381
+1953,YAR,Yemen Arab Republic,-0.388
+1953,AFG,Afghanistan,-0.386
+1953,TAW,Taiwan,0.818
+1953,IND,India,-0.147
+1953,PAK,Pakistan,-0.096
+1953,MYA,Myanmar,-0.43
+1953,THI,Thailand,0.661
+1953,PHI,Philippines,0.374
+1953,INS,Indonesia,-0.482
+1953,AUL,Australia,2.203
+1953,NEW,New Zealand,2.071
+1954,USA,United States of America,1.482
+1954,CAN,Canada,1.746
+1954,CUB,Cuba,1.019
+1954,HAI,Haiti,0.737
+1954,DOM,Dominican Republic,1.477
+1954,MEX,Mexico,0.306
+1954,GUA,Guatemala,0.455
+1954,HON,Honduras,0.62
+1954,SAL,El Salvador,0.443
+1954,NIC,Nicaragua,1.082
+1954,COS,Costa Rica,0.755
+1954,PAN,Panama,1.166
+1954,COL,Colombia,1.773
+1954,VEN,Venezuela,0.974
+1954,ECU,Ecuador,0.554
+1954,PER,Peru,1.351
+1954,BRA,Brazil,1.378
+1954,BOL,Bolivia,0.337
+1954,PAR,Paraguay,0.864
+1954,CHL,Chile,0.906
+1954,ARG,Argentina,0.829
+1954,URU,Uruguay,0.467
+1954,UKG,United Kingdom,2.391
+1954,NTH,Netherlands,2.225
+1954,BEL,Belgium,2.53
+1954,LUX,Luxembourg,2.386
+1954,FRN,France,2.402
+1954,POL,Poland,-2.427
+1954,CZE,Czechoslovakia,-2.424
+1954,YUG,Yugoslavia,-0.295
+1954,GRC,Greece,0.744
+1954,RUS,Russia,-2.498
+1954,UKR,Ukraine,-2.47
+1954,BLR,Belarus,-2.479
+1954,SWD,Sweden,1.443
+1954,NOR,Norway,1.364
+1954,DEN,Denmark,1.522
+1954,ICE,Iceland,1.032
+1954,LBR,Liberia,0.242
+1954,ETH,Ethiopia,0.385
+1954,SAF,South Africa,2.525
+1954,IRN,Iran,0.076
+1954,TUR,Turkey,1.782
+1954,IRQ,Iraq,0.128
+1954,EGY,Egypt,-0.169
+1954,SYR,Syria,-0.402
+1954,LEB,Lebanon,0.302
+1954,ISR,Israel,1.171
+1954,SAU,Saudi Arabia,-0.382
+1954,YAR,Yemen Arab Republic,-0.427
+1954,AFG,Afghanistan,-0.331
+1954,TAW,Taiwan,1.122
+1954,IND,India,-0.288
+1954,PAK,Pakistan,0.198
+1954,MYA,Myanmar,-0.581
+1954,THI,Thailand,0.555
+1954,PHI,Philippines,0.305
+1954,INS,Indonesia,-0.467
+1954,AUL,Australia,2.414
+1954,NEW,New Zealand,2.102
+1955,USA,United States of America,1.719
+1955,CAN,Canada,1.714
+1955,CUB,Cuba,1.439
+1955,HAI,Haiti,0.682
+1955,DOM,Dominican Republic,1.53
+1955,MEX,Mexico,0.352
+1955,GUA,Guatemala,0.319
+1955,HON,Honduras,0.968
+1955,SAL,El Salvador,0.4
+1955,NIC,Nicaragua,1.471
+1955,COS,Costa Rica,0.637
+1955,PAN,Panama,1.212
+1955,COL,Colombia,1.727
+1955,VEN,Venezuela,1.069
+1955,ECU,Ecuador,0.559
+1955,PER,Peru,1.284
+1955,BRA,Brazil,1.568
+1955,BOL,Bolivia,0.344
+1955,PAR,Paraguay,0.862
+1955,CHL,Chile,0.946
+1955,ARG,Argentina,0.645
+1955,URU,Uruguay,0.303
+1955,UKG,United Kingdom,2.209
+1955,NTH,Netherlands,2.189
+1955,BEL,Belgium,2.219
+1955,LUX,Luxembourg,2.325
+1955,FRN,France,2.392
+1955,SPN,Spain,1.121
+1955,POL,Poland,-2.458
+1955,HUN,Hungary,-2.653
+1955,CZE,Czechoslovakia,-2.474
+1955,YUG,Yugoslavia,-0.495
+1955,GRC,Greece,0.727
+1955,ROM,Romania,-2.65
+1955,RUS,Russia,-2.536
+1955,UKR,Ukraine,-2.506
+1955,BLR,Belarus,-2.512
+1955,SWD,Sweden,1.31
+1955,NOR,Norway,1.319
+1955,DEN,Denmark,1.387
+1955,ICE,Iceland,1.052
+1955,LBR,Liberia,0.23
+1955,ETH,Ethiopia,0.382
+1955,SAF,South Africa,2.506
+1955,IRN,Iran,0.106
+1955,TUR,Turkey,1.596
+1955,IRQ,Iraq,0.22
+1955,EGY,Egypt,-0.283
+1955,SYR,Syria,-0.596
+1955,LEB,Lebanon,0.154
+1955,JOR,Jordan,-0.789
+1955,ISR,Israel,1.326
+1955,SAU,Saudi Arabia,-0.475
+1955,YAR,Yemen Arab Republic,-0.557
+1955,AFG,Afghanistan,-0.551
+1955,TAW,Taiwan,1.266
+1955,IND,India,-0.397
+1955,PAK,Pakistan,0.361
+1955,MYA,Myanmar,-0.467
+1955,SRI,Sri Lanka,-0.178
+1955,THI,Thailand,0.614
+1955,PHI,Philippines,0.507
+1955,INS,Indonesia,-0.387
+1955,AUL,Australia,2.249
+1955,NEW,New Zealand,1.941
+1956,USA,United States of America,1.285
+1956,CAN,Canada,1.554
+1956,CUB,Cuba,1.264
+1956,HAI,Haiti,0.6
+1956,DOM,Dominican Republic,1.554
+1956,MEX,Mexico,0.63
+1956,GUA,Guatemala,0.546
+1956,HON,Honduras,1.041
+1956,SAL,El Salvador,0.608
+1956,NIC,Nicaragua,1.187
+1956,COS,Costa Rica,0.869
+1956,PAN,Panama,0.97
+1956,COL,Colombia,1.313
+1956,VEN,Venezuela,0.968
+1956,ECU,Ecuador,0.802
+1956,PER,Peru,1.283
+1956,BRA,Brazil,1.331
+1956,BOL,Bolivia,0.596
+1956,PAR,Paraguay,0.948
+1956,CHL,Chile,1.05
+1956,ARG,Argentina,0.846
+1956,URU,Uruguay,0.726
+1956,UKG,United Kingdom,2.247
+1956,IRE,Ireland,1.119
+1956,NTH,Netherlands,2.05
+1956,BEL,Belgium,2.044
+1956,LUX,Luxembourg,2.128
+1956,FRN,France,2.447
+1956,SPN,Spain,1.129
+1956,POR,Portugal,1.65
+1956,POL,Poland,-2.474
+1956,AUS,Austria,0.57
+1956,HUN,Hungary,-2.654
+1956,CZE,Czechoslovakia,-2.523
+1956,ITA,Italy,1.775
+1956,ALB,Albania,-2.582
+1956,YUG,Yugoslavia,-1.074
+1956,GRC,Greece,0.617
+1956,BUL,Bulgaria,-2.575
+1956,ROM,Romania,-2.655
+1956,RUS,Russia,-2.558
+1956,UKR,Ukraine,-2.562
+1956,BLR,Belarus,-2.541
+1956,FIN,Finland,0.336
+1956,SWD,Sweden,1.189
+1956,NOR,Norway,1.185
+1956,DEN,Denmark,1.328
+1956,ICE,Iceland,1.31
+1956,LBR,Liberia,0.501
+1956,GHA,Ghana,-0.605
+1956,ETH,Ethiopia,0.354
+1956,SAF,South Africa,1.986
+1956,MOR,Morocco,-0.528
+1956,TUN,Tunisia,0.049
+1956,LIB,Libya,-0.317
+1956,SUD,Sudan,-0.722
+1956,IRN,Iran,0.421
+1956,TUR,Turkey,1.382
+1956,IRQ,Iraq,0.338
+1956,EGY,Egypt,-0.761
+1956,SYR,Syria,-0.901
+1956,LEB,Lebanon,-0.196
+1956,JOR,Jordan,-0.885
+1956,ISR,Israel,1.593
+1956,SAU,Saudi Arabia,-0.667
+1956,YAR,Yemen Arab Republic,-0.736
+1956,AFG,Afghanistan,-0.577
+1956,TAW,Taiwan,1.365
+1956,JPN,Japan,0.699
+1956,IND,India,-0.65
+1956,PAK,Pakistan,0.706
+1956,MYA,Myanmar,-0.216
+1956,SRI,Sri Lanka,-0.209
+1956,NEP,Nepal,-0.064
+1956,THI,Thailand,0.823
+1956,CAM,Cambodia,0.126
+1956,LAO,Laos,0.784
+1956,PHI,Philippines,0.769
+1956,INS,Indonesia,-0.526
+1956,AUL,Australia,2.222
+1956,NEW,New Zealand,2.058
+1957,USA,United States of America,1.247
+1957,CAN,Canada,1.608
+1957,CUB,Cuba,1.234
+1957,HAI,Haiti,0.399
+1957,DOM,Dominican Republic,1.728
+1957,MEX,Mexico,0.338
+1957,GUA,Guatemala,0.215
+1957,HON,Honduras,1.19
+1957,SAL,El Salvador,0.65
+1957,NIC,Nicaragua,1.428
+1957,COS,Costa Rica,0.607
+1957,PAN,Panama,0.746
+1957,COL,Colombia,1.244
+1957,VEN,Venezuela,1.006
+1957,ECU,Ecuador,0.79
+1957,PER,Peru,1.102
+1957,BRA,Brazil,1.139
+1957,BOL,Bolivia,0.39
+1957,PAR,Paraguay,1.039
+1957,CHL,Chile,1.187
+1957,ARG,Argentina,1.237
+1957,URU,Uruguay,0.667
+1957,UKG,United Kingdom,2.225
+1957,IRE,Ireland,0.736
+1957,NTH,Netherlands,2.038
+1957,BEL,Belgium,2.162
+1957,LUX,Luxembourg,2.185
+1957,FRN,France,2.386
+1957,SPN,Spain,1.397
+1957,POR,Portugal,1.697
+1957,POL,Poland,-2.503
+1957,AUS,Austria,0.921
+1957,HUN,Hungary,-2.634
+1957,CZE,Czechoslovakia,-2.488
+1957,ITA,Italy,1.846
+1957,ALB,Albania,-2.579
+1957,YUG,Yugoslavia,-0.966
+1957,GRC,Greece,0.394
+1957,BUL,Bulgaria,-2.581
+1957,ROM,Romania,-2.64
+1957,RUS,Russia,-2.557
+1957,UKR,Ukraine,-2.582
+1957,BLR,Belarus,-2.573
+1957,FIN,Finland,0.699
+1957,SWD,Sweden,1.117
+1957,NOR,Norway,1.18
+1957,DEN,Denmark,1.229
+1957,ICE,Iceland,1.304
+1957,LBR,Liberia,0.621
+1957,GHA,Ghana,-0.606
+1957,ETH,Ethiopia,0.132
+1957,SAF,South Africa,2.067
+1957,MOR,Morocco,-0.634
+1957,TUN,Tunisia,0.081
+1957,LIB,Libya,-0.165
+1957,SUD,Sudan,-0.71
+1957,IRN,Iran,0.428
+1957,TUR,Turkey,1.418
+1957,IRQ,Iraq,0.069
+1957,EGY,Egypt,-0.849
+1957,SYR,Syria,-0.954
+1957,LEB,Lebanon,0.102
+1957,JOR,Jordan,-0.444
+1957,ISR,Israel,1.18
+1957,SAU,Saudi Arabia,-0.613
+1957,YAR,Yemen Arab Republic,-0.736
+1957,AFG,Afghanistan,-0.403
+1957,TAW,Taiwan,1.428
+1957,JPN,Japan,0.634
+1957,IND,India,-0.544
+1957,PAK,Pakistan,0.739
+1957,MYA,Myanmar,-0.4
+1957,SRI,Sri Lanka,-0.322
+1957,NEP,Nepal,-0.131
+1957,THI,Thailand,0.777
+1957,CAM,Cambodia,0.134
+1957,LAO,Laos,0.74
+1957,MAL,Malaysia,0.518
+1957,PHI,Philippines,0.879
+1957,INS,Indonesia,-0.583
+1957,AUL,Australia,2.215
+1957,NEW,New Zealand,1.999
+1958,USA,United States of America,1.266
+1958,CAN,Canada,1.579
+1958,CUB,Cuba,1.243
+1958,HAI,Haiti,0.762
+1958,DOM,Dominican Republic,1.846
+1958,MEX,Mexico,0.453
+1958,GUA,Guatemala,0.621
+1958,HON,Honduras,1.04
+1958,SAL,El Salvador,0.844
+1958,NIC,Nicaragua,1.385
+1958,COS,Costa Rica,0.881
+1958,PAN,Panama,0.896
+1958,COL,Colombia,1.149
+1958,VEN,Venezuela,0.972
+1958,ECU,Ecuador,1.01
+1958,PER,Peru,1.062
+1958,BRA,Brazil,1.209
+1958,BOL,Bolivia,0.657
+1958,PAR,Paraguay,1.134
+1958,CHL,Chile,1.122
+1958,ARG,Argentina,1.196
+1958,URU,Uruguay,0.897
+1958,UKG,United Kingdom,2.389
+1958,IRE,Ireland,0.456
+1958,NTH,Netherlands,1.873
+1958,BEL,Belgium,2.41
+1958,LUX,Luxembourg,2.107
+1958,FRN,France,2.327
+1958,SPN,Spain,1.715
+1958,POR,Portugal,1.688
+1958,POL,Poland,-2.536
+1958,AUS,Austria,0.691
+1958,HUN,Hungary,-2.626
+1958,CZE,Czechoslovakia,-2.522
+1958,ITA,Italy,1.744
+1958,ALB,Albania,-2.598
+1958,YUG,Yugoslavia,-1.041
+1958,GRC,Greece,0.344
+1958,BUL,Bulgaria,-2.59
+1958,ROM,Romania,-2.618
+1958,RUS,Russia,-2.574
+1958,UKR,Ukraine,-2.593
+1958,BLR,Belarus,-2.484
+1958,FIN,Finland,0.504
+1958,SWD,Sweden,0.767
+1958,NOR,Norway,0.852
+1958,DEN,Denmark,0.879
+1958,ICE,Iceland,0.893
+1958,GUI,Guinea,-0.895
+1958,LBR,Liberia,0.451
+1958,GHA,Ghana,-0.613
+1958,ETH,Ethiopia,0.091
+1958,SAF,South Africa,2.06
+1958,MOR,Morocco,-0.692
+1958,TUN,Tunisia,0.184
+1958,LIB,Libya,-0.362
+1958,SUD,Sudan,-0.572
+1958,IRN,Iran,0.614
+1958,TUR,Turkey,1.29
+1958,IRQ,Iraq,-0.436
+1958,EGY,Egypt,-0.878
+1958,LEB,Lebanon,0.011
+1958,JOR,Jordan,0.048
+1958,ISR,Israel,0.966
+1958,SAU,Saudi Arabia,-0.516
+1958,YAR,Yemen Arab Republic,-0.827
+1958,AFG,Afghanistan,-0.718
+1958,TAW,Taiwan,1.253
+1958,JPN,Japan,0.71
+1958,IND,India,-0.552
+1958,PAK,Pakistan,0.811
+1958,MYA,Myanmar,-0.355
+1958,SRI,Sri Lanka,-0.447
+1958,NEP,Nepal,-0.194
+1958,THI,Thailand,0.941
+1958,CAM,Cambodia,-0.044
+1958,LAO,Laos,0.804
+1958,MAL,Malaysia,0.51
+1958,PHI,Philippines,0.907
+1958,INS,Indonesia,-0.644
+1958,AUL,Australia,2.324
+1958,NEW,New Zealand,1.502
+1959,USA,United States of America,1.547
+1959,CAN,Canada,1.096
+1959,CUB,Cuba,0.243
+1959,HAI,Haiti,0.979
+1959,DOM,Dominican Republic,1.957
+1959,MEX,Mexico,0.418
+1959,GUA,Guatemala,0.998
+1959,HON,Honduras,1.604
+1959,SAL,El Salvador,0.939
+1959,NIC,Nicaragua,1.661
+1959,COS,Costa Rica,0.932
+1959,PAN,Panama,0.709
+1959,COL,Colombia,1.402
+1959,VEN,Venezuela,0.491
+1959,ECU,Ecuador,0.916
+1959,PER,Peru,1.777
+1959,BRA,Brazil,1.517
+1959,BOL,Bolivia,0.944
+1959,PAR,Paraguay,1.223
+1959,CHL,Chile,1.307
+1959,ARG,Argentina,1.048
+1959,URU,Uruguay,1.234
+1959,UKG,United Kingdom,2.067
+1959,IRE,Ireland,0.384
+1959,NTH,Netherlands,1.766
+1959,BEL,Belgium,2
+1959,LUX,Luxembourg,1.989
+1959,FRN,France,2.114
+1959,SPN,Spain,1.626
+1959,POR,Portugal,1.621
+1959,POL,Poland,-2.443
+1959,AUS,Austria,0.948
+1959,HUN,Hungary,-2.607
+1959,CZE,Czechoslovakia,-2.552
+1959,ITA,Italy,1.768
+1959,ALB,Albania,-2.599
+1959,YUG,Yugoslavia,-1.302
+1959,GRC,Greece,0.906
+1959,BUL,Bulgaria,-2.59
+1959,ROM,Romania,-2.62
+1959,RUS,Russia,-2.578
+1959,UKR,Ukraine,-2.597
+1959,BLR,Belarus,-2.519
+1959,FIN,Finland,0.403
+1959,SWD,Sweden,0.489
+1959,NOR,Norway,0.768
+1959,DEN,Denmark,0.819
+1959,ICE,Iceland,0.574
+1959,GUI,Guinea,-0.89
+1959,LBR,Liberia,0.399
+1959,GHA,Ghana,-0.502
+1959,ETH,Ethiopia,-0.054
+1959,SAF,South Africa,2.239
+1959,MOR,Morocco,-0.902
+1959,TUN,Tunisia,-0.034
+1959,LIB,Libya,-0.382
+1959,SUD,Sudan,-0.497
+1959,IRN,Iran,0.478
+1959,TUR,Turkey,1.063
+1959,IRQ,Iraq,-0.815
+1959,EGY,Egypt,-0.762
+1959,LEB,Lebanon,0.032
+1959,JOR,Jordan,0.139
+1959,ISR,Israel,1.188
+1959,SAU,Saudi Arabia,-0.277
+1959,YAR,Yemen Arab Republic,-0.739
+1959,AFG,Afghanistan,-0.686
+1959,TAW,Taiwan,1.132
+1959,JPN,Japan,0.667
+1959,IND,India,-0.587
+1959,PAK,Pakistan,0.479
+1959,MYA,Myanmar,-0.323
+1959,SRI,Sri Lanka,-0.262
+1959,NEP,Nepal,-0.535
+1959,THI,Thailand,1.061
+1959,CAM,Cambodia,0.176
+1959,LAO,Laos,1.157
+1959,MAL,Malaysia,0.391
+1959,PHI,Philippines,0.631
+1959,INS,Indonesia,-0.655
+1959,AUL,Australia,1.638
+1959,NEW,New Zealand,1.126
+1960,USA,United States of America,1.532
+1960,CAN,Canada,1.106
+1960,CUB,Cuba,-1.264
+1960,HAI,Haiti,0.838
+1960,DOM,Dominican Republic,0.817
+1960,MEX,Mexico,0.278
+1960,GUA,Guatemala,1.027
+1960,HON,Honduras,1.11
+1960,SAL,El Salvador,1.054
+1960,NIC,Nicaragua,1.39
+1960,COS,Costa Rica,1.085
+1960,PAN,Panama,1.01
+1960,COL,Colombia,1.331
+1960,VEN,Venezuela,0.346
+1960,ECU,Ecuador,0.531
+1960,PER,Peru,1.164
+1960,BRA,Brazil,1.044
+1960,BOL,Bolivia,0.807
+1960,PAR,Paraguay,1.011
+1960,CHL,Chile,1.059
+1960,ARG,Argentina,1.08
+1960,URU,Uruguay,0.972
+1960,UKG,United Kingdom,1.899
+1960,IRE,Ireland,0.575
+1960,NTH,Netherlands,1.768
+1960,BEL,Belgium,1.953
+1960,LUX,Luxembourg,1.864
+1960,FRN,France,1.792
+1960,SPN,Spain,1.664
+1960,POR,Portugal,1.515
+1960,POL,Poland,-2.348
+1960,AUS,Austria,0.775
+1960,HUN,Hungary,-2.432
+1960,CZE,Czechoslovakia,-2.412
+1960,ITA,Italy,1.406
+1960,ALB,Albania,-2.344
+1960,YUG,Yugoslavia,-1.311
+1960,GRC,Greece,1.219
+1960,CYP,Cyprus,0.39
+1960,BUL,Bulgaria,-2.413
+1960,ROM,Romania,-2.408
+1960,RUS,Russia,-2.414
+1960,UKR,Ukraine,-2.457
+1960,BLR,Belarus,-2.448
+1960,FIN,Finland,0.372
+1960,SWD,Sweden,0.642
+1960,NOR,Norway,0.904
+1960,DEN,Denmark,0.908
+1960,ICE,Iceland,0.996
+1960,MLI,Mali,-1.511
+1960,SEN,Senegal,0.29
+1960,BEN,Benin,0.589
+1960,NIR,Niger,0.586
+1960,CDI,Ivory Coast,0.573
+1960,GUI,Guinea,-1.471
+1960,BFO,Burkina Faso,0.307
+1960,LBR,Liberia,0.096
+1960,GHA,Ghana,-0.84
+1960,TOG,Togo,-0.185
+1960,CAO,Cameroon,0.562
+1960,NIG,Nigeria,-0.349
+1960,GAB,Gabon,0.604
+1960,CEN,Central African Republic,0.416
+1960,CHA,Chad,0.353
+1960,CON,Congo,0.505
+1960,DRC,Democratic Republic of the Congo,0.569
+1960,SOM,Somalia,-0.058
+1960,ETH,Ethiopia,-0.543
+1960,SAF,South Africa,1.884
+1960,MAG,Madagascar,0.581
+1960,MOR,Morocco,-1.254
+1960,TUN,Tunisia,-0.073
+1960,LIB,Libya,-0.539
+1960,SUD,Sudan,-0.657
+1960,IRN,Iran,0.393
+1960,TUR,Turkey,1.133
+1960,IRQ,Iraq,-1.022
+1960,EGY,Egypt,-1.053
+1960,LEB,Lebanon,-0.222
+1960,JOR,Jordan,-0.052
+1960,ISR,Israel,0.827
+1960,SAU,Saudi Arabia,-0.655
+1960,YAR,Yemen Arab Republic,-1.002
+1960,AFG,Afghanistan,-0.888
+1960,TAW,Taiwan,1.199
+1960,JPN,Japan,1.144
+1960,IND,India,-0.791
+1960,PAK,Pakistan,0.472
+1960,MYA,Myanmar,-0.334
+1960,SRI,Sri Lanka,-0.624
+1960,NEP,Nepal,-0.372
+1960,THI,Thailand,1.002
+1960,CAM,Cambodia,-0.056
+1960,LAO,Laos,0.55
+1960,MAL,Malaysia,0.226
+1960,PHI,Philippines,0.914
+1960,INS,Indonesia,-0.964
+1960,AUL,Australia,1.888
+1960,NEW,New Zealand,1.347
+1961,USA,United States of America,1.698
+1961,CAN,Canada,1.467
+1961,CUB,Cuba,-1.731
+1961,HAI,Haiti,0.647
+1961,DOM,Dominican Republic,1.23
+1961,MEX,Mexico,0.598
+1961,GUA,Guatemala,1.118
+1961,HON,Honduras,1.017
+1961,SAL,El Salvador,1.085
+1961,NIC,Nicaragua,1.584
+1961,COS,Costa Rica,1.147
+1961,PAN,Panama,1.11
+1961,COL,Colombia,1.085
+1961,VEN,Venezuela,0.73
+1961,ECU,Ecuador,0.785
+1961,PER,Peru,1.191
+1961,BRA,Brazil,0.986
+1961,BOL,Bolivia,0.834
+1961,PAR,Paraguay,1.183
+1961,CHL,Chile,0.967
+1961,ARG,Argentina,1.069
+1961,URU,Uruguay,0.917
+1961,UKG,United Kingdom,1.844
+1961,IRE,Ireland,1.213
+1961,NTH,Netherlands,1.707
+1961,BEL,Belgium,1.747
+1961,LUX,Luxembourg,1.862
+1961,FRN,France,1.436
+1961,SPN,Spain,1.628
+1961,POR,Portugal,1.562
+1961,POL,Poland,-2.331
+1961,AUS,Austria,0.928
+1961,HUN,Hungary,-2.37
+1961,CZE,Czechoslovakia,-2.369
+1961,ITA,Italy,1.587
+1961,ALB,Albania,-2.305
+1961,YUG,Yugoslavia,-1.272
+1961,GRC,Greece,1.454
+1961,CYP,Cyprus,0.196
+1961,BUL,Bulgaria,-2.362
+1961,ROM,Romania,-2.371
+1961,RUS,Russia,-2.371
+1961,UKR,Ukraine,-2.384
+1961,BLR,Belarus,-2.379
+1961,FIN,Finland,0.663
+1961,SWD,Sweden,0.912
+1961,NOR,Norway,1.056
+1961,DEN,Denmark,1.142
+1961,ICE,Iceland,1.084
+1961,MLI,Mali,-1.433
+1961,SEN,Senegal,-0.093
+1961,BEN,Benin,0.256
+1961,MAA,Mauritania,-0.074
+1961,NIR,Niger,0.305
+1961,CDI,Ivory Coast,0.201
+1961,GUI,Guinea,-1.368
+1961,BFO,Burkina Faso,0
+1961,LBR,Liberia,0.156
+1961,SIE,Sierra Leone,-0.221
+1961,GHA,Ghana,-0.849
+1961,TOG,Togo,-0.045
+1961,CAO,Cameroon,0.25
+1961,NIG,Nigeria,-0.222
+1961,GAB,Gabon,0.254
+1961,CEN,Central African Republic,0.178
+1961,CHA,Chad,0.075
+1961,CON,Congo,0.168
+1961,DRC,Democratic Republic of the Congo,-0.11
+1961,TAZ,Tanzania,-0.688
+1961,SOM,Somalia,-0.355
+1961,ETH,Ethiopia,-0.641
+1961,SAF,South Africa,1.768
+1961,MAG,Madagascar,0.238
+1961,MOR,Morocco,-1.041
+1961,TUN,Tunisia,-0.167
+1961,LIB,Libya,-0.456
+1961,SUD,Sudan,-0.486
+1961,IRN,Iran,0.545
+1961,TUR,Turkey,1.219
+1961,IRQ,Iraq,-1.172
+1961,EGY,Egypt,-1.119
+1961,SYR,Syria,-0.71
+1961,LEB,Lebanon,-0.121
+1961,JOR,Jordan,-0.191
+1961,ISR,Israel,0.768
+1961,SAU,Saudi Arabia,-0.574
+1961,YAR,Yemen Arab Republic,-0.85
+1961,AFG,Afghanistan,-0.746
+1961,MON,Mongolia,-2.043
+1961,TAW,Taiwan,1.245
+1961,JPN,Japan,0.92
+1961,IND,India,-0.615
+1961,PAK,Pakistan,0.295
+1961,MYA,Myanmar,-0.395
+1961,SRI,Sri Lanka,-0.638
+1961,NEP,Nepal,-0.423
+1961,THI,Thailand,0.785
+1961,CAM,Cambodia,-0.365
+1961,LAO,Laos,0.438
+1961,MAL,Malaysia,0.479
+1961,PHI,Philippines,0.713
+1961,INS,Indonesia,-1.123
+1961,AUL,Australia,1.798
+1961,NEW,New Zealand,1.48
+1962,USA,United States of America,1.889
+1962,CAN,Canada,1.555
+1962,CUB,Cuba,-1.844
+1962,HAI,Haiti,0.459
+1962,DOM,Dominican Republic,1.226
+1962,JAM,Jamaica,0.535
+1962,TRI,Trinidad and Tobago,0.513
+1962,MEX,Mexico,0.786
+1962,GUA,Guatemala,0.982
+1962,HON,Honduras,1.032
+1962,SAL,El Salvador,1.115
+1962,NIC,Nicaragua,1.451
+1962,COS,Costa Rica,0.978
+1962,PAN,Panama,0.797
+1962,COL,Colombia,1.017
+1962,VEN,Venezuela,0.797
+1962,ECU,Ecuador,0.775
+1962,PER,Peru,1.135
+1962,BRA,Brazil,0.891
+1962,BOL,Bolivia,0.83
+1962,PAR,Paraguay,1.101
+1962,CHL,Chile,0.826
+1962,ARG,Argentina,0.962
+1962,URU,Uruguay,0.862
+1962,UKG,United Kingdom,2.002
+1962,IRE,Ireland,1.343
+1962,NTH,Netherlands,1.773
+1962,BEL,Belgium,1.89
+1962,LUX,Luxembourg,1.886
+1962,FRN,France,1.543
+1962,SPN,Spain,1.737
+1962,POR,Portugal,1.73
+1962,POL,Poland,-2.32
+1962,AUS,Austria,1.179
+1962,HUN,Hungary,-2.345
+1962,CZE,Czechoslovakia,-2.322
+1962,ITA,Italy,1.633
+1962,ALB,Albania,-2.302
+1962,YUG,Yugoslavia,-1.385
+1962,GRC,Greece,1.361
+1962,CYP,Cyprus,0.322
+1962,BUL,Bulgaria,-2.35
+1962,ROM,Romania,-2.346
+1962,RUS,Russia,-2.343
+1962,UKR,Ukraine,-2.365
+1962,BLR,Belarus,-2.348
+1962,FIN,Finland,0.817
+1962,SWD,Sweden,1.104
+1962,NOR,Norway,1.252
+1962,DEN,Denmark,1.282
+1962,ICE,Iceland,1.307
+1962,MLI,Mali,-1.219
+1962,SEN,Senegal,-0.353
+1962,BEN,Benin,-0.058
+1962,MAA,Mauritania,-0.497
+1962,NIR,Niger,-0.2
+1962,CDI,Ivory Coast,0.024
+1962,GUI,Guinea,-1.36
+1962,BFO,Burkina Faso,-0.18
+1962,LBR,Liberia,0.103
+1962,SIE,Sierra Leone,-0.205
+1962,GHA,Ghana,-0.614
+1962,TOG,Togo,-0.169
+1962,CAO,Cameroon,0.127
+1962,NIG,Nigeria,-0.263
+1962,GAB,Gabon,0.242
+1962,CEN,Central African Republic,-0.034
+1962,CHA,Chad,-0.15
+1962,CON,Congo,-0.017
+1962,DRC,Democratic Republic of the Congo,-0.131
+1962,UGA,Uganda,-0.37
+1962,TAZ,Tanzania,-0.736
+1962,BUI,Burundi,-0.325
+1962,RWA,Rwanda,0.24
+1962,SOM,Somalia,-0.595
+1962,ETH,Ethiopia,-0.352
+1962,SAF,South Africa,1.998
+1962,MAG,Madagascar,0.028
+1962,MOR,Morocco,-0.895
+1962,ALG,Algeria,-0.96
+1962,TUN,Tunisia,-0.28
+1962,LIB,Libya,-0.508
+1962,SUD,Sudan,-0.608
+1962,IRN,Iran,0.342
+1962,TUR,Turkey,1.593
+1962,IRQ,Iraq,-1.029
+1962,EGY,Egypt,-1.029
+1962,SYR,Syria,-0.817
+1962,LEB,Lebanon,-0.085
+1962,JOR,Jordan,-0.384
+1962,ISR,Israel,0.508
+1962,SAU,Saudi Arabia,-0.655
+1962,YAR,Yemen Arab Republic,-0.741
+1962,AFG,Afghanistan,-0.668
+1962,MON,Mongolia,-2.138
+1962,TAW,Taiwan,0.843
+1962,JPN,Japan,1.166
+1962,IND,India,0.169
+1962,PAK,Pakistan,0.231
+1962,MYA,Myanmar,-0.089
+1962,SRI,Sri Lanka,-0.174
+1962,NEP,Nepal,-0.05
+1962,THI,Thailand,0.654
+1962,CAM,Cambodia,-0.263
+1962,LAO,Laos,0.125
+1962,MAL,Malaysia,0.517
+1962,PHI,Philippines,0.507
+1962,INS,Indonesia,-0.909
+1962,AUL,Australia,1.858
+1962,NEW,New Zealand,1.638
+1963,USA,United States of America,1.852
+1963,CAN,Canada,1.61
+1963,CUB,Cuba,-2.075
+1963,HAI,Haiti,0.452
+1963,DOM,Dominican Republic,1.111
+1963,JAM,Jamaica,0.665
+1963,TRI,Trinidad and Tobago,0.595
+1963,MEX,Mexico,0.695
+1963,GUA,Guatemala,0.985
+1963,HON,Honduras,1.041
+1963,SAL,El Salvador,1.179
+1963,NIC,Nicaragua,1.328
+1963,COS,Costa Rica,0.895
+1963,PAN,Panama,0.742
+1963,COL,Colombia,0.837
+1963,VEN,Venezuela,0.78
+1963,ECU,Ecuador,0.812
+1963,PER,Peru,0.75
+1963,BRA,Brazil,0.851
+1963,BOL,Bolivia,0.72
+1963,PAR,Paraguay,0.943
+1963,CHL,Chile,0.589
+1963,ARG,Argentina,0.914
+1963,URU,Uruguay,0.673
+1963,UKG,United Kingdom,2.072
+1963,IRE,Ireland,1.358
+1963,NTH,Netherlands,1.702
+1963,BEL,Belgium,1.667
+1963,LUX,Luxembourg,1.754
+1963,FRN,France,1.504
+1963,SPN,Spain,1.691
+1963,POR,Portugal,1.524
+1963,POL,Poland,-2.467
+1963,AUS,Austria,1.105
+1963,HUN,Hungary,-2.491
+1963,CZE,Czechoslovakia,-2.468
+1963,ITA,Italy,1.551
+1963,ALB,Albania,-2.364
+1963,YUG,Yugoslavia,-1.161
+1963,GRC,Greece,1.431
+1963,CYP,Cyprus,0.384
+1963,BUL,Bulgaria,-2.487
+1963,ROM,Romania,-2.444
+1963,RUS,Russia,-2.474
+1963,UKR,Ukraine,-2.503
+1963,BLR,Belarus,-2.487
+1963,FIN,Finland,0.943
+1963,SWD,Sweden,1.12
+1963,NOR,Norway,1.257
+1963,DEN,Denmark,1.192
+1963,ICE,Iceland,1.312
+1963,MLI,Mali,-1.035
+1963,SEN,Senegal,-0.128
+1963,BEN,Benin,0.047
+1963,MAA,Mauritania,-0.177
+1963,NIR,Niger,0.052
+1963,CDI,Ivory Coast,0.076
+1963,GUI,Guinea,-0.987
+1963,BFO,Burkina Faso,-0.042
+1963,LBR,Liberia,0.331
+1963,SIE,Sierra Leone,0.007
+1963,GHA,Ghana,-0.526
+1963,TOG,Togo,-0.093
+1963,CAO,Cameroon,0.164
+1963,NIG,Nigeria,-0.291
+1963,GAB,Gabon,0.292
+1963,CEN,Central African Republic,0.29
+1963,CHA,Chad,0.072
+1963,CON,Congo,0.164
+1963,DRC,Democratic Republic of the Congo,0.046
+1963,UGA,Uganda,-0.381
+1963,KEN,Kenya,-0.427
+1963,TAZ,Tanzania,-0.58
+1963,ZAN,Zanzibar,-0.142
+1963,BUI,Burundi,-0.476
+1963,RWA,Rwanda,0.351
+1963,SOM,Somalia,-0.422
+1963,ETH,Ethiopia,-0.54
+1963,SAF,South Africa,2.207
+1963,MAG,Madagascar,0.244
+1963,MOR,Morocco,-0.704
+1963,ALG,Algeria,-0.98
+1963,TUN,Tunisia,-0.261
+1963,LIB,Libya,-0.25
+1963,SUD,Sudan,-0.593
+1963,IRN,Iran,0.519
+1963,TUR,Turkey,1.623
+1963,IRQ,Iraq,-0.93
+1963,EGY,Egypt,-0.925
+1963,SYR,Syria,-0.856
+1963,LEB,Lebanon,-0.041
+1963,JOR,Jordan,-0.45
+1963,ISR,Israel,0.644
+1963,SAU,Saudi Arabia,-0.595
+1963,YAR,Yemen Arab Republic,-0.797
+1963,KUW,Kuwait,-0.247
+1963,AFG,Afghanistan,-0.559
+1963,MON,Mongolia,-2.327
+1963,TAW,Taiwan,0.866
+1963,JPN,Japan,1.244
+1963,IND,India,-0.096
+1963,PAK,Pakistan,0.4
+1963,MYA,Myanmar,-0.17
+1963,SRI,Sri Lanka,-0.265
+1963,NEP,Nepal,-0.003
+1963,THI,Thailand,0.777
+1963,CAM,Cambodia,-0.41
+1963,LAO,Laos,0.03
+1963,MAL,Malaysia,0.615
+1963,PHI,Philippines,0.563
+1963,INS,Indonesia,-0.73
+1963,AUL,Australia,1.686
+1963,NEW,New Zealand,1.438
+1965,USA,United States of America,2.011
+1965,CAN,Canada,1.685
+1965,CUB,Cuba,-1.88
+1965,HAI,Haiti,0.652
+1965,DOM,Dominican Republic,0.917
+1965,JAM,Jamaica,0.215
+1965,TRI,Trinidad and Tobago,0.315
+1965,MEX,Mexico,0.783
+1965,GUA,Guatemala,1.112
+1965,HON,Honduras,1.117
+1965,SAL,El Salvador,1.038
+1965,NIC,Nicaragua,1.237
+1965,COS,Costa Rica,0.998
+1965,PAN,Panama,0.875
+1965,COL,Colombia,0.963
+1965,VEN,Venezuela,0.826
+1965,ECU,Ecuador,0.761
+1965,PER,Peru,1.049
+1965,BRA,Brazil,1.039
+1965,BOL,Bolivia,1.158
+1965,PAR,Paraguay,1.199
+1965,CHL,Chile,0.665
+1965,ARG,Argentina,0.784
+1965,URU,Uruguay,0.886
+1965,UKG,United Kingdom,2.006
+1965,IRE,Ireland,1.357
+1965,NTH,Netherlands,1.752
+1965,BEL,Belgium,1.785
+1965,LUX,Luxembourg,1.766
+1965,FRN,France,1.508
+1965,SPN,Spain,1.389
+1965,POR,Portugal,2.078
+1965,POL,Poland,-2.245
+1965,AUS,Austria,1.249
+1965,HUN,Hungary,-2.261
+1965,CZE,Czechoslovakia,-2.249
+1965,ITA,Italy,1.549
+1965,MLT,Malta,1.789
+1965,ALB,Albania,-2.341
+1965,YUG,Yugoslavia,-1.176
+1965,GRC,Greece,0.724
+1965,CYP,Cyprus,0.015
+1965,BUL,Bulgaria,-2.264
+1965,ROM,Romania,-2.215
+1965,RUS,Russia,-2.245
+1965,UKR,Ukraine,-2.262
+1965,BLR,Belarus,-2.255
+1965,FIN,Finland,1.056
+1965,SWD,Sweden,1.275
+1965,NOR,Norway,1.387
+1965,DEN,Denmark,1.311
+1965,ICE,Iceland,1.459
+1965,GAM,Gambia,0.448
+1965,MLI,Mali,-1.055
+1965,SEN,Senegal,-0.138
+1965,BEN,Benin,-0.162
+1965,MAA,Mauritania,-0.602
+1965,NIR,Niger,-0.021
+1965,CDI,Ivory Coast,0.092
+1965,GUI,Guinea,-1.152
+1965,BFO,Burkina Faso,-0.069
+1965,LBR,Liberia,-0.019
+1965,SIE,Sierra Leone,-0.168
+1965,GHA,Ghana,-0.748
+1965,TOG,Togo,0.117
+1965,CAO,Cameroon,-0.166
+1965,NIG,Nigeria,-0.365
+1965,GAB,Gabon,0.122
+1965,CEN,Central African Republic,0.091
+1965,CHA,Chad,-0.191
+1965,CON,Congo,-0.698
+1965,DRC,Democratic Republic of the Congo,-0.071
+1965,UGA,Uganda,-0.669
+1965,KEN,Kenya,-0.439
+1965,TAZ,Tanzania,-0.816
+1965,BUI,Burundi,-0.453
+1965,RWA,Rwanda,-0.148
+1965,SOM,Somalia,-0.759
+1965,ETH,Ethiopia,-0.267
+1965,ZAM,Zambia,-0.655
+1965,MAW,Malawi,0.329
+1965,SAF,South Africa,2.138
+1965,MAG,Madagascar,0.243
+1965,MOR,Morocco,-0.641
+1965,ALG,Algeria,-1.151
+1965,TUN,Tunisia,-0.413
+1965,LIB,Libya,-0.496
+1965,SUD,Sudan,-0.846
+1965,IRN,Iran,0.48
+1965,TUR,Turkey,0.88
+1965,IRQ,Iraq,-0.838
+1965,EGY,Egypt,-0.945
+1965,SYR,Syria,-0.899
+1965,LEB,Lebanon,-0.146
+1965,JOR,Jordan,-0.287
+1965,ISR,Israel,0.872
+1965,SAU,Saudi Arabia,-0.516
+1965,YAR,Yemen Arab Republic,-0.842
+1965,KUW,Kuwait,-0.091
+1965,AFG,Afghanistan,-0.646
+1965,MON,Mongolia,-2.106
+1965,TAW,Taiwan,0.903
+1965,JPN,Japan,1.088
+1965,IND,India,0.081
+1965,PAK,Pakistan,-0.223
+1965,MYA,Myanmar,-0.003
+1965,SRI,Sri Lanka,0.207
+1965,MAD,Maldives,0.38
+1965,NEP,Nepal,-0.093
+1965,THI,Thailand,0.743
+1965,CAM,Cambodia,-0.273
+1965,LAO,Laos,0.389
+1965,MAL,Malaysia,0.502
+1965,SIN,Singapore,-0.528
+1965,PHI,Philippines,0.629
+1965,AUL,Australia,1.917
+1965,NEW,New Zealand,1.782
+1966,USA,United States of America,2.069
+1966,CAN,Canada,1.774
+1966,CUB,Cuba,-1.853
+1966,HAI,Haiti,0.905
+1966,DOM,Dominican Republic,0.815
+1966,JAM,Jamaica,0.236
+1966,TRI,Trinidad and Tobago,0.714
+1966,BAR,Barbados,0.907
+1966,MEX,Mexico,0.828
+1966,GUA,Guatemala,0.644
+1966,HON,Honduras,0.901
+1966,SAL,El Salvador,1.039
+1966,NIC,Nicaragua,1.122
+1966,COS,Costa Rica,0.885
+1966,PAN,Panama,0.638
+1966,COL,Colombia,0.893
+1966,VEN,Venezuela,0.901
+1966,GUY,Guyana,0.475
+1966,ECU,Ecuador,0.825
+1966,PER,Peru,0.798
+1966,BRA,Brazil,1.097
+1966,BOL,Bolivia,1.019
+1966,PAR,Paraguay,0.93
+1966,CHL,Chile,0.588
+1966,ARG,Argentina,0.891
+1966,URU,Uruguay,0.866
+1966,UKG,United Kingdom,2.108
+1966,IRE,Ireland,1.289
+1966,NTH,Netherlands,1.763
+1966,BEL,Belgium,1.789
+1966,LUX,Luxembourg,1.838
+1966,FRN,France,1.269
+1966,SPN,Spain,0.764
+1966,POR,Portugal,2.066
+1966,POL,Poland,-2.154
+1966,AUS,Austria,1.334
+1966,HUN,Hungary,-2.169
+1966,CZE,Czechoslovakia,-2.155
+1966,ITA,Italy,1.583
+1966,MLT,Malta,1.876
+1966,ALB,Albania,-2.29
+1966,YUG,Yugoslavia,-1.258
+1966,GRC,Greece,1.086
+1966,CYP,Cyprus,-0.098
+1966,BUL,Bulgaria,-2.164
+1966,ROM,Romania,-2.162
+1966,RUS,Russia,-2.17
+1966,UKR,Ukraine,-2.169
+1966,BLR,Belarus,-2.171
+1966,FIN,Finland,0.55
+1966,SWD,Sweden,1.335
+1966,NOR,Norway,1.407
+1966,DEN,Denmark,1.283
+1966,ICE,Iceland,1.427
+1966,GAM,Gambia,0.448
+1966,MLI,Mali,-1.425
+1966,SEN,Senegal,-0.245
+1966,BEN,Benin,0.295
+1966,MAA,Mauritania,-1.177
+1966,NIR,Niger,0.297
+1966,CDI,Ivory Coast,0.387
+1966,GUI,Guinea,-1.253
+1966,BFO,Burkina Faso,0.301
+1966,LBR,Liberia,0.179
+1966,SIE,Sierra Leone,-0.3
+1966,GHA,Ghana,0.072
+1966,TOG,Togo,0.205
+1966,CAO,Cameroon,-0.25
+1966,NIG,Nigeria,-0.46
+1966,GAB,Gabon,0.045
+1966,CEN,Central African Republic,0.34
+1966,CHA,Chad,-0.167
+1966,CON,Congo,-1.235
+1966,DRC,Democratic Republic of the Congo,-0.053
+1966,UGA,Uganda,-0.481
+1966,KEN,Kenya,-0.304
+1966,TAZ,Tanzania,-0.642
+1966,BUI,Burundi,-0.572
+1966,RWA,Rwanda,0.089
+1966,SOM,Somalia,-0.518
+1966,ETH,Ethiopia,-0.352
+1966,ZAM,Zambia,-0.565
+1966,MAW,Malawi,0.679
+1966,SAF,South Africa,2.124
+1966,LES,Lesotho,0.718
+1966,BOT,Botswana,0.758
+1966,MAG,Madagascar,0.608
+1966,MOR,Morocco,-0.447
+1966,ALG,Algeria,-1.447
+1966,TUN,Tunisia,-0.348
+1966,LIB,Libya,-0.353
+1966,SUD,Sudan,-0.642
+1966,IRN,Iran,0.265
+1966,TUR,Turkey,0.83
+1966,IRQ,Iraq,-1.04
+1966,EGY,Egypt,-1.11
+1966,SYR,Syria,-1.338
+1966,LEB,Lebanon,-0.255
+1966,JOR,Jordan,-0.283
+1966,ISR,Israel,1.001
+1966,SAU,Saudi Arabia,-0.373
+1966,YAR,Yemen Arab Republic,-0.867
+1966,KUW,Kuwait,-0.328
+1966,AFG,Afghanistan,-0.545
+1966,MON,Mongolia,-2.064
+1966,TAW,Taiwan,1.068
+1966,JPN,Japan,1.082
+1966,IND,India,-0.222
+1966,PAK,Pakistan,-0.363
+1966,MYA,Myanmar,-0.238
+1966,SRI,Sri Lanka,-0.189
+1966,MAD,Maldives,0.676
+1966,NEP,Nepal,-0.252
+1966,THI,Thailand,0.742
+1966,CAM,Cambodia,-1.15
+1966,LAO,Laos,0.657
+1966,MAL,Malaysia,0.761
+1966,SIN,Singapore,-0.497
+1966,PHI,Philippines,0.665
+1966,INS,Indonesia,-0.273
+1966,AUL,Australia,2.009
+1966,NEW,New Zealand,1.963
+1967,USA,United States of America,2.259
+1967,CAN,Canada,1.769
+1967,CUB,Cuba,-1.406
+1967,HAI,Haiti,0.521
+1967,DOM,Dominican Republic,0.787
+1967,JAM,Jamaica,0.625
+1967,TRI,Trinidad and Tobago,0.829
+1967,BAR,Barbados,0.919
+1967,MEX,Mexico,0.795
+1967,GUA,Guatemala,0.691
+1967,HON,Honduras,0.789
+1967,SAL,El Salvador,0.823
+1967,NIC,Nicaragua,0.928
+1967,COS,Costa Rica,1.06
+1967,PAN,Panama,0.775
+1967,COL,Colombia,0.841
+1967,VEN,Venezuela,0.839
+1967,GUY,Guyana,0.7
+1967,ECU,Ecuador,0.712
+1967,PER,Peru,0.908
+1967,BRA,Brazil,1.107
+1967,BOL,Bolivia,0.96
+1967,PAR,Paraguay,0.902
+1967,CHL,Chile,0.683
+1967,ARG,Argentina,0.802
+1967,URU,Uruguay,0.878
+1967,UKG,United Kingdom,2.192
+1967,IRE,Ireland,1.217
+1967,NTH,Netherlands,1.816
+1967,BEL,Belgium,1.865
+1967,LUX,Luxembourg,1.829
+1967,FRN,France,0.765
+1967,SPN,Spain,0.512
+1967,POR,Portugal,1.855
+1967,POL,Poland,-1.913
+1967,AUS,Austria,1.323
+1967,HUN,Hungary,-1.91
+1967,CZE,Czechoslovakia,-1.901
+1967,ITA,Italy,1.589
+1967,MLT,Malta,1.615
+1967,ALB,Albania,-2.254
+1967,YUG,Yugoslavia,-1.192
+1967,GRC,Greece,0.866
+1967,CYP,Cyprus,-0.094
+1967,BUL,Bulgaria,-1.907
+1967,ROM,Romania,-1.035
+1967,RUS,Russia,-1.914
+1967,UKR,Ukraine,-1.917
+1967,BLR,Belarus,-1.92
+1967,FIN,Finland,0.96
+1967,SWD,Sweden,1.36
+1967,NOR,Norway,1.47
+1967,DEN,Denmark,1.488
+1967,ICE,Iceland,1.709
+1967,GAM,Gambia,0.848
+1967,MLI,Mali,-0.991
+1967,SEN,Senegal,-0.334
+1967,BEN,Benin,0.451
+1967,MAA,Mauritania,-1.448
+1967,NIR,Niger,0.208
+1967,CDI,Ivory Coast,0.518
+1967,GUI,Guinea,-1.331
+1967,BFO,Burkina Faso,0.197
+1967,LBR,Liberia,0.696
+1967,SIE,Sierra Leone,0.44
+1967,GHA,Ghana,0.3
+1967,TOG,Togo,0.423
+1967,CAO,Cameroon,-0.184
+1967,NIG,Nigeria,-0.196
+1967,GAB,Gabon,0.144
+1967,CEN,Central African Republic,0.047
+1967,CHA,Chad,-0.062
+1967,CON,Congo,-0.83
+1967,DRC,Democratic Republic of the Congo,-0.003
+1967,UGA,Uganda,-0.403
+1967,KEN,Kenya,-0.202
+1967,TAZ,Tanzania,-0.774
+1967,BUI,Burundi,-0.946
+1967,RWA,Rwanda,0.528
+1967,SOM,Somalia,-0.807
+1967,ETH,Ethiopia,-0.24
+1967,ZAM,Zambia,-0.897
+1967,MAW,Malawi,1.311
+1967,SAF,South Africa,1.396
+1967,LES,Lesotho,0.822
+1967,BOT,Botswana,0.932
+1967,MAG,Madagascar,0.564
+1967,MAS,Mauritius,0.489
+1967,MOR,Morocco,-0.708
+1967,ALG,Algeria,-1.624
+1967,TUN,Tunisia,-0.701
+1967,LIB,Libya,-0.588
+1967,SUD,Sudan,-1.159
+1967,IRN,Iran,0.129
+1967,TUR,Turkey,0.384
+1967,IRQ,Iraq,-1.34
+1967,EGY,Egypt,-1.332
+1967,SYR,Syria,-1.597
+1967,LEB,Lebanon,-0.67
+1967,JOR,Jordan,-0.769
+1967,ISR,Israel,0.952
+1967,SAU,Saudi Arabia,-0.769
+1967,YAR,Yemen Arab Republic,-1.241
+1967,YPR,Yemen People's Republic,-0.898
+1967,KUW,Kuwait,-0.823
+1967,AFG,Afghanistan,-0.56
+1967,MON,Mongolia,-1.831
+1967,TAW,Taiwan,0.69
+1967,JPN,Japan,1.013
+1967,IND,India,-0.535
+1967,PAK,Pakistan,-0.715
+1967,MYA,Myanmar,-0.228
+1967,SRI,Sri Lanka,-0.376
+1967,MAD,Maldives,0.447
+1967,NEP,Nepal,-0.106
+1967,THI,Thailand,0.512
+1967,CAM,Cambodia,-1.089
+1967,LAO,Laos,0.397
+1967,MAL,Malaysia,0.141
+1967,SIN,Singapore,-0.217
+1967,PHI,Philippines,0.752
+1967,INS,Indonesia,-0.449
+1967,AUL,Australia,2.13
+1967,NEW,New Zealand,1.952
+1968,USA,United States of America,2.213
+1968,CAN,Canada,1.577
+1968,CUB,Cuba,-1.347
+1968,HAI,Haiti,0.496
+1968,DOM,Dominican Republic,0.513
+1968,JAM,Jamaica,0.542
+1968,TRI,Trinidad and Tobago,0.618
+1968,BAR,Barbados,0.802
+1968,MEX,Mexico,0.621
+1968,GUA,Guatemala,0.431
+1968,HON,Honduras,0.942
+1968,SAL,El Salvador,0.593
+1968,NIC,Nicaragua,1.011
+1968,COS,Costa Rica,0.955
+1968,PAN,Panama,0.701
+1968,COL,Colombia,0.621
+1968,VEN,Venezuela,0.626
+1968,GUY,Guyana,0.364
+1968,ECU,Ecuador,0.393
+1968,PER,Peru,0.546
+1968,BRA,Brazil,1.149
+1968,BOL,Bolivia,0.884
+1968,PAR,Paraguay,0.82
+1968,CHL,Chile,0.474
+1968,ARG,Argentina,0.601
+1968,URU,Uruguay,0.711
+1968,UKG,United Kingdom,2.108
+1968,IRE,Ireland,1.303
+1968,NTH,Netherlands,1.865
+1968,BEL,Belgium,1.896
+1968,LUX,Luxembourg,1.886
+1968,FRN,France,0.977
+1968,SPN,Spain,0.463
+1968,POR,Portugal,2.078
+1968,POL,Poland,-1.903
+1968,AUS,Austria,1.303
+1968,HUN,Hungary,-1.98
+1968,CZE,Czechoslovakia,-1.894
+1968,ITA,Italy,1.752
+1968,MLT,Malta,1.689
+1968,ALB,Albania,-2.284
+1968,YUG,Yugoslavia,-1.17
+1968,GRC,Greece,0.926
+1968,CYP,Cyprus,0.026
+1968,BUL,Bulgaria,-1.954
+1968,ROM,Romania,-1.15
+1968,RUS,Russia,-1.941
+1968,UKR,Ukraine,-1.836
+1968,BLR,Belarus,-1.954
+1968,FIN,Finland,0.965
+1968,SWD,Sweden,1.387
+1968,NOR,Norway,1.503
+1968,DEN,Denmark,1.51
+1968,ICE,Iceland,1.643
+1968,EQG,Equatorial Guinea,-0.525
+1968,GAM,Gambia,0.829
+1968,MLI,Mali,-1.111
+1968,SEN,Senegal,0.012
+1968,BEN,Benin,0.273
+1968,MAA,Mauritania,-1.417
+1968,NIR,Niger,0.279
+1968,CDI,Ivory Coast,0.51
+1968,GUI,Guinea,-1.389
+1968,BFO,Burkina Faso,0.063
+1968,LBR,Liberia,0.698
+1968,SIE,Sierra Leone,0.219
+1968,GHA,Ghana,0.076
+1968,TOG,Togo,0.228
+1968,CAO,Cameroon,-0.321
+1968,NIG,Nigeria,-0.031
+1968,GAB,Gabon,0.417
+1968,CEN,Central African Republic,0.428
+1968,CHA,Chad,0.024
+1968,CON,Congo,-1.019
+1968,DRC,Democratic Republic of the Congo,0.077
+1968,UGA,Uganda,-0.593
+1968,KEN,Kenya,-0.357
+1968,TAZ,Tanzania,-0.931
+1968,BUI,Burundi,-0.967
+1968,RWA,Rwanda,0.285
+1968,SOM,Somalia,-0.843
+1968,ETH,Ethiopia,-0.065
+1968,ZAM,Zambia,-0.823
+1968,MAW,Malawi,1.485
+1968,SAF,South Africa,2.156
+1968,LES,Lesotho,0.922
+1968,BOT,Botswana,1.095
+1968,SWA,Swaziland,1.097
+1968,MAG,Madagascar,0.514
+1968,MAS,Mauritius,0.492
+1968,MOR,Morocco,-0.173
+1968,ALG,Algeria,-1.384
+1968,TUN,Tunisia,-0.231
+1968,LIB,Libya,-0.487
+1968,SUD,Sudan,-1.188
+1968,IRN,Iran,0.273
+1968,TUR,Turkey,0.518
+1968,IRQ,Iraq,-1.307
+1968,EGY,Egypt,-1.236
+1968,SYR,Syria,-1.565
+1968,LEB,Lebanon,-0.242
+1968,JOR,Jordan,-0.758
+1968,ISR,Israel,0.905
+1968,SAU,Saudi Arabia,-0.239
+1968,YAR,Yemen Arab Republic,-1.329
+1968,YPR,Yemen People's Republic,-0.866
+1968,KUW,Kuwait,-0.704
+1968,AFG,Afghanistan,-0.215
+1968,MON,Mongolia,-1.804
+1968,TAW,Taiwan,0.847
+1968,JPN,Japan,1.259
+1968,IND,India,-0.481
+1968,PAK,Pakistan,-0.634
+1968,MYA,Myanmar,-0.239
+1968,SRI,Sri Lanka,-0.254
+1968,MAD,Maldives,0.685
+1968,NEP,Nepal,-0.237
+1968,THI,Thailand,0.58
+1968,CAM,Cambodia,-1.242
+1968,LAO,Laos,0.468
+1968,MAL,Malaysia,0.5
+1968,SIN,Singapore,-0.033
+1968,PHI,Philippines,0.518
+1968,INS,Indonesia,-0.259
+1968,AUL,Australia,2.052
+1968,NEW,New Zealand,1.922
+1969,USA,United States of America,2.093
+1969,CAN,Canada,1.615
+1969,CUB,Cuba,-0.49
+1969,HAI,Haiti,0.308
+1969,DOM,Dominican Republic,0.584
+1969,JAM,Jamaica,0.633
+1969,TRI,Trinidad and Tobago,0.574
+1969,BAR,Barbados,0.507
+1969,MEX,Mexico,0.64
+1969,GUA,Guatemala,0.572
+1969,HON,Honduras,0.931
+1969,SAL,El Salvador,0.74
+1969,NIC,Nicaragua,1.084
+1969,COS,Costa Rica,0.994
+1969,PAN,Panama,0.759
+1969,COL,Colombia,0.79
+1969,VEN,Venezuela,0.488
+1969,GUY,Guyana,0.481
+1969,ECU,Ecuador,0.517
+1969,PER,Peru,0.378
+1969,BRA,Brazil,1.064
+1969,BOL,Bolivia,0.798
+1969,PAR,Paraguay,0.945
+1969,CHL,Chile,0.632
+1969,ARG,Argentina,0.764
+1969,URU,Uruguay,1.048
+1969,UKG,United Kingdom,2.024
+1969,IRE,Ireland,1.092
+1969,NTH,Netherlands,1.767
+1969,BEL,Belgium,1.787
+1969,LUX,Luxembourg,1.801
+1969,FRN,France,1.337
+1969,SPN,Spain,0.619
+1969,POR,Portugal,2.207
+1969,POL,Poland,-1.817
+1969,AUS,Austria,1.264
+1969,HUN,Hungary,-1.905
+1969,CZE,Czechoslovakia,-1.797
+1969,ITA,Italy,1.537
+1969,MLT,Malta,1.715
+1969,ALB,Albania,-2.293
+1969,YUG,Yugoslavia,-1.184
+1969,GRC,Greece,0.648
+1969,CYP,Cyprus,0.036
+1969,BUL,Bulgaria,-1.943
+1969,ROM,Romania,-1.146
+1969,RUS,Russia,-1.85
+1969,UKR,Ukraine,-1.768
+1969,BLR,Belarus,-1.86
+1969,FIN,Finland,1.043
+1969,SWD,Sweden,1.424
+1969,NOR,Norway,1.452
+1969,DEN,Denmark,1.446
+1969,ICE,Iceland,1.565
+1969,EQG,Equatorial Guinea,-0.35
+1969,GAM,Gambia,0.88
+1969,MLI,Mali,-1.206
+1969,SEN,Senegal,0.286
+1969,BEN,Benin,0.326
+1969,MAA,Mauritania,-1.106
+1969,NIR,Niger,0.216
+1969,CDI,Ivory Coast,0.701
+1969,GUI,Guinea,-1.352
+1969,BFO,Burkina Faso,0.072
+1969,LBR,Liberia,0.619
+1969,SIE,Sierra Leone,0.176
+1969,GHA,Ghana,-0.026
+1969,TOG,Togo,0.312
+1969,CAO,Cameroon,-0.049
+1969,NIG,Nigeria,0.002
+1969,GAB,Gabon,0.725
+1969,CEN,Central African Republic,0.177
+1969,CHA,Chad,-0.023
+1969,CON,Congo,-1.092
+1969,DRC,Democratic Republic of the Congo,0.119
+1969,UGA,Uganda,-0.469
+1969,KEN,Kenya,-0.343
+1969,TAZ,Tanzania,-0.958
+1969,BUI,Burundi,-0.93
+1969,RWA,Rwanda,0.378
+1969,SOM,Somalia,-0.699
+1969,ETH,Ethiopia,-0.038
+1969,ZAM,Zambia,-0.945
+1969,MAW,Malawi,1.554
+1969,SAF,South Africa,2.425
+1969,LES,Lesotho,0.806
+1969,BOT,Botswana,1.227
+1969,SWA,Swaziland,1.186
+1969,MAG,Madagascar,0.464
+1969,MAS,Mauritius,0.225
+1969,MOR,Morocco,-0.25
+1969,ALG,Algeria,-1.484
+1969,TUN,Tunisia,-0.272
+1969,LIB,Libya,-0.635
+1969,SUD,Sudan,-1.306
+1969,IRN,Iran,0.174
+1969,TUR,Turkey,0.472
+1969,IRQ,Iraq,-1.425
+1969,EGY,Egypt,-1.341
+1969,SYR,Syria,-1.663
+1969,LEB,Lebanon,-0.447
+1969,JOR,Jordan,-0.733
+1969,ISR,Israel,0.845
+1969,SAU,Saudi Arabia,-0.32
+1969,YAR,Yemen Arab Republic,-1.45
+1969,YPR,Yemen People's Republic,-1.114
+1969,KUW,Kuwait,-0.799
+1969,AFG,Afghanistan,-0.278
+1969,MON,Mongolia,-1.796
+1969,TAW,Taiwan,0.728
+1969,JPN,Japan,1.143
+1969,IND,India,-0.367
+1969,PAK,Pakistan,-0.562
+1969,MYA,Myanmar,-0.155
+1969,SRI,Sri Lanka,-0.424
+1969,MAD,Maldives,0.436
+1969,NEP,Nepal,-0.17
+1969,THI,Thailand,0.534
+1969,CAM,Cambodia,-1.28
+1969,LAO,Laos,0.43
+1969,MAL,Malaysia,0.42
+1969,SIN,Singapore,0.144
+1969,PHI,Philippines,0.47
+1969,INS,Indonesia,-0.383
+1969,AUL,Australia,1.968
+1969,NEW,New Zealand,1.8
+1970,USA,United States of America,2.132
+1970,CAN,Canada,1.577
+1970,CUB,Cuba,-1.392
+1970,HAI,Haiti,0.709
+1970,DOM,Dominican Republic,0.767
+1970,JAM,Jamaica,0.563
+1970,TRI,Trinidad and Tobago,0.423
+1970,BAR,Barbados,0.63
+1970,MEX,Mexico,0.64
+1970,GUA,Guatemala,0.81
+1970,HON,Honduras,0.757
+1970,SAL,El Salvador,1.192
+1970,NIC,Nicaragua,0.974
+1970,COS,Costa Rica,1.001
+1970,PAN,Panama,0.899
+1970,COL,Colombia,0.731
+1970,VEN,Venezuela,0.533
+1970,GUY,Guyana,0.276
+1970,ECU,Ecuador,0.4
+1970,PER,Peru,0.37
+1970,BRA,Brazil,1.187
+1970,BOL,Bolivia,0.51
+1970,PAR,Paraguay,0.994
+1970,CHL,Chile,-0.029
+1970,ARG,Argentina,0.897
+1970,URU,Uruguay,0.916
+1970,UKG,United Kingdom,1.85
+1970,IRE,Ireland,1.064
+1970,NTH,Netherlands,1.633
+1970,BEL,Belgium,1.622
+1970,LUX,Luxembourg,1.664
+1970,FRN,France,1.431
+1970,SPN,Spain,0.917
+1970,POR,Portugal,2.107
+1970,POL,Poland,-2.089
+1970,AUS,Austria,1.309
+1970,HUN,Hungary,-2.199
+1970,CZE,Czechoslovakia,-2.145
+1970,ITA,Italy,1.492
+1970,MLT,Malta,1.688
+1970,ALB,Albania,-2.253
+1970,YUG,Yugoslavia,-1.019
+1970,GRC,Greece,0.476
+1970,CYP,Cyprus,-0.058
+1970,BUL,Bulgaria,-2.206
+1970,ROM,Romania,-1.471
+1970,RUS,Russia,-2.167
+1970,UKR,Ukraine,-2.116
+1970,BLR,Belarus,-2.166
+1970,FIN,Finland,1.085
+1970,SWD,Sweden,1.29
+1970,NOR,Norway,1.319
+1970,DEN,Denmark,1.357
+1970,ICE,Iceland,0.992
+1970,EQG,Equatorial Guinea,-0.555
+1970,GAM,Gambia,0.244
+1970,MLI,Mali,-1.031
+1970,SEN,Senegal,0.095
+1970,BEN,Benin,0.607
+1970,MAA,Mauritania,-0.944
+1970,NIR,Niger,0.262
+1970,CDI,Ivory Coast,0.456
+1970,GUI,Guinea,-1.17
+1970,BFO,Burkina Faso,-0.024
+1970,LBR,Liberia,0.675
+1970,SIE,Sierra Leone,-0.026
+1970,GHA,Ghana,-0.032
+1970,TOG,Togo,0.383
+1970,CAO,Cameroon,0.052
+1970,NIG,Nigeria,-0.692
+1970,GAB,Gabon,0.72
+1970,CEN,Central African Republic,-0.01
+1970,CHA,Chad,0.135
+1970,CON,Congo,-1.036
+1970,DRC,Democratic Republic of the Congo,0.281
+1970,UGA,Uganda,-0.791
+1970,KEN,Kenya,-0.215
+1970,TAZ,Tanzania,-1.016
+1970,BUI,Burundi,-0.53
+1970,RWA,Rwanda,0.575
+1970,SOM,Somalia,-0.984
+1970,ETH,Ethiopia,0.022
+1970,ZAM,Zambia,-1.009
+1970,MAW,Malawi,1.332
+1970,SAF,South Africa,2.408
+1970,LES,Lesotho,0.884
+1970,BOT,Botswana,1.004
+1970,SWA,Swaziland,0.786
+1970,MAG,Madagascar,0.67
+1970,MAS,Mauritius,0.261
+1970,MOR,Morocco,-0.325
+1970,ALG,Algeria,-1.22
+1970,TUN,Tunisia,-0.221
+1970,LIB,Libya,-0.923
+1970,SUD,Sudan,-1.309
+1970,IRN,Iran,0.21
+1970,TUR,Turkey,0.515
+1970,IRQ,Iraq,-1.364
+1970,EGY,Egypt,-1.049
+1970,SYR,Syria,-1.507
+1970,LEB,Lebanon,-0.411
+1970,JOR,Jordan,-0.562
+1970,ISR,Israel,0.811
+1970,SAU,Saudi Arabia,-0.453
+1970,YAR,Yemen Arab Republic,-1.319
+1970,YPR,Yemen People's Republic,-1.258
+1970,KUW,Kuwait,-0.713
+1970,AFG,Afghanistan,-0.194
+1970,MON,Mongolia,-2.086
+1970,TAW,Taiwan,0.726
+1970,JPN,Japan,1.056
+1970,IND,India,-0.308
+1970,PAK,Pakistan,-0.415
+1970,MYA,Myanmar,0.084
+1970,SRI,Sri Lanka,-0.541
+1970,NEP,Nepal,0.004
+1970,THI,Thailand,0.636
+1970,CAM,Cambodia,0.127
+1970,LAO,Laos,0.303
+1970,MAL,Malaysia,0.125
+1970,SIN,Singapore,0.196
+1970,PHI,Philippines,0.437
+1970,INS,Indonesia,-0.293
+1970,AUL,Australia,1.972
+1970,NEW,New Zealand,1.735
+1970,FIJ,Fiji,0.631
+1971,USA,United States of America,1.805
+1971,CAN,Canada,1.391
+1971,CUB,Cuba,-1.479
+1971,HAI,Haiti,0.863
+1971,DOM,Dominican Republic,0.977
+1971,JAM,Jamaica,0.443
+1971,TRI,Trinidad and Tobago,0.079
+1971,BAR,Barbados,0.809
+1971,MEX,Mexico,0.427
+1971,GUA,Guatemala,0.818
+1971,HON,Honduras,0.679
+1971,SAL,El Salvador,0.788
+1971,NIC,Nicaragua,0.963
+1971,COS,Costa Rica,1.171
+1971,PAN,Panama,0.708
+1971,COL,Colombia,0.763
+1971,VEN,Venezuela,0.62
+1971,GUY,Guyana,-0.1
+1971,ECU,Ecuador,0.341
+1971,PER,Peru,-0.036
+1971,BRA,Brazil,0.976
+1971,BOL,Bolivia,0.934
+1971,PAR,Paraguay,0.915
+1971,CHL,Chile,-0.505
+1971,ARG,Argentina,0.99
+1971,URU,Uruguay,1.016
+1971,UKG,United Kingdom,1.599
+1971,IRE,Ireland,0.922
+1971,NTH,Netherlands,1.362
+1971,BEL,Belgium,1.386
+1971,LUX,Luxembourg,1.458
+1971,FRN,France,1.366
+1971,SPN,Spain,0.853
+1971,POR,Portugal,2.026
+1971,POL,Poland,-2.297
+1971,AUS,Austria,0.939
+1971,HUN,Hungary,-2.35
+1971,CZE,Czechoslovakia,-2.333
+1971,ITA,Italy,1.299
+1971,MLT,Malta,0.654
+1971,ALB,Albania,-2.145
+1971,YUG,Yugoslavia,-0.965
+1971,GRC,Greece,0.546
+1971,CYP,Cyprus,-0.178
+1971,BUL,Bulgaria,-2.35
+1971,ROM,Romania,-1.648
+1971,RUS,Russia,-2.343
+1971,UKR,Ukraine,-2.325
+1971,BLR,Belarus,-2.336
+1971,FIN,Finland,0.801
+1971,SWD,Sweden,0.862
+1971,NOR,Norway,0.931
+1971,DEN,Denmark,1.006
+1971,ICE,Iceland,0.699
+1971,EQG,Equatorial Guinea,-0.922
+1971,GAM,Gambia,0.447
+1971,MLI,Mali,-1.039
+1971,SEN,Senegal,0.201
+1971,BEN,Benin,0.603
+1971,MAA,Mauritania,-0.738
+1971,NIR,Niger,0.288
+1971,CDI,Ivory Coast,0.674
+1971,GUI,Guinea,-1.191
+1971,BFO,Burkina Faso,0.34
+1971,LBR,Liberia,0.742
+1971,SIE,Sierra Leone,0.005
+1971,GHA,Ghana,0.44
+1971,TOG,Togo,0.198
+1971,CAO,Cameroon,-0.253
+1971,NIG,Nigeria,-0.387
+1971,GAB,Gabon,0.482
+1971,CEN,Central African Republic,0.542
+1971,CHA,Chad,0.171
+1971,CON,Congo,-1.004
+1971,DRC,Democratic Republic of the Congo,0.637
+1971,UGA,Uganda,-0.392
+1971,KEN,Kenya,0.193
+1971,TAZ,Tanzania,-0.945
+1971,BUI,Burundi,-0.436
+1971,RWA,Rwanda,0.243
+1971,SOM,Somalia,-0.896
+1971,ETH,Ethiopia,0.013
+1971,ZAM,Zambia,-0.579
+1971,MAW,Malawi,1.632
+1971,SAF,South Africa,2.703
+1971,LES,Lesotho,1.037
+1971,BOT,Botswana,0.595
+1971,SWA,Swaziland,0.986
+1971,MAG,Madagascar,0.872
+1971,MAS,Mauritius,0.441
+1971,MOR,Morocco,-0.293
+1971,ALG,Algeria,-1.068
+1971,TUN,Tunisia,-0.262
+1971,LIB,Libya,-0.914
+1971,SUD,Sudan,-1.109
+1971,IRN,Iran,0.086
+1971,TUR,Turkey,0.231
+1971,IRQ,Iraq,-1.27
+1971,EGY,Egypt,-0.817
+1971,SYR,Syria,-1.057
+1971,LEB,Lebanon,-0.155
+1971,JOR,Jordan,-0.17
+1971,ISR,Israel,0.983
+1971,SAU,Saudi Arabia,-0.229
+1971,YAR,Yemen Arab Republic,-1.036
+1971,YPR,Yemen People's Republic,-1.362
+1971,KUW,Kuwait,-0.732
+1971,BAH,Bahrain,-0.312
+1971,QAT,Qatar,-0.321
+1971,UAE,United Arab Emirates,-0.63
+1971,OMA,Oman,-0.657
+1971,AFG,Afghanistan,-0.501
+1971,CHN,China,-0.889
+1971,MON,Mongolia,-2.282
+1971,TAW,Taiwan,0.767
+1971,JPN,Japan,0.909
+1971,IND,India,-0.719
+1971,BHU,Bhutan,-0.024
+1971,PAK,Pakistan,-0.455
+1971,MYA,Myanmar,-0.185
+1971,SRI,Sri Lanka,-0.656
+1971,NEP,Nepal,0.137
+1971,THI,Thailand,0.486
+1971,CAM,Cambodia,0.565
+1971,LAO,Laos,0.428
+1971,MAL,Malaysia,-0.154
+1971,SIN,Singapore,0.252
+1971,PHI,Philippines,0.459
+1971,INS,Indonesia,-0.282
+1971,AUL,Australia,1.483
+1971,NEW,New Zealand,1.465
+1971,FIJ,Fiji,0.897
+1972,USA,United States of America,2.04
+1972,CAN,Canada,1.549
+1972,CUB,Cuba,-1.606
+1972,HAI,Haiti,0.708
+1972,DOM,Dominican Republic,0.988
+1972,JAM,Jamaica,0.305
+1972,TRI,Trinidad and Tobago,-0.062
+1972,BAR,Barbados,0.676
+1972,MEX,Mexico,0.186
+1972,GUA,Guatemala,0.852
+1972,HON,Honduras,0.74
+1972,SAL,El Salvador,0.943
+1972,NIC,Nicaragua,1
+1972,COS,Costa Rica,0.959
+1972,PAN,Panama,0.564
+1972,COL,Colombia,0.796
+1972,VEN,Venezuela,0.479
+1972,GUY,Guyana,-0.268
+1972,ECU,Ecuador,0.244
+1972,PER,Peru,-0.082
+1972,BRA,Brazil,1.012
+1972,BOL,Bolivia,0.993
+1972,PAR,Paraguay,0.954
+1972,CHL,Chile,-0.566
+1972,ARG,Argentina,0.769
+1972,URU,Uruguay,0.967
+1972,UKG,United Kingdom,1.965
+1972,IRE,Ireland,1.148
+1972,NTH,Netherlands,1.558
+1972,BEL,Belgium,1.622
+1972,LUX,Luxembourg,1.511
+1972,FRN,France,1.516
+1972,SPN,Spain,1.108
+1972,POR,Portugal,2.179
+1972,POL,Poland,-2.281
+1972,AUS,Austria,1.197
+1972,HUN,Hungary,-2.308
+1972,CZE,Czechoslovakia,-2.287
+1972,ITA,Italy,1.468
+1972,MLT,Malta,0.418
+1972,ALB,Albania,-1.732
+1972,YUG,Yugoslavia,-0.587
+1972,GRC,Greece,0.844
+1972,CYP,Cyprus,-0.071
+1972,BUL,Bulgaria,-2.281
+1972,ROM,Romania,-1.447
+1972,RUS,Russia,-2.275
+1972,UKR,Ukraine,-2.254
+1972,BLR,Belarus,-2.324
+1972,FIN,Finland,1.112
+1972,SWD,Sweden,1.097
+1972,NOR,Norway,1.157
+1972,DEN,Denmark,1.293
+1972,ICE,Iceland,0.847
+1972,EQG,Equatorial Guinea,-0.806
+1972,GAM,Gambia,0.338
+1972,MLI,Mali,-1.181
+1972,SEN,Senegal,-0.185
+1972,BEN,Benin,0.223
+1972,MAA,Mauritania,-0.72
+1972,NIR,Niger,0.002
+1972,CDI,Ivory Coast,0.505
+1972,GUI,Guinea,-1.092
+1972,BFO,Burkina Faso,0.073
+1972,LBR,Liberia,0.647
+1972,SIE,Sierra Leone,-0.331
+1972,GHA,Ghana,0.27
+1972,TOG,Togo,0.164
+1972,CAO,Cameroon,-0.344
+1972,NIG,Nigeria,-0.517
+1972,GAB,Gabon,0.331
+1972,CEN,Central African Republic,0.301
+1972,CHA,Chad,0.059
+1972,CON,Congo,-0.955
+1972,DRC,Democratic Republic of the Congo,0.347
+1972,UGA,Uganda,-0.415
+1972,KEN,Kenya,-0.012
+1972,TAZ,Tanzania,-0.9
+1972,BUI,Burundi,-0.537
+1972,RWA,Rwanda,0.203
+1972,SOM,Somalia,-0.793
+1972,ETH,Ethiopia,-0.078
+1972,ZAM,Zambia,-0.675
+1972,MAW,Malawi,1.308
+1972,SAF,South Africa,2.572
+1972,LES,Lesotho,0.72
+1972,BOT,Botswana,0.162
+1972,SWA,Swaziland,0.476
+1972,MAG,Madagascar,0.041
+1972,MAS,Mauritius,-0.078
+1972,MOR,Morocco,-0.282
+1972,ALG,Algeria,-1.134
+1972,TUN,Tunisia,-0.283
+1972,LIB,Libya,-0.971
+1972,SUD,Sudan,-0.926
+1972,IRN,Iran,0.231
+1972,TUR,Turkey,0.463
+1972,IRQ,Iraq,-1.16
+1972,EGY,Egypt,-0.777
+1972,SYR,Syria,-1.128
+1972,LEB,Lebanon,-0.34
+1972,JOR,Jordan,0.161
+1972,ISR,Israel,1.083
+1972,SAU,Saudi Arabia,-0.253
+1972,YAR,Yemen Arab Republic,-1.07
+1972,YPR,Yemen People's Republic,-1.229
+1972,KUW,Kuwait,-0.642
+1972,BAH,Bahrain,-0.216
+1972,QAT,Qatar,-0.361
+1972,UAE,United Arab Emirates,-0.593
+1972,OMA,Oman,-0.447
+1972,AFG,Afghanistan,-0.224
+1972,CHN,China,-0.782
+1972,MON,Mongolia,-2.243
+1972,JPN,Japan,1.231
+1972,IND,India,-0.507
+1972,BHU,Bhutan,0.169
+1972,PAK,Pakistan,-0.212
+1972,MYA,Myanmar,-0.13
+1972,SRI,Sri Lanka,-0.379
+1972,MAD,Maldives,0.124
+1972,NEP,Nepal,0.379
+1972,THI,Thailand,0.458
+1972,CAM,Cambodia,0.419
+1972,LAO,Laos,0.687
+1972,MAL,Malaysia,-0.152
+1972,SIN,Singapore,0.336
+1972,PHI,Philippines,0.495
+1972,INS,Indonesia,-0.238
+1972,AUL,Australia,1.091
+1972,NEW,New Zealand,1.381
+1972,FIJ,Fiji,0.682
+1973,USA,United States of America,2.209
+1973,CAN,Canada,1.479
+1973,BHM,Bahamas,0.71
+1973,CUB,Cuba,-1.886
+1973,HAI,Haiti,0.534
+1973,DOM,Dominican Republic,1.111
+1973,JAM,Jamaica,0.16
+1973,TRI,Trinidad and Tobago,0.003
+1973,BAR,Barbados,0.808
+1973,MEX,Mexico,0.353
+1973,GUA,Guatemala,0.86
+1973,HON,Honduras,0.772
+1973,SAL,El Salvador,0.822
+1973,NIC,Nicaragua,1.459
+1973,COS,Costa Rica,1.016
+1973,PAN,Panama,0.279
+1973,COL,Colombia,0.764
+1973,VEN,Venezuela,0.633
+1973,GUY,Guyana,-0.272
+1973,ECU,Ecuador,0.359
+1973,PER,Peru,0.035
+1973,BRA,Brazil,1.172
+1973,BOL,Bolivia,1.244
+1973,PAR,Paraguay,1.101
+1973,CHL,Chile,0.113
+1973,ARG,Argentina,0.269
+1973,URU,Uruguay,1.24
+1973,UKG,United Kingdom,2.118
+1973,IRE,Ireland,1.353
+1973,NTH,Netherlands,1.529
+1973,BEL,Belgium,1.562
+1973,LUX,Luxembourg,1.484
+1973,FRN,France,1.746
+1973,SPN,Spain,1.293
+1973,POR,Portugal,2.352
+1973,GFR,German Federal Republic,1.63
+1973,GDR,German Democratic Republic,-2.366
+1973,POL,Poland,-2.338
+1973,AUS,Austria,1.28
+1973,HUN,Hungary,-2.339
+1973,CZE,Czechoslovakia,-2.326
+1973,ITA,Italy,1.562
+1973,MLT,Malta,0.128
+1973,ALB,Albania,-1.377
+1973,YUG,Yugoslavia,-0.573
+1973,GRC,Greece,1.086
+1973,CYP,Cyprus,0.047
+1973,BUL,Bulgaria,-2.333
+1973,ROM,Romania,-1.266
+1973,RUS,Russia,-2.314
+1973,UKR,Ukraine,-2.315
+1973,BLR,Belarus,-2.29
+1973,FIN,Finland,1.161
+1973,SWD,Sweden,1.255
+1973,NOR,Norway,1.328
+1973,DEN,Denmark,1.437
+1973,ICE,Iceland,0.849
+1973,EQG,Equatorial Guinea,-0.768
+1973,GAM,Gambia,0.103
+1973,MLI,Mali,-0.92
+1973,SEN,Senegal,-0.326
+1973,BEN,Benin,0.058
+1973,MAA,Mauritania,-0.735
+1973,NIR,Niger,-0.233
+1973,CDI,Ivory Coast,0.356
+1973,GUI,Guinea,-0.934
+1973,BFO,Burkina Faso,-0.055
+1973,LBR,Liberia,0.238
+1973,SIE,Sierra Leone,-0.127
+1973,GHA,Ghana,0.196
+1973,TOG,Togo,-0.049
+1973,CAO,Cameroon,-0.269
+1973,NIG,Nigeria,-0.307
+1973,GAB,Gabon,-0.018
+1973,CEN,Central African Republic,0.144
+1973,CHA,Chad,-0.267
+1973,CON,Congo,-0.782
+1973,DRC,Democratic Republic of the Congo,-0.029
+1973,UGA,Uganda,-0.355
+1973,KEN,Kenya,-0.098
+1973,TAZ,Tanzania,-0.757
+1973,BUI,Burundi,-0.524
+1973,RWA,Rwanda,0.035
+1973,SOM,Somalia,-0.707
+1973,ETH,Ethiopia,-0.055
+1973,ZAM,Zambia,-0.611
+1973,MAW,Malawi,1.207
+1973,SAF,South Africa,2.662
+1973,LES,Lesotho,0.418
+1973,BOT,Botswana,0.216
+1973,SWA,Swaziland,0.508
+1973,MAG,Madagascar,-0.261
+1973,MAS,Mauritius,-0.094
+1973,MOR,Morocco,-0.255
+1973,ALG,Algeria,-1.013
+1973,TUN,Tunisia,-0.398
+1973,LIB,Libya,-0.908
+1973,SUD,Sudan,-0.827
+1973,IRN,Iran,0.319
+1973,TUR,Turkey,0.554
+1973,IRQ,Iraq,-0.973
+1973,EGY,Egypt,-0.765
+1973,SYR,Syria,-0.896
+1973,LEB,Lebanon,-0.361
+1973,JOR,Jordan,-0.112
+1973,ISR,Israel,1.638
+1973,SAU,Saudi Arabia,-0.365
+1973,YAR,Yemen Arab Republic,-0.895
+1973,YPR,Yemen People's Republic,-0.971
+1973,KUW,Kuwait,-0.567
+1973,BAH,Bahrain,-0.451
+1973,QAT,Qatar,-0.439
+1973,UAE,United Arab Emirates,-0.58
+1973,OMA,Oman,-0.499
+1973,AFG,Afghanistan,-0.398
+1973,CHN,China,-0.721
+1973,MON,Mongolia,-2.302
+1973,JPN,Japan,1.321
+1973,IND,India,-0.334
+1973,BHU,Bhutan,0.204
+1973,PAK,Pakistan,-0.312
+1973,MYA,Myanmar,0.024
+1973,SRI,Sri Lanka,-0.242
+1973,NEP,Nepal,0.299
+1973,THI,Thailand,0.302
+1973,CAM,Cambodia,0.648
+1973,LAO,Laos,0.433
+1973,MAL,Malaysia,0.004
+1973,SIN,Singapore,0.223
+1973,PHI,Philippines,0.281
+1973,INS,Indonesia,-0.057
+1973,AUL,Australia,0.894
+1973,NEW,New Zealand,0.976
+1973,FIJ,Fiji,0.499
+1974,USA,United States of America,2.031
+1974,CAN,Canada,1.471
+1974,BHM,Bahamas,0.688
+1974,CUB,Cuba,-2.047
+1974,HAI,Haiti,0.683
+1974,DOM,Dominican Republic,0.901
+1974,JAM,Jamaica,0.147
+1974,TRI,Trinidad and Tobago,-0.009
+1974,BAR,Barbados,0.87
+1974,GRN,Grenada,0.504
+1974,MEX,Mexico,0.385
+1974,GUA,Guatemala,0.827
+1974,HON,Honduras,0.891
+1974,SAL,El Salvador,0.842
+1974,NIC,Nicaragua,1.575
+1974,COS,Costa Rica,1.138
+1974,PAN,Panama,0.526
+1974,COL,Colombia,0.804
+1974,VEN,Venezuela,0.654
+1974,GUY,Guyana,-0.437
+1974,ECU,Ecuador,0.543
+1974,PER,Peru,0.1
+1974,BRA,Brazil,0.652
+1974,BOL,Bolivia,1.397
+1974,PAR,Paraguay,1.002
+1974,CHL,Chile,0.84
+1974,ARG,Argentina,0.17
+1974,URU,Uruguay,1.222
+1974,UKG,United Kingdom,1.777
+1974,IRE,Ireland,1.37
+1974,NTH,Netherlands,1.57
+1974,BEL,Belgium,1.621
+1974,LUX,Luxembourg,1.614
+1974,FRN,France,1.318
+1974,SPN,Spain,0.843
+1974,POR,Portugal,0.85
+1974,GFR,German Federal Republic,1.625
+1974,GDR,German Democratic Republic,-2.336
+1974,POL,Poland,-2.318
+1974,AUS,Austria,1.272
+1974,HUN,Hungary,-2.323
+1974,CZE,Czechoslovakia,-2.322
+1974,ITA,Italy,1.513
+1974,MLT,Malta,-0.224
+1974,ALB,Albania,-1.389
+1974,YUG,Yugoslavia,-0.689
+1974,GRC,Greece,0.662
+1974,CYP,Cyprus,-0.1
+1974,BUL,Bulgaria,-2.321
+1974,ROM,Romania,-1.172
+1974,RUS,Russia,-2.314
+1974,UKR,Ukraine,-2.307
+1974,BLR,Belarus,-2.292
+1974,FIN,Finland,0.843
+1974,SWD,Sweden,1.148
+1974,NOR,Norway,1.297
+1974,DEN,Denmark,1.504
+1974,ICE,Iceland,1.176
+1974,GNB,Guinea-Bissau,-0.626
+1974,EQG,Equatorial Guinea,-0.799
+1974,GAM,Gambia,-0.114
+1974,MLI,Mali,-0.801
+1974,SEN,Senegal,-0.465
+1974,BEN,Benin,-0.273
+1974,MAA,Mauritania,-0.776
+1974,NIR,Niger,-0.253
+1974,CDI,Ivory Coast,0.381
+1974,GUI,Guinea,-0.992
+1974,BFO,Burkina Faso,-0.263
+1974,LBR,Liberia,-0.309
+1974,SIE,Sierra Leone,-0.327
+1974,GHA,Ghana,0.063
+1974,TOG,Togo,-0.284
+1974,CAO,Cameroon,-0.294
+1974,NIG,Nigeria,-0.117
+1974,GAB,Gabon,-0.131
+1974,CEN,Central African Republic,0.047
+1974,CHA,Chad,-0.327
+1974,CON,Congo,-0.568
+1974,DRC,Democratic Republic of the Congo,-0.285
+1974,UGA,Uganda,-0.54
+1974,KEN,Kenya,0.103
+1974,TAZ,Tanzania,-0.676
+1974,BUI,Burundi,-0.807
+1974,RWA,Rwanda,-0.025
+1974,SOM,Somalia,-0.649
+1974,ETH,Ethiopia,-0.095
+1974,ZAM,Zambia,-0.686
+1974,MAW,Malawi,1.22
+1974,SAF,South Africa,2.666
+1974,LES,Lesotho,0.47
+1974,BOT,Botswana,0.113
+1974,SWA,Swaziland,0.46
+1974,MAG,Madagascar,-0.43
+1974,MAS,Mauritius,-0.487
+1974,MOR,Morocco,0.036
+1974,ALG,Algeria,-1.086
+1974,TUN,Tunisia,-0.161
+1974,LIB,Libya,-0.097
+1974,SUD,Sudan,-0.739
+1974,IRN,Iran,0.303
+1974,TUR,Turkey,0.4
+1974,IRQ,Iraq,-0.997
+1974,EGY,Egypt,-0.442
+1974,SYR,Syria,-0.921
+1974,LEB,Lebanon,-0.136
+1974,JOR,Jordan,0.035
+1974,ISR,Israel,2.023
+1974,SAU,Saudi Arabia,-0.244
+1974,YAR,Yemen Arab Republic,-0.888
+1974,YPR,Yemen People's Republic,-1.135
+1974,KUW,Kuwait,-0.777
+1974,BAH,Bahrain,-0.362
+1974,QAT,Qatar,-0.233
+1974,UAE,United Arab Emirates,-0.519
+1974,OMA,Oman,-0.159
+1974,AFG,Afghanistan,-0.375
+1974,CHN,China,-0.782
+1974,MON,Mongolia,-2.302
+1974,JPN,Japan,1.059
+1974,IND,India,-0.587
+1974,BHU,Bhutan,-0.198
+1974,PAK,Pakistan,-0.258
+1974,BNG,Bangladesh,-0.249
+1974,MYA,Myanmar,0.081
+1974,SRI,Sri Lanka,-0.171
+1974,NEP,Nepal,0.389
+1974,THI,Thailand,0.359
+1974,CAM,Cambodia,0.383
+1974,LAO,Laos,0.543
+1974,MAL,Malaysia,0.124
+1974,SIN,Singapore,0.272
+1974,PHI,Philippines,0.282
+1974,INS,Indonesia,0.05
+1974,AUL,Australia,0.982
+1974,NEW,New Zealand,0.919
+1974,FIJ,Fiji,0.506
+1975,USA,United States of America,2.145
+1975,CAN,Canada,1.457
+1975,BHM,Bahamas,0.933
+1975,CUB,Cuba,-1.873
+1975,HAI,Haiti,1.077
+1975,DOM,Dominican Republic,0.829
+1975,JAM,Jamaica,-0.021
+1975,TRI,Trinidad and Tobago,-0.054
+1975,BAR,Barbados,0.974
+1975,GRN,Grenada,0.386
+1975,MEX,Mexico,0.109
+1975,GUA,Guatemala,0.898
+1975,HON,Honduras,0.956
+1975,SAL,El Salvador,0.959
+1975,NIC,Nicaragua,1.687
+1975,COS,Costa Rica,1.295
+1975,PAN,Panama,0.241
+1975,COL,Colombia,0.612
+1975,VEN,Venezuela,0.486
+1975,GUY,Guyana,-0.545
+1975,SUR,Suriname,0.409
+1975,ECU,Ecuador,0.366
+1975,PER,Peru,0.011
+1975,BRA,Brazil,0.335
+1975,BOL,Bolivia,0.942
+1975,PAR,Paraguay,1.048
+1975,CHL,Chile,0.786
+1975,ARG,Argentina,0.096
+1975,URU,Uruguay,0.993
+1975,UKG,United Kingdom,1.859
+1975,IRE,Ireland,1.334
+1975,NTH,Netherlands,1.674
+1975,BEL,Belgium,1.669
+1975,LUX,Luxembourg,1.639
+1975,FRN,France,1.475
+1975,SPN,Spain,0.687
+1975,POR,Portugal,0.483
+1975,GFR,German Federal Republic,1.805
+1975,GDR,German Democratic Republic,-2.402
+1975,POL,Poland,-2.383
+1975,AUS,Austria,0.997
+1975,HUN,Hungary,-2.397
+1975,CZE,Czechoslovakia,-2.384
+1975,ITA,Italy,1.447
+1975,MLT,Malta,-0.369
+1975,ALB,Albania,-1.1
+1975,YUG,Yugoslavia,-0.643
+1975,GRC,Greece,0.447
+1975,CYP,Cyprus,-0.175
+1975,BUL,Bulgaria,-2.387
+1975,ROM,Romania,-1.326
+1975,RUS,Russia,-2.402
+1975,UKR,Ukraine,-2.388
+1975,BLR,Belarus,-2.359
+1975,FIN,Finland,0.654
+1975,SWD,Sweden,0.992
+1975,NOR,Norway,1.274
+1975,DEN,Denmark,1.476
+1975,ICE,Iceland,1.186
+1975,CAP,Cape Verde,-1.284
+1975,STP,Sao Tome and Principe,-0.989
+1975,GNB,Guinea-Bissau,-0.624
+1975,EQG,Equatorial Guinea,-0.715
+1975,GAM,Gambia,-0.169
+1975,MLI,Mali,-0.704
+1975,SEN,Senegal,-0.557
+1975,BEN,Benin,-0.907
+1975,MAA,Mauritania,-0.577
+1975,NIR,Niger,-0.247
+1975,CDI,Ivory Coast,0.404
+1975,GUI,Guinea,-0.819
+1975,BFO,Burkina Faso,-0.204
+1975,LBR,Liberia,0.716
+1975,SIE,Sierra Leone,-0.029
+1975,GHA,Ghana,-0.193
+1975,TOG,Togo,-0.169
+1975,CAO,Cameroon,-0.346
+1975,NIG,Nigeria,-0.345
+1975,GAB,Gabon,0.453
+1975,CEN,Central African Republic,0.767
+1975,CHA,Chad,-0.39
+1975,CON,Congo,-0.602
+1975,DRC,Democratic Republic of the Congo,-0.091
+1975,UGA,Uganda,-0.529
+1975,KEN,Kenya,0.108
+1975,TAZ,Tanzania,-0.676
+1975,BUI,Burundi,-0.695
+1975,RWA,Rwanda,-0.25
+1975,SOM,Somalia,-0.609
+1975,ETH,Ethiopia,-0.077
+1975,MZM,Mozambique,-0.754
+1975,ZAM,Zambia,-0.54
+1975,MAW,Malawi,1.167
+1975,LES,Lesotho,0.331
+1975,BOT,Botswana,-0.17
+1975,SWA,Swaziland,0.676
+1975,MAG,Madagascar,-0.61
+1975,COM,Comoros,-0.769
+1975,MAS,Mauritius,-0.145
+1975,MOR,Morocco,0.053
+1975,ALG,Algeria,-0.89
+1975,TUN,Tunisia,-0.15
+1975,LIB,Libya,-0.371
+1975,SUD,Sudan,-0.605
+1975,IRN,Iran,0.241
+1975,TUR,Turkey,0.312
+1975,IRQ,Iraq,-0.733
+1975,EGY,Egypt,-0.453
+1975,SYR,Syria,-0.811
+1975,LEB,Lebanon,-0.136
+1975,JOR,Jordan,0.021
+1975,ISR,Israel,2.326
+1975,SAU,Saudi Arabia,0.064
+1975,YAR,Yemen Arab Republic,-0.803
+1975,YPR,Yemen People's Republic,-1.019
+1975,KUW,Kuwait,-0.536
+1975,BAH,Bahrain,-0.242
+1975,QAT,Qatar,-0.159
+1975,UAE,United Arab Emirates,-0.294
+1975,OMA,Oman,0.005
+1975,AFG,Afghanistan,-0.248
+1975,CHN,China,-0.503
+1975,MON,Mongolia,-2.374
+1975,JPN,Japan,0.952
+1975,IND,India,-0.362
+1975,BHU,Bhutan,-0.111
+1975,PAK,Pakistan,-0.162
+1975,BNG,Bangladesh,-0.215
+1975,MYA,Myanmar,-0.002
+1975,SRI,Sri Lanka,-0.221
+1975,MAD,Maldives,0.017
+1975,NEP,Nepal,0.28
+1975,THI,Thailand,0.254
+1975,CAM,Cambodia,0.056
+1975,LAO,Laos,-0.082
+1975,MAL,Malaysia,-0.029
+1975,SIN,Singapore,0.224
+1975,PHI,Philippines,0.211
+1975,INS,Indonesia,-0.048
+1975,AUL,Australia,1.092
+1975,PNG,Papua New Guinea,0.494
+1975,NEW,New Zealand,0.924
+1975,FIJ,Fiji,0.396
+1976,USA,United States of America,2.393
+1976,CAN,Canada,1.501
+1976,BHM,Bahamas,0.711
+1976,CUB,Cuba,-1.763
+1976,HAI,Haiti,1.123
+1976,DOM,Dominican Republic,0.619
+1976,JAM,Jamaica,-0.089
+1976,TRI,Trinidad and Tobago,-0.206
+1976,BAR,Barbados,0.405
+1976,GRN,Grenada,0.547
+1976,MEX,Mexico,0.116
+1976,GUA,Guatemala,1.185
+1976,HON,Honduras,0.912
+1976,SAL,El Salvador,0.738
+1976,NIC,Nicaragua,1.273
+1976,COS,Costa Rica,1.119
+1976,PAN,Panama,-0.1
+1976,COL,Colombia,0.356
+1976,VEN,Venezuela,0.285
+1976,GUY,Guyana,-0.555
+1976,SUR,Suriname,0.414
+1976,ECU,Ecuador,0.203
+1976,PER,Peru,-0.175
+1976,BRA,Brazil,0.287
+1976,BOL,Bolivia,0.589
+1976,PAR,Paraguay,1.139
+1976,CHL,Chile,0.693
+1976,ARG,Argentina,0.174
+1976,URU,Uruguay,1.097
+1976,UKG,United Kingdom,2.071
+1976,IRE,Ireland,1.376
+1976,NTH,Netherlands,1.665
+1976,BEL,Belgium,1.781
+1976,LUX,Luxembourg,1.813
+1976,FRN,France,1.863
+1976,SPN,Spain,0.724
+1976,POR,Portugal,0.791
+1976,GFR,German Federal Republic,1.979
+1976,GDR,German Democratic Republic,-2.308
+1976,POL,Poland,-2.201
+1976,AUS,Austria,1.003
+1976,HUN,Hungary,-2.293
+1976,CZE,Czechoslovakia,-2.3
+1976,ITA,Italy,1.57
+1976,MLT,Malta,-0.376
+1976,ALB,Albania,-1.209
+1976,YUG,Yugoslavia,-0.568
+1976,GRC,Greece,0.361
+1976,CYP,Cyprus,-0.304
+1976,BUL,Bulgaria,-2.303
+1976,ROM,Romania,-1.606
+1976,RUS,Russia,-2.304
+1976,UKR,Ukraine,-2.293
+1976,BLR,Belarus,-2.283
+1976,FIN,Finland,0.788
+1976,SWD,Sweden,1.072
+1976,NOR,Norway,1.147
+1976,DEN,Denmark,1.41
+1976,ICE,Iceland,1.13
+1976,CAP,Cape Verde,-1.278
+1976,STP,Sao Tome and Principe,-0.979
+1976,GNB,Guinea-Bissau,-0.636
+1976,EQG,Equatorial Guinea,-0.641
+1976,GAM,Gambia,-0.232
+1976,MLI,Mali,-0.449
+1976,SEN,Senegal,-0.518
+1976,BEN,Benin,-0.791
+1976,MAA,Mauritania,-0.371
+1976,NIR,Niger,-0.221
+1976,CDI,Ivory Coast,0.263
+1976,GUI,Guinea,-0.849
+1976,BFO,Burkina Faso,-0.172
+1976,LBR,Liberia,0.42
+1976,SIE,Sierra Leone,-0.161
+1976,GHA,Ghana,-0.336
+1976,TOG,Togo,-0.33
+1976,CAO,Cameroon,-0.393
+1976,NIG,Nigeria,-0.565
+1976,GAB,Gabon,0.32
+1976,CEN,Central African Republic,0.566
+1976,CHA,Chad,-0.28
+1976,CON,Congo,-0.583
+1976,DRC,Democratic Republic of the Congo,-0.084
+1976,UGA,Uganda,-0.596
+1976,KEN,Kenya,-0.023
+1976,TAZ,Tanzania,-0.389
+1976,BUI,Burundi,-0.624
+1976,RWA,Rwanda,-0.38
+1976,SOM,Somalia,-0.553
+1976,ETH,Ethiopia,-0.272
+1976,ANG,Angola,-0.633
+1976,MZM,Mozambique,-0.749
+1976,ZAM,Zambia,-0.525
+1976,MAW,Malawi,1.076
+1976,LES,Lesotho,0.187
+1976,BOT,Botswana,-0.047
+1976,SWA,Swaziland,0.609
+1976,MAG,Madagascar,-0.579
+1976,COM,Comoros,-0.717
+1976,MAS,Mauritius,-0.316
+1976,MOR,Morocco,-0.078
+1976,ALG,Algeria,-0.765
+1976,TUN,Tunisia,-0.155
+1976,LIB,Libya,-0.35
+1976,SUD,Sudan,-0.532
+1976,IRN,Iran,0.419
+1976,TUR,Turkey,0.271
+1976,IRQ,Iraq,-0.62
+1976,EGY,Egypt,-0.467
+1976,SYR,Syria,-0.91
+1976,LEB,Lebanon,-0.229
+1976,JOR,Jordan,-0.073
+1976,ISR,Israel,2.395
+1976,SAU,Saudi Arabia,-0.048
+1976,YAR,Yemen Arab Republic,-0.757
+1976,YPR,Yemen People's Republic,-0.923
+1976,KUW,Kuwait,-0.38
+1976,BAH,Bahrain,-0.216
+1976,QAT,Qatar,-0.183
+1976,UAE,United Arab Emirates,-0.314
+1976,OMA,Oman,-0.091
+1976,AFG,Afghanistan,-0.321
+1976,CHN,China,-0.814
+1976,MON,Mongolia,-2.285
+1976,JPN,Japan,1.112
+1976,IND,India,-0.421
+1976,BHU,Bhutan,-0.159
+1976,PAK,Pakistan,-0.262
+1976,BNG,Bangladesh,-0.255
+1976,MYA,Myanmar,-0.159
+1976,SRI,Sri Lanka,-0.333
+1976,MAD,Maldives,-0.224
+1976,NEP,Nepal,0.154
+1976,THI,Thailand,-0.009
+1976,CAM,Cambodia,-0.188
+1976,LAO,Laos,-0.289
+1976,MAL,Malaysia,-0.094
+1976,SIN,Singapore,0.097
+1976,PHI,Philippines,-0.017
+1976,INS,Indonesia,-0.114
+1976,AUL,Australia,1.212
+1976,PNG,Papua New Guinea,0.673
+1976,NEW,New Zealand,1.101
+1976,FIJ,Fiji,0.538
+1977,USA,United States of America,2.206
+1977,CAN,Canada,1.687
+1977,BHM,Bahamas,0.537
+1977,CUB,Cuba,-1.793
+1977,HAI,Haiti,0.367
+1977,DOM,Dominican Republic,0.631
+1977,JAM,Jamaica,-0.184
+1977,TRI,Trinidad and Tobago,-0.276
+1977,BAR,Barbados,0.138
+1977,GRN,Grenada,0.382
+1977,MEX,Mexico,0.205
+1977,GUA,Guatemala,1.192
+1977,HON,Honduras,0.802
+1977,SAL,El Salvador,0.829
+1977,NIC,Nicaragua,1.062
+1977,COS,Costa Rica,0.796
+1977,PAN,Panama,0.351
+1977,COL,Colombia,0.316
+1977,VEN,Venezuela,0.123
+1977,GUY,Guyana,-0.635
+1977,SUR,Suriname,0.498
+1977,ECU,Ecuador,0.218
+1977,PER,Peru,0.021
+1977,BRA,Brazil,0.238
+1977,BOL,Bolivia,0.446
+1977,PAR,Paraguay,0.965
+1977,CHL,Chile,0.439
+1977,ARG,Argentina,0.187
+1977,URU,Uruguay,0.879
+1977,UKG,United Kingdom,1.957
+1977,IRE,Ireland,1.432
+1977,NTH,Netherlands,1.555
+1977,BEL,Belgium,1.766
+1977,LUX,Luxembourg,1.721
+1977,FRN,France,1.72
+1977,SPN,Spain,0.671
+1977,POR,Portugal,0.784
+1977,GFR,German Federal Republic,1.874
+1977,GDR,German Democratic Republic,-2.444
+1977,POL,Poland,-2.337
+1977,AUS,Austria,1.109
+1977,HUN,Hungary,-2.467
+1977,CZE,Czechoslovakia,-2.45
+1977,ITA,Italy,1.471
+1977,MLT,Malta,-0.349
+1977,ALB,Albania,-1.351
+1977,YUG,Yugoslavia,-0.474
+1977,GRC,Greece,0.384
+1977,CYP,Cyprus,-0.332
+1977,BUL,Bulgaria,-2.454
+1977,ROM,Romania,-1.227
+1977,RUS,Russia,-2.454
+1977,UKR,Ukraine,-2.44
+1977,BLR,Belarus,-2.454
+1977,FIN,Finland,0.902
+1977,SWD,Sweden,1.075
+1977,NOR,Norway,1.248
+1977,DEN,Denmark,1.396
+1977,ICE,Iceland,1.219
+1977,CAP,Cape Verde,-0.866
+1977,STP,Sao Tome and Principe,-0.799
+1977,GNB,Guinea-Bissau,-0.498
+1977,EQG,Equatorial Guinea,-0.642
+1977,GAM,Gambia,-0.297
+1977,MLI,Mali,-0.361
+1977,SEN,Senegal,-0.328
+1977,BEN,Benin,-0.646
+1977,MAA,Mauritania,-0.227
+1977,NIR,Niger,-0.167
+1977,CDI,Ivory Coast,0.549
+1977,GUI,Guinea,-0.64
+1977,BFO,Burkina Faso,-0.298
+1977,LBR,Liberia,0.367
+1977,SIE,Sierra Leone,-0.185
+1977,GHA,Ghana,-0.266
+1977,TOG,Togo,-0.346
+1977,CAO,Cameroon,-0.371
+1977,NIG,Nigeria,-0.367
+1977,GAB,Gabon,-0.063
+1977,CEN,Central African Republic,0.43
+1977,CHA,Chad,-0.189
+1977,CON,Congo,-0.656
+1977,DRC,Democratic Republic of the Congo,0.126
+1977,UGA,Uganda,-0.676
+1977,KEN,Kenya,-0.228
+1977,TAZ,Tanzania,-0.438
+1977,BUI,Burundi,-0.537
+1977,RWA,Rwanda,-0.215
+1977,SOM,Somalia,-0.388
+1977,DJI,Djibouti,-0.36
+1977,ETH,Ethiopia,-0.324
+1977,ANG,Angola,-0.621
+1977,MZM,Mozambique,-0.553
+1977,ZAM,Zambia,-0.439
+1977,MAW,Malawi,0.861
+1977,LES,Lesotho,0.179
+1977,BOT,Botswana,-0.068
+1977,SWA,Swaziland,0.48
+1977,MAG,Madagascar,-0.525
+1977,COM,Comoros,-0.578
+1977,MAS,Mauritius,-0.327
+1977,MOR,Morocco,-0.054
+1977,ALG,Algeria,-0.597
+1977,TUN,Tunisia,-0.259
+1977,LIB,Libya,-0.344
+1977,SUD,Sudan,-0.428
+1977,IRN,Iran,0.379
+1977,TUR,Turkey,0.262
+1977,IRQ,Iraq,-0.474
+1977,EGY,Egypt,-0.409
+1977,SYR,Syria,-0.835
+1977,LEB,Lebanon,-0.098
+1977,JOR,Jordan,-0.193
+1977,ISR,Israel,2.202
+1977,SAU,Saudi Arabia,-0.136
+1977,YAR,Yemen Arab Republic,-0.698
+1977,YPR,Yemen People's Republic,-0.679
+1977,KUW,Kuwait,-0.357
+1977,BAH,Bahrain,-0.29
+1977,QAT,Qatar,-0.271
+1977,UAE,United Arab Emirates,-0.338
+1977,OMA,Oman,-0.15
+1977,AFG,Afghanistan,-0.188
+1977,CHN,China,-0.797
+1977,MON,Mongolia,-2.432
+1977,JPN,Japan,1.09
+1977,IND,India,-0.386
+1977,BHU,Bhutan,-0.265
+1977,PAK,Pakistan,-0.136
+1977,BNG,Bangladesh,-0.234
+1977,MYA,Myanmar,-0.361
+1977,SRI,Sri Lanka,-0.323
+1977,MAD,Maldives,-0.156
+1977,NEP,Nepal,0.075
+1977,THI,Thailand,0.084
+1977,LAO,Laos,-0.367
+1977,DRV,Vietnam,-1.198
+1977,MAL,Malaysia,-0.061
+1977,SIN,Singapore,0.149
+1977,PHI,Philippines,0.024
+1977,INS,Indonesia,-0.108
+1977,AUL,Australia,1.371
+1977,PNG,Papua New Guinea,0.555
+1977,NEW,New Zealand,1.222
+1977,FIJ,Fiji,0.598
+1977,WSM,Samoa,0.572
+1978,USA,United States of America,2.228
+1978,CAN,Canada,1.768
+1978,BHM,Bahamas,0.525
+1978,CUB,Cuba,-1.643
+1978,HAI,Haiti,0.308
+1978,DOM,Dominican Republic,0.664
+1978,JAM,Jamaica,-0.188
+1978,TRI,Trinidad and Tobago,-0.126
+1978,BAR,Barbados,-0.118
+1978,GRN,Grenada,0.287
+1978,MEX,Mexico,0.309
+1978,GUA,Guatemala,1.14
+1978,HON,Honduras,0.742
+1978,SAL,El Salvador,0.84
+1978,NIC,Nicaragua,0.953
+1978,COS,Costa Rica,0.478
+1978,PAN,Panama,0.332
+1978,COL,Colombia,0.301
+1978,VEN,Venezuela,0.05
+1978,GUY,Guyana,-0.312
+1978,SUR,Suriname,0.413
+1978,ECU,Ecuador,0.26
+1978,PER,Peru,0.029
+1978,BRA,Brazil,0.25
+1978,BOL,Bolivia,0.352
+1978,PAR,Paraguay,0.722
+1978,CHL,Chile,0.45
+1978,ARG,Argentina,0.089
+1978,URU,Uruguay,0.64
+1978,UKG,United Kingdom,2.018
+1978,IRE,Ireland,1.442
+1978,NTH,Netherlands,1.628
+1978,BEL,Belgium,1.688
+1978,LUX,Luxembourg,1.789
+1978,FRN,France,1.685
+1978,SPN,Spain,0.728
+1978,POR,Portugal,0.977
+1978,GFR,German Federal Republic,1.886
+1978,GDR,German Democratic Republic,-2.377
+1978,POL,Poland,-2.057
+1978,AUS,Austria,1.243
+1978,HUN,Hungary,-2.441
+1978,CZE,Czechoslovakia,-2.461
+1978,ITA,Italy,1.641
+1978,MLT,Malta,-0.228
+1978,ALB,Albania,-1.645
+1978,YUG,Yugoslavia,-0.439
+1978,GRC,Greece,0.644
+1978,CYP,Cyprus,-0.224
+1978,BUL,Bulgaria,-2.486
+1978,ROM,Romania,-0.904
+1978,RUS,Russia,-2.494
+1978,UKR,Ukraine,-2.49
+1978,BLR,Belarus,-2.493
+1978,FIN,Finland,1.022
+1978,SWD,Sweden,1.119
+1978,NOR,Norway,1.285
+1978,DEN,Denmark,1.449
+1978,ICE,Iceland,1.257
+1978,CAP,Cape Verde,-0.631
+1978,STP,Sao Tome and Principe,-0.918
+1978,GNB,Guinea-Bissau,-0.675
+1978,EQG,Equatorial Guinea,-0.871
+1978,GAM,Gambia,-0.078
+1978,MLI,Mali,-0.278
+1978,SEN,Senegal,-0.101
+1978,BEN,Benin,-0.517
+1978,MAA,Mauritania,-0.036
+1978,NIR,Niger,-0.21
+1978,CDI,Ivory Coast,0.534
+1978,GUI,Guinea,-0.551
+1978,BFO,Burkina Faso,-0.007
+1978,LBR,Liberia,0.309
+1978,SIE,Sierra Leone,-0.059
+1978,GHA,Ghana,-0.11
+1978,TOG,Togo,0
+1978,CAO,Cameroon,-0.133
+1978,NIG,Nigeria,-0.242
+1978,GAB,Gabon,0.331
+1978,CEN,Central African Republic,0.298
+1978,CHA,Chad,0.036
+1978,CON,Congo,-0.957
+1978,DRC,Democratic Republic of the Congo,0.299
+1978,UGA,Uganda,-0.38
+1978,KEN,Kenya,-0.166
+1978,TAZ,Tanzania,-0.346
+1978,BUI,Burundi,-0.421
+1978,RWA,Rwanda,0.001
+1978,SOM,Somalia,-0.257
+1978,DJI,Djibouti,-0.364
+1978,ETH,Ethiopia,-0.514
+1978,ANG,Angola,-0.85
+1978,MZM,Mozambique,-0.815
+1978,ZAM,Zambia,-0.335
+1978,MAW,Malawi,0.628
+1978,LES,Lesotho,0.386
+1978,BOT,Botswana,0
+1978,SWA,Swaziland,0.645
+1978,MAG,Madagascar,-0.401
+1978,COM,Comoros,-0.314
+1978,MAS,Mauritius,-0.189
+1978,SEY,Seychelles,-1.322
+1978,MOR,Morocco,-0.127
+1978,ALG,Algeria,-0.635
+1978,TUN,Tunisia,-0.198
+1978,LIB,Libya,-0.251
+1978,SUD,Sudan,-0.411
+1978,IRN,Iran,0.134
+1978,TUR,Turkey,0.331
+1978,IRQ,Iraq,-0.989
+1978,EGY,Egypt,-0.271
+1978,SYR,Syria,-1.016
+1978,LEB,Lebanon,0.018
+1978,JOR,Jordan,-0.186
+1978,ISR,Israel,1.997
+1978,SAU,Saudi Arabia,-0.147
+1978,YAR,Yemen Arab Republic,-0.623
+1978,YPR,Yemen People's Republic,-0.972
+1978,KUW,Kuwait,-0.281
+1978,BAH,Bahrain,-0.234
+1978,QAT,Qatar,-0.203
+1978,UAE,United Arab Emirates,-0.292
+1978,OMA,Oman,-0.141
+1978,AFG,Afghanistan,-1.097
+1978,CHN,China,-0.51
+1978,MON,Mongolia,-2.465
+1978,JPN,Japan,1.029
+1978,IND,India,-0.204
+1978,BHU,Bhutan,-0.289
+1978,PAK,Pakistan,-0.281
+1978,BNG,Bangladesh,-0.179
+1978,MYA,Myanmar,-0.004
+1978,SRI,Sri Lanka,-0.265
+1978,MAD,Maldives,-0.181
+1978,NEP,Nepal,0.376
+1978,THI,Thailand,0.099
+1978,CAM,Cambodia,-0.128
+1978,LAO,Laos,-0.62
+1978,DRV,Vietnam,-1.15
+1978,MAL,Malaysia,-0.164
+1978,SIN,Singapore,0.235
+1978,PHI,Philippines,-0.002
+1978,INS,Indonesia,-0.162
+1978,AUL,Australia,1.516
+1978,PNG,Papua New Guinea,0.454
+1978,NEW,New Zealand,1.388
+1978,FIJ,Fiji,0.44
+1978,WSM,Samoa,0.464
+1979,USA,United States of America,2.277
+1979,CAN,Canada,1.81
+1979,BHM,Bahamas,0.188
+1979,CUB,Cuba,-1.39
+1979,HAI,Haiti,0.131
+1979,DOM,Dominican Republic,0.301
+1979,JAM,Jamaica,-0.204
+1979,TRI,Trinidad and Tobago,-0.202
+1979,BAR,Barbados,-0.049
+1979,GRN,Grenada,-0.201
+1979,SLU,St. Lucia,-0.016
+1979,MEX,Mexico,0.067
+1979,GUA,Guatemala,1.013
+1979,HON,Honduras,0.664
+1979,SAL,El Salvador,0.26
+1979,NIC,Nicaragua,-0.143
+1979,COS,Costa Rica,0.24
+1979,PAN,Panama,0.213
+1979,COL,Colombia,0.118
+1979,VEN,Venezuela,0.069
+1979,GUY,Guyana,-0.366
+1979,SUR,Suriname,0.162
+1979,ECU,Ecuador,0.012
+1979,PER,Peru,-0.065
+1979,BRA,Brazil,0.093
+1979,BOL,Bolivia,0.116
+1979,PAR,Paraguay,0.638
+1979,CHL,Chile,0.486
+1979,ARG,Argentina,0.045
+1979,URU,Uruguay,0.514
+1979,UKG,United Kingdom,2.028
+1979,IRE,Ireland,1.414
+1979,NTH,Netherlands,1.574
+1979,BEL,Belgium,1.868
+1979,LUX,Luxembourg,1.853
+1979,FRN,France,1.798
+1979,SPN,Spain,0.854
+1979,POR,Portugal,1.152
+1979,GFR,German Federal Republic,1.919
+1979,GDR,German Democratic Republic,-2.412
+1979,POL,Poland,-1.929
+1979,AUS,Austria,1.189
+1979,HUN,Hungary,-2.438
+1979,CZE,Czechoslovakia,-2.45
+1979,ITA,Italy,1.573
+1979,MLT,Malta,-0.198
+1979,ALB,Albania,-1.826
+1979,YUG,Yugoslavia,-0.351
+1979,GRC,Greece,0.751
+1979,CYP,Cyprus,-0.259
+1979,BUL,Bulgaria,-2.464
+1979,ROM,Romania,-0.862
+1979,RUS,Russia,-2.464
+1979,UKR,Ukraine,-2.461
+1979,BLR,Belarus,-2.459
+1979,FIN,Finland,1.076
+1979,SWD,Sweden,1.144
+1979,NOR,Norway,1.367
+1979,DEN,Denmark,1.541
+1979,ICE,Iceland,1.381
+1979,CAP,Cape Verde,-0.632
+1979,STP,Sao Tome and Principe,-0.891
+1979,GNB,Guinea-Bissau,-0.725
+1979,EQG,Equatorial Guinea,-0.183
+1979,GAM,Gambia,-0.058
+1979,MLI,Mali,-0.209
+1979,SEN,Senegal,0.172
+1979,BEN,Benin,-0.49
+1979,MAA,Mauritania,-0.6
+1979,NIR,Niger,-0.169
+1979,CDI,Ivory Coast,0.393
+1979,GUI,Guinea,-0.474
+1979,BFO,Burkina Faso,0.095
+1979,LBR,Liberia,0.204
+1979,SIE,Sierra Leone,-0.193
+1979,GHA,Ghana,-0.177
+1979,TOG,Togo,0.008
+1979,CAO,Cameroon,0.178
+1979,NIG,Nigeria,-0.177
+1979,GAB,Gabon,0.236
+1979,CEN,Central African Republic,0.434
+1979,CHA,Chad,0.049
+1979,CON,Congo,-0.88
+1979,DRC,Democratic Republic of the Congo,0.155
+1979,UGA,Uganda,-0.296
+1979,KEN,Kenya,-0.106
+1979,TAZ,Tanzania,-0.38
+1979,BUI,Burundi,-0.382
+1979,RWA,Rwanda,-0.046
+1979,SOM,Somalia,-0.223
+1979,DJI,Djibouti,-0.226
+1979,ETH,Ethiopia,-0.571
+1979,ANG,Angola,-0.907
+1979,MZM,Mozambique,-0.939
+1979,ZAM,Zambia,-0.256
+1979,MAW,Malawi,0.671
+1979,LES,Lesotho,0.193
+1979,BOT,Botswana,0.12
+1979,SWA,Swaziland,0.399
+1979,MAG,Madagascar,-0.494
+1979,COM,Comoros,-0.237
+1979,MAS,Mauritius,-0.12
+1979,SEY,Seychelles,-1.314
+1979,MOR,Morocco,0.203
+1979,ALG,Algeria,-0.803
+1979,TUN,Tunisia,-0.165
+1979,LIB,Libya,-0.354
+1979,SUD,Sudan,-0.302
+1979,IRN,Iran,-0.156
+1979,TUR,Turkey,0.403
+1979,IRQ,Iraq,-1.23
+1979,EGY,Egypt,-0.153
+1979,SYR,Syria,-1.351
+1979,LEB,Lebanon,0.133
+1979,JOR,Jordan,-0.197
+1979,ISR,Israel,2.044
+1979,SAU,Saudi Arabia,-0.089
+1979,YAR,Yemen Arab Republic,-0.754
+1979,YPR,Yemen People's Republic,-1.024
+1979,KUW,Kuwait,-0.283
+1979,BAH,Bahrain,-0.249
+1979,QAT,Qatar,-0.212
+1979,UAE,United Arab Emirates,-0.295
+1979,OMA,Oman,-0.079
+1979,AFG,Afghanistan,-1.207
+1979,CHN,China,-0.229
+1979,MON,Mongolia,-2.458
+1979,JPN,Japan,1.157
+1979,IND,India,-0.293
+1979,BHU,Bhutan,-0.241
+1979,PAK,Pakistan,-0.194
+1979,BNG,Bangladesh,-0.162
+1979,MYA,Myanmar,0.245
+1979,SRI,Sri Lanka,-0.246
+1979,MAD,Maldives,-0.096
+1979,NEP,Nepal,0.234
+1979,THI,Thailand,0.171
+1979,CAM,Cambodia,-0.162
+1979,LAO,Laos,-0.846
+1979,DRV,Vietnam,-1.288
+1979,MAL,Malaysia,-0.132
+1979,SIN,Singapore,0.313
+1979,PHI,Philippines,-0.044
+1979,INS,Indonesia,-0.149
+1979,AUL,Australia,1.472
+1979,PNG,Papua New Guinea,0.295
+1979,NEW,New Zealand,1.446
+1979,SOL,Solomon Islands,0.459
+1979,FIJ,Fiji,0.487
+1979,WSM,Samoa,0.513
+1980,USA,United States of America,2.318
+1980,CAN,Canada,1.824
+1980,BHM,Bahamas,0.179
+1980,CUB,Cuba,-1.478
+1980,HAI,Haiti,0.179
+1980,DOM,Dominican Republic,0.558
+1980,JAM,Jamaica,-0.2
+1980,TRI,Trinidad and Tobago,-0.1
+1980,BAR,Barbados,-0.095
+1980,GRN,Grenada,-0.929
+1980,SLU,St. Lucia,-0.006
+1980,MEX,Mexico,-0.143
+1980,GUA,Guatemala,1.05
+1980,HON,Honduras,0.418
+1980,SAL,El Salvador,0.231
+1980,NIC,Nicaragua,-0.555
+1980,COS,Costa Rica,0.3
+1980,PAN,Panama,0.015
+1980,COL,Colombia,0.306
+1980,VEN,Venezuela,0.042
+1980,GUY,Guyana,-0.332
+1980,SUR,Suriname,0.088
+1980,ECU,Ecuador,0.005
+1980,PER,Peru,0.063
+1980,BRA,Brazil,0.019
+1980,BOL,Bolivia,0.344
+1980,PAR,Paraguay,0.498
+1980,CHL,Chile,0.572
+1980,ARG,Argentina,0.102
+1980,URU,Uruguay,0.344
+1980,UKG,United Kingdom,1.976
+1980,IRE,Ireland,1.217
+1980,NTH,Netherlands,1.577
+1980,BEL,Belgium,1.826
+1980,LUX,Luxembourg,1.84
+1980,FRN,France,1.857
+1980,SPN,Spain,0.81
+1980,POR,Portugal,1.298
+1980,GFR,German Federal Republic,1.85
+1980,GDR,German Democratic Republic,-2.324
+1980,POL,Poland,-1.751
+1980,AUS,Austria,1.064
+1980,HUN,Hungary,-2.324
+1980,CZE,Czechoslovakia,-2.342
+1980,ITA,Italy,1.556
+1980,MLT,Malta,-0.191
+1980,ALB,Albania,-1.557
+1980,YUG,Yugoslavia,-0.304
+1980,GRC,Greece,1.032
+1980,CYP,Cyprus,-0.241
+1980,BUL,Bulgaria,-2.357
+1980,ROM,Romania,-0.895
+1980,RUS,Russia,-2.353
+1980,UKR,Ukraine,-2.351
+1980,BLR,Belarus,-2.35
+1980,FIN,Finland,0.805
+1980,SWD,Sweden,1.05
+1980,NOR,Norway,1.283
+1980,DEN,Denmark,1.351
+1980,ICE,Iceland,1.316
+1980,CAP,Cape Verde,-0.721
+1980,STP,Sao Tome and Principe,-0.956
+1980,GNB,Guinea-Bissau,-0.768
+1980,EQG,Equatorial Guinea,-0.047
+1980,GAM,Gambia,0.018
+1980,MLI,Mali,-0.334
+1980,SEN,Senegal,0.067
+1980,BEN,Benin,-0.856
+1980,MAA,Mauritania,-0.328
+1980,NIR,Niger,-0.056
+1980,CDI,Ivory Coast,0.359
+1980,GUI,Guinea,-0.604
+1980,BFO,Burkina Faso,0.11
+1980,LBR,Liberia,0.217
+1980,SIE,Sierra Leone,-0.21
+1980,GHA,Ghana,-0.124
+1980,TOG,Togo,0.028
+1980,CAO,Cameroon,0.007
+1980,NIG,Nigeria,-0.213
+1980,GAB,Gabon,0.225
+1980,CEN,Central African Republic,0.421
+1980,CHA,Chad,-0.156
+1980,CON,Congo,-0.887
+1980,DRC,Democratic Republic of the Congo,0.151
+1980,UGA,Uganda,-0.441
+1980,KEN,Kenya,-0.141
+1980,TAZ,Tanzania,-0.435
+1980,BUI,Burundi,-0.32
+1980,RWA,Rwanda,-0.073
+1980,SOM,Somalia,-0.191
+1980,DJI,Djibouti,-0.199
+1980,ETH,Ethiopia,-0.737
+1980,ANG,Angola,-1.184
+1980,MZM,Mozambique,-1.185
+1980,ZAM,Zambia,-0.241
+1980,ZIM,Zimbabwe,-0.17
+1980,MAW,Malawi,0.862
+1980,LES,Lesotho,0.224
+1980,BOT,Botswana,0.161
+1980,SWA,Swaziland,0.465
+1980,MAG,Madagascar,-0.627
+1980,COM,Comoros,-0.085
+1980,MAS,Mauritius,0.162
+1980,SEY,Seychelles,-1.417
+1980,MOR,Morocco,0.146
+1980,ALG,Algeria,-0.801
+1980,TUN,Tunisia,-0.258
+1980,LIB,Libya,-0.553
+1980,SUD,Sudan,-0.188
+1980,IRN,Iran,-0.329
+1980,TUR,Turkey,0.475
+1980,IRQ,Iraq,-1.138
+1980,EGY,Egypt,-0.077
+1980,SYR,Syria,-1.393
+1980,LEB,Lebanon,-0.105
+1980,JOR,Jordan,-0.315
+1980,ISR,Israel,2.012
+1980,SAU,Saudi Arabia,-0.129
+1980,YAR,Yemen Arab Republic,-0.678
+1980,YPR,Yemen People's Republic,-1.2
+1980,KUW,Kuwait,-0.303
+1980,BAH,Bahrain,-0.271
+1980,QAT,Qatar,-0.288
+1980,UAE,United Arab Emirates,-0.329
+1980,OMA,Oman,-0.181
+1980,AFG,Afghanistan,-1.57
+1980,CHN,China,-0.139
+1980,MON,Mongolia,-2.343
+1980,JPN,Japan,1.202
+1980,IND,India,-0.461
+1980,BHU,Bhutan,-0.269
+1980,PAK,Pakistan,-0.112
+1980,BNG,Bangladesh,-0.303
+1980,MYA,Myanmar,0.319
+1980,SRI,Sri Lanka,-0.217
+1980,MAD,Maldives,-0.357
+1980,NEP,Nepal,0.104
+1980,THI,Thailand,0.184
+1980,CAM,Cambodia,-0.062
+1980,LAO,Laos,-1.379
+1980,DRV,Vietnam,-1.499
+1980,MAL,Malaysia,-0.133
+1980,SIN,Singapore,0.265
+1980,PHI,Philippines,-0.039
+1980,INS,Indonesia,-0.226
+1980,AUL,Australia,1.395
+1980,PNG,Papua New Guinea,0.373
+1980,NEW,New Zealand,1.362
+1980,SOL,Solomon Islands,0.472
+1980,FIJ,Fiji,0.43
+1980,WSM,Samoa,0.43
+1981,USA,United States of America,2.658
+1981,CAN,Canada,1.773
+1981,BHM,Bahamas,0.131
+1981,CUB,Cuba,-1.605
+1981,HAI,Haiti,0.243
+1981,DOM,Dominican Republic,0.461
+1981,JAM,Jamaica,0.46
+1981,TRI,Trinidad and Tobago,-0.254
+1981,BAR,Barbados,-0.062
+1981,GRN,Grenada,-0.97
+1981,SLU,St. Lucia,-0.11
+1981,SVG,St. Vincent and the Grenadines,0.075
+1981,AAB,Antigua & Barbuda,0.013
+1981,MEX,Mexico,-0.339
+1981,BLZ,Belize,-0.165
+1981,GUA,Guatemala,1.081
+1981,HON,Honduras,0.38
+1981,SAL,El Salvador,0.093
+1981,NIC,Nicaragua,-0.74
+1981,COS,Costa Rica,0.221
+1981,PAN,Panama,-0.378
+1981,COL,Colombia,0.18
+1981,VEN,Venezuela,-0.035
+1981,GUY,Guyana,-0.336
+1981,SUR,Suriname,-0.153
+1981,ECU,Ecuador,-0.171
+1981,PER,Peru,-0.083
+1981,BRA,Brazil,-0.097
+1981,BOL,Bolivia,0.2
+1981,PAR,Paraguay,0.531
+1981,CHL,Chile,0.404
+1981,ARG,Argentina,0.029
+1981,URU,Uruguay,0.159
+1981,UKG,United Kingdom,1.925
+1981,IRE,Ireland,1.181
+1981,NTH,Netherlands,1.488
+1981,BEL,Belgium,1.689
+1981,LUX,Luxembourg,1.718
+1981,FRN,France,1.717
+1981,SPN,Spain,0.823
+1981,POR,Portugal,1.105
+1981,GFR,German Federal Republic,1.754
+1981,GDR,German Democratic Republic,-2.263
+1981,POL,Poland,-1.849
+1981,AUS,Austria,1.032
+1981,HUN,Hungary,-2.253
+1981,CZE,Czechoslovakia,-2.262
+1981,ITA,Italy,1.604
+1981,MLT,Malta,-0.364
+1981,ALB,Albania,-1.436
+1981,YUG,Yugoslavia,-0.35
+1981,GRC,Greece,0.757
+1981,CYP,Cyprus,-0.469
+1981,BUL,Bulgaria,-2.25
+1981,ROM,Romania,-0.944
+1981,RUS,Russia,-2.27
+1981,UKR,Ukraine,-2.268
+1981,BLR,Belarus,-2.273
+1981,FIN,Finland,0.813
+1981,SWD,Sweden,1.062
+1981,NOR,Norway,1.335
+1981,DEN,Denmark,1.335
+1981,ICE,Iceland,1.332
+1981,CAP,Cape Verde,-0.748
+1981,STP,Sao Tome and Principe,-0.889
+1981,GNB,Guinea-Bissau,-0.635
+1981,EQG,Equatorial Guinea,0.019
+1981,GAM,Gambia,-0.247
+1981,MLI,Mali,-0.551
+1981,SEN,Senegal,-0.062
+1981,BEN,Benin,-0.798
+1981,MAA,Mauritania,-0.242
+1981,NIR,Niger,-0.051
+1981,CDI,Ivory Coast,0.31
+1981,GUI,Guinea,-0.48
+1981,BFO,Burkina Faso,0.087
+1981,LBR,Liberia,0.225
+1981,SIE,Sierra Leone,-0.281
+1981,GHA,Ghana,-0.064
+1981,TOG,Togo,-0.14
+1981,CAO,Cameroon,-0.185
+1981,NIG,Nigeria,-0.217
+1981,GAB,Gabon,0.101
+1981,CEN,Central African Republic,0.301
+1981,CHA,Chad,-0.492
+1981,CON,Congo,-0.888
+1981,DRC,Democratic Republic of the Congo,0.16
+1981,UGA,Uganda,-0.56
+1981,KEN,Kenya,-0.104
+1981,TAZ,Tanzania,-0.48
+1981,BUI,Burundi,-0.362
+1981,RWA,Rwanda,-0.1
+1981,SOM,Somalia,-0.086
+1981,DJI,Djibouti,-0.219
+1981,ETH,Ethiopia,-0.905
+1981,ANG,Angola,-1.093
+1981,MZM,Mozambique,-1.202
+1981,ZAM,Zambia,-0.335
+1981,ZIM,Zimbabwe,-0.281
+1981,MAW,Malawi,0.701
+1981,LES,Lesotho,0.076
+1981,BOT,Botswana,0.176
+1981,SWA,Swaziland,0.374
+1981,MAG,Madagascar,-0.698
+1981,COM,Comoros,0.016
+1981,MAS,Mauritius,-0.214
+1981,SEY,Seychelles,-1.522
+1981,MOR,Morocco,0.057
+1981,ALG,Algeria,-0.837
+1981,TUN,Tunisia,-0.062
+1981,LIB,Libya,-0.793
+1981,SUD,Sudan,-0.132
+1981,IRN,Iran,-0.458
+1981,TUR,Turkey,0.5
+1981,IRQ,Iraq,-0.998
+1981,EGY,Egypt,-0.083
+1981,SYR,Syria,-1.162
+1981,LEB,Lebanon,-0.288
+1981,JOR,Jordan,-0.416
+1981,ISR,Israel,2.24
+1981,SAU,Saudi Arabia,-0.069
+1981,YAR,Yemen Arab Republic,-0.616
+1981,YPR,Yemen People's Republic,-1.235
+1981,KUW,Kuwait,-0.456
+1981,BAH,Bahrain,-0.376
+1981,QAT,Qatar,-0.455
+1981,UAE,United Arab Emirates,-0.4
+1981,OMA,Oman,-0.182
+1981,AFG,Afghanistan,-1.758
+1981,CHN,China,-0.108
+1981,MON,Mongolia,-2.302
+1981,JPN,Japan,1.256
+1981,IND,India,-0.438
+1981,BHU,Bhutan,-0.394
+1981,PAK,Pakistan,-0.188
+1981,BNG,Bangladesh,-0.19
+1981,MYA,Myanmar,0.22
+1981,SRI,Sri Lanka,-0.288
+1981,MAD,Maldives,-0.28
+1981,NEP,Nepal,0.096
+1981,THI,Thailand,-0.135
+1981,CAM,Cambodia,-0.09
+1981,LAO,Laos,-1.711
+1981,DRV,Vietnam,-1.774
+1981,MAL,Malaysia,-0.153
+1981,SIN,Singapore,0.204
+1981,PHI,Philippines,-0.196
+1981,INS,Indonesia,-0.357
+1981,AUL,Australia,1.449
+1981,PNG,Papua New Guinea,0.347
+1981,NEW,New Zealand,1.508
+1981,VAN,Vanuatu,-0.64
+1981,SOL,Solomon Islands,0.372
+1981,FIJ,Fiji,0.369
+1981,WSM,Samoa,0.285
+1982,USA,United States of America,2.568
+1982,CAN,Canada,1.656
+1982,BHM,Bahamas,0.128
+1982,CUB,Cuba,-1.546
+1982,HAI,Haiti,0.107
+1982,DOM,Dominican Republic,0.09
+1982,JAM,Jamaica,0.049
+1982,TRI,Trinidad and Tobago,-0.27
+1982,BAR,Barbados,0.028
+1982,DMA,Dominica,0.273
+1982,GRN,Grenada,-1.294
+1982,SLU,St. Lucia,-0.041
+1982,SVG,St. Vincent and the Grenadines,0.067
+1982,AAB,Antigua & Barbuda,-0.047
+1982,MEX,Mexico,-0.461
+1982,BLZ,Belize,-0.292
+1982,GUA,Guatemala,0.734
+1982,HON,Honduras,0.204
+1982,SAL,El Salvador,0.016
+1982,NIC,Nicaragua,-0.818
+1982,COS,Costa Rica,0.24
+1982,PAN,Panama,-0.524
+1982,COL,Colombia,0.029
+1982,VEN,Venezuela,-0.166
+1982,GUY,Guyana,-0.464
+1982,SUR,Suriname,-0.265
+1982,ECU,Ecuador,-0.213
+1982,PER,Peru,-0.201
+1982,BRA,Brazil,-0.151
+1982,BOL,Bolivia,-0.241
+1982,PAR,Paraguay,0.553
+1982,CHL,Chile,0.195
+1982,ARG,Argentina,-0.305
+1982,URU,Uruguay,0.318
+1982,UKG,United Kingdom,1.955
+1982,IRE,Ireland,1.014
+1982,NTH,Netherlands,1.553
+1982,BEL,Belgium,1.711
+1982,LUX,Luxembourg,1.717
+1982,FRN,France,1.678
+1982,SPN,Spain,0.942
+1982,POR,Portugal,1.182
+1982,GFR,German Federal Republic,1.519
+1982,GDR,German Democratic Republic,-0.996
+1982,POL,Poland,-1.864
+1982,AUS,Austria,0.876
+1982,HUN,Hungary,-2.184
+1982,CZE,Czechoslovakia,-2.182
+1982,ITA,Italy,1.558
+1982,MLT,Malta,-0.313
+1982,ALB,Albania,-1.496
+1982,YUG,Yugoslavia,-0.399
+1982,GRC,Greece,0.601
+1982,CYP,Cyprus,-0.479
+1982,BUL,Bulgaria,-2.136
+1982,ROM,Romania,-0.861
+1982,RUS,Russia,-2.197
+1982,UKR,Ukraine,-2.203
+1982,BLR,Belarus,-2.205
+1982,FIN,Finland,0.732
+1982,SWD,Sweden,0.914
+1982,NOR,Norway,1.305
+1982,DEN,Denmark,1.307
+1982,ICE,Iceland,1.286
+1982,CAP,Cape Verde,-0.768
+1982,STP,Sao Tome and Principe,-0.948
+1982,GNB,Guinea-Bissau,-0.585
+1982,EQG,Equatorial Guinea,-0.218
+1982,GAM,Gambia,-0.28
+1982,MLI,Mali,-0.522
+1982,SEN,Senegal,-0.202
+1982,BEN,Benin,-0.809
+1982,MAA,Mauritania,-0.252
+1982,NIR,Niger,-0.18
+1982,CDI,Ivory Coast,0.444
+1982,GUI,Guinea,-0.535
+1982,BFO,Burkina Faso,-0.112
+1982,LBR,Liberia,0.011
+1982,SIE,Sierra Leone,-0.352
+1982,GHA,Ghana,-0.369
+1982,TOG,Togo,-0.249
+1982,CAO,Cameroon,-0.221
+1982,NIG,Nigeria,-0.247
+1982,GAB,Gabon,-0.131
+1982,CEN,Central African Republic,-0.096
+1982,CHA,Chad,-0.262
+1982,CON,Congo,-0.909
+1982,DRC,Democratic Republic of the Congo,0.14
+1982,UGA,Uganda,-0.654
+1982,KEN,Kenya,-0.261
+1982,TAZ,Tanzania,-0.448
+1982,BUI,Burundi,-0.426
+1982,RWA,Rwanda,-0.203
+1982,SOM,Somalia,0.064
+1982,DJI,Djibouti,-0.158
+1982,ETH,Ethiopia,-1.146
+1982,ANG,Angola,-1.078
+1982,MZM,Mozambique,-1.32
+1982,ZAM,Zambia,-0.431
+1982,ZIM,Zimbabwe,-0.348
+1982,MAW,Malawi,0.699
+1982,LES,Lesotho,-0.001
+1982,BOT,Botswana,-0.025
+1982,SWA,Swaziland,0.055
+1982,MAG,Madagascar,-0.778
+1982,COM,Comoros,-0.128
+1982,MAS,Mauritius,-0.301
+1982,SEY,Seychelles,-1.525
+1982,MOR,Morocco,-0.022
+1982,ALG,Algeria,-0.792
+1982,TUN,Tunisia,-0.165
+1982,LIB,Libya,-0.987
+1982,SUD,Sudan,-0.14
+1982,IRN,Iran,-0.593
+1982,TUR,Turkey,0.641
+1982,IRQ,Iraq,-0.971
+1982,EGY,Egypt,-0.124
+1982,SYR,Syria,-1.324
+1982,LEB,Lebanon,0.099
+1982,JOR,Jordan,-0.42
+1982,ISR,Israel,2.058
+1982,SAU,Saudi Arabia,0.132
+1982,YAR,Yemen Arab Republic,-0.773
+1982,YPR,Yemen People's Republic,-1.272
+1982,KUW,Kuwait,-0.388
+1982,BAH,Bahrain,-0.438
+1982,QAT,Qatar,-0.448
+1982,UAE,United Arab Emirates,-0.494
+1982,OMA,Oman,-0.197
+1982,AFG,Afghanistan,-1.807
+1982,CHN,China,0.092
+1982,MON,Mongolia,-2.173
+1982,JPN,Japan,1.231
+1982,IND,India,-0.63
+1982,BHU,Bhutan,-0.356
+1982,PAK,Pakistan,-0.175
+1982,BNG,Bangladesh,-0.216
+1982,MYA,Myanmar,0.233
+1982,SRI,Sri Lanka,-0.265
+1982,MAD,Maldives,-0.307
+1982,NEP,Nepal,-0.092
+1982,THI,Thailand,-0.117
+1982,CAM,Cambodia,-0.08
+1982,LAO,Laos,-1.78
+1982,DRV,Vietnam,-1.799
+1982,MAL,Malaysia,-0.117
+1982,SIN,Singapore,0.201
+1982,PHI,Philippines,0.087
+1982,INS,Indonesia,-0.422
+1982,AUL,Australia,1.36
+1982,PNG,Papua New Guinea,0.07
+1982,NEW,New Zealand,1.403
+1982,VAN,Vanuatu,-0.631
+1982,SOL,Solomon Islands,0.093
+1982,FIJ,Fiji,0.18
+1982,WSM,Samoa,0.013
+1983,USA,United States of America,2.592
+1983,CAN,Canada,1.658
+1983,BHM,Bahamas,0.247
+1983,CUB,Cuba,-1.566
+1983,HAI,Haiti,0.265
+1983,DOM,Dominican Republic,0.158
+1983,JAM,Jamaica,-0.034
+1983,TRI,Trinidad and Tobago,-0.219
+1983,BAR,Barbados,0.036
+1983,DMA,Dominica,0.376
+1983,GRN,Grenada,-1.22
+1983,SLU,St. Lucia,0.367
+1983,SVG,St. Vincent and the Grenadines,0.233
+1983,AAB,Antigua & Barbuda,-0.23
+1983,SKN,St. Kitts and Nevis,0.505
+1983,MEX,Mexico,-0.473
+1983,BLZ,Belize,-0.031
+1983,GUA,Guatemala,0.479
+1983,HON,Honduras,0.259
+1983,SAL,El Salvador,-0.063
+1983,NIC,Nicaragua,-0.806
+1983,COS,Costa Rica,0.263
+1983,PAN,Panama,-0.341
+1983,COL,Colombia,0.023
+1983,VEN,Venezuela,-0.247
+1983,GUY,Guyana,-0.535
+1983,SUR,Suriname,-0.191
+1983,ECU,Ecuador,-0.263
+1983,PER,Peru,-0.198
+1983,BRA,Brazil,-0.059
+1983,BOL,Bolivia,-0.223
+1983,PAR,Paraguay,0.638
+1983,CHL,Chile,0.183
+1983,ARG,Argentina,-0.153
+1983,URU,Uruguay,0.148
+1983,UKG,United Kingdom,1.971
+1983,IRE,Ireland,0.91
+1983,NTH,Netherlands,1.497
+1983,BEL,Belgium,1.614
+1983,LUX,Luxembourg,1.607
+1983,FRN,France,1.624
+1983,SPN,Spain,0.883
+1983,POR,Portugal,1.196
+1983,GFR,German Federal Republic,1.696
+1983,GDR,German Democratic Republic,-1.685
+1983,POL,Poland,-1.874
+1983,AUS,Austria,0.763
+1983,HUN,Hungary,-2.143
+1983,CZE,Czechoslovakia,-2.144
+1983,ITA,Italy,1.54
+1983,MLT,Malta,-0.379
+1983,ALB,Albania,-1.543
+1983,YUG,Yugoslavia,-0.477
+1983,GRC,Greece,0.448
+1983,CYP,Cyprus,-0.566
+1983,BUL,Bulgaria,-2.13
+1983,ROM,Romania,-0.996
+1983,RUS,Russia,-2.137
+1983,UKR,Ukraine,-2.144
+1983,BLR,Belarus,-2.15
+1983,FIN,Finland,0.679
+1983,SWD,Sweden,0.861
+1983,NOR,Norway,1.293
+1983,DEN,Denmark,1.168
+1983,ICE,Iceland,1.227
+1983,CAP,Cape Verde,-0.789
+1983,STP,Sao Tome and Principe,-0.834
+1983,GNB,Guinea-Bissau,-0.675
+1983,EQG,Equatorial Guinea,-0.246
+1983,GAM,Gambia,-0.189
+1983,MLI,Mali,-0.513
+1983,SEN,Senegal,-0.124
+1983,BEN,Benin,-0.742
+1983,MAA,Mauritania,-0.394
+1983,NIR,Niger,-0.297
+1983,CDI,Ivory Coast,0.462
+1983,GUI,Guinea,-0.491
+1983,BFO,Burkina Faso,-0.511
+1983,LBR,Liberia,0.145
+1983,SIE,Sierra Leone,-0.416
+1983,GHA,Ghana,-0.466
+1983,TOG,Togo,-0.311
+1983,CAO,Cameroon,-0.366
+1983,NIG,Nigeria,-0.356
+1983,GAB,Gabon,-0.234
+1983,CEN,Central African Republic,-0.097
+1983,CHA,Chad,0.12
+1983,CON,Congo,-0.989
+1983,DRC,Democratic Republic of the Congo,0.225
+1983,UGA,Uganda,-0.648
+1983,KEN,Kenya,-0.319
+1983,TAZ,Tanzania,-0.639
+1983,BUI,Burundi,-0.529
+1983,RWA,Rwanda,-0.295
+1983,SOM,Somalia,-0.02
+1983,DJI,Djibouti,-0.198
+1983,ETH,Ethiopia,-1.023
+1983,ANG,Angola,-0.893
+1983,MZM,Mozambique,-1.37
+1983,ZAM,Zambia,-0.493
+1983,ZIM,Zimbabwe,-0.505
+1983,MAW,Malawi,0.454
+1983,LES,Lesotho,0.121
+1983,BOT,Botswana,-0.112
+1983,SWA,Swaziland,0.293
+1983,MAG,Madagascar,-0.739
+1983,COM,Comoros,-0.118
+1983,MAS,Mauritius,-0.351
+1983,SEY,Seychelles,-0.975
+1983,MOR,Morocco,-0.082
+1983,ALG,Algeria,-0.817
+1983,TUN,Tunisia,-0.336
+1983,LIB,Libya,-1.024
+1983,SUD,Sudan,-0.188
+1983,IRN,Iran,-0.616
+1983,TUR,Turkey,0.695
+1983,IRQ,Iraq,-0.89
+1983,EGY,Egypt,-0.204
+1983,SYR,Syria,-1.367
+1983,LEB,Lebanon,0.027
+1983,JOR,Jordan,-0.469
+1983,ISR,Israel,2.099
+1983,SAU,Saudi Arabia,-0.283
+1983,YAR,Yemen Arab Republic,-0.837
+1983,YPR,Yemen People's Republic,-1.307
+1983,KUW,Kuwait,-0.462
+1983,BAH,Bahrain,-0.477
+1983,QAT,Qatar,-0.48
+1983,UAE,United Arab Emirates,-0.496
+1983,OMA,Oman,-0.236
+1983,AFG,Afghanistan,-1.599
+1983,CHN,China,0.094
+1983,MON,Mongolia,-2.119
+1983,JPN,Japan,1.256
+1983,IND,India,-0.447
+1983,BHU,Bhutan,-0.322
+1983,PAK,Pakistan,-0.288
+1983,BNG,Bangladesh,-0.246
+1983,MYA,Myanmar,0.113
+1983,SRI,Sri Lanka,-0.252
+1983,MAD,Maldives,-0.349
+1983,NEP,Nepal,-0.141
+1983,THI,Thailand,-0.165
+1983,CAM,Cambodia,-0.104
+1983,LAO,Laos,-1.787
+1983,DRV,Vietnam,-1.812
+1983,MAL,Malaysia,-0.228
+1983,SIN,Singapore,0.039
+1983,PHI,Philippines,0.181
+1983,INS,Indonesia,-0.372
+1983,AUL,Australia,1.299
+1983,PNG,Papua New Guinea,-0.134
+1983,NEW,New Zealand,1.366
+1983,VAN,Vanuatu,-0.518
+1983,SOL,Solomon Islands,0.339
+1983,FIJ,Fiji,0.06
+1983,WSM,Samoa,0.116
+1984,USA,United States of America,2.737
+1984,CAN,Canada,1.703
+1984,BHM,Bahamas,0.228
+1984,CUB,Cuba,-1.702
+1984,HAI,Haiti,0.22
+1984,DOM,Dominican Republic,0.11
+1984,JAM,Jamaica,0.027
+1984,TRI,Trinidad and Tobago,-0.363
+1984,BAR,Barbados,-0.024
+1984,DMA,Dominica,0.352
+1984,GRN,Grenada,-0.357
+1984,SLU,St. Lucia,0.342
+1984,SVG,St. Vincent and the Grenadines,0.107
+1984,AAB,Antigua & Barbuda,-0.33
+1984,SKN,St. Kitts and Nevis,0.454
+1984,MEX,Mexico,-0.709
+1984,BLZ,Belize,0.076
+1984,GUA,Guatemala,0.185
+1984,HON,Honduras,0.158
+1984,SAL,El Salvador,-0.035
+1984,NIC,Nicaragua,-0.911
+1984,COS,Costa Rica,0.122
+1984,PAN,Panama,-0.148
+1984,COL,Colombia,0.023
+1984,VEN,Venezuela,-0.218
+1984,GUY,Guyana,-0.464
+1984,SUR,Suriname,-0.301
+1984,ECU,Ecuador,-0.222
+1984,PER,Peru,-0.245
+1984,BRA,Brazil,0.061
+1984,BOL,Bolivia,-0.245
+1984,PAR,Paraguay,0.442
+1984,CHL,Chile,0.064
+1984,ARG,Argentina,-0.292
+1984,URU,Uruguay,0.139
+1984,UKG,United Kingdom,2.047
+1984,IRE,Ireland,0.938
+1984,NTH,Netherlands,1.571
+1984,BEL,Belgium,1.671
+1984,LUX,Luxembourg,1.607
+1984,FRN,France,1.715
+1984,SPN,Spain,0.902
+1984,POR,Portugal,1.117
+1984,GFR,German Federal Republic,1.75
+1984,GDR,German Democratic Republic,-1.64
+1984,POL,Poland,-1.713
+1984,AUS,Austria,0.773
+1984,HUN,Hungary,-1.833
+1984,CZE,Czechoslovakia,-1.845
+1984,ITA,Italy,1.622
+1984,MLT,Malta,-0.45
+1984,ALB,Albania,-1.374
+1984,YUG,Yugoslavia,-0.673
+1984,GRC,Greece,0.356
+1984,CYP,Cyprus,-0.649
+1984,BUL,Bulgaria,-1.833
+1984,ROM,Romania,-0.871
+1984,RUS,Russia,-1.834
+1984,UKR,Ukraine,-1.84
+1984,BLR,Belarus,-1.843
+1984,FIN,Finland,0.684
+1984,SWD,Sweden,0.853
+1984,NOR,Norway,1.284
+1984,DEN,Denmark,1.23
+1984,ICE,Iceland,1.292
+1984,CAP,Cape Verde,-0.704
+1984,STP,Sao Tome and Principe,-0.735
+1984,GNB,Guinea-Bissau,-0.462
+1984,EQG,Equatorial Guinea,-0.352
+1984,GAM,Gambia,-0.223
+1984,MLI,Mali,-0.549
+1984,SEN,Senegal,-0.148
+1984,BEN,Benin,-0.91
+1984,MAA,Mauritania,-0.434
+1984,NIR,Niger,-0.222
+1984,CDI,Ivory Coast,0.416
+1984,GUI,Guinea,-0.507
+1984,BFO,Burkina Faso,-0.742
+1984,LBR,Liberia,0.204
+1984,SIE,Sierra Leone,-0.467
+1984,GHA,Ghana,-0.455
+1984,TOG,Togo,-0.454
+1984,CAO,Cameroon,-0.255
+1984,NIG,Nigeria,-0.475
+1984,GAB,Gabon,-0.231
+1984,CEN,Central African Republic,-0.359
+1984,CHA,Chad,-0.077
+1984,CON,Congo,-1.008
+1984,DRC,Democratic Republic of the Congo,0.253
+1984,UGA,Uganda,-0.733
+1984,KEN,Kenya,-0.451
+1984,TAZ,Tanzania,-0.625
+1984,BUI,Burundi,-0.524
+1984,RWA,Rwanda,-0.188
+1984,SOM,Somalia,-0.22
+1984,DJI,Djibouti,-0.229
+1984,ETH,Ethiopia,-0.945
+1984,ANG,Angola,-1.119
+1984,MZM,Mozambique,-1.158
+1984,ZAM,Zambia,-0.559
+1984,ZIM,Zimbabwe,-0.631
+1984,MAW,Malawi,0.28
+1984,LES,Lesotho,-0.182
+1984,BOT,Botswana,-0.312
+1984,SWA,Swaziland,0.153
+1984,MAG,Madagascar,-0.875
+1984,COM,Comoros,-0.334
+1984,MAS,Mauritius,-0.445
+1984,SEY,Seychelles,-0.795
+1984,MOR,Morocco,-0.159
+1984,ALG,Algeria,-0.926
+1984,TUN,Tunisia,-0.425
+1984,LIB,Libya,-1.269
+1984,SUD,Sudan,-0.267
+1984,IRN,Iran,-0.588
+1984,TUR,Turkey,0.702
+1984,IRQ,Iraq,-0.886
+1984,EGY,Egypt,-0.254
+1984,SYR,Syria,-1.506
+1984,LEB,Lebanon,-0.382
+1984,JOR,Jordan,-0.515
+1984,ISR,Israel,2.152
+1984,SAU,Saudi Arabia,-0.409
+1984,YAR,Yemen Arab Republic,-0.84
+1984,YPR,Yemen People's Republic,-1.391
+1984,KUW,Kuwait,-0.548
+1984,BAH,Bahrain,-0.562
+1984,QAT,Qatar,-0.568
+1984,UAE,United Arab Emirates,-0.533
+1984,OMA,Oman,-0.352
+1984,AFG,Afghanistan,-1.718
+1984,CHN,China,0.089
+1984,MON,Mongolia,-1.831
+1984,JPN,Japan,1.31
+1984,IND,India,-0.467
+1984,BHU,Bhutan,-0.358
+1984,PAK,Pakistan,-0.286
+1984,BNG,Bangladesh,-0.275
+1984,MYA,Myanmar,0.034
+1984,SRI,Sri Lanka,-0.288
+1984,MAD,Maldives,-0.47
+1984,NEP,Nepal,-0.165
+1984,THI,Thailand,-0.241
+1984,CAM,Cambodia,-0.178
+1984,LAO,Laos,-1.794
+1984,DRV,Vietnam,-1.734
+1984,MAL,Malaysia,-0.289
+1984,SIN,Singapore,-0.108
+1984,BRU,Brunei,-0.194
+1984,PHI,Philippines,-0.055
+1984,INS,Indonesia,-0.378
+1984,AUL,Australia,1.196
+1984,PNG,Papua New Guinea,-0.14
+1984,NEW,New Zealand,1.231
+1984,VAN,Vanuatu,-0.558
+1984,SOL,Solomon Islands,0.293
+1984,FIJ,Fiji,0.013
+1984,WSM,Samoa,0.16
+1985,USA,United States of America,2.742
+1985,CAN,Canada,1.645
+1985,BHM,Bahamas,0.219
+1985,CUB,Cuba,-1.803
+1985,HAI,Haiti,0.194
+1985,DOM,Dominican Republic,0.025
+1985,JAM,Jamaica,0.09
+1985,TRI,Trinidad and Tobago,-0.371
+1985,BAR,Barbados,0.007
+1985,DMA,Dominica,0.382
+1985,GRN,Grenada,1.039
+1985,SLU,St. Lucia,0.314
+1985,SVG,St. Vincent and the Grenadines,0.283
+1985,AAB,Antigua & Barbuda,0.042
+1985,SKN,St. Kitts and Nevis,0.578
+1985,MEX,Mexico,-0.631
+1985,BLZ,Belize,0.245
+1985,GUA,Guatemala,0.151
+1985,HON,Honduras,0.141
+1985,SAL,El Salvador,0.121
+1985,NIC,Nicaragua,-1.017
+1985,COS,Costa Rica,0.365
+1985,PAN,Panama,-0.024
+1985,COL,Colombia,0.1
+1985,VEN,Venezuela,-0.246
+1985,GUY,Guyana,-0.503
+1985,SUR,Suriname,-0.326
+1985,ECU,Ecuador,-0.149
+1985,PER,Peru,-0.241
+1985,BRA,Brazil,-0.056
+1985,BOL,Bolivia,-0.264
+1985,PAR,Paraguay,0.523
+1985,CHL,Chile,0.282
+1985,ARG,Argentina,-0.193
+1985,URU,Uruguay,0.001
+1985,UKG,United Kingdom,2.04
+1985,IRE,Ireland,0.975
+1985,NTH,Netherlands,1.566
+1985,BEL,Belgium,1.699
+1985,LUX,Luxembourg,1.619
+1985,FRN,France,1.832
+1985,SPN,Spain,1.022
+1985,POR,Portugal,1.438
+1985,GFR,German Federal Republic,1.77
+1985,GDR,German Democratic Republic,-1.726
+1985,POL,Poland,-1.431
+1985,AUS,Austria,0.829
+1985,HUN,Hungary,-1.788
+1985,CZE,Czechoslovakia,-1.792
+1985,ITA,Italy,1.613
+1985,MLT,Malta,-0.395
+1985,ALB,Albania,-1.468
+1985,YUG,Yugoslavia,-0.651
+1985,GRC,Greece,0.53
+1985,CYP,Cyprus,-0.672
+1985,BUL,Bulgaria,-1.791
+1985,ROM,Romania,-0.886
+1985,RUS,Russia,-1.746
+1985,UKR,Ukraine,-1.721
+1985,BLR,Belarus,-1.798
+1985,FIN,Finland,0.719
+1985,SWD,Sweden,0.88
+1985,NOR,Norway,1.337
+1985,DEN,Denmark,1.242
+1985,ICE,Iceland,1.352
+1985,CAP,Cape Verde,-0.686
+1985,STP,Sao Tome and Principe,-0.668
+1985,GNB,Guinea-Bissau,-0.52
+1985,EQG,Equatorial Guinea,-0.17
+1985,GAM,Gambia,-0.312
+1985,MLI,Mali,-0.596
+1985,SEN,Senegal,-0.244
+1985,BEN,Benin,-1.045
+1985,MAA,Mauritania,-0.398
+1985,NIR,Niger,-0.298
+1985,CDI,Ivory Coast,0.37
+1985,GUI,Guinea,-0.519
+1985,BFO,Burkina Faso,-0.665
+1985,LBR,Liberia,0.085
+1985,SIE,Sierra Leone,-0.129
+1985,GHA,Ghana,-0.585
+1985,TOG,Togo,-0.42
+1985,CAO,Cameroon,-0.055
+1985,NIG,Nigeria,-0.505
+1985,GAB,Gabon,-0.196
+1985,CEN,Central African Republic,-0.174
+1985,CHA,Chad,-0.193
+1985,CON,Congo,-0.919
+1985,DRC,Democratic Republic of the Congo,0.22
+1985,UGA,Uganda,-0.668
+1985,KEN,Kenya,-0.432
+1985,TAZ,Tanzania,-0.717
+1985,BUI,Burundi,-0.494
+1985,RWA,Rwanda,-0.25
+1985,SOM,Somalia,-0.323
+1985,DJI,Djibouti,-0.287
+1985,ETH,Ethiopia,-0.947
+1985,ANG,Angola,-1.153
+1985,MZM,Mozambique,-1.067
+1985,ZAM,Zambia,-0.485
+1985,ZIM,Zimbabwe,-0.611
+1985,MAW,Malawi,0.568
+1985,LES,Lesotho,-0.291
+1985,BOT,Botswana,-0.322
+1985,SWA,Swaziland,0.242
+1985,MAG,Madagascar,-0.857
+1985,COM,Comoros,-0.323
+1985,MAS,Mauritius,-0.435
+1985,SEY,Seychelles,-0.815
+1985,MOR,Morocco,-0.322
+1985,ALG,Algeria,-1.093
+1985,TUN,Tunisia,-0.521
+1985,LIB,Libya,-1.191
+1985,SUD,Sudan,-0.337
+1985,IRN,Iran,-0.633
+1985,TUR,Turkey,0.68
+1985,IRQ,Iraq,-1.003
+1985,EGY,Egypt,-0.304
+1985,SYR,Syria,-1.58
+1985,LEB,Lebanon,-0.56
+1985,JOR,Jordan,-0.486
+1985,ISR,Israel,2.114
+1985,SAU,Saudi Arabia,-0.524
+1985,YAR,Yemen Arab Republic,-1.012
+1985,YPR,Yemen People's Republic,-1.406
+1985,KUW,Kuwait,-0.575
+1985,BAH,Bahrain,-0.595
+1985,QAT,Qatar,-0.59
+1985,UAE,United Arab Emirates,-0.595
+1985,OMA,Oman,-0.316
+1985,AFG,Afghanistan,-1.821
+1985,CHN,China,0.062
+1985,MON,Mongolia,-1.829
+1985,JPN,Japan,1.252
+1985,IND,India,-0.608
+1985,BHU,Bhutan,-0.424
+1985,PAK,Pakistan,-0.337
+1985,BNG,Bangladesh,-0.357
+1985,MYA,Myanmar,-0.149
+1985,SRI,Sri Lanka,-0.35
+1985,MAD,Maldives,-0.561
+1985,NEP,Nepal,-0.135
+1985,THI,Thailand,-0.209
+1985,CAM,Cambodia,-0.122
+1985,LAO,Laos,-1.836
+1985,DRV,Vietnam,-1.783
+1985,MAL,Malaysia,-0.216
+1985,SIN,Singapore,-0.118
+1985,BRU,Brunei,-0.248
+1985,PHI,Philippines,-0.125
+1985,INS,Indonesia,-0.509
+1985,AUL,Australia,1.162
+1985,PNG,Papua New Guinea,-0.026
+1985,NEW,New Zealand,1.137
+1985,VAN,Vanuatu,-0.541
+1985,SOL,Solomon Islands,0.644
+1985,FIJ,Fiji,0.16
+1985,WSM,Samoa,0.205
+1986,USA,United States of America,2.851
+1986,CAN,Canada,1.555
+1986,BHM,Bahamas,0.236
+1986,CUB,Cuba,-1.701
+1986,HAI,Haiti,0.018
+1986,DOM,Dominican Republic,0.147
+1986,JAM,Jamaica,0.17
+1986,TRI,Trinidad and Tobago,-0.272
+1986,BAR,Barbados,0.101
+1986,DMA,Dominica,0.259
+1986,GRN,Grenada,0.352
+1986,SLU,St. Lucia,0.35
+1986,SVG,St. Vincent and the Grenadines,0.28
+1986,AAB,Antigua & Barbuda,0.224
+1986,SKN,St. Kitts and Nevis,0.349
+1986,MEX,Mexico,-0.524
+1986,BLZ,Belize,0.232
+1986,GUA,Guatemala,0.107
+1986,HON,Honduras,0.258
+1986,SAL,El Salvador,0.518
+1986,NIC,Nicaragua,-1.168
+1986,COS,Costa Rica,0.583
+1986,PAN,Panama,-0.036
+1986,COL,Colombia,0.077
+1986,VEN,Venezuela,-0.142
+1986,GUY,Guyana,-0.676
+1986,SUR,Suriname,-0.418
+1986,ECU,Ecuador,-0.136
+1986,PER,Peru,-0.299
+1986,BRA,Brazil,-0.045
+1986,BOL,Bolivia,-0.31
+1986,PAR,Paraguay,0.372
+1986,CHL,Chile,0.439
+1986,ARG,Argentina,-0.255
+1986,URU,Uruguay,0.085
+1986,UKG,United Kingdom,2.103
+1986,IRE,Ireland,1.02
+1986,NTH,Netherlands,1.621
+1986,BEL,Belgium,1.727
+1986,LUX,Luxembourg,1.656
+1986,FRN,France,1.927
+1986,SPN,Spain,1.037
+1986,POR,Portugal,1.514
+1986,GFR,German Federal Republic,1.819
+1986,GDR,German Democratic Republic,-1.558
+1986,POL,Poland,-1.421
+1986,AUS,Austria,0.851
+1986,HUN,Hungary,-1.58
+1986,CZE,Czechoslovakia,-1.588
+1986,ITA,Italy,1.624
+1986,MLT,Malta,-0.459
+1986,ALB,Albania,-1.389
+1986,YUG,Yugoslavia,-0.705
+1986,GRC,Greece,0.666
+1986,CYP,Cyprus,-0.602
+1986,BUL,Bulgaria,-1.531
+1986,ROM,Romania,-0.934
+1986,RUS,Russia,-1.234
+1986,UKR,Ukraine,-1.565
+1986,BLR,Belarus,-1.578
+1986,FIN,Finland,0.768
+1986,SWD,Sweden,0.882
+1986,NOR,Norway,1.191
+1986,DEN,Denmark,1.204
+1986,ICE,Iceland,1.315
+1986,CAP,Cape Verde,-0.673
+1986,STP,Sao Tome and Principe,-0.454
+1986,GNB,Guinea-Bissau,-0.518
+1986,EQG,Equatorial Guinea,0.221
+1986,GAM,Gambia,-0.221
+1986,MLI,Mali,-0.745
+1986,SEN,Senegal,-0.147
+1986,BEN,Benin,-1.137
+1986,MAA,Mauritania,-0.457
+1986,NIR,Niger,-0.351
+1986,CDI,Ivory Coast,0.423
+1986,GUI,Guinea,-0.438
+1986,BFO,Burkina Faso,-0.857
+1986,LBR,Liberia,0.26
+1986,SIE,Sierra Leone,-0.262
+1986,GHA,Ghana,-0.42
+1986,TOG,Togo,-0.295
+1986,CAO,Cameroon,0.271
+1986,NIG,Nigeria,-0.516
+1986,GAB,Gabon,-0.097
+1986,CEN,Central African Republic,-0.149
+1986,CHA,Chad,0.018
+1986,CON,Congo,-0.889
+1986,DRC,Democratic Republic of the Congo,0.154
+1986,UGA,Uganda,-0.701
+1986,KEN,Kenya,-0.361
+1986,TAZ,Tanzania,-0.733
+1986,BUI,Burundi,-0.572
+1986,RWA,Rwanda,-0.243
+1986,SOM,Somalia,-0.402
+1986,DJI,Djibouti,-0.285
+1986,ETH,Ethiopia,-1.06
+1986,ANG,Angola,-1.504
+1986,MZM,Mozambique,-0.821
+1986,ZAM,Zambia,-0.64
+1986,ZIM,Zimbabwe,-0.709
+1986,MAW,Malawi,0.473
+1986,LES,Lesotho,-0.106
+1986,BOT,Botswana,-0.259
+1986,SWA,Swaziland,0.103
+1986,MAG,Madagascar,-0.995
+1986,COM,Comoros,-0.48
+1986,MAS,Mauritius,-0.221
+1986,SEY,Seychelles,-0.999
+1986,MOR,Morocco,-0.248
+1986,ALG,Algeria,-1.255
+1986,TUN,Tunisia,-0.306
+1986,LIB,Libya,-1.48
+1986,SUD,Sudan,-0.424
+1986,IRN,Iran,-0.706
+1986,TUR,Turkey,0.685
+1986,IRQ,Iraq,-1.108
+1986,EGY,Egypt,-0.314
+1986,SYR,Syria,-1.714
+1986,LEB,Lebanon,-0.535
+1986,JOR,Jordan,-0.409
+1986,ISR,Israel,1.955
+1986,SAU,Saudi Arabia,-0.42
+1986,YAR,Yemen Arab Republic,-1.222
+1986,YPR,Yemen People's Republic,-1.418
+1986,KUW,Kuwait,-0.602
+1986,BAH,Bahrain,-0.474
+1986,QAT,Qatar,-0.556
+1986,UAE,United Arab Emirates,-0.535
+1986,OMA,Oman,-0.288
+1986,AFG,Afghanistan,-1.604
+1986,CHN,China,0.069
+1986,MON,Mongolia,-1.614
+1986,JPN,Japan,1.225
+1986,IND,India,-0.799
+1986,BHU,Bhutan,-0.493
+1986,PAK,Pakistan,-0.465
+1986,BNG,Bangladesh,-0.396
+1986,MYA,Myanmar,-0.06
+1986,SRI,Sri Lanka,-0.436
+1986,MAD,Maldives,-0.802
+1986,NEP,Nepal,-0.225
+1986,THI,Thailand,-0.279
+1986,CAM,Cambodia,-0.215
+1986,LAO,Laos,-1.754
+1986,DRV,Vietnam,-1.745
+1986,MAL,Malaysia,-0.366
+1986,SIN,Singapore,-0.067
+1986,BRU,Brunei,-0.273
+1986,PHI,Philippines,-0.244
+1986,INS,Indonesia,-0.632
+1986,AUL,Australia,1.204
+1986,PNG,Papua New Guinea,-0.037
+1986,NEW,New Zealand,1.123
+1986,VAN,Vanuatu,-0.576
+1986,SOL,Solomon Islands,0.062
+1986,FIJ,Fiji,0.381
+1986,WSM,Samoa,0.329
+1987,USA,United States of America,2.927
+1987,CAN,Canada,1.546
+1987,BHM,Bahamas,0.081
+1987,CUB,Cuba,-1.705
+1987,HAI,Haiti,-0.007
+1987,DOM,Dominican Republic,0.141
+1987,JAM,Jamaica,0.073
+1987,TRI,Trinidad and Tobago,-0.352
+1987,BAR,Barbados,0.004
+1987,DMA,Dominica,0.26
+1987,GRN,Grenada,0.245
+1987,SLU,St. Lucia,0.079
+1987,SVG,St. Vincent and the Grenadines,0.098
+1987,AAB,Antigua & Barbuda,0.03
+1987,SKN,St. Kitts and Nevis,0.094
+1987,MEX,Mexico,-0.588
+1987,BLZ,Belize,0.193
+1987,GUA,Guatemala,0.052
+1987,HON,Honduras,0.263
+1987,SAL,El Salvador,0.409
+1987,NIC,Nicaragua,-1.163
+1987,COS,Costa Rica,0.446
+1987,PAN,Panama,-0.162
+1987,COL,Colombia,-0.037
+1987,VEN,Venezuela,-0.138
+1987,GUY,Guyana,-0.749
+1987,SUR,Suriname,-0.53
+1987,ECU,Ecuador,-0.23
+1987,PER,Peru,-0.383
+1987,BRA,Brazil,-0.103
+1987,BOL,Bolivia,-0.403
+1987,PAR,Paraguay,0.134
+1987,CHL,Chile,0.424
+1987,ARG,Argentina,-0.28
+1987,URU,Uruguay,0.156
+1987,UKG,United Kingdom,2.03
+1987,IRE,Ireland,0.988
+1987,NTH,Netherlands,1.673
+1987,BEL,Belgium,1.693
+1987,LUX,Luxembourg,1.621
+1987,FRN,France,1.864
+1987,SPN,Spain,1.031
+1987,POR,Portugal,1.617
+1987,GFR,German Federal Republic,1.686
+1987,GDR,German Democratic Republic,-0.981
+1987,POL,Poland,-1.326
+1987,AUS,Austria,0.864
+1987,HUN,Hungary,-1.401
+1987,CZE,Czechoslovakia,-1.352
+1987,ITA,Italy,1.562
+1987,MLT,Malta,0.352
+1987,ALB,Albania,-1.476
+1987,YUG,Yugoslavia,-0.74
+1987,GRC,Greece,0.737
+1987,CYP,Cyprus,-0.789
+1987,BUL,Bulgaria,-1.297
+1987,ROM,Romania,-0.969
+1987,RUS,Russia,-1.185
+1987,UKR,Ukraine,-1.336
+1987,BLR,Belarus,-1.347
+1987,FIN,Finland,0.769
+1987,SWD,Sweden,0.905
+1987,NOR,Norway,1.253
+1987,DEN,Denmark,1.241
+1987,ICE,Iceland,1.146
+1987,CAP,Cape Verde,-0.621
+1987,STP,Sao Tome and Principe,-0.517
+1987,GNB,Guinea-Bissau,-0.591
+1987,EQG,Equatorial Guinea,0.237
+1987,GAM,Gambia,-0.306
+1987,MLI,Mali,-0.725
+1987,SEN,Senegal,-0.294
+1987,BEN,Benin,-1.079
+1987,MAA,Mauritania,-0.487
+1987,NIR,Niger,-0.377
+1987,CDI,Ivory Coast,0.48
+1987,GUI,Guinea,-0.416
+1987,BFO,Burkina Faso,-0.853
+1987,LBR,Liberia,0.231
+1987,SIE,Sierra Leone,-0.244
+1987,GHA,Ghana,-0.406
+1987,TOG,Togo,-0.184
+1987,CAO,Cameroon,0.145
+1987,NIG,Nigeria,-0.59
+1987,GAB,Gabon,-0.305
+1987,CEN,Central African Republic,0.12
+1987,CHA,Chad,-0.202
+1987,CON,Congo,-0.931
+1987,DRC,Democratic Republic of the Congo,0.202
+1987,UGA,Uganda,-0.822
+1987,KEN,Kenya,-0.353
+1987,TAZ,Tanzania,-0.721
+1987,BUI,Burundi,-0.489
+1987,RWA,Rwanda,-0.3
+1987,SOM,Somalia,-0.445
+1987,DJI,Djibouti,-0.383
+1987,ETH,Ethiopia,-1.165
+1987,ANG,Angola,-1.551
+1987,MZM,Mozambique,-0.841
+1987,ZAM,Zambia,-0.444
+1987,ZIM,Zimbabwe,-0.768
+1987,MAW,Malawi,0.269
+1987,LES,Lesotho,-0.031
+1987,BOT,Botswana,-0.404
+1987,SWA,Swaziland,0.1
+1987,MAG,Madagascar,-1.046
+1987,COM,Comoros,-0.272
+1987,MAS,Mauritius,-0.171
+1987,SEY,Seychelles,-0.922
+1987,MOR,Morocco,-0.355
+1987,ALG,Algeria,-1.365
+1987,TUN,Tunisia,-0.406
+1987,LIB,Libya,-1.672
+1987,SUD,Sudan,-0.464
+1987,IRN,Iran,-1.042
+1987,TUR,Turkey,0.778
+1987,IRQ,Iraq,-1.137
+1987,EGY,Egypt,-0.344
+1987,SYR,Syria,-1.922
+1987,LEB,Lebanon,-0.626
+1987,JOR,Jordan,-0.376
+1987,ISR,Israel,2.061
+1987,SAU,Saudi Arabia,-0.322
+1987,YAR,Yemen Arab Republic,-1.151
+1987,YPR,Yemen People's Republic,-1.549
+1987,KUW,Kuwait,-0.673
+1987,BAH,Bahrain,-0.445
+1987,QAT,Qatar,-0.535
+1987,UAE,United Arab Emirates,-0.542
+1987,OMA,Oman,-0.225
+1987,AFG,Afghanistan,-1.569
+1987,CHN,China,0.079
+1987,MON,Mongolia,-1.418
+1987,JPN,Japan,1.217
+1987,IND,India,-0.81
+1987,BHU,Bhutan,-0.594
+1987,PAK,Pakistan,-0.514
+1987,BNG,Bangladesh,-0.465
+1987,MYA,Myanmar,-0.213
+1987,SRI,Sri Lanka,-0.556
+1987,MAD,Maldives,-0.968
+1987,NEP,Nepal,-0.271
+1987,THI,Thailand,-0.342
+1987,CAM,Cambodia,-0.333
+1987,LAO,Laos,-1.598
+1987,DRV,Vietnam,-1.528
+1987,MAL,Malaysia,-0.384
+1987,SIN,Singapore,-0.044
+1987,BRU,Brunei,-0.347
+1987,PHI,Philippines,-0.292
+1987,INS,Indonesia,-0.69
+1987,AUL,Australia,1.163
+1987,PNG,Papua New Guinea,-0.195
+1987,NEW,New Zealand,1.079
+1987,VAN,Vanuatu,-0.684
+1987,SOL,Solomon Islands,-0.073
+1987,FIJ,Fiji,0.085
+1987,WSM,Samoa,0.197
+1988,USA,United States of America,2.902
+1988,CAN,Canada,1.403
+1988,BHM,Bahamas,0.073
+1988,CUB,Cuba,-1.593
+1988,HAI,Haiti,-0.155
+1988,DOM,Dominican Republic,0.141
+1988,JAM,Jamaica,-0.127
+1988,TRI,Trinidad and Tobago,-0.301
+1988,BAR,Barbados,-0.123
+1988,DMA,Dominica,0.46
+1988,GRN,Grenada,0.081
+1988,SLU,St. Lucia,-0.094
+1988,SVG,St. Vincent and the Grenadines,0.092
+1988,AAB,Antigua & Barbuda,0.041
+1988,SKN,St. Kitts and Nevis,0.035
+1988,MEX,Mexico,-0.628
+1988,BLZ,Belize,0.082
+1988,GUA,Guatemala,-0.211
+1988,HON,Honduras,0.213
+1988,SAL,El Salvador,0.241
+1988,NIC,Nicaragua,-0.552
+1988,COS,Costa Rica,0.359
+1988,PAN,Panama,-0.254
+1988,COL,Colombia,0.142
+1988,VEN,Venezuela,-0.157
+1988,GUY,Guyana,-0.832
+1988,SUR,Suriname,-0.504
+1988,ECU,Ecuador,-0.304
+1988,PER,Peru,-0.362
+1988,BRA,Brazil,-0.245
+1988,BOL,Bolivia,-0.277
+1988,PAR,Paraguay,0.128
+1988,CHL,Chile,0.346
+1988,ARG,Argentina,-0.352
+1988,URU,Uruguay,0.017
+1988,UKG,United Kingdom,2.052
+1988,IRE,Ireland,1.018
+1988,NTH,Netherlands,1.68
+1988,BEL,Belgium,1.682
+1988,LUX,Luxembourg,1.578
+1988,FRN,France,1.833
+1988,SPN,Spain,1.081
+1988,POR,Portugal,1.565
+1988,GFR,German Federal Republic,1.821
+1988,GDR,German Democratic Republic,-0.834
+1988,POL,Poland,-1.168
+1988,AUS,Austria,0.932
+1988,HUN,Hungary,-1.057
+1988,CZE,Czechoslovakia,-1.187
+1988,ITA,Italy,1.527
+1988,MLT,Malta,0.444
+1988,ALB,Albania,-1.422
+1988,YUG,Yugoslavia,-0.771
+1988,GRC,Greece,0.807
+1988,CYP,Cyprus,-0.663
+1988,BUL,Bulgaria,-0.855
+1988,ROM,Romania,-0.847
+1988,RUS,Russia,-1.097
+1988,UKR,Ukraine,-1.181
+1988,BLR,Belarus,-1.173
+1988,FIN,Finland,0.929
+1988,SWD,Sweden,0.942
+1988,NOR,Norway,1.182
+1988,DEN,Denmark,1.232
+1988,ICE,Iceland,1.174
+1988,CAP,Cape Verde,-0.56
+1988,STP,Sao Tome and Principe,-0.583
+1988,GNB,Guinea-Bissau,-0.588
+1988,EQG,Equatorial Guinea,0.308
+1988,GAM,Gambia,-0.385
+1988,MLI,Mali,-0.692
+1988,SEN,Senegal,-0.328
+1988,BEN,Benin,-0.865
+1988,MAA,Mauritania,-0.598
+1988,NIR,Niger,-0.427
+1988,CDI,Ivory Coast,0.272
+1988,GUI,Guinea,-0.487
+1988,BFO,Burkina Faso,-0.747
+1988,LBR,Liberia,0.128
+1988,SIE,Sierra Leone,-0.387
+1988,GHA,Ghana,-0.575
+1988,TOG,Togo,-0.358
+1988,CAO,Cameroon,-0.014
+1988,NIG,Nigeria,-0.451
+1988,GAB,Gabon,-0.403
+1988,CEN,Central African Republic,-0.017
+1988,CHA,Chad,-0.288
+1988,CON,Congo,-0.96
+1988,DRC,Democratic Republic of the Congo,0.186
+1988,UGA,Uganda,-0.858
+1988,KEN,Kenya,-0.384
+1988,TAZ,Tanzania,-0.929
+1988,BUI,Burundi,-0.604
+1988,RWA,Rwanda,-0.383
+1988,SOM,Somalia,-0.414
+1988,DJI,Djibouti,-0.375
+1988,ETH,Ethiopia,-1.239
+1988,ANG,Angola,-1.497
+1988,MZM,Mozambique,-0.87
+1988,ZAM,Zambia,-0.668
+1988,ZIM,Zimbabwe,-0.891
+1988,MAW,Malawi,0.312
+1988,LES,Lesotho,0.033
+1988,BOT,Botswana,-0.35
+1988,SWA,Swaziland,0.002
+1988,MAG,Madagascar,-1.073
+1988,COM,Comoros,-0.419
+1988,MAS,Mauritius,-0.245
+1988,SEY,Seychelles,-0.83
+1988,MOR,Morocco,-0.462
+1988,ALG,Algeria,-1.345
+1988,TUN,Tunisia,-0.54
+1988,LIB,Libya,-1.587
+1988,SUD,Sudan,-0.726
+1988,IRN,Iran,-1.109
+1988,TUR,Turkey,0.641
+1988,IRQ,Iraq,-1.142
+1988,EGY,Egypt,-0.48
+1988,SYR,Syria,-1.86
+1988,LEB,Lebanon,-0.638
+1988,JOR,Jordan,-0.349
+1988,ISR,Israel,2.069
+1988,SAU,Saudi Arabia,-0.477
+1988,YAR,Yemen Arab Republic,-0.938
+1988,YPR,Yemen People's Republic,-1.544
+1988,KUW,Kuwait,-0.755
+1988,BAH,Bahrain,-0.404
+1988,QAT,Qatar,-0.541
+1988,UAE,United Arab Emirates,-0.697
+1988,OMA,Oman,-0.471
+1988,AFG,Afghanistan,-1.514
+1988,CHN,China,-0.125
+1988,MON,Mongolia,-1.263
+1988,JPN,Japan,1.253
+1988,IND,India,-0.714
+1988,BHU,Bhutan,-0.418
+1988,PAK,Pakistan,-0.651
+1988,BNG,Bangladesh,-0.528
+1988,MYA,Myanmar,-0.326
+1988,SRI,Sri Lanka,-0.569
+1988,MAD,Maldives,-0.95
+1988,NEP,Nepal,-0.296
+1988,THI,Thailand,-0.392
+1988,CAM,Cambodia,-0.491
+1988,LAO,Laos,-1.364
+1988,DRV,Vietnam,-1.54
+1988,MAL,Malaysia,-0.43
+1988,SIN,Singapore,-0.112
+1988,BRU,Brunei,-0.449
+1988,PHI,Philippines,-0.194
+1988,INS,Indonesia,-0.755
+1988,AUL,Australia,1.081
+1988,PNG,Papua New Guinea,-0.17
+1988,NEW,New Zealand,0.95
+1988,VAN,Vanuatu,-0.727
+1988,SOL,Solomon Islands,0.015
+1988,FIJ,Fiji,0.117
+1988,WSM,Samoa,0.238
+1989,USA,United States of America,2.962
+1989,CAN,Canada,1.356
+1989,BHM,Bahamas,-0.048
+1989,CUB,Cuba,-1.542
+1989,HAI,Haiti,-0.345
+1989,DOM,Dominican Republic,-0.029
+1989,JAM,Jamaica,-0.135
+1989,TRI,Trinidad and Tobago,-0.345
+1989,BAR,Barbados,-0.223
+1989,DMA,Dominica,0.58
+1989,GRN,Grenada,0.085
+1989,SLU,St. Lucia,0.053
+1989,SVG,St. Vincent and the Grenadines,0.045
+1989,AAB,Antigua & Barbuda,0.056
+1989,SKN,St. Kitts and Nevis,0.052
+1989,MEX,Mexico,-0.556
+1989,BLZ,Belize,0.273
+1989,GUA,Guatemala,-0.269
+1989,HON,Honduras,0.101
+1989,SAL,El Salvador,0.296
+1989,NIC,Nicaragua,-0.748
+1989,COS,Costa Rica,0.186
+1989,PAN,Panama,-0.272
+1989,COL,Colombia,-0.193
+1989,VEN,Venezuela,-0.28
+1989,GUY,Guyana,-0.836
+1989,SUR,Suriname,-0.543
+1989,ECU,Ecuador,-0.405
+1989,PER,Peru,-0.442
+1989,BRA,Brazil,-0.276
+1989,BOL,Bolivia,-0.436
+1989,PAR,Paraguay,0.071
+1989,CHL,Chile,0.397
+1989,ARG,Argentina,-0.359
+1989,URU,Uruguay,0.02
+1989,UKG,United Kingdom,2.105
+1989,IRE,Ireland,0.954
+1989,NTH,Netherlands,1.631
+1989,BEL,Belgium,1.625
+1989,LUX,Luxembourg,1.63
+1989,FRN,France,1.873
+1989,SPN,Spain,1.008
+1989,POR,Portugal,1.56
+1989,GFR,German Federal Republic,1.735
+1989,GDR,German Democratic Republic,-0.869
+1989,POL,Poland,-0.782
+1989,AUS,Austria,0.777
+1989,HUN,Hungary,-0.151
+1989,CZE,Czechoslovakia,-1.094
+1989,ITA,Italy,1.583
+1989,MLT,Malta,0.541
+1989,ALB,Albania,-1.351
+1989,YUG,Yugoslavia,-0.802
+1989,GRC,Greece,0.775
+1989,CYP,Cyprus,-0.633
+1989,BUL,Bulgaria,-0.877
+1989,ROM,Romania,-0.796
+1989,RUS,Russia,-1.027
+1989,UKR,Ukraine,-1.1
+1989,BLR,Belarus,-1.085
+1989,FIN,Finland,0.853
+1989,SWD,Sweden,0.844
+1989,NOR,Norway,1.131
+1989,DEN,Denmark,1.201
+1989,ICE,Iceland,1.162
+1989,CAP,Cape Verde,-0.563
+1989,STP,Sao Tome and Principe,-0.621
+1989,GNB,Guinea-Bissau,-0.642
+1989,EQG,Equatorial Guinea,0.07
+1989,GAM,Gambia,-0.457
+1989,MLI,Mali,-0.76
+1989,SEN,Senegal,-0.442
+1989,BEN,Benin,-0.811
+1989,MAA,Mauritania,-0.679
+1989,NIR,Niger,-0.493
+1989,CDI,Ivory Coast,-0.001
+1989,GUI,Guinea,-0.589
+1989,BFO,Burkina Faso,-0.729
+1989,LBR,Liberia,-0.099
+1989,SIE,Sierra Leone,-0.38
+1989,GHA,Ghana,-0.678
+1989,TOG,Togo,-0.215
+1989,CAO,Cameroon,-0.201
+1989,NIG,Nigeria,-0.615
+1989,GAB,Gabon,-0.545
+1989,CEN,Central African Republic,-0.057
+1989,CHA,Chad,-0.402
+1989,CON,Congo,-0.947
+1989,DRC,Democratic Republic of the Congo,-0.086
+1989,UGA,Uganda,-0.951
+1989,KEN,Kenya,-0.092
+1989,TAZ,Tanzania,-1.003
+1989,BUI,Burundi,-0.563
+1989,RWA,Rwanda,-0.447
+1989,SOM,Somalia,-0.548
+1989,DJI,Djibouti,-0.483
+1989,ETH,Ethiopia,-1.087
+1989,ANG,Angola,-1.481
+1989,MZM,Mozambique,-0.83
+1989,ZAM,Zambia,-0.899
+1989,ZIM,Zimbabwe,-0.919
+1989,MAW,Malawi,0.192
+1989,LES,Lesotho,-0.113
+1989,BOT,Botswana,-0.316
+1989,SWA,Swaziland,-0.11
+1989,MAG,Madagascar,-0.983
+1989,COM,Comoros,-0.497
+1989,MAS,Mauritius,-0.306
+1989,SEY,Seychelles,-0.804
+1989,MOR,Morocco,-0.521
+1989,ALG,Algeria,-1.291
+1989,TUN,Tunisia,-0.601
+1989,LIB,Libya,-1.375
+1989,SUD,Sudan,-0.684
+1989,IRN,Iran,-1.124
+1989,TUR,Turkey,0.654
+1989,IRQ,Iraq,-1.193
+1989,EGY,Egypt,-0.411
+1989,SYR,Syria,-1.662
+1989,LEB,Lebanon,-0.635
+1989,JOR,Jordan,-0.554
+1989,ISR,Israel,2.076
+1989,SAU,Saudi Arabia,-0.56
+1989,YAR,Yemen Arab Republic,-0.827
+1989,YPR,Yemen People's Republic,-1.475
+1989,KUW,Kuwait,-0.799
+1989,BAH,Bahrain,-0.539
+1989,QAT,Qatar,-0.651
+1989,UAE,United Arab Emirates,-0.746
+1989,OMA,Oman,-0.519
+1989,AFG,Afghanistan,-1.462
+1989,CHN,China,-0.187
+1989,MON,Mongolia,-1.137
+1989,JPN,Japan,1.309
+1989,IND,India,-0.795
+1989,BHU,Bhutan,-0.571
+1989,PAK,Pakistan,-0.787
+1989,BNG,Bangladesh,-0.638
+1989,MYA,Myanmar,-0.413
+1989,SRI,Sri Lanka,-0.619
+1989,MAD,Maldives,-0.906
+1989,NEP,Nepal,-0.441
+1989,THI,Thailand,-0.401
+1989,CAM,Cambodia,-0.561
+1989,LAO,Laos,-1.222
+1989,DRV,Vietnam,-1.333
+1989,MAL,Malaysia,-0.571
+1989,SIN,Singapore,-0.228
+1989,BRU,Brunei,-0.469
+1989,PHI,Philippines,-0.244
+1989,INS,Indonesia,-0.825
+1989,AUL,Australia,0.963
+1989,PNG,Papua New Guinea,-0.076
+1989,NEW,New Zealand,0.852
+1989,VAN,Vanuatu,-0.694
+1989,SOL,Solomon Islands,-0.187
+1989,FIJ,Fiji,0.173
+1989,WSM,Samoa,0.164
+1990,USA,United States of America,2.893
+1990,CAN,Canada,1.286
+1990,BHM,Bahamas,-0.088
+1990,CUB,Cuba,-1.655
+1990,HAI,Haiti,-0.336
+1990,DOM,Dominican Republic,-0.121
+1990,JAM,Jamaica,-0.219
+1990,TRI,Trinidad and Tobago,-0.512
+1990,BAR,Barbados,-0.26
+1990,DMA,Dominica,0.256
+1990,GRN,Grenada,-0.067
+1990,SLU,St. Lucia,-0.171
+1990,SVG,St. Vincent and the Grenadines,-0.015
+1990,AAB,Antigua & Barbuda,-0.137
+1990,SKN,St. Kitts and Nevis,-0.001
+1990,MEX,Mexico,-0.733
+1990,BLZ,Belize,-0.086
+1990,GUA,Guatemala,-0.478
+1990,HON,Honduras,-0.042
+1990,SAL,El Salvador,-0.002
+1990,NIC,Nicaragua,-0.702
+1990,COS,Costa Rica,0.14
+1990,PAN,Panama,-0.082
+1990,COL,Colombia,-0.475
+1990,VEN,Venezuela,-0.39
+1990,GUY,Guyana,-0.85
+1990,SUR,Suriname,-0.581
+1990,ECU,Ecuador,-0.525
+1990,PER,Peru,-0.504
+1990,BRA,Brazil,-0.471
+1990,BOL,Bolivia,-0.567
+1990,PAR,Paraguay,-0.244
+1990,CHL,Chile,-0.174
+1990,ARG,Argentina,-0.498
+1990,URU,Uruguay,-0.097
+1990,UKG,United Kingdom,2.108
+1990,IRE,Ireland,0.853
+1990,NTH,Netherlands,1.625
+1990,BEL,Belgium,1.626
+1990,LUX,Luxembourg,1.629
+1990,FRN,France,1.929
+1990,LIE,Liechtenstein,0.901
+1990,SPN,Spain,0.892
+1990,POR,Portugal,1.419
+1990,GFR,German Federal Republic,1.654
+1990,POL,Poland,0.832
+1990,AUS,Austria,0.691
+1990,HUN,Hungary,0.736
+1990,CZE,Czechoslovakia,0.701
+1990,ITA,Italy,1.501
+1990,MLT,Malta,0.459
+1990,ALB,Albania,-1.236
+1990,YUG,Yugoslavia,-0.851
+1990,GRC,Greece,0.794
+1990,CYP,Cyprus,-0.469
+1990,BUL,Bulgaria,0.554
+1990,ROM,Romania,0.664
+1990,RUS,Russia,-0.527
+1990,UKR,Ukraine,-0.41
+1990,BLR,Belarus,-0.548
+1990,FIN,Finland,0.806
+1990,SWD,Sweden,0.775
+1990,NOR,Norway,1.083
+1990,DEN,Denmark,1.156
+1990,ICE,Iceland,1.113
+1990,CAP,Cape Verde,-0.614
+1990,GNB,Guinea-Bissau,-0.648
+1990,GAM,Gambia,-0.569
+1990,MLI,Mali,-0.769
+1990,SEN,Senegal,-0.558
+1990,BEN,Benin,-0.776
+1990,MAA,Mauritania,-0.759
+1990,NIR,Niger,-0.659
+1990,CDI,Ivory Coast,-0.046
+1990,GUI,Guinea,-0.632
+1990,BFO,Burkina Faso,-0.815
+1990,SIE,Sierra Leone,-0.585
+1990,GHA,Ghana,-0.801
+1990,TOG,Togo,-0.318
+1990,CAO,Cameroon,-0.278
+1990,NIG,Nigeria,-0.715
+1990,GAB,Gabon,-0.535
+1990,CEN,Central African Republic,-0.125
+1990,CHA,Chad,-0.546
+1990,CON,Congo,-0.765
+1990,DRC,Democratic Republic of the Congo,-0.233
+1990,UGA,Uganda,-0.912
+1990,KEN,Kenya,-0.283
+1990,TAZ,Tanzania,-0.973
+1990,BUI,Burundi,-0.723
+1990,RWA,Rwanda,-0.567
+1990,SOM,Somalia,-0.676
+1990,DJI,Djibouti,-0.58
+1990,ETH,Ethiopia,-0.956
+1990,ANG,Angola,-1.442
+1990,MZM,Mozambique,-0.851
+1990,ZAM,Zambia,-0.898
+1990,ZIM,Zimbabwe,-0.901
+1990,MAW,Malawi,0.199
+1990,NAM,Namibia,-0.987
+1990,LES,Lesotho,-0.207
+1990,BOT,Botswana,-0.232
+1990,SWA,Swaziland,-0.354
+1990,MAG,Madagascar,-0.993
+1990,COM,Comoros,-0.615
+1990,MAS,Mauritius,-0.384
+1990,SEY,Seychelles,-0.829
+1990,MOR,Morocco,-0.656
+1990,ALG,Algeria,-1.196
+1990,TUN,Tunisia,-0.708
+1990,LIB,Libya,-1.235
+1990,SUD,Sudan,-0.832
+1990,IRN,Iran,-1.122
+1990,TUR,Turkey,0.606
+1990,IRQ,Iraq,-1.19
+1990,EGY,Egypt,-0.514
+1990,SYR,Syria,-1.507
+1990,LEB,Lebanon,-0.662
+1990,JOR,Jordan,-0.676
+1990,ISR,Israel,2.02
+1990,SAU,Saudi Arabia,-0.617
+1990,YAR,Yemen Arab Republic,-0.871
+1990,KUW,Kuwait,-0.79
+1990,BAH,Bahrain,-0.604
+1990,QAT,Qatar,-0.656
+1990,UAE,United Arab Emirates,-0.731
+1990,OMA,Oman,-0.603
+1990,AFG,Afghanistan,-1.395
+1990,CHN,China,-0.251
+1990,MON,Mongolia,-1.028
+1990,JPN,Japan,1.268
+1990,IND,India,-0.874
+1990,BHU,Bhutan,-0.698
+1990,PAK,Pakistan,-0.664
+1990,BNG,Bangladesh,-0.723
+1990,MYA,Myanmar,-0.433
+1990,SRI,Sri Lanka,-0.654
+1990,MAD,Maldives,-0.89
+1990,NEP,Nepal,-0.369
+1990,THI,Thailand,-0.466
+1990,LAO,Laos,-1.138
+1990,DRV,Vietnam,-1.286
+1990,MAL,Malaysia,-0.696
+1990,SIN,Singapore,-0.314
+1990,BRU,Brunei,-0.579
+1990,PHI,Philippines,-0.531
+1990,INS,Indonesia,-0.857
+1990,AUL,Australia,0.829
+1990,PNG,Papua New Guinea,-0.216
+1990,NEW,New Zealand,0.803
+1990,VAN,Vanuatu,-0.549
+1990,SOL,Solomon Islands,-0.236
+1990,FIJ,Fiji,-0.059
+1990,WSM,Samoa,0.026
+1991,USA,United States of America,2.754
+1991,CAN,Canada,1.271
+1991,BHM,Bahamas,-0.271
+1991,CUB,Cuba,-1.781
+1991,HAI,Haiti,-0.524
+1991,DOM,Dominican Republic,-0.121
+1991,JAM,Jamaica,-0.148
+1991,TRI,Trinidad and Tobago,-0.646
+1991,BAR,Barbados,-0.323
+1991,DMA,Dominica,0.157
+1991,GRN,Grenada,-0.272
+1991,SLU,St. Lucia,-0.223
+1991,SVG,St. Vincent and the Grenadines,-0.104
+1991,AAB,Antigua & Barbuda,-0.209
+1991,SKN,St. Kitts and Nevis,-0.109
+1991,MEX,Mexico,-0.767
+1991,BLZ,Belize,-0.284
+1991,GUA,Guatemala,-0.491
+1991,HON,Honduras,-0.285
+1991,SAL,El Salvador,-0.272
+1991,NIC,Nicaragua,-0.696
+1991,COS,Costa Rica,-0.031
+1991,PAN,Panama,0.316
+1991,COL,Colombia,-0.563
+1991,VEN,Venezuela,-0.441
+1991,GUY,Guyana,-0.757
+1991,SUR,Suriname,-0.63
+1991,ECU,Ecuador,-0.497
+1991,PER,Peru,-0.489
+1991,BRA,Brazil,-0.508
+1991,BOL,Bolivia,-0.386
+1991,PAR,Paraguay,-0.189
+1991,CHL,Chile,-0.312
+1991,ARG,Argentina,0.56
+1991,URU,Uruguay,0.218
+1991,UKG,United Kingdom,1.992
+1991,IRE,Ireland,0.922
+1991,NTH,Netherlands,1.547
+1991,BEL,Belgium,1.565
+1991,LUX,Luxembourg,1.49
+1991,FRN,France,1.69
+1991,LIE,Liechtenstein,0.906
+1991,SPN,Spain,0.813
+1991,POR,Portugal,1.212
+1991,GFR,German Federal Republic,1.566
+1991,POL,Poland,1.091
+1991,AUS,Austria,0.845
+1991,HUN,Hungary,1.009
+1991,CZE,Czechoslovakia,1.079
+1991,ITA,Italy,1.411
+1991,SNM,San Marino,1.13
+1991,MLT,Malta,0.367
+1991,ALB,Albania,0.6
+1991,YUG,Yugoslavia,-0.83
+1991,BOS,Bosnia and Herzegovina,-0.304
+1991,GRC,Greece,0.75
+1991,CYP,Cyprus,-0.417
+1991,BUL,Bulgaria,1.087
+1991,ROM,Romania,1.182
+1991,RUS,Russia,0.405
+1991,EST,Estonia,1.024
+1991,LAT,Latvia,1.079
+1991,LIT,Lithuania,0.942
+1991,UKR,Ukraine,0.134
+1991,BLR,Belarus,0.202
+1991,ARM,Armenia,0.222
+1991,AZE,Azerbaijan,0.113
+1991,FIN,Finland,1.031
+1991,SWD,Sweden,0.972
+1991,NOR,Norway,1.115
+1991,DEN,Denmark,1.148
+1991,ICE,Iceland,1.105
+1991,CAP,Cape Verde,-0.621
+1991,STP,Sao Tome and Principe,-0.334
+1991,GNB,Guinea-Bissau,-0.681
+1991,GAM,Gambia,-0.624
+1991,MLI,Mali,-0.818
+1991,SEN,Senegal,-0.646
+1991,BEN,Benin,-0.405
+1991,MAA,Mauritania,-0.832
+1991,NIR,Niger,-0.706
+1991,CDI,Ivory Coast,0.172
+1991,GUI,Guinea,-0.692
+1991,BFO,Burkina Faso,-0.805
+1991,LBR,Liberia,-0.232
+1991,SIE,Sierra Leone,-0.611
+1991,GHA,Ghana,-0.897
+1991,TOG,Togo,-0.365
+1991,CAO,Cameroon,-0.322
+1991,NIG,Nigeria,-0.739
+1991,GAB,Gabon,-0.608
+1991,CEN,Central African Republic,-0.16
+1991,CHA,Chad,-0.648
+1991,CON,Congo,-0.397
+1991,DRC,Democratic Republic of the Congo,-0.282
+1991,UGA,Uganda,-0.986
+1991,KEN,Kenya,-0.47
+1991,TAZ,Tanzania,-0.94
+1991,BUI,Burundi,-0.702
+1991,RWA,Rwanda,-0.577
+1991,SOM,Somalia,-0.808
+1991,DJI,Djibouti,-0.662
+1991,ETH,Ethiopia,-0.701
+1991,ANG,Angola,-1.282
+1991,MZM,Mozambique,-0.767
+1991,ZAM,Zambia,-0.807
+1991,ZIM,Zimbabwe,-0.967
+1991,MAW,Malawi,0.033
+1991,NAM,Namibia,-1.056
+1991,LES,Lesotho,-0.312
+1991,BOT,Botswana,-0.42
+1991,SWA,Swaziland,-0.415
+1991,MAG,Madagascar,-0.902
+1991,COM,Comoros,-0.673
+1991,MAS,Mauritius,-0.206
+1991,SEY,Seychelles,-0.765
+1991,MOR,Morocco,-0.76
+1991,ALG,Algeria,-1.221
+1991,TUN,Tunisia,-0.729
+1991,LIB,Libya,-1.163
+1991,SUD,Sudan,-1.044
+1991,IRN,Iran,-1.077
+1991,TUR,Turkey,0.548
+1991,IRQ,Iraq,-1.453
+1991,EGY,Egypt,-0.627
+1991,SYR,Syria,-1.344
+1991,LEB,Lebanon,-0.617
+1991,JOR,Jordan,-0.882
+1991,ISR,Israel,2.098
+1991,SAU,Saudi Arabia,-0.755
+1991,YAR,Yemen Arab Republic,-1.016
+1991,KUW,Kuwait,-0.776
+1991,BAH,Bahrain,-0.665
+1991,QAT,Qatar,-0.769
+1991,UAE,United Arab Emirates,-0.795
+1991,OMA,Oman,-0.646
+1991,AFG,Afghanistan,-1.24
+1991,CHN,China,-0.36
+1991,MON,Mongolia,-0.538
+1991,PRK,North Korea,-1.449
+1991,ROK,South Korea,0.347
+1991,JPN,Japan,1.114
+1991,IND,India,-0.822
+1991,BHU,Bhutan,-0.774
+1991,PAK,Pakistan,-0.823
+1991,BNG,Bangladesh,-0.861
+1991,MYA,Myanmar,-0.474
+1991,SRI,Sri Lanka,-0.814
+1991,MAD,Maldives,-0.853
+1991,NEP,Nepal,-0.509
+1991,THI,Thailand,-0.573
+1991,CAM,Cambodia,-0.207
+1991,LAO,Laos,-1.047
+1991,DRV,Vietnam,-1.255
+1991,MAL,Malaysia,-1.001
+1991,SIN,Singapore,-0.44
+1991,BRU,Brunei,-0.789
+1991,PHI,Philippines,-0.661
+1991,INS,Indonesia,-1.026
+1991,AUL,Australia,0.956
+1991,PNG,Papua New Guinea,-0.117
+1991,NEW,New Zealand,0.867
+1991,VAN,Vanuatu,-0.629
+1991,SOL,Solomon Islands,-0.016
+1991,FIJ,Fiji,-0.001
+1991,MSI,Marshall Islands,0.496
+1991,FSM,Federated States of Micronesia,0.401
+1991,WSM,Samoa,0.205
+1992,USA,United States of America,2.766
+1992,CAN,Canada,1.234
+1992,BHM,Bahamas,-0.355
+1992,CUB,Cuba,-1.75
+1992,HAI,Haiti,-0.529
+1992,DOM,Dominican Republic,0.148
+1992,JAM,Jamaica,-0.235
+1992,TRI,Trinidad and Tobago,-0.613
+1992,BAR,Barbados,-0.404
+1992,DMA,Dominica,0.08
+1992,GRN,Grenada,-0.432
+1992,SLU,St. Lucia,-0.067
+1992,SVG,St. Vincent and the Grenadines,-0.011
+1992,AAB,Antigua & Barbuda,-0.281
+1992,SKN,St. Kitts and Nevis,-0.209
+1992,MEX,Mexico,-0.613
+1992,BLZ,Belize,-0.36
+1992,GUA,Guatemala,-0.433
+1992,HON,Honduras,-0.242
+1992,SAL,El Salvador,-0.284
+1992,NIC,Nicaragua,-0.543
+1992,COS,Costa Rica,0.022
+1992,PAN,Panama,0.183
+1992,COL,Colombia,-0.588
+1992,VEN,Venezuela,-0.403
+1992,GUY,Guyana,-0.711
+1992,SUR,Suriname,-0.5
+1992,ECU,Ecuador,-0.449
+1992,PER,Peru,-0.344
+1992,BRA,Brazil,-0.494
+1992,BOL,Bolivia,-0.07
+1992,PAR,Paraguay,-0.074
+1992,CHL,Chile,-0.299
+1992,ARG,Argentina,0.632
+1992,URU,Uruguay,0.213
+1992,UKG,United Kingdom,1.863
+1992,IRE,Ireland,0.899
+1992,NTH,Netherlands,1.409
+1992,BEL,Belgium,1.412
+1992,LUX,Luxembourg,1.396
+1992,FRN,France,1.578
+1992,LIE,Liechtenstein,0.945
+1992,SPN,Spain,0.788
+1992,POR,Portugal,1.123
+1992,GFR,German Federal Republic,1.408
+1992,POL,Poland,1.12
+1992,AUS,Austria,0.926
+1992,HUN,Hungary,1.128
+1992,CZE,Czechoslovakia,1.147
+1992,ITA,Italy,1.374
+1992,SNM,San Marino,1.124
+1992,MLT,Malta,0.569
+1992,ALB,Albania,0.738
+1992,CRO,Croatia,0.901
+1992,YUG,Yugoslavia,-0.828
+1992,BOS,Bosnia and Herzegovina,-0.306
+1992,SLV,Slovenia,1.063
+1992,GRC,Greece,0.742
+1992,CYP,Cyprus,-0.543
+1992,BUL,Bulgaria,1.201
+1992,MLD,Moldova,0.951
+1992,ROM,Romania,1.239
+1992,RUS,Russia,0.874
+1992,EST,Estonia,0.985
+1992,LAT,Latvia,1.048
+1992,LIT,Lithuania,0.934
+1992,UKR,Ukraine,0.276
+1992,BLR,Belarus,0.282
+1992,ARM,Armenia,0.213
+1992,AZE,Azerbaijan,0.11
+1992,FIN,Finland,1.08
+1992,SWD,Sweden,0.983
+1992,NOR,Norway,1.06
+1992,DEN,Denmark,1.109
+1992,ICE,Iceland,1.095
+1992,CAP,Cape Verde,-0.502
+1992,STP,Sao Tome and Principe,-0.491
+1992,GNB,Guinea-Bissau,-0.776
+1992,GAM,Gambia,-0.509
+1992,MLI,Mali,-0.596
+1992,SEN,Senegal,-0.718
+1992,BEN,Benin,-0.319
+1992,MAA,Mauritania,-0.914
+1992,NIR,Niger,-0.787
+1992,CDI,Ivory Coast,-0.103
+1992,GUI,Guinea,-0.723
+1992,BFO,Burkina Faso,-0.753
+1992,LBR,Liberia,-0.429
+1992,SIE,Sierra Leone,-0.659
+1992,GHA,Ghana,-0.901
+1992,TOG,Togo,-0.212
+1992,CAO,Cameroon,-0.406
+1992,NIG,Nigeria,-0.796
+1992,GAB,Gabon,-0.655
+1992,CEN,Central African Republic,-0.316
+1992,CHA,Chad,-0.663
+1992,CON,Congo,-0.22
+1992,DRC,Democratic Republic of the Congo,-0.194
+1992,UGA,Uganda,-1.065
+1992,KEN,Kenya,-0.433
+1992,TAZ,Tanzania,-1.088
+1992,BUI,Burundi,-0.536
+1992,RWA,Rwanda,-0.529
+1992,DJI,Djibouti,-0.708
+1992,ETH,Ethiopia,-0.538
+1992,ANG,Angola,-1.109
+1992,MZM,Mozambique,-0.738
+1992,ZAM,Zambia,-0.878
+1992,ZIM,Zimbabwe,-1.096
+1992,MAW,Malawi,0.029
+1992,NAM,Namibia,-1.096
+1992,LES,Lesotho,-0.433
+1992,BOT,Botswana,-0.494
+1992,SWA,Swaziland,-0.386
+1992,MAG,Madagascar,-0.882
+1992,COM,Comoros,-0.609
+1992,MAS,Mauritius,-0.3
+1992,SEY,Seychelles,-0.688
+1992,MOR,Morocco,-0.773
+1992,ALG,Algeria,-1.012
+1992,TUN,Tunisia,-0.749
+1992,LIB,Libya,-1.273
+1992,SUD,Sudan,-1.496
+1992,IRN,Iran,-1.149
+1992,TUR,Turkey,0.549
+1992,IRQ,Iraq,-1.512
+1992,EGY,Egypt,-0.664
+1992,SYR,Syria,-1.334
+1992,LEB,Lebanon,-0.779
+1992,JOR,Jordan,-0.842
+1992,ISR,Israel,2.126
+1992,SAU,Saudi Arabia,-0.631
+1992,YAR,Yemen Arab Republic,-1.127
+1992,KUW,Kuwait,-0.595
+1992,BAH,Bahrain,-0.687
+1992,QAT,Qatar,-0.634
+1992,UAE,United Arab Emirates,-0.733
+1992,OMA,Oman,-0.696
+1992,AFG,Afghanistan,-0.821
+1992,TKM,Turkmenistan,0.074
+1992,TAJ,Tajikistan,-0.032
+1992,KYR,Kyrgyzstan,0.407
+1992,KZK,Kazakhstan,0.409
+1992,CHN,China,-0.665
+1992,MON,Mongolia,-0.5
+1992,PRK,North Korea,-1.455
+1992,ROK,South Korea,0.374
+1992,JPN,Japan,0.877
+1992,IND,India,-0.812
+1992,BHU,Bhutan,-0.658
+1992,PAK,Pakistan,-0.965
+1992,BNG,Bangladesh,-0.933
+1992,MYA,Myanmar,-0.776
+1992,SRI,Sri Lanka,-0.954
+1992,MAD,Maldives,-0.74
+1992,NEP,Nepal,-0.472
+1992,THI,Thailand,-0.611
+1992,CAM,Cambodia,-0.217
+1992,LAO,Laos,-1.063
+1992,DRV,Vietnam,-1.331
+1992,MAL,Malaysia,-1.076
+1992,SIN,Singapore,-0.318
+1992,BRU,Brunei,-0.857
+1992,PHI,Philippines,-0.572
+1992,INS,Indonesia,-1.074
+1992,AUL,Australia,0.942
+1992,PNG,Papua New Guinea,-0.194
+1992,NEW,New Zealand,0.81
+1992,VAN,Vanuatu,-0.632
+1992,SOL,Solomon Islands,0.231
+1992,FIJ,Fiji,-0.028
+1992,MSI,Marshall Islands,0.767
+1992,FSM,Federated States of Micronesia,0.965
+1992,WSM,Samoa,0.526
+1993,USA,United States of America,2.734
+1993,CAN,Canada,1.138
+1993,BHM,Bahamas,-0.282
+1993,CUB,Cuba,-1.734
+1993,HAI,Haiti,-0.425
+1993,DOM,Dominican Republic,0.211
+1993,JAM,Jamaica,-0.167
+1993,TRI,Trinidad and Tobago,-0.576
+1993,BAR,Barbados,-0.298
+1993,DMA,Dominica,0.045
+1993,GRN,Grenada,-0.375
+1993,SLU,St. Lucia,-0.284
+1993,SVG,St. Vincent and the Grenadines,-0.188
+1993,AAB,Antigua & Barbuda,-0.241
+1993,SKN,St. Kitts and Nevis,-0.204
+1993,MEX,Mexico,-0.5
+1993,BLZ,Belize,-0.149
+1993,GUA,Guatemala,-0.449
+1993,HON,Honduras,-0.186
+1993,SAL,El Salvador,-0.255
+1993,NIC,Nicaragua,-0.229
+1993,COS,Costa Rica,-0.064
+1993,PAN,Panama,0.229
+1993,COL,Colombia,-0.597
+1993,VEN,Venezuela,-0.395
+1993,GUY,Guyana,-0.429
+1993,SUR,Suriname,-0.509
+1993,ECU,Ecuador,-0.382
+1993,PER,Peru,-0.413
+1993,BRA,Brazil,-0.366
+1993,BOL,Bolivia,-0.229
+1993,PAR,Paraguay,0.107
+1993,CHL,Chile,-0.267
+1993,ARG,Argentina,0.678
+1993,URU,Uruguay,0.118
+1993,UKG,United Kingdom,1.904
+1993,IRE,Ireland,0.95
+1993,NTH,Netherlands,1.43
+1993,BEL,Belgium,1.365
+1993,LUX,Luxembourg,1.381
+1993,FRN,France,1.646
+1993,MNC,Monaco,-0.076
+1993,LIE,Liechtenstein,1.013
+1993,SPN,Spain,0.846
+1993,AND,Andorra,1.185
+1993,POR,Portugal,1.167
+1993,GFR,German Federal Republic,1.452
+1993,POL,Poland,1.163
+1993,AUS,Austria,0.939
+1993,HUN,Hungary,1.147
+1993,CZR,Czech Republic,1.276
+1993,SLO,Slovakia,1.138
+1993,ITA,Italy,1.37
+1993,SNM,San Marino,1.038
+1993,MLT,Malta,0.694
+1993,ALB,Albania,0.727
+1993,MAC,Macedonia,0.468
+1993,CRO,Croatia,0.933
+1993,BOS,Bosnia and Herzegovina,-0.208
+1993,SLV,Slovenia,0.936
+1993,GRC,Greece,0.769
+1993,CYP,Cyprus,-0.269
+1993,BUL,Bulgaria,1.268
+1993,MLD,Moldova,0.964
+1993,ROM,Romania,1.246
+1993,RUS,Russia,1.067
+1993,EST,Estonia,0.883
+1993,LAT,Latvia,0.906
+1993,LIT,Lithuania,0.905
+1993,UKR,Ukraine,0.164
+1993,BLR,Belarus,0.284
+1993,ARM,Armenia,0.416
+1993,GRG,Georgia,1.037
+1993,AZE,Azerbaijan,0.094
+1993,FIN,Finland,1.137
+1993,SWD,Sweden,0.989
+1993,NOR,Norway,1.112
+1993,DEN,Denmark,1.087
+1993,ICE,Iceland,1.135
+1993,CAP,Cape Verde,-0.556
+1993,GNB,Guinea-Bissau,-0.727
+1993,GAM,Gambia,-0.379
+1993,MLI,Mali,-0.833
+1993,SEN,Senegal,-0.641
+1993,BEN,Benin,-0.438
+1993,MAA,Mauritania,-0.926
+1993,NIR,Niger,-0.814
+1993,CDI,Ivory Coast,-0.355
+1993,GUI,Guinea,-0.845
+1993,BFO,Burkina Faso,-0.834
+1993,SIE,Sierra Leone,-0.817
+1993,GHA,Ghana,-0.669
+1993,TOG,Togo,-0.486
+1993,CAO,Cameroon,-0.564
+1993,NIG,Nigeria,-0.572
+1993,GAB,Gabon,-0.396
+1993,CEN,Central African Republic,-0.199
+1993,CHA,Chad,-0.763
+1993,CON,Congo,-0.38
+1993,DRC,Democratic Republic of the Congo,-0.279
+1993,UGA,Uganda,-1.152
+1993,KEN,Kenya,-0.11
+1993,TAZ,Tanzania,-0.901
+1993,BUI,Burundi,-0.58
+1993,RWA,Rwanda,-0.462
+1993,DJI,Djibouti,-0.766
+1993,ETH,Ethiopia,-0.411
+1993,ERI,Eritrea,-0.448
+1993,ANG,Angola,-0.932
+1993,MZM,Mozambique,-0.75
+1993,ZAM,Zambia,-0.581
+1993,ZIM,Zimbabwe,-1.063
+1993,MAW,Malawi,-0.186
+1993,SAF,South Africa,-0.004
+1993,NAM,Namibia,-1.026
+1993,LES,Lesotho,-0.583
+1993,BOT,Botswana,-0.381
+1993,SWA,Swaziland,-0.326
+1993,MAG,Madagascar,-0.86
+1993,COM,Comoros,-0.747
+1993,MAS,Mauritius,-0.298
+1993,SEY,Seychelles,-0.695
+1993,MOR,Morocco,-0.885
+1993,ALG,Algeria,-0.888
+1993,TUN,Tunisia,-0.859
+1993,LIB,Libya,-1.439
+1993,SUD,Sudan,-1.591
+1993,IRN,Iran,-1.269
+1993,TUR,Turkey,0.607
+1993,IRQ,Iraq,-1.605
+1993,EGY,Egypt,-0.683
+1993,SYR,Syria,-1.415
+1993,LEB,Lebanon,-0.809
+1993,JOR,Jordan,-0.883
+1993,ISR,Israel,2.101
+1993,SAU,Saudi Arabia,-0.541
+1993,YAR,Yemen Arab Republic,-1.086
+1993,KUW,Kuwait,-0.503
+1993,BAH,Bahrain,-0.724
+1993,QAT,Qatar,-0.735
+1993,UAE,United Arab Emirates,-0.7
+1993,OMA,Oman,-0.756
+1993,AFG,Afghanistan,-0.85
+1993,TKM,Turkmenistan,0.168
+1993,TAJ,Tajikistan,0.266
+1993,KYR,Kyrgyzstan,0.241
+1993,KZK,Kazakhstan,0.366
+1993,CHN,China,-1.104
+1993,MON,Mongolia,0.993
+1993,PRK,North Korea,-1.586
+1993,ROK,South Korea,0.396
+1993,JPN,Japan,1.009
+1993,IND,India,-1.036
+1993,BHU,Bhutan,-0.696
+1993,PAK,Pakistan,-0.979
+1993,BNG,Bangladesh,-0.773
+1993,MYA,Myanmar,-0.996
+1993,SRI,Sri Lanka,-0.954
+1993,MAD,Maldives,-0.701
+1993,NEP,Nepal,-0.356
+1993,THI,Thailand,-0.698
+1993,CAM,Cambodia,-0.531
+1993,LAO,Laos,-1.11
+1993,DRV,Vietnam,-1.465
+1993,MAL,Malaysia,-1.107
+1993,SIN,Singapore,-0.317
+1993,BRU,Brunei,-0.912
+1993,PHI,Philippines,-0.72
+1993,INS,Indonesia,-1.216
+1993,AUL,Australia,0.697
+1993,PNG,Papua New Guinea,-0.104
+1993,NEW,New Zealand,0.634
+1993,VAN,Vanuatu,-0.632
+1993,SOL,Solomon Islands,0.228
+1993,FIJ,Fiji,0.1
+1993,MSI,Marshall Islands,0.641
+1993,FSM,Federated States of Micronesia,0.645
+1993,WSM,Samoa,0.315
+1994,USA,United States of America,2.688
+1994,CAN,Canada,1.145
+1994,BHM,Bahamas,-0.124
+1994,CUB,Cuba,-1.569
+1994,HAI,Haiti,-0.338
+1994,JAM,Jamaica,-0.227
+1994,TRI,Trinidad and Tobago,-0.45
+1994,BAR,Barbados,-0.304
+1994,DMA,Dominica,-0.07
+1994,GRN,Grenada,-0.328
+1994,SLU,St. Lucia,-0.349
+1994,SVG,St. Vincent and the Grenadines,-0.131
+1994,AAB,Antigua & Barbuda,-0.246
+1994,SKN,St. Kitts and Nevis,-0.219
+1994,MEX,Mexico,-0.637
+1994,BLZ,Belize,-0.089
+1994,GUA,Guatemala,-0.442
+1994,HON,Honduras,-0.307
+1994,SAL,El Salvador,-0.16
+1994,NIC,Nicaragua,-0.131
+1994,COS,Costa Rica,-0.1
+1994,PAN,Panama,-0.122
+1994,COL,Colombia,-0.656
+1994,VEN,Venezuela,-0.551
+1994,GUY,Guyana,-0.456
+1994,SUR,Suriname,-0.45
+1994,ECU,Ecuador,-0.495
+1994,PER,Peru,-0.279
+1994,BRA,Brazil,-0.333
+1994,BOL,Bolivia,-0.251
+1994,PAR,Paraguay,-0.069
+1994,CHL,Chile,-0.154
+1994,ARG,Argentina,0.713
+1994,URU,Uruguay,-0.005
+1994,UKG,United Kingdom,1.986
+1994,IRE,Ireland,0.883
+1994,NTH,Netherlands,1.347
+1994,BEL,Belgium,1.311
+1994,LUX,Luxembourg,1.313
+1994,FRN,France,1.717
+1994,MNC,Monaco,0.98
+1994,LIE,Liechtenstein,0.914
+1994,SPN,Spain,0.948
+1994,AND,Andorra,1.037
+1994,POR,Portugal,1.146
+1994,GFR,German Federal Republic,1.351
+1994,POL,Poland,1.193
+1994,AUS,Austria,0.935
+1994,HUN,Hungary,1.253
+1994,CZR,Czech Republic,1.22
+1994,SLO,Slovakia,1.178
+1994,ITA,Italy,1.328
+1994,MLT,Malta,0.796
+1994,ALB,Albania,0.825
+1994,MAC,Macedonia,0.785
+1994,CRO,Croatia,0.739
+1994,BOS,Bosnia and Herzegovina,-0.053
+1994,SLV,Slovenia,0.942
+1994,GRC,Greece,0.955
+1994,CYP,Cyprus,-0.255
+1994,BUL,Bulgaria,1.065
+1994,MLD,Moldova,0.96
+1994,ROM,Romania,1.176
+1994,RUS,Russia,0.909
+1994,EST,Estonia,0.932
+1994,LAT,Latvia,1.039
+1994,LIT,Lithuania,1.016
+1994,UKR,Ukraine,0.494
+1994,BLR,Belarus,0.383
+1994,ARM,Armenia,0.501
+1994,GRG,Georgia,0.984
+1994,AZE,Azerbaijan,0.141
+1994,FIN,Finland,1.213
+1994,SWD,Sweden,0.924
+1994,NOR,Norway,1.112
+1994,DEN,Denmark,1.15
+1994,ICE,Iceland,1.119
+1994,CAP,Cape Verde,-0.504
+1994,GAM,Gambia,-0.408
+1994,MLI,Mali,-0.832
+1994,SEN,Senegal,-0.605
+1994,BEN,Benin,-0.514
+1994,MAA,Mauritania,-0.797
+1994,NIR,Niger,-0.756
+1994,CDI,Ivory Coast,-0.164
+1994,GUI,Guinea,-0.75
+1994,BFO,Burkina Faso,-0.833
+1994,SIE,Sierra Leone,-0.75
+1994,GHA,Ghana,-0.782
+1994,TOG,Togo,-0.545
+1994,CAO,Cameroon,-0.486
+1994,NIG,Nigeria,-0.627
+1994,GAB,Gabon,-0.348
+1994,CEN,Central African Republic,-0.167
+1994,CON,Congo,-0.511
+1994,DRC,Democratic Republic of the Congo,-0.283
+1994,UGA,Uganda,-1.111
+1994,KEN,Kenya,-0.378
+1994,TAZ,Tanzania,-1.074
+1994,BUI,Burundi,-0.728
+1994,RWA,Rwanda,-0.422
+1994,DJI,Djibouti,-0.623
+1994,ETH,Ethiopia,-0.504
+1994,ERI,Eritrea,-0.354
+1994,ANG,Angola,-0.583
+1994,MZM,Mozambique,-0.795
+1994,ZAM,Zambia,-0.619
+1994,ZIM,Zimbabwe,-1.078
+1994,MAW,Malawi,-0.336
+1994,SAF,South Africa,-0.315
+1994,NAM,Namibia,-0.99
+1994,LES,Lesotho,-0.852
+1994,BOT,Botswana,-0.446
+1994,SWA,Swaziland,-0.226
+1994,MAG,Madagascar,-0.72
+1994,COM,Comoros,-0.612
+1994,MAS,Mauritius,-0.371
+1994,SEY,Seychelles,-0.713
+1994,MOR,Morocco,-0.473
+1994,ALG,Algeria,-0.911
+1994,TUN,Tunisia,-0.796
+1994,LIB,Libya,-1.592
+1994,SUD,Sudan,-1.76
+1994,IRN,Iran,-1.556
+1994,TUR,Turkey,0.668
+1994,IRQ,Iraq,-1.601
+1994,EGY,Egypt,-0.807
+1994,SYR,Syria,-1.445
+1994,LEB,Lebanon,-0.919
+1994,JOR,Jordan,-0.935
+1994,ISR,Israel,2.042
+1994,SAU,Saudi Arabia,-0.633
+1994,YAR,Yemen Arab Republic,-1.061
+1994,KUW,Kuwait,-0.491
+1994,BAH,Bahrain,-0.729
+1994,QAT,Qatar,-0.836
+1994,UAE,United Arab Emirates,-0.715
+1994,OMA,Oman,-0.815
+1994,AFG,Afghanistan,-1.019
+1994,TKM,Turkmenistan,0.101
+1994,TAJ,Tajikistan,0.574
+1994,KYR,Kyrgyzstan,-0.003
+1994,UZB,Uzbekistan,0.283
+1994,KZK,Kazakhstan,0.379
+1994,CHN,China,-1.363
+1994,MON,Mongolia,-0.072
+1994,PRK,North Korea,-1.725
+1994,ROK,South Korea,0.365
+1994,JPN,Japan,1.029
+1994,IND,India,-1.212
+1994,BHU,Bhutan,-0.704
+1994,PAK,Pakistan,-0.993
+1994,BNG,Bangladesh,-0.835
+1994,MYA,Myanmar,-0.999
+1994,SRI,Sri Lanka,-1.052
+1994,MAD,Maldives,-0.611
+1994,NEP,Nepal,-0.498
+1994,THI,Thailand,-0.714
+1994,CAM,Cambodia,-0.278
+1994,LAO,Laos,-1.224
+1994,DRV,Vietnam,-1.517
+1994,MAL,Malaysia,-0.933
+1994,SIN,Singapore,-0.364
+1994,BRU,Brunei,-0.874
+1994,PHI,Philippines,-0.607
+1994,INS,Indonesia,-1.281
+1994,AUL,Australia,0.772
+1994,PNG,Papua New Guinea,-0.107
+1994,NEW,New Zealand,0.655
+1994,VAN,Vanuatu,-0.052
+1994,SOL,Solomon Islands,0.261
+1994,FIJ,Fiji,0.04
+1994,MSI,Marshall Islands,0.632
+1994,WSM,Samoa,0.376
+1995,USA,United States of America,2.939
+1995,CAN,Canada,1.106
+1995,BHM,Bahamas,0.111
+1995,CUB,Cuba,-1.739
+1995,HAI,Haiti,-0.296
+1995,JAM,Jamaica,-0.169
+1995,TRI,Trinidad and Tobago,-0.371
+1995,BAR,Barbados,0.001
+1995,DMA,Dominica,0.193
+1995,GRN,Grenada,-0.014
+1995,SLU,St. Lucia,-0.316
+1995,SVG,St. Vincent and the Grenadines,-0.088
+1995,AAB,Antigua & Barbuda,0.095
+1995,SKN,St. Kitts and Nevis,-0.235
+1995,MEX,Mexico,-0.527
+1995,BLZ,Belize,-0.023
+1995,GUA,Guatemala,-0.384
+1995,HON,Honduras,-0.22
+1995,SAL,El Salvador,-0.077
+1995,NIC,Nicaragua,-0.114
+1995,COS,Costa Rica,-0.112
+1995,PAN,Panama,-0.128
+1995,COL,Colombia,-0.631
+1995,VEN,Venezuela,-0.403
+1995,GUY,Guyana,-0.428
+1995,SUR,Suriname,-0.256
+1995,ECU,Ecuador,-0.097
+1995,PER,Peru,-0.235
+1995,BRA,Brazil,-0.266
+1995,BOL,Bolivia,-0.232
+1995,PAR,Paraguay,0.061
+1995,CHL,Chile,-0.201
+1995,ARG,Argentina,1.241
+1995,URU,Uruguay,-0.146
+1995,UKG,United Kingdom,2.228
+1995,IRE,Ireland,0.864
+1995,NTH,Netherlands,1.226
+1995,BEL,Belgium,1.566
+1995,LUX,Luxembourg,1.132
+1995,FRN,France,1.694
+1995,MNC,Monaco,1.161
+1995,LIE,Liechtenstein,1.344
+1995,SPN,Spain,0.91
+1995,AND,Andorra,0.962
+1995,POR,Portugal,1.025
+1995,GFR,German Federal Republic,1.171
+1995,POL,Poland,1.116
+1995,AUS,Austria,0.94
+1995,HUN,Hungary,1.146
+1995,CZR,Czech Republic,1.08
+1995,SLO,Slovakia,1.084
+1995,ITA,Italy,1.154
+1995,SNM,San Marino,0.086
+1995,MLT,Malta,0.698
+1995,ALB,Albania,0.804
+1995,MAC,Macedonia,0.816
+1995,CRO,Croatia,0.816
+1995,BOS,Bosnia and Herzegovina,0.362
+1995,SLV,Slovenia,0.98
+1995,GRC,Greece,1.319
+1995,CYP,Cyprus,-0.118
+1995,BUL,Bulgaria,0.992
+1995,MLD,Moldova,0.946
+1995,ROM,Romania,1.055
+1995,RUS,Russia,0.98
+1995,EST,Estonia,0.983
+1995,LAT,Latvia,1.513
+1995,LIT,Lithuania,1.039
+1995,UKR,Ukraine,0.433
+1995,BLR,Belarus,0.41
+1995,ARM,Armenia,0.568
+1995,GRG,Georgia,1.656
+1995,AZE,Azerbaijan,0.189
+1995,FIN,Finland,1.104
+1995,SWD,Sweden,0.943
+1995,NOR,Norway,1.046
+1995,DEN,Denmark,1.063
+1995,ICE,Iceland,1.022
+1995,CAP,Cape Verde,-0.636
+1995,GNB,Guinea-Bissau,-0.296
+1995,EQG,Equatorial Guinea,-0.006
+1995,GAM,Gambia,-0.646
+1995,MLI,Mali,-0.69
+1995,SEN,Senegal,-0.649
+1995,BEN,Benin,-0.417
+1995,MAA,Mauritania,-0.787
+1995,NIR,Niger,-0.784
+1995,CDI,Ivory Coast,-0.278
+1995,GUI,Guinea,-0.606
+1995,BFO,Burkina Faso,-0.813
+1995,LBR,Liberia,0.002
+1995,SIE,Sierra Leone,-0.618
+1995,GHA,Ghana,-0.865
+1995,TOG,Togo,-0.753
+1995,CAO,Cameroon,-0.47
+1995,NIG,Nigeria,-1.109
+1995,GAB,Gabon,-0.33
+1995,CHA,Chad,-0.517
+1995,CON,Congo,-0.4
+1995,DRC,Democratic Republic of the Congo,-0.664
+1995,UGA,Uganda,-0.929
+1995,KEN,Kenya,-0.38
+1995,TAZ,Tanzania,-1.214
+1995,BUI,Burundi,-0.799
+1995,RWA,Rwanda,-0.446
+1995,DJI,Djibouti,-0.733
+1995,ETH,Ethiopia,-0.317
+1995,ERI,Eritrea,-0.297
+1995,ANG,Angola,-0.667
+1995,MZM,Mozambique,-0.703
+1995,ZAM,Zambia,-0.482
+1995,ZIM,Zimbabwe,-0.952
+1995,MAW,Malawi,-0.351
+1995,SAF,South Africa,-0.319
+1995,NAM,Namibia,-0.892
+1995,LES,Lesotho,-0.418
+1995,BOT,Botswana,-0.411
+1995,SWA,Swaziland,-0.056
+1995,MAG,Madagascar,-0.682
+1995,COM,Comoros,-0.613
+1995,MAS,Mauritius,-0.461
+1995,SEY,Seychelles,-0.719
+1995,MOR,Morocco,-0.375
+1995,ALG,Algeria,-0.897
+1995,TUN,Tunisia,-0.746
+1995,LIB,Libya,-1.845
+1995,SUD,Sudan,-1.818
+1995,IRN,Iran,-1.529
+1995,TUR,Turkey,0.662
+1995,EGY,Egypt,-0.787
+1995,SYR,Syria,-1.497
+1995,LEB,Lebanon,-1.046
+1995,JOR,Jordan,-0.837
+1995,ISR,Israel,2.529
+1995,SAU,Saudi Arabia,-0.707
+1995,YAR,Yemen Arab Republic,-0.81
+1995,KUW,Kuwait,-0.516
+1995,BAH,Bahrain,-0.763
+1995,QAT,Qatar,-0.863
+1995,UAE,United Arab Emirates,-0.778
+1995,OMA,Oman,-0.792
+1995,AFG,Afghanistan,-0.851
+1995,TKM,Turkmenistan,0.005
+1995,TAJ,Tajikistan,0.54
+1995,KYR,Kyrgyzstan,-0.011
+1995,UZB,Uzbekistan,0.587
+1995,KZK,Kazakhstan,0.417
+1995,CHN,China,-1.443
+1995,MON,Mongolia,-0.137
+1995,PRK,North Korea,-1.69
+1995,ROK,South Korea,0.39
+1995,JPN,Japan,0.901
+1995,IND,India,-1.431
+1995,BHU,Bhutan,-0.654
+1995,PAK,Pakistan,-1.101
+1995,BNG,Bangladesh,-0.844
+1995,MYA,Myanmar,-1.369
+1995,SRI,Sri Lanka,-1.082
+1995,MAD,Maldives,-0.583
+1995,NEP,Nepal,-0.492
+1995,THI,Thailand,-0.763
+1995,CAM,Cambodia,-0.191
+1995,LAO,Laos,-1.188
+1995,DRV,Vietnam,-1.516
+1995,MAL,Malaysia,-0.812
+1995,SIN,Singapore,-0.382
+1995,BRU,Brunei,-0.937
+1995,PHI,Philippines,-0.581
+1995,INS,Indonesia,-1.317
+1995,AUL,Australia,1.091
+1995,PNG,Papua New Guinea,-0.144
+1995,NEW,New Zealand,0.572
+1995,VAN,Vanuatu,-0.27
+1995,SOL,Solomon Islands,0.211
+1995,FIJ,Fiji,-0.048
+1995,MSI,Marshall Islands,0.48
+1995,PAL,Palau,0.826
+1995,FSM,Federated States of Micronesia,0.333
+1995,WSM,Samoa,0.229
+1996,USA,United States of America,3.014
+1996,CAN,Canada,1.196
+1996,BHM,Bahamas,-0.012
+1996,CUB,Cuba,-1.857
+1996,HAI,Haiti,-0.246
+1996,DOM,Dominican Republic,-0.087
+1996,JAM,Jamaica,-0.205
+1996,TRI,Trinidad and Tobago,-0.264
+1996,BAR,Barbados,-0.027
+1996,DMA,Dominica,-0.127
+1996,GRN,Grenada,-0.048
+1996,SLU,St. Lucia,-0.316
+1996,SVG,St. Vincent and the Grenadines,-0.082
+1996,AAB,Antigua & Barbuda,-0.137
+1996,SKN,St. Kitts and Nevis,-0.209
+1996,MEX,Mexico,-0.487
+1996,BLZ,Belize,-0.066
+1996,GUA,Guatemala,-0.093
+1996,HON,Honduras,-0.188
+1996,SAL,El Salvador,-0.141
+1996,NIC,Nicaragua,-0.148
+1996,COS,Costa Rica,-0.189
+1996,PAN,Panama,-0.27
+1996,COL,Colombia,-0.661
+1996,VEN,Venezuela,-0.364
+1996,GUY,Guyana,-0.425
+1996,SUR,Suriname,-0.132
+1996,ECU,Ecuador,-0.266
+1996,PER,Peru,-0.217
+1996,BRA,Brazil,-0.211
+1996,BOL,Bolivia,-0.205
+1996,PAR,Paraguay,0.054
+1996,CHL,Chile,-0.095
+1996,ARG,Argentina,0.693
+1996,URU,Uruguay,-0.016
+1996,UKG,United Kingdom,2.034
+1996,IRE,Ireland,0.905
+1996,NTH,Netherlands,1.403
+1996,BEL,Belgium,1.485
+1996,LUX,Luxembourg,1.355
+1996,FRN,France,1.727
+1996,MNC,Monaco,1.251
+1996,LIE,Liechtenstein,1.049
+1996,SPN,Spain,1.154
+1996,AND,Andorra,1.146
+1996,POR,Portugal,1.248
+1996,GFR,German Federal Republic,1.252
+1996,POL,Poland,1.221
+1996,AUS,Austria,0.992
+1996,HUN,Hungary,1.327
+1996,CZR,Czech Republic,1.232
+1996,SLO,Slovakia,1.208
+1996,ITA,Italy,1.284
+1996,SNM,San Marino,0.634
+1996,MLT,Malta,0.342
+1996,ALB,Albania,1.03
+1996,MAC,Macedonia,0.973
+1996,CRO,Croatia,0.917
+1996,BOS,Bosnia and Herzegovina,0.458
+1996,SLV,Slovenia,1.184
+1996,GRC,Greece,1.297
+1996,CYP,Cyprus,0.479
+1996,BUL,Bulgaria,1.173
+1996,MLD,Moldova,0.948
+1996,ROM,Romania,1.233
+1996,RUS,Russia,0.708
+1996,EST,Estonia,1.141
+1996,LAT,Latvia,1.304
+1996,LIT,Lithuania,1.238
+1996,UKR,Ukraine,0.531
+1996,BLR,Belarus,0.477
+1996,ARM,Armenia,0.592
+1996,GRG,Georgia,0.932
+1996,AZE,Azerbaijan,0.223
+1996,FIN,Finland,1.332
+1996,SWD,Sweden,0.981
+1996,NOR,Norway,1.206
+1996,DEN,Denmark,1.266
+1996,ICE,Iceland,1.209
+1996,CAP,Cape Verde,-0.62
+1996,GNB,Guinea-Bissau,-0.536
+1996,EQG,Equatorial Guinea,0.009
+1996,GAM,Gambia,-0.781
+1996,MLI,Mali,-0.668
+1996,SEN,Senegal,-0.509
+1996,BEN,Benin,-0.606
+1996,MAA,Mauritania,-0.65
+1996,NIR,Niger,-0.743
+1996,CDI,Ivory Coast,-0.412
+1996,GUI,Guinea,-0.74
+1996,BFO,Burkina Faso,-0.556
+1996,LBR,Liberia,-0.487
+1996,SIE,Sierra Leone,-0.485
+1996,GHA,Ghana,-1.046
+1996,TOG,Togo,-0.717
+1996,CAO,Cameroon,-0.544
+1996,NIG,Nigeria,-1.322
+1996,GAB,Gabon,-0.512
+1996,CHA,Chad,-0.714
+1996,CON,Congo,-0.473
+1996,DRC,Democratic Republic of the Congo,-0.399
+1996,UGA,Uganda,-0.973
+1996,KEN,Kenya,-0.351
+1996,TAZ,Tanzania,-1.098
+1996,BUI,Burundi,-0.659
+1996,RWA,Rwanda,-0.454
+1996,DJI,Djibouti,-0.825
+1996,ETH,Ethiopia,-0.346
+1996,ERI,Eritrea,-0.477
+1996,ANG,Angola,-0.69
+1996,MZM,Mozambique,-0.625
+1996,ZAM,Zambia,-0.485
+1996,ZIM,Zimbabwe,-0.831
+1996,MAW,Malawi,-0.358
+1996,SAF,South Africa,-0.213
+1996,NAM,Namibia,-0.796
+1996,LES,Lesotho,-0.467
+1996,BOT,Botswana,-0.443
+1996,SWA,Swaziland,-0.137
+1996,MAG,Madagascar,-0.638
+1996,COM,Comoros,-0.361
+1996,MAS,Mauritius,-0.242
+1996,SEY,Seychelles,-0.654
+1996,MOR,Morocco,-0.583
+1996,ALG,Algeria,-0.923
+1996,TUN,Tunisia,-0.769
+1996,LIB,Libya,-1.799
+1996,SUD,Sudan,-1.727
+1996,IRN,Iran,-1.685
+1996,TUR,Turkey,0.665
+1996,EGY,Egypt,-0.844
+1996,SYR,Syria,-1.69
+1996,LEB,Lebanon,-1.192
+1996,JOR,Jordan,-0.729
+1996,ISR,Israel,2.439
+1996,SAU,Saudi Arabia,-0.784
+1996,YAR,Yemen Arab Republic,-0.841
+1996,KUW,Kuwait,-0.476
+1996,BAH,Bahrain,-0.736
+1996,QAT,Qatar,-0.912
+1996,UAE,United Arab Emirates,-0.662
+1996,OMA,Oman,-0.798
+1996,AFG,Afghanistan,-1.023
+1996,TKM,Turkmenistan,-0.2
+1996,TAJ,Tajikistan,0.682
+1996,KYR,Kyrgyzstan,0.215
+1996,UZB,Uzbekistan,0.698
+1996,KZK,Kazakhstan,0.466
+1996,CHN,China,-1.337
+1996,MON,Mongolia,-0.088
+1996,PRK,North Korea,-1.779
+1996,ROK,South Korea,0.47
+1996,JPN,Japan,0.976
+1996,IND,India,-1.506
+1996,BHU,Bhutan,-0.509
+1996,PAK,Pakistan,-1.068
+1996,BNG,Bangladesh,-0.843
+1996,MYA,Myanmar,-1.507
+1996,SRI,Sri Lanka,-0.971
+1996,MAD,Maldives,-0.556
+1996,NEP,Nepal,-0.603
+1996,THI,Thailand,-0.64
+1996,CAM,Cambodia,-0.193
+1996,LAO,Laos,-1.142
+1996,DRV,Vietnam,-1.449
+1996,MAL,Malaysia,-0.768
+1996,SIN,Singapore,-0.439
+1996,BRU,Brunei,-0.832
+1996,PHI,Philippines,-0.625
+1996,INS,Indonesia,-1.386
+1996,AUL,Australia,0.824
+1996,PNG,Papua New Guinea,-0.544
+1996,NEW,New Zealand,0.61
+1996,VAN,Vanuatu,-0.2
+1996,SOL,Solomon Islands,0.109
+1996,FIJ,Fiji,0.026
+1996,MSI,Marshall Islands,0.778
+1996,PAL,Palau,0.822
+1996,FSM,Federated States of Micronesia,0.693
+1996,WSM,Samoa,0.235
+1997,USA,United States of America,2.923
+1997,CAN,Canada,1.116
+1997,BHM,Bahamas,-0.14
+1997,CUB,Cuba,-1.903
+1997,HAI,Haiti,-0.273
+1997,DOM,Dominican Republic,-0.121
+1997,JAM,Jamaica,-0.279
+1997,TRI,Trinidad and Tobago,-0.285
+1997,BAR,Barbados,-0.197
+1997,DMA,Dominica,-0.295
+1997,GRN,Grenada,-0.301
+1997,SLU,St. Lucia,-0.583
+1997,SVG,St. Vincent and the Grenadines,-0.197
+1997,AAB,Antigua & Barbuda,-0.303
+1997,SKN,St. Kitts and Nevis,-0.458
+1997,MEX,Mexico,-0.474
+1997,BLZ,Belize,-0.202
+1997,GUA,Guatemala,-0.108
+1997,HON,Honduras,-0.196
+1997,SAL,El Salvador,-0.216
+1997,NIC,Nicaragua,-0.093
+1997,COS,Costa Rica,-0.186
+1997,PAN,Panama,-0.373
+1997,COL,Colombia,-0.724
+1997,VEN,Venezuela,-0.332
+1997,GUY,Guyana,-0.431
+1997,SUR,Suriname,-0.475
+1997,ECU,Ecuador,-0.137
+1997,PER,Peru,-0.23
+1997,BRA,Brazil,-0.194
+1997,BOL,Bolivia,-0.111
+1997,PAR,Paraguay,-0.084
+1997,CHL,Chile,-0.059
+1997,ARG,Argentina,0.533
+1997,URU,Uruguay,-0.035
+1997,UKG,United Kingdom,1.977
+1997,IRE,Ireland,1.044
+1997,NTH,Netherlands,1.471
+1997,BEL,Belgium,1.447
+1997,LUX,Luxembourg,1.433
+1997,FRN,France,1.723
+1997,MNC,Monaco,1.22
+1997,LIE,Liechtenstein,1.124
+1997,SPN,Spain,1.271
+1997,AND,Andorra,1.27
+1997,POR,Portugal,1.302
+1997,GFR,German Federal Republic,1.373
+1997,POL,Poland,1.289
+1997,AUS,Austria,1.132
+1997,HUN,Hungary,1.293
+1997,CZR,Czech Republic,1.318
+1997,SLO,Slovakia,1.242
+1997,ITA,Italy,1.337
+1997,SNM,San Marino,0.887
+1997,MLT,Malta,0.341
+1997,ALB,Albania,1.045
+1997,MAC,Macedonia,1.026
+1997,CRO,Croatia,1.019
+1997,SLV,Slovenia,1.203
+1997,GRC,Greece,1.277
+1997,CYP,Cyprus,0.622
+1997,BUL,Bulgaria,1.291
+1997,MLD,Moldova,0.99
+1997,ROM,Romania,1.22
+1997,RUS,Russia,0.524
+1997,EST,Estonia,1.201
+1997,LAT,Latvia,1.311
+1997,LIT,Lithuania,1.263
+1997,UKR,Ukraine,0.483
+1997,BLR,Belarus,0.355
+1997,ARM,Armenia,0.68
+1997,GRG,Georgia,0.804
+1997,AZE,Azerbaijan,0.229
+1997,FIN,Finland,1.367
+1997,SWD,Sweden,1.141
+1997,NOR,Norway,1.305
+1997,DEN,Denmark,1.296
+1997,ICE,Iceland,1.326
+1997,CAP,Cape Verde,-0.571
+1997,GNB,Guinea-Bissau,-0.666
+1997,EQG,Equatorial Guinea,-0.239
+1997,GAM,Gambia,-0.783
+1997,MLI,Mali,-0.706
+1997,SEN,Senegal,-0.396
+1997,BEN,Benin,-0.71
+1997,MAA,Mauritania,-0.548
+1997,NIR,Niger,-0.911
+1997,CDI,Ivory Coast,-0.629
+1997,GUI,Guinea,-0.751
+1997,BFO,Burkina Faso,-0.694
+1997,LBR,Liberia,-0.503
+1997,SIE,Sierra Leone,-0.499
+1997,GHA,Ghana,-1.098
+1997,TOG,Togo,-0.838
+1997,CAO,Cameroon,-0.613
+1997,NIG,Nigeria,-1.451
+1997,GAB,Gabon,-0.65
+1997,CHA,Chad,-0.782
+1997,CON,Congo,-0.611
+1997,DRC,Democratic Republic of the Congo,-0.522
+1997,UGA,Uganda,-0.932
+1997,KEN,Kenya,-0.585
+1997,TAZ,Tanzania,-1.087
+1997,BUI,Burundi,-0.588
+1997,RWA,Rwanda,0.132
+1997,DJI,Djibouti,-0.791
+1997,ETH,Ethiopia,-0.195
+1997,ERI,Eritrea,-0.497
+1997,ANG,Angola,-0.659
+1997,MZM,Mozambique,-0.668
+1997,ZAM,Zambia,-0.366
+1997,ZIM,Zimbabwe,-0.879
+1997,MAW,Malawi,-0.287
+1997,SAF,South Africa,-0.133
+1997,NAM,Namibia,-0.861
+1997,LES,Lesotho,-0.654
+1997,BOT,Botswana,-0.476
+1997,SWA,Swaziland,-0.039
+1997,MAG,Madagascar,-0.541
+1997,COM,Comoros,-0.716
+1997,MAS,Mauritius,-0.161
+1997,MOR,Morocco,-0.668
+1997,ALG,Algeria,-0.888
+1997,TUN,Tunisia,-0.747
+1997,LIB,Libya,-1.866
+1997,SUD,Sudan,-1.641
+1997,IRN,Iran,-1.72
+1997,TUR,Turkey,0.602
+1997,EGY,Egypt,-0.842
+1997,SYR,Syria,-1.649
+1997,LEB,Lebanon,-1.091
+1997,JOR,Jordan,-0.63
+1997,ISR,Israel,2.42
+1997,SAU,Saudi Arabia,-0.729
+1997,YAR,Yemen Arab Republic,-0.826
+1997,KUW,Kuwait,-0.446
+1997,BAH,Bahrain,-0.66
+1997,QAT,Qatar,-0.942
+1997,UAE,United Arab Emirates,-0.699
+1997,OMA,Oman,-0.778
+1997,AFG,Afghanistan,-0.859
+1997,TKM,Turkmenistan,0.08
+1997,TAJ,Tajikistan,0.298
+1997,KYR,Kyrgyzstan,0.303
+1997,UZB,Uzbekistan,0.593
+1997,KZK,Kazakhstan,0.378
+1997,CHN,China,-1.302
+1997,MON,Mongolia,-0.135
+1997,PRK,North Korea,-1.893
+1997,ROK,South Korea,0.521
+1997,JPN,Japan,0.854
+1997,IND,India,-1.297
+1997,BHU,Bhutan,-0.588
+1997,PAK,Pakistan,-0.914
+1997,BNG,Bangladesh,-0.811
+1997,MYA,Myanmar,-1.485
+1997,SRI,Sri Lanka,-0.894
+1997,MAD,Maldives,-0.549
+1997,NEP,Nepal,-0.673
+1997,THI,Thailand,-0.627
+1997,LAO,Laos,-1.226
+1997,DRV,Vietnam,-1.492
+1997,MAL,Malaysia,-0.723
+1997,SIN,Singapore,-0.505
+1997,BRU,Brunei,-0.684
+1997,PHI,Philippines,-0.768
+1997,INS,Indonesia,-1.29
+1997,AUL,Australia,0.889
+1997,PNG,Papua New Guinea,-0.659
+1997,NEW,New Zealand,0.788
+1997,VAN,Vanuatu,-0.142
+1997,SOL,Solomon Islands,0.183
+1997,FIJ,Fiji,-0.323
+1997,MSI,Marshall Islands,0.783
+1997,FSM,Federated States of Micronesia,1.202
+1997,WSM,Samoa,0.248
+1998,USA,United States of America,2.854
+1998,CAN,Canada,1.178
+1998,BHM,Bahamas,-0.194
+1998,CUB,Cuba,-1.742
+1998,HAI,Haiti,-0.381
+1998,DOM,Dominican Republic,-0.278
+1998,JAM,Jamaica,-0.271
+1998,TRI,Trinidad and Tobago,-0.263
+1998,BAR,Barbados,-0.184
+1998,DMA,Dominica,-0.295
+1998,GRN,Grenada,-0.254
+1998,SLU,St. Lucia,-0.591
+1998,SVG,St. Vincent and the Grenadines,-0.318
+1998,AAB,Antigua & Barbuda,-0.382
+1998,SKN,St. Kitts and Nevis,-0.484
+1998,MEX,Mexico,-0.588
+1998,BLZ,Belize,-0.376
+1998,GUA,Guatemala,-0.047
+1998,HON,Honduras,-0.02
+1998,SAL,El Salvador,-0.249
+1998,NIC,Nicaragua,0.098
+1998,COS,Costa Rica,-0.166
+1998,PAN,Panama,-0.359
+1998,COL,Colombia,-0.714
+1998,VEN,Venezuela,-0.426
+1998,GUY,Guyana,-0.461
+1998,SUR,Suriname,-0.62
+1998,ECU,Ecuador,-0.099
+1998,PER,Peru,-0.166
+1998,BRA,Brazil,-0.133
+1998,BOL,Bolivia,-0.215
+1998,PAR,Paraguay,-0.037
+1998,CHL,Chile,-0.134
+1998,ARG,Argentina,0.4
+1998,URU,Uruguay,-0.084
+1998,UKG,United Kingdom,1.759
+1998,IRE,Ireland,0.993
+1998,NTH,Netherlands,1.317
+1998,BEL,Belgium,1.316
+1998,LUX,Luxembourg,1.302
+1998,FRN,France,1.591
+1998,MNC,Monaco,1.338
+1998,LIE,Liechtenstein,1.057
+1998,SPN,Spain,1.17
+1998,AND,Andorra,1.174
+1998,POR,Portugal,1.178
+1998,GFR,German Federal Republic,1.289
+1998,POL,Poland,1.201
+1998,AUS,Austria,1.116
+1998,HUN,Hungary,1.286
+1998,CZR,Czech Republic,1.208
+1998,SLO,Slovakia,1.189
+1998,ITA,Italy,1.243
+1998,SNM,San Marino,0.756
+1998,MLT,Malta,0.374
+1998,ALB,Albania,1.121
+1998,MAC,Macedonia,0.961
+1998,CRO,Croatia,0.992
+1998,SLV,Slovenia,1.147
+1998,GRC,Greece,1.183
+1998,CYP,Cyprus,0.529
+1998,BUL,Bulgaria,1.206
+1998,MLD,Moldova,0.949
+1998,ROM,Romania,1.174
+1998,RUS,Russia,0.543
+1998,EST,Estonia,1.191
+1998,LAT,Latvia,1.221
+1998,LIT,Lithuania,1.203
+1998,UKR,Ukraine,0.399
+1998,BLR,Belarus,0.153
+1998,ARM,Armenia,0.482
+1998,GRG,Georgia,0.601
+1998,AZE,Azerbaijan,0.092
+1998,FIN,Finland,1.241
+1998,SWD,Sweden,1.067
+1998,NOR,Norway,1.193
+1998,DEN,Denmark,1.183
+1998,ICE,Iceland,1.197
+1998,CAP,Cape Verde,-0.687
+1998,GNB,Guinea-Bissau,-0.758
+1998,EQG,Equatorial Guinea,-0.269
+1998,GAM,Gambia,-0.439
+1998,MLI,Mali,-0.755
+1998,SEN,Senegal,-0.382
+1998,BEN,Benin,-0.803
+1998,MAA,Mauritania,-0.642
+1998,NIR,Niger,-0.846
+1998,CDI,Ivory Coast,-0.693
+1998,GUI,Guinea,-0.815
+1998,BFO,Burkina Faso,-0.719
+1998,SIE,Sierra Leone,-0.543
+1998,GHA,Ghana,-1.005
+1998,TOG,Togo,-0.74
+1998,CAO,Cameroon,-0.475
+1998,NIG,Nigeria,-1.375
+1998,GAB,Gabon,-0.418
+1998,CEN,Central African Republic,-0.376
+1998,CHA,Chad,-0.678
+1998,CON,Congo,-0.579
+1998,DRC,Democratic Republic of the Congo,-0.524
+1998,UGA,Uganda,-0.895
+1998,KEN,Kenya,-0.555
+1998,TAZ,Tanzania,-1.03
+1998,BUI,Burundi,-0.485
+1998,RWA,Rwanda,0.053
+1998,DJI,Djibouti,-0.749
+1998,ETH,Ethiopia,-0.333
+1998,ERI,Eritrea,-0.708
+1998,ANG,Angola,-0.441
+1998,MZM,Mozambique,-0.61
+1998,ZAM,Zambia,-0.028
+1998,ZIM,Zimbabwe,-1.024
+1998,MAW,Malawi,-0.425
+1998,SAF,South Africa,-0.099
+1998,NAM,Namibia,-0.723
+1998,LES,Lesotho,-0.521
+1998,BOT,Botswana,-0.53
+1998,SWA,Swaziland,-0.049
+1998,MAG,Madagascar,-0.614
+1998,COM,Comoros,-0.353
+1998,MAS,Mauritius,-0.269
+1998,SEY,Seychelles,-0.19
+1998,MOR,Morocco,-0.717
+1998,ALG,Algeria,-0.838
+1998,TUN,Tunisia,-0.818
+1998,LIB,Libya,-1.777
+1998,SUD,Sudan,-1.556
+1998,IRN,Iran,-1.594
+1998,TUR,Turkey,0.588
+1998,EGY,Egypt,-0.882
+1998,SYR,Syria,-1.647
+1998,LEB,Lebanon,-1.23
+1998,JOR,Jordan,-0.809
+1998,ISR,Israel,2.334
+1998,SAU,Saudi Arabia,-0.765
+1998,YAR,Yemen Arab Republic,-0.856
+1998,KUW,Kuwait,-0.529
+1998,BAH,Bahrain,-0.757
+1998,QAT,Qatar,-0.926
+1998,UAE,United Arab Emirates,-0.744
+1998,OMA,Oman,-0.822
+1998,AFG,Afghanistan,-0.88
+1998,TKM,Turkmenistan,0.067
+1998,TAJ,Tajikistan,0.159
+1998,KYR,Kyrgyzstan,0.339
+1998,UZB,Uzbekistan,0.722
+1998,KZK,Kazakhstan,0.337
+1998,CHN,China,-0.76
+1998,MON,Mongolia,-0.217
+1998,PRK,North Korea,-1.882
+1998,ROK,South Korea,0.632
+1998,JPN,Japan,0.737
+1998,IND,India,-0.985
+1998,BHU,Bhutan,-0.656
+1998,PAK,Pakistan,-0.851
+1998,BNG,Bangladesh,-0.792
+1998,MYA,Myanmar,-1.306
+1998,SRI,Sri Lanka,-0.869
+1998,MAD,Maldives,-0.477
+1998,NEP,Nepal,-0.607
+1998,THI,Thailand,-0.557
+1998,LAO,Laos,-1.199
+1998,DRV,Vietnam,-1.443
+1998,MAL,Malaysia,-0.702
+1998,SIN,Singapore,-0.347
+1998,BRU,Brunei,-0.779
+1998,PHI,Philippines,-0.843
+1998,INS,Indonesia,-1.154
+1998,AUL,Australia,0.953
+1998,PNG,Papua New Guinea,-0.707
+1998,NEW,New Zealand,0.865
+1998,VAN,Vanuatu,-0.064
+1998,SOL,Solomon Islands,-0.001
+1998,FIJ,Fiji,-0.45
+1998,MSI,Marshall Islands,1.136
+1998,FSM,Federated States of Micronesia,1.832
+1998,WSM,Samoa,0.166
+1999,USA,United States of America,2.678
+1999,CAN,Canada,1.173
+1999,BHM,Bahamas,-0.229
+1999,CUB,Cuba,-1.609
+1999,HAI,Haiti,-0.4
+1999,DOM,Dominican Republic,-0.326
+1999,JAM,Jamaica,-0.35
+1999,TRI,Trinidad and Tobago,-0.35
+1999,BAR,Barbados,-0.251
+1999,DMA,Dominica,-0.422
+1999,GRN,Grenada,-0.33
+1999,SLU,St. Lucia,-0.752
+1999,SVG,St. Vincent and the Grenadines,-0.101
+1999,AAB,Antigua & Barbuda,-0.471
+1999,SKN,St. Kitts and Nevis,-0.537
+1999,MEX,Mexico,-0.532
+1999,BLZ,Belize,-0.408
+1999,GUA,Guatemala,0.048
+1999,HON,Honduras,-0.349
+1999,SAL,El Salvador,-0.33
+1999,NIC,Nicaragua,-0.05
+1999,COS,Costa Rica,-0.235
+1999,PAN,Panama,-0.33
+1999,COL,Colombia,-0.531
+1999,VEN,Venezuela,-0.505
+1999,GUY,Guyana,-0.462
+1999,SUR,Suriname,-0.788
+1999,ECU,Ecuador,-0.331
+1999,PER,Peru,-0.206
+1999,BRA,Brazil,-0.047
+1999,BOL,Bolivia,-0.198
+1999,PAR,Paraguay,-0.004
+1999,CHL,Chile,-0.099
+1999,ARG,Argentina,0.362
+1999,URU,Uruguay,-0.016
+1999,UKG,United Kingdom,1.687
+1999,IRE,Ireland,0.876
+1999,NTH,Netherlands,1.289
+1999,BEL,Belgium,1.237
+1999,LUX,Luxembourg,1.238
+1999,FRN,France,1.511
+1999,MNC,Monaco,1.361
+1999,LIE,Liechtenstein,1.06
+1999,SPN,Spain,1.111
+1999,AND,Andorra,1.111
+1999,POR,Portugal,1.106
+1999,GFR,German Federal Republic,1.358
+1999,POL,Poland,1.134
+1999,AUS,Austria,1.023
+1999,HUN,Hungary,1.306
+1999,CZR,Czech Republic,1.112
+1999,SLO,Slovakia,1.115
+1999,ITA,Italy,1.124
+1999,SNM,San Marino,0.735
+1999,MLT,Malta,0.594
+1999,ALB,Albania,1.038
+1999,MAC,Macedonia,0.889
+1999,CRO,Croatia,0.898
+1999,BOS,Bosnia and Herzegovina,0.57
+1999,SLV,Slovenia,1.102
+1999,GRC,Greece,1.101
+1999,CYP,Cyprus,0.605
+1999,BUL,Bulgaria,1.134
+1999,MLD,Moldova,0.942
+1999,ROM,Romania,1.127
+1999,RUS,Russia,0.401
+1999,EST,Estonia,1.199
+1999,LAT,Latvia,1.136
+1999,LIT,Lithuania,1.158
+1999,UKR,Ukraine,0.387
+1999,BLR,Belarus,-0.029
+1999,ARM,Armenia,0.357
+1999,GRG,Georgia,0.796
+1999,AZE,Azerbaijan,0.165
+1999,FIN,Finland,1.118
+1999,SWD,Sweden,1.014
+1999,NOR,Norway,1.123
+1999,DEN,Denmark,1.209
+1999,ICE,Iceland,1.215
+1999,CAP,Cape Verde,-0.744
+1999,GNB,Guinea-Bissau,-0.776
+1999,EQG,Equatorial Guinea,-0.271
+1999,GAM,Gambia,-0.447
+1999,MLI,Mali,-0.818
+1999,SEN,Senegal,-0.38
+1999,BEN,Benin,-0.856
+1999,CDI,Ivory Coast,-0.75
+1999,GUI,Guinea,-0.788
+1999,BFO,Burkina Faso,-0.889
+1999,SIE,Sierra Leone,-0.388
+1999,GHA,Ghana,-0.862
+1999,TOG,Togo,-0.546
+1999,CAO,Cameroon,-0.56
+1999,NIG,Nigeria,-0.778
+1999,GAB,Gabon,-0.626
+1999,CHA,Chad,-0.976
+1999,CON,Congo,-0.752
+1999,DRC,Democratic Republic of the Congo,-1.06
+1999,UGA,Uganda,-0.821
+1999,KEN,Kenya,-0.601
+1999,TAZ,Tanzania,-1.117
+1999,RWA,Rwanda,-0.081
+1999,DJI,Djibouti,-0.799
+1999,ETH,Ethiopia,-0.426
+1999,ERI,Eritrea,-0.627
+1999,ANG,Angola,-0.656
+1999,MZM,Mozambique,-0.678
+1999,ZAM,Zambia,-0.344
+1999,ZIM,Zimbabwe,-0.831
+1999,MAW,Malawi,-0.313
+1999,SAF,South Africa,-0.013
+1999,NAM,Namibia,-0.772
+1999,LES,Lesotho,-0.514
+1999,BOT,Botswana,-0.543
+1999,SWA,Swaziland,-0.199
+1999,MAG,Madagascar,-0.527
+1999,COM,Comoros,-0.51
+1999,MAS,Mauritius,-0.34
+1999,SEY,Seychelles,-0.408
+1999,MOR,Morocco,-0.778
+1999,ALG,Algeria,-0.92
+1999,TUN,Tunisia,-0.841
+1999,LIB,Libya,-1.613
+1999,SUD,Sudan,-1.56
+1999,IRN,Iran,-1.645
+1999,TUR,Turkey,0.615
+1999,EGY,Egypt,-0.987
+1999,SYR,Syria,-1.611
+1999,LEB,Lebanon,-1.206
+1999,JOR,Jordan,-0.828
+1999,ISR,Israel,2.172
+1999,SAU,Saudi Arabia,-0.721
+1999,YAR,Yemen Arab Republic,-0.758
+1999,KUW,Kuwait,-0.528
+1999,BAH,Bahrain,-0.713
+1999,QAT,Qatar,-0.917
+1999,UAE,United Arab Emirates,-0.691
+1999,OMA,Oman,-0.855
+1999,AFG,Afghanistan,-0.936
+1999,TKM,Turkmenistan,0.001
+1999,TAJ,Tajikistan,0.026
+1999,UZB,Uzbekistan,0.727
+1999,KZK,Kazakhstan,0.401
+1999,CHN,China,-0.717
+1999,MON,Mongolia,-0.259
+1999,PRK,North Korea,-1.752
+1999,ROK,South Korea,0.556
+1999,JPN,Japan,0.712
+1999,IND,India,-0.791
+1999,BHU,Bhutan,-0.725
+1999,PAK,Pakistan,-0.715
+1999,BNG,Bangladesh,-0.835
+1999,MYA,Myanmar,-1.358
+1999,SRI,Sri Lanka,-0.86
+1999,MAD,Maldives,-0.454
+1999,NEP,Nepal,-0.768
+1999,THI,Thailand,-0.545
+1999,CAM,Cambodia,-0.568
+1999,LAO,Laos,-1.205
+1999,DRV,Vietnam,-1.478
+1999,MAL,Malaysia,-0.707
+1999,SIN,Singapore,-0.39
+1999,BRU,Brunei,-0.842
+1999,PHI,Philippines,-0.846
+1999,INS,Indonesia,-1.038
+1999,AUL,Australia,0.912
+1999,PNG,Papua New Guinea,-0.751
+1999,NEW,New Zealand,0.767
+1999,SOL,Solomon Islands,0.033
+1999,FIJ,Fiji,-0.609
+1999,TON,Tonga,0.367
+1999,MSI,Marshall Islands,1.236
+1999,FSM,Federated States of Micronesia,1.871
+1999,WSM,Samoa,0.26
+2000,USA,United States of America,2.611
+2000,CAN,Canada,1.112
+2000,BHM,Bahamas,-0.319
+2000,CUB,Cuba,-1.418
+2000,HAI,Haiti,-0.423
+2000,DOM,Dominican Republic,-0.324
+2000,JAM,Jamaica,-0.345
+2000,TRI,Trinidad and Tobago,-0.297
+2000,BAR,Barbados,-0.351
+2000,DMA,Dominica,-0.346
+2000,GRN,Grenada,-0.372
+2000,SLU,St. Lucia,-0.75
+2000,SVG,St. Vincent and the Grenadines,-0.274
+2000,AAB,Antigua & Barbuda,-0.582
+2000,SKN,St. Kitts and Nevis,-0.593
+2000,MEX,Mexico,-0.503
+2000,BLZ,Belize,-0.391
+2000,GUA,Guatemala,0.014
+2000,HON,Honduras,-0.311
+2000,SAL,El Salvador,-0.299
+2000,NIC,Nicaragua,-0.12
+2000,COS,Costa Rica,-0.127
+2000,PAN,Panama,-0.4
+2000,COL,Colombia,-0.352
+2000,VEN,Venezuela,-0.521
+2000,GUY,Guyana,-0.482
+2000,SUR,Suriname,-0.581
+2000,ECU,Ecuador,-0.377
+2000,PER,Peru,-0.054
+2000,BRA,Brazil,-0.009
+2000,BOL,Bolivia,-0.183
+2000,PAR,Paraguay,0.064
+2000,CHL,Chile,-0.056
+2000,ARG,Argentina,0.291
+2000,URU,Uruguay,0.008
+2000,UKG,United Kingdom,1.689
+2000,IRE,Ireland,0.831
+2000,NTH,Netherlands,1.236
+2000,BEL,Belgium,1.22
+2000,LUX,Luxembourg,1.224
+2000,FRN,France,1.454
+2000,MNC,Monaco,1.207
+2000,LIE,Liechtenstein,0.975
+2000,SPN,Spain,1.048
+2000,AND,Andorra,1.053
+2000,POR,Portugal,1.026
+2000,GFR,German Federal Republic,1.264
+2000,POL,Poland,1.154
+2000,AUS,Austria,1.013
+2000,HUN,Hungary,1.243
+2000,CZR,Czech Republic,1.142
+2000,SLO,Slovakia,1.089
+2000,ITA,Italy,1.085
+2000,SNM,San Marino,0.823
+2000,MLT,Malta,0.61
+2000,ALB,Albania,1.188
+2000,MAC,Macedonia,0.948
+2000,CRO,Croatia,0.999
+2000,YUG,Yugoslavia,0.567
+2000,BOS,Bosnia and Herzegovina,0.623
+2000,SLV,Slovenia,1.088
+2000,GRC,Greece,1.083
+2000,CYP,Cyprus,0.547
+2000,BUL,Bulgaria,1.033
+2000,MLD,Moldova,0.88
+2000,ROM,Romania,1.092
+2000,RUS,Russia,0.196
+2000,EST,Estonia,1.122
+2000,LAT,Latvia,1.172
+2000,LIT,Lithuania,1.156
+2000,UKR,Ukraine,0.386
+2000,BLR,Belarus,-0.412
+2000,ARM,Armenia,0.235
+2000,GRG,Georgia,0.825
+2000,AZE,Azerbaijan,0.027
+2000,FIN,Finland,1.138
+2000,SWD,Sweden,0.985
+2000,NOR,Norway,1.112
+2000,DEN,Denmark,1.157
+2000,ICE,Iceland,1.165
+2000,CAP,Cape Verde,-0.556
+2000,STP,Sao Tome and Principe,-0.045
+2000,EQG,Equatorial Guinea,-0.387
+2000,GAM,Gambia,-0.722
+2000,MLI,Mali,-0.764
+2000,SEN,Senegal,-0.41
+2000,BEN,Benin,-0.767
+2000,MAA,Mauritania,-0.469
+2000,NIR,Niger,-0.001
+2000,CDI,Ivory Coast,-0.624
+2000,GUI,Guinea,-0.629
+2000,BFO,Burkina Faso,-0.819
+2000,SIE,Sierra Leone,-0.514
+2000,GHA,Ghana,-0.808
+2000,TOG,Togo,-0.732
+2000,CAO,Cameroon,-0.689
+2000,NIG,Nigeria,-0.76
+2000,GAB,Gabon,-0.688
+2000,CHA,Chad,-0.998
+2000,CON,Congo,-0.784
+2000,DRC,Democratic Republic of the Congo,-1.166
+2000,UGA,Uganda,-0.72
+2000,KEN,Kenya,-0.529
+2000,TAZ,Tanzania,-1.054
+2000,BUI,Burundi,-0.454
+2000,RWA,Rwanda,-0.436
+2000,DJI,Djibouti,-0.658
+2000,ETH,Ethiopia,-0.409
+2000,ERI,Eritrea,-0.552
+2000,ANG,Angola,-0.452
+2000,MZM,Mozambique,-0.714
+2000,ZAM,Zambia,-0.517
+2000,ZIM,Zimbabwe,-0.793
+2000,MAW,Malawi,-0.485
+2000,SAF,South Africa,0.013
+2000,NAM,Namibia,-0.719
+2000,LES,Lesotho,-0.63
+2000,BOT,Botswana,-0.573
+2000,SWA,Swaziland,-0.485
+2000,MAG,Madagascar,-0.669
+2000,COM,Comoros,-0.702
+2000,MAS,Mauritius,-0.375
+2000,MOR,Morocco,-0.597
+2000,ALG,Algeria,-1.073
+2000,TUN,Tunisia,-0.963
+2000,LIB,Libya,-1.702
+2000,SUD,Sudan,-1.541
+2000,IRN,Iran,-1.533
+2000,TUR,Turkey,0.642
+2000,EGY,Egypt,-1.127
+2000,SYR,Syria,-1.67
+2000,LEB,Lebanon,-1.123
+2000,JOR,Jordan,-1.022
+2000,ISR,Israel,2.184
+2000,SAU,Saudi Arabia,-0.913
+2000,YAR,Yemen Arab Republic,-0.758
+2000,KUW,Kuwait,-0.622
+2000,BAH,Bahrain,-0.847
+2000,QAT,Qatar,-1.055
+2000,UAE,United Arab Emirates,-0.643
+2000,OMA,Oman,-1.038
+2000,AFG,Afghanistan,-0.79
+2000,TKM,Turkmenistan,0.039
+2000,TAJ,Tajikistan,0.089
+2000,KYR,Kyrgyzstan,0.028
+2000,UZB,Uzbekistan,0.526
+2000,KZK,Kazakhstan,0.358
+2000,CHN,China,-0.738
+2000,MON,Mongolia,-0.259
+2000,PRK,North Korea,-1.636
+2000,ROK,South Korea,0.58
+2000,JPN,Japan,0.705
+2000,IND,India,-0.702
+2000,BHU,Bhutan,-0.848
+2000,PAK,Pakistan,-0.841
+2000,BNG,Bangladesh,-0.839
+2000,MYA,Myanmar,-1.392
+2000,SRI,Sri Lanka,-0.857
+2000,MAD,Maldives,-0.542
+2000,NEP,Nepal,-0.709
+2000,THI,Thailand,-0.374
+2000,CAM,Cambodia,-0.687
+2000,LAO,Laos,-1.21
+2000,DRV,Vietnam,-1.401
+2000,MAL,Malaysia,-0.724
+2000,SIN,Singapore,-0.252
+2000,BRU,Brunei,-0.866
+2000,PHI,Philippines,-0.606
+2000,INS,Indonesia,-1.011
+2000,AUL,Australia,0.892
+2000,PNG,Papua New Guinea,-0.521
+2000,NEW,New Zealand,0.753
+2000,VAN,Vanuatu,-0.053
+2000,SOL,Solomon Islands,-0.002
+2000,NAU,Nauru,0.207
+2000,FIJ,Fiji,-0.262
+2000,TON,Tonga,0.369
+2000,NAU,Nauru,0.202
+2000,MSI,Marshall Islands,1.291
+2000,PAL,Palau,0.56
+2000,FSM,Federated States of Micronesia,1.849
+2000,WSM,Samoa,0.307
+2001,USA,United States of America,2.576
+2001,CAN,Canada,1.184
+2001,BHM,Bahamas,-0.257
+2001,CUB,Cuba,-1.336
+2001,HAI,Haiti,-0.433
+2001,DOM,Dominican Republic,-0.361
+2001,JAM,Jamaica,-0.364
+2001,TRI,Trinidad and Tobago,-0.293
+2001,BAR,Barbados,-0.377
+2001,DMA,Dominica,-0.387
+2001,GRN,Grenada,-0.308
+2001,SLU,St. Lucia,-0.752
+2001,SVG,St. Vincent and the Grenadines,-0.279
+2001,AAB,Antigua & Barbuda,-0.65
+2001,SKN,St. Kitts and Nevis,-0.565
+2001,MEX,Mexico,-0.529
+2001,BLZ,Belize,-0.336
+2001,GUA,Guatemala,-0.039
+2001,HON,Honduras,-0.316
+2001,SAL,El Salvador,-0.323
+2001,NIC,Nicaragua,0.187
+2001,COS,Costa Rica,-0.281
+2001,PAN,Panama,-0.312
+2001,COL,Colombia,-0.347
+2001,VEN,Venezuela,-0.52
+2001,GUY,Guyana,-0.493
+2001,SUR,Suriname,-0.453
+2001,ECU,Ecuador,-0.394
+2001,PER,Peru,-0.024
+2001,BRA,Brazil,-0.021
+2001,BOL,Bolivia,-0.208
+2001,PAR,Paraguay,0.111
+2001,CHL,Chile,0.122
+2001,ARG,Argentina,0.3
+2001,URU,Uruguay,0.006
+2001,UKG,United Kingdom,1.626
+2001,IRE,Ireland,0.853
+2001,NTH,Netherlands,1.201
+2001,BEL,Belgium,1.173
+2001,LUX,Luxembourg,1.176
+2001,FRN,France,1.471
+2001,MNC,Monaco,1.129
+2001,LIE,Liechtenstein,0.995
+2001,SPN,Spain,1.051
+2001,AND,Andorra,1.038
+2001,POR,Portugal,1.03
+2001,GFR,German Federal Republic,1.2
+2001,POL,Poland,1.135
+2001,AUS,Austria,0.988
+2001,HUN,Hungary,1.202
+2001,CZR,Czech Republic,1.126
+2001,SLO,Slovakia,0.999
+2001,ITA,Italy,1.082
+2001,SNM,San Marino,0.913
+2001,MLT,Malta,0.674
+2001,ALB,Albania,1.258
+2001,MAC,Macedonia,0.959
+2001,CRO,Croatia,0.849
+2001,YUG,Yugoslavia,0.847
+2001,BOS,Bosnia and Herzegovina,0.925
+2001,SLV,Slovenia,1.109
+2001,GRC,Greece,1.085
+2001,CYP,Cyprus,0.624
+2001,BUL,Bulgaria,1.077
+2001,MLD,Moldova,0.87
+2001,ROM,Romania,1.087
+2001,RUS,Russia,0.167
+2001,EST,Estonia,1.104
+2001,LAT,Latvia,1.151
+2001,LIT,Lithuania,1.097
+2001,UKR,Ukraine,0.433
+2001,BLR,Belarus,-0.181
+2001,ARM,Armenia,0.181
+2001,GRG,Georgia,0.82
+2001,AZE,Azerbaijan,-0.038
+2001,FIN,Finland,1.112
+2001,SWD,Sweden,0.997
+2001,NOR,Norway,1.159
+2001,DEN,Denmark,1.219
+2001,ICE,Iceland,1.133
+2001,CAP,Cape Verde,-0.658
+2001,EQG,Equatorial Guinea,-0.6
+2001,GAM,Gambia,-0.702
+2001,MLI,Mali,-0.811
+2001,SEN,Senegal,-0.518
+2001,BEN,Benin,-0.586
+2001,MAA,Mauritania,-0.839
+2001,CDI,Ivory Coast,-0.666
+2001,GUI,Guinea,-0.479
+2001,BFO,Burkina Faso,-0.838
+2001,SIE,Sierra Leone,-0.554
+2001,GHA,Ghana,-0.709
+2001,TOG,Togo,-0.791
+2001,CAO,Cameroon,-0.672
+2001,NIG,Nigeria,-0.676
+2001,GAB,Gabon,-0.62
+2001,CHA,Chad,-1.038
+2001,CON,Congo,-0.855
+2001,DRC,Democratic Republic of the Congo,-1.176
+2001,UGA,Uganda,-0.698
+2001,KEN,Kenya,-0.527
+2001,TAZ,Tanzania,-0.973
+2001,BUI,Burundi,-0.595
+2001,RWA,Rwanda,-0.644
+2001,DJI,Djibouti,-0.895
+2001,ETH,Ethiopia,-0.499
+2001,ERI,Eritrea,-0.599
+2001,ANG,Angola,-0.352
+2001,MZM,Mozambique,-0.736
+2001,ZAM,Zambia,-0.579
+2001,ZIM,Zimbabwe,-0.764
+2001,MAW,Malawi,-0.335
+2001,SAF,South Africa,0.031
+2001,NAM,Namibia,-0.685
+2001,LES,Lesotho,-0.651
+2001,BOT,Botswana,-0.551
+2001,SWA,Swaziland,-0.549
+2001,MAG,Madagascar,-0.528
+2001,COM,Comoros,-0.832
+2001,MAS,Mauritius,-0.279
+2001,SEY,Seychelles,-0.208
+2001,MOR,Morocco,-0.795
+2001,ALG,Algeria,-1.13
+2001,TUN,Tunisia,-1.064
+2001,LIB,Libya,-1.703
+2001,SUD,Sudan,-1.517
+2001,IRN,Iran,-1.537
+2001,TUR,Turkey,0.617
+2001,EGY,Egypt,-1.161
+2001,SYR,Syria,-1.44
+2001,LEB,Lebanon,-1.166
+2001,JOR,Jordan,-1.103
+2001,ISR,Israel,2.288
+2001,SAU,Saudi Arabia,-0.949
+2001,YAR,Yemen Arab Republic,-0.876
+2001,KUW,Kuwait,-0.764
+2001,BAH,Bahrain,-0.924
+2001,QAT,Qatar,-1.106
+2001,UAE,United Arab Emirates,-0.77
+2001,OMA,Oman,-1.066
+2001,AFG,Afghanistan,-0.839
+2001,TKM,Turkmenistan,-0.009
+2001,KZK,Kazakhstan,0.19
+2001,CHN,China,-0.674
+2001,MON,Mongolia,-0.309
+2001,PRK,North Korea,-1.621
+2001,ROK,South Korea,0.589
+2001,JPN,Japan,0.736
+2001,IND,India,-0.656
+2001,BHU,Bhutan,-0.793
+2001,PAK,Pakistan,-0.839
+2001,BNG,Bangladesh,-0.781
+2001,MYA,Myanmar,-1.411
+2001,SRI,Sri Lanka,-0.86
+2001,MAD,Maldives,-0.536
+2001,NEP,Nepal,-0.731
+2001,THI,Thailand,-0.453
+2001,CAM,Cambodia,-0.745
+2001,LAO,Laos,-1.258
+2001,DRV,Vietnam,-1.429
+2001,MAL,Malaysia,-0.846
+2001,SIN,Singapore,-0.297
+2001,BRU,Brunei,-0.954
+2001,PHI,Philippines,-0.479
+2001,INS,Indonesia,-0.996
+2001,AUL,Australia,1.016
+2001,PNG,Papua New Guinea,-0.106
+2001,NEW,New Zealand,0.817
+2001,VAN,Vanuatu,0.088
+2001,SOL,Solomon Islands,-0.027
+2001,NAU,Nauru,0.256
+2001,TUV,Tuvalu,1.275
+2001,FIJ,Fiji,-0.231
+2001,TON,Tonga,0.27
+2001,NAU,Nauru,0.296
+2001,MSI,Marshall Islands,2.073
+2001,PAL,Palau,0.519
+2001,FSM,Federated States of Micronesia,1.981
+2001,WSM,Samoa,0.268
+2002,USA,United States of America,2.628
+2002,CAN,Canada,1.129
+2002,BHM,Bahamas,-0.328
+2002,CUB,Cuba,-1.159
+2002,HAI,Haiti,-0.662
+2002,DOM,Dominican Republic,-0.369
+2002,JAM,Jamaica,-0.443
+2002,TRI,Trinidad and Tobago,-0.388
+2002,BAR,Barbados,-0.336
+2002,DMA,Dominica,-0.559
+2002,GRN,Grenada,-0.42
+2002,SLU,St. Lucia,-0.839
+2002,SVG,St. Vincent and the Grenadines,0.078
+2002,AAB,Antigua & Barbuda,-0.511
+2002,SKN,St. Kitts and Nevis,-0.59
+2002,MEX,Mexico,-0.383
+2002,BLZ,Belize,-0.404
+2002,GUA,Guatemala,0.004
+2002,HON,Honduras,-0.222
+2002,SAL,El Salvador,-0.341
+2002,NIC,Nicaragua,-0.166
+2002,COS,Costa Rica,-0.216
+2002,PAN,Panama,-0.263
+2002,COL,Colombia,-0.271
+2002,VEN,Venezuela,-0.484
+2002,GUY,Guyana,-0.488
+2002,SUR,Suriname,-0.609
+2002,ECU,Ecuador,-0.319
+2002,PER,Peru,0.01
+2002,BRA,Brazil,-0.04
+2002,BOL,Bolivia,-0.207
+2002,PAR,Paraguay,-0.063
+2002,CHL,Chile,-0.073
+2002,ARG,Argentina,0.289
+2002,URU,Uruguay,0.028
+2002,UKG,United Kingdom,1.674
+2002,IRE,Ireland,0.81
+2002,NTH,Netherlands,1.236
+2002,BEL,Belgium,1.136
+2002,LUX,Luxembourg,1.093
+2002,FRN,France,1.473
+2002,MNC,Monaco,1.081
+2002,LIE,Liechtenstein,0.881
+2002,SWZ,Switzerland,0.966
+2002,SPN,Spain,1.062
+2002,AND,Andorra,0.905
+2002,POR,Portugal,1.077
+2002,GFR,German Federal Republic,1.152
+2002,POL,Poland,1.076
+2002,AUS,Austria,0.89
+2002,HUN,Hungary,1.144
+2002,CZR,Czech Republic,1.045
+2002,SLO,Slovakia,0.962
+2002,ITA,Italy,1.138
+2002,SNM,San Marino,0.858
+2002,MLT,Malta,0.59
+2002,ALB,Albania,1.096
+2002,MAC,Macedonia,0.92
+2002,CRO,Croatia,0.886
+2002,YUG,Yugoslavia,0.899
+2002,BOS,Bosnia and Herzegovina,0.974
+2002,SLV,Slovenia,1.072
+2002,GRC,Greece,0.987
+2002,CYP,Cyprus,0.577
+2002,BUL,Bulgaria,1.112
+2002,MLD,Moldova,0.847
+2002,ROM,Romania,1.085
+2002,RUS,Russia,0.173
+2002,EST,Estonia,1.04
+2002,LAT,Latvia,1.075
+2002,LIT,Lithuania,1.062
+2002,UKR,Ukraine,0.387
+2002,BLR,Belarus,-0.159
+2002,ARM,Armenia,0.195
+2002,GRG,Georgia,0.783
+2002,AZE,Azerbaijan,-0.082
+2002,FIN,Finland,1.042
+2002,SWD,Sweden,0.887
+2002,NOR,Norway,1.1
+2002,DEN,Denmark,1.124
+2002,ICE,Iceland,1.091
+2002,CAP,Cape Verde,-0.649
+2002,STP,Sao Tome and Principe,-0.491
+2002,GNB,Guinea-Bissau,-0.033
+2002,EQG,Equatorial Guinea,-0.687
+2002,GAM,Gambia,-0.848
+2002,MLI,Mali,-0.876
+2002,SEN,Senegal,-0.508
+2002,BEN,Benin,-0.663
+2002,MAA,Mauritania,-0.901
+2002,CDI,Ivory Coast,-0.628
+2002,GUI,Guinea,-0.676
+2002,BFO,Burkina Faso,-0.865
+2002,SIE,Sierra Leone,-0.73
+2002,GHA,Ghana,-0.7
+2002,TOG,Togo,-0.888
+2002,CAO,Cameroon,-0.513
+2002,NIG,Nigeria,-0.754
+2002,GAB,Gabon,-0.63
+2002,CHA,Chad,-1.033
+2002,CON,Congo,-0.853
+2002,DRC,Democratic Republic of the Congo,-1.155
+2002,UGA,Uganda,-0.773
+2002,KEN,Kenya,-0.57
+2002,TAZ,Tanzania,-0.851
+2002,BUI,Burundi,-0.723
+2002,RWA,Rwanda,-0.433
+2002,SOM,Somalia,-0.413
+2002,DJI,Djibouti,-0.924
+2002,ETH,Ethiopia,-0.37
+2002,ERI,Eritrea,-0.643
+2002,ANG,Angola,-0.575
+2002,MZM,Mozambique,-0.688
+2002,ZAM,Zambia,-0.618
+2002,ZIM,Zimbabwe,-0.875
+2002,MAW,Malawi,-0.444
+2002,SAF,South Africa,-0.278
+2002,NAM,Namibia,-0.646
+2002,LES,Lesotho,-0.577
+2002,BOT,Botswana,-0.531
+2002,SWA,Swaziland,-0.607
+2002,MAG,Madagascar,-0.284
+2002,COM,Comoros,-0.904
+2002,MAS,Mauritius,-0.283
+2002,SEY,Seychelles,-0.354
+2002,MOR,Morocco,-0.931
+2002,ALG,Algeria,-1.13
+2002,TUN,Tunisia,-1.087
+2002,LIB,Libya,-1.685
+2002,SUD,Sudan,-1.566
+2002,IRN,Iran,-1.461
+2002,TUR,Turkey,0.686
+2002,EGY,Egypt,-1.212
+2002,SYR,Syria,-1.461
+2002,LEB,Lebanon,-1.121
+2002,JOR,Jordan,-1.138
+2002,ISR,Israel,2.289
+2002,SAU,Saudi Arabia,-1.074
+2002,YAR,Yemen Arab Republic,-0.995
+2002,KUW,Kuwait,-0.756
+2002,BAH,Bahrain,-1.031
+2002,QAT,Qatar,-1.168
+2002,UAE,United Arab Emirates,-0.949
+2002,OMA,Oman,-1.144
+2002,AFG,Afghanistan,-0.761
+2002,TKM,Turkmenistan,0.057
+2002,TAJ,Tajikistan,-0.006
+2002,KYR,Kyrgyzstan,0.027
+2002,UZB,Uzbekistan,0.393
+2002,KZK,Kazakhstan,0.141
+2002,CHN,China,-0.727
+2002,MON,Mongolia,-0.398
+2002,PRK,North Korea,-1.565
+2002,ROK,South Korea,0.733
+2002,JPN,Japan,0.736
+2002,IND,India,-0.305
+2002,BHU,Bhutan,-0.655
+2002,PAK,Pakistan,-0.581
+2002,BNG,Bangladesh,-0.878
+2002,MYA,Myanmar,-1.368
+2002,SRI,Sri Lanka,-0.815
+2002,MAD,Maldives,-0.521
+2002,NEP,Nepal,-0.766
+2002,THI,Thailand,-0.405
+2002,CAM,Cambodia,-0.75
+2002,LAO,Laos,-1.202
+2002,DRV,Vietnam,-1.416
+2002,MAL,Malaysia,-0.804
+2002,SIN,Singapore,-0.209
+2002,BRU,Brunei,-0.865
+2002,PHI,Philippines,-0.487
+2002,INS,Indonesia,-0.992
+2002,ETM,East Timor,-0.23
+2002,AUL,Australia,1.123
+2002,PNG,Papua New Guinea,0.006
+2002,NEW,New Zealand,0.727
+2002,VAN,Vanuatu,0.275
+2002,SOL,Solomon Islands,0.197
+2002,NAU,Nauru,0.142
+2002,TUV,Tuvalu,0.498
+2002,FIJ,Fiji,0.058
+2002,TON,Tonga,0.219
+2002,NAU,Nauru,0.374
+2002,MSI,Marshall Islands,2.447
+2002,PAL,Palau,1.897
+2002,FSM,Federated States of Micronesia,2.224
+2002,WSM,Samoa,0.23
+2003,USA,United States of America,2.796
+2003,CAN,Canada,1.127
+2003,BHM,Bahamas,-0.32
+2003,CUB,Cuba,-1.306
+2003,HAI,Haiti,-0.723
+2003,DOM,Dominican Republic,-0.091
+2003,JAM,Jamaica,-0.525
+2003,TRI,Trinidad and Tobago,-0.511
+2003,BAR,Barbados,-0.451
+2003,DMA,Dominica,-0.603
+2003,GRN,Grenada,-0.556
+2003,SLU,St. Lucia,-0.817
+2003,SVG,St. Vincent and the Grenadines,-0.016
+2003,AAB,Antigua & Barbuda,-0.562
+2003,SKN,St. Kitts and Nevis,-0.573
+2003,MEX,Mexico,-0.355
+2003,BLZ,Belize,-0.529
+2003,GUA,Guatemala,0.075
+2003,HON,Honduras,0.149
+2003,SAL,El Salvador,-0.095
+2003,NIC,Nicaragua,-0.082
+2003,COS,Costa Rica,-0.021
+2003,PAN,Panama,-0.351
+2003,COL,Colombia,-0.341
+2003,VEN,Venezuela,-0.535
+2003,GUY,Guyana,-0.596
+2003,SUR,Suriname,-0.577
+2003,ECU,Ecuador,-0.42
+2003,PER,Peru,-0.005
+2003,BRA,Brazil,-0.078
+2003,BOL,Bolivia,-0.219
+2003,PAR,Paraguay,-0.103
+2003,CHL,Chile,-0.06
+2003,ARG,Argentina,0.254
+2003,URU,Uruguay,0.012
+2003,UKG,United Kingdom,1.67
+2003,IRE,Ireland,0.74
+2003,NTH,Netherlands,1.112
+2003,BEL,Belgium,1.162
+2003,LUX,Luxembourg,1.157
+2003,FRN,France,1.434
+2003,MNC,Monaco,1.049
+2003,LIE,Liechtenstein,0.903
+2003,SWZ,Switzerland,1.001
+2003,SPN,Spain,1.083
+2003,AND,Andorra,0.884
+2003,POR,Portugal,1.047
+2003,GFR,German Federal Republic,1.225
+2003,POL,Poland,1.181
+2003,AUS,Austria,0.895
+2003,HUN,Hungary,1.111
+2003,CZR,Czech Republic,1.129
+2003,SLO,Slovakia,1.005
+2003,ITA,Italy,1.096
+2003,SNM,San Marino,0.858
+2003,MLT,Malta,0.671
+2003,ALB,Albania,1.214
+2003,MAC,Macedonia,0.956
+2003,CRO,Croatia,0.964
+2003,YUG,Yugoslavia,0.928
+2003,BOS,Bosnia and Herzegovina,0.887
+2003,SLV,Slovenia,1.006
+2003,GRC,Greece,0.994
+2003,CYP,Cyprus,0.647
+2003,BUL,Bulgaria,1.156
+2003,MLD,Moldova,0.7
+2003,ROM,Romania,0.979
+2003,RUS,Russia,0.224
+2003,EST,Estonia,1.08
+2003,LAT,Latvia,1.083
+2003,LIT,Lithuania,1.146
+2003,UKR,Ukraine,0.37
+2003,BLR,Belarus,-0.123
+2003,ARM,Armenia,0.24
+2003,GRG,Georgia,0.799
+2003,AZE,Azerbaijan,-0.114
+2003,FIN,Finland,1.057
+2003,SWD,Sweden,0.914
+2003,NOR,Norway,1.124
+2003,DEN,Denmark,1.086
+2003,ICE,Iceland,1.118
+2003,CAP,Cape Verde,-0.676
+2003,STP,Sao Tome and Principe,-0.496
+2003,GNB,Guinea-Bissau,-0.528
+2003,EQG,Equatorial Guinea,-0.475
+2003,GAM,Gambia,-0.84
+2003,MLI,Mali,-0.798
+2003,SEN,Senegal,-0.698
+2003,BEN,Benin,-0.659
+2003,MAA,Mauritania,-1.071
+2003,NIR,Niger,-0.644
+2003,CDI,Ivory Coast,-0.717
+2003,GUI,Guinea,-0.703
+2003,BFO,Burkina Faso,-0.816
+2003,SIE,Sierra Leone,-0.542
+2003,GHA,Ghana,-0.636
+2003,TOG,Togo,-0.871
+2003,CAO,Cameroon,-0.204
+2003,NIG,Nigeria,-0.669
+2003,GAB,Gabon,-0.746
+2003,CEN,Central African Republic,-0.356
+2003,CHA,Chad,-1.033
+2003,CON,Congo,-0.781
+2003,DRC,Democratic Republic of the Congo,-1.094
+2003,UGA,Uganda,-0.49
+2003,KEN,Kenya,-0.677
+2003,TAZ,Tanzania,-0.709
+2003,BUI,Burundi,-0.544
+2003,RWA,Rwanda,0.015
+2003,SOM,Somalia,-0.736
+2003,DJI,Djibouti,-0.998
+2003,ETH,Ethiopia,-0.419
+2003,ERI,Eritrea,-0.677
+2003,ANG,Angola,-0.417
+2003,MZM,Mozambique,-0.702
+2003,ZAM,Zambia,-0.678
+2003,ZIM,Zimbabwe,-0.918
+2003,MAW,Malawi,-0.463
+2003,SAF,South Africa,-0.358
+2003,NAM,Namibia,-0.666
+2003,LES,Lesotho,-0.661
+2003,BOT,Botswana,-0.511
+2003,SWA,Swaziland,-0.594
+2003,MAG,Madagascar,-0.492
+2003,COM,Comoros,-1.021
+2003,MAS,Mauritius,-0.323
+2003,SEY,Seychelles,-0.502
+2003,MOR,Morocco,-1.008
+2003,ALG,Algeria,-1.076
+2003,TUN,Tunisia,-1.176
+2003,LIB,Libya,-1.633
+2003,SUD,Sudan,-1.246
+2003,IRN,Iran,-1.357
+2003,TUR,Turkey,0.589
+2003,EGY,Egypt,-1.202
+2003,SYR,Syria,-1.273
+2003,LEB,Lebanon,-1.21
+2003,JOR,Jordan,-1.22
+2003,ISR,Israel,2.31
+2003,SAU,Saudi Arabia,-1.198
+2003,YAR,Yemen Arab Republic,-1.13
+2003,KUW,Kuwait,-0.99
+2003,BAH,Bahrain,-1.148
+2003,QAT,Qatar,-1.236
+2003,UAE,United Arab Emirates,-1.037
+2003,OMA,Oman,-1.223
+2003,AFG,Afghanistan,-0.833
+2003,TKM,Turkmenistan,-0.567
+2003,TAJ,Tajikistan,0.035
+2003,KYR,Kyrgyzstan,0.031
+2003,UZB,Uzbekistan,0.4
+2003,KZK,Kazakhstan,0.121
+2003,CHN,China,-0.876
+2003,MON,Mongolia,-0.409
+2003,PRK,North Korea,-1.404
+2003,ROK,South Korea,0.722
+2003,JPN,Japan,0.713
+2003,IND,India,-0.198
+2003,BHU,Bhutan,-0.346
+2003,PAK,Pakistan,-0.484
+2003,BNG,Bangladesh,-0.917
+2003,MYA,Myanmar,-1.446
+2003,SRI,Sri Lanka,-0.697
+2003,MAD,Maldives,-0.627
+2003,NEP,Nepal,-0.605
+2003,THI,Thailand,-0.299
+2003,CAM,Cambodia,-0.701
+2003,LAO,Laos,-0.949
+2003,DRV,Vietnam,-1.463
+2003,MAL,Malaysia,-0.916
+2003,SIN,Singapore,-0.209
+2003,BRU,Brunei,-1.04
+2003,PHI,Philippines,-0.514
+2003,INS,Indonesia,-0.941
+2003,ETM,East Timor,-0.208
+2003,AUL,Australia,1.201
+2003,PNG,Papua New Guinea,0.091
+2003,NEW,New Zealand,0.711
+2003,VAN,Vanuatu,0.017
+2003,SOL,Solomon Islands,0.197
+2003,NAU,Nauru,0.133
+2003,TUV,Tuvalu,0.245
+2003,FIJ,Fiji,-0.091
+2003,TON,Tonga,0.233
+2003,NAU,Nauru,0.459
+2003,MSI,Marshall Islands,1.945
+2003,PAL,Palau,2.284
+2003,FSM,Federated States of Micronesia,2.192
+2003,WSM,Samoa,0.215
+2004,USA,United States of America,2.771
+2004,CAN,Canada,1.236
+2004,BHM,Bahamas,-0.46
+2004,CUB,Cuba,-1.283
+2004,HAI,Haiti,0.235
+2004,DOM,Dominican Republic,-0.062
+2004,JAM,Jamaica,-0.594
+2004,TRI,Trinidad and Tobago,-0.553
+2004,BAR,Barbados,-0.553
+2004,DMA,Dominica,-0.631
+2004,GRN,Grenada,0.078
+2004,SLU,St. Lucia,-0.733
+2004,SVG,St. Vincent and the Grenadines,-0.26
+2004,AAB,Antigua & Barbuda,-0.528
+2004,SKN,St. Kitts and Nevis,-0.585
+2004,MEX,Mexico,-0.235
+2004,BLZ,Belize,-0.549
+2004,GUA,Guatemala,-0.047
+2004,HON,Honduras,0.005
+2004,SAL,El Salvador,-0.166
+2004,NIC,Nicaragua,-0.092
+2004,COS,Costa Rica,-0.079
+2004,PAN,Panama,-0.326
+2004,COL,Colombia,-0.455
+2004,VEN,Venezuela,-0.795
+2004,GUY,Guyana,-0.632
+2004,SUR,Suriname,-0.62
+2004,ECU,Ecuador,-0.445
+2004,PER,Peru,0.02
+2004,BRA,Brazil,-0.115
+2004,BOL,Bolivia,-0.224
+2004,PAR,Paraguay,-0.062
+2004,CHL,Chile,0.057
+2004,ARG,Argentina,0.155
+2004,URU,Uruguay,-0.02
+2004,UKG,United Kingdom,1.635
+2004,IRE,Ireland,0.829
+2004,NTH,Netherlands,1.011
+2004,BEL,Belgium,1.072
+2004,LUX,Luxembourg,1.021
+2004,FRN,France,1.455
+2004,MNC,Monaco,0.99
+2004,LIE,Liechtenstein,0.88
+2004,SWZ,Switzerland,0.898
+2004,SPN,Spain,1.018
+2004,AND,Andorra,0.902
+2004,POR,Portugal,0.994
+2004,GFR,German Federal Republic,1.084
+2004,POL,Poland,1.125
+2004,AUS,Austria,0.923
+2004,HUN,Hungary,1.035
+2004,CZR,Czech Republic,1.052
+2004,SLO,Slovakia,0.976
+2004,ITA,Italy,1.001
+2004,SNM,San Marino,0.845
+2004,MLT,Malta,0.703
+2004,ALB,Albania,1.231
+2004,MAC,Macedonia,0.943
+2004,CRO,Croatia,0.939
+2004,YUG,Yugoslavia,0.929
+2004,BOS,Bosnia and Herzegovina,0.925
+2004,SLV,Slovenia,1.011
+2004,GRC,Greece,0.91
+2004,CYP,Cyprus,0.751
+2004,BUL,Bulgaria,1.05
+2004,MLD,Moldova,0.723
+2004,ROM,Romania,1.009
+2004,RUS,Russia,0.104
+2004,EST,Estonia,0.998
+2004,LAT,Latvia,1.16
+2004,LIT,Lithuania,1.021
+2004,UKR,Ukraine,0.405
+2004,BLR,Belarus,-0.239
+2004,ARM,Armenia,0.222
+2004,GRG,Georgia,0.83
+2004,AZE,Azerbaijan,-0.112
+2004,FIN,Finland,0.997
+2004,SWD,Sweden,0.924
+2004,NOR,Norway,0.949
+2004,DEN,Denmark,1.054
+2004,ICE,Iceland,1.051
+2004,CAP,Cape Verde,-0.649
+2004,STP,Sao Tome and Principe,-0.564
+2004,GNB,Guinea-Bissau,-0.468
+2004,EQG,Equatorial Guinea,-0.232
+2004,GAM,Gambia,-0.858
+2004,MLI,Mali,-0.739
+2004,SEN,Senegal,-0.762
+2004,BEN,Benin,-0.509
+2004,MAA,Mauritania,-1.052
+2004,NIR,Niger,-0.734
+2004,CDI,Ivory Coast,-0.343
+2004,GUI,Guinea,-0.708
+2004,BFO,Burkina Faso,-0.693
+2004,LBR,Liberia,-0.31
+2004,SIE,Sierra Leone,-0.579
+2004,GHA,Ghana,-0.621
+2004,TOG,Togo,-0.792
+2004,CAO,Cameroon,0.006
+2004,NIG,Nigeria,-0.781
+2004,GAB,Gabon,-0.722
+2004,CEN,Central African Republic,-0.448
+2004,CHA,Chad,-1.067
+2004,CON,Congo,-0.793
+2004,DRC,Democratic Republic of the Congo,-0.889
+2004,UGA,Uganda,-0.306
+2004,KEN,Kenya,-0.324
+2004,TAZ,Tanzania,-0.737
+2004,BUI,Burundi,-0.295
+2004,RWA,Rwanda,-0.435
+2004,SOM,Somalia,-0.802
+2004,DJI,Djibouti,-1.004
+2004,ETH,Ethiopia,-0.502
+2004,ERI,Eritrea,-0.652
+2004,ANG,Angola,-0.51
+2004,MZM,Mozambique,-0.669
+2004,ZAM,Zambia,-0.66
+2004,ZIM,Zimbabwe,-1.074
+2004,MAW,Malawi,-0.107
+2004,SAF,South Africa,-0.444
+2004,NAM,Namibia,-0.654
+2004,LES,Lesotho,-0.657
+2004,BOT,Botswana,-0.582
+2004,SWA,Swaziland,-0.577
+2004,MAG,Madagascar,-0.572
+2004,COM,Comoros,-1.021
+2004,MAS,Mauritius,-0.447
+2004,SEY,Seychelles,-0.565
+2004,MOR,Morocco,-1.011
+2004,ALG,Algeria,-1.08
+2004,TUN,Tunisia,-1.142
+2004,LIB,Libya,-1.523
+2004,SUD,Sudan,-1.187
+2004,IRN,Iran,-1.356
+2004,TUR,Turkey,0.608
+2004,IRQ,Iraq,-0.465
+2004,EGY,Egypt,-1.297
+2004,SYR,Syria,-1.011
+2004,LEB,Lebanon,-1.19
+2004,JOR,Jordan,-0.912
+2004,ISR,Israel,2.402
+2004,SAU,Saudi Arabia,-1.234
+2004,YAR,Yemen Arab Republic,-1.148
+2004,KUW,Kuwait,-1.003
+2004,BAH,Bahrain,-1.16
+2004,QAT,Qatar,-1.142
+2004,UAE,United Arab Emirates,-1.08
+2004,OMA,Oman,-1.166
+2004,AFG,Afghanistan,-0.799
+2004,TKM,Turkmenistan,-0.871
+2004,TAJ,Tajikistan,-0.07
+2004,KYR,Kyrgyzstan,-0.14
+2004,UZB,Uzbekistan,0.148
+2004,KZK,Kazakhstan,-0.045
+2004,CHN,China,-0.831
+2004,MON,Mongolia,-0.464
+2004,PRK,North Korea,-1.446
+2004,ROK,South Korea,0.721
+2004,JPN,Japan,0.751
+2004,IND,India,-0.354
+2004,BHU,Bhutan,-0.518
+2004,PAK,Pakistan,-0.593
+2004,BNG,Bangladesh,-1.025
+2004,MYA,Myanmar,-1.386
+2004,SRI,Sri Lanka,-0.703
+2004,MAD,Maldives,-0.683
+2004,NEP,Nepal,-0.667
+2004,THI,Thailand,-0.194
+2004,CAM,Cambodia,-0.68
+2004,LAO,Laos,-0.922
+2004,DRV,Vietnam,-1.497
+2004,MAL,Malaysia,-0.861
+2004,SIN,Singapore,-0.204
+2004,BRU,Brunei,-0.866
+2004,PHI,Philippines,-0.508
+2004,INS,Indonesia,-1.038
+2004,ETM,East Timor,-0.288
+2004,AUL,Australia,1.346
+2004,PNG,Papua New Guinea,0.071
+2004,NEW,New Zealand,0.728
+2004,VAN,Vanuatu,0.262
+2004,SOL,Solomon Islands,0.146
+2004,NAU,Nauru,0.221
+2004,TUV,Tuvalu,0.11
+2004,FIJ,Fiji,-0.072
+2004,TON,Tonga,0.255
+2004,NAU,Nauru,0.448
+2004,MSI,Marshall Islands,1.194
+2004,PAL,Palau,2.658
+2004,FSM,Federated States of Micronesia,2.022
+2004,WSM,Samoa,0.249
+2005,USA,United States of America,2.894
+2005,CAN,Canada,1.264
+2005,BHM,Bahamas,-0.539
+2005,CUB,Cuba,-1.37
+2005,HAI,Haiti,-0.119
+2005,DOM,Dominican Republic,-0.024
+2005,JAM,Jamaica,-0.656
+2005,TRI,Trinidad and Tobago,-0.577
+2005,BAR,Barbados,-0.641
+2005,DMA,Dominica,-0.615
+2005,GRN,Grenada,0.008
+2005,SLU,St. Lucia,-0.758
+2005,SVG,St. Vincent and the Grenadines,-0.313
+2005,AAB,Antigua & Barbuda,-0.613
+2005,SKN,St. Kitts and Nevis,-0.493
+2005,MEX,Mexico,-0.142
+2005,BLZ,Belize,-0.531
+2005,GUA,Guatemala,-0.03
+2005,HON,Honduras,0.012
+2005,SAL,El Salvador,-0.123
+2005,NIC,Nicaragua,-0.09
+2005,COS,Costa Rica,-0.067
+2005,PAN,Panama,-0.274
+2005,COL,Colombia,-0.511
+2005,VEN,Venezuela,-1.003
+2005,GUY,Guyana,-0.644
+2005,SUR,Suriname,-0.655
+2005,ECU,Ecuador,-0.306
+2005,PER,Peru,-0.083
+2005,BRA,Brazil,-0.159
+2005,BOL,Bolivia,-0.197
+2005,PAR,Paraguay,-0.118
+2005,CHL,Chile,-0.051
+2005,ARG,Argentina,0.16
+2005,URU,Uruguay,-0.09
+2005,UKG,United Kingdom,1.671
+2005,IRE,Ireland,0.811
+2005,NTH,Netherlands,1.044
+2005,BEL,Belgium,1.071
+2005,LUX,Luxembourg,1.002
+2005,FRN,France,1.535
+2005,MNC,Monaco,0.994
+2005,LIE,Liechtenstein,0.939
+2005,SWZ,Switzerland,0.937
+2005,SPN,Spain,0.939
+2005,AND,Andorra,0.848
+2005,POR,Portugal,0.917
+2005,GFR,German Federal Republic,1.013
+2005,POL,Poland,1.045
+2005,AUS,Austria,0.952
+2005,HUN,Hungary,1.021
+2005,CZR,Czech Republic,1.012
+2005,SLO,Slovakia,0.95
+2005,ITA,Italy,0.994
+2005,SNM,San Marino,0.82
+2005,MLT,Malta,0.651
+2005,ALB,Albania,1.483
+2005,MAC,Macedonia,0.935
+2005,CRO,Croatia,0.95
+2005,YUG,Yugoslavia,0.918
+2005,BOS,Bosnia and Herzegovina,0.952
+2005,SLV,Slovenia,1.024
+2005,GRC,Greece,0.948
+2005,CYP,Cyprus,0.712
+2005,BUL,Bulgaria,0.92
+2005,MLD,Moldova,0.753
+2005,ROM,Romania,0.974
+2005,RUS,Russia,0.075
+2005,EST,Estonia,0.979
+2005,LAT,Latvia,1.133
+2005,LIT,Lithuania,1.007
+2005,UKR,Ukraine,0.558
+2005,BLR,Belarus,-0.223
+2005,ARM,Armenia,0.143
+2005,GRG,Georgia,0.976
+2005,AZE,Azerbaijan,-0.203
+2005,FIN,Finland,1.002
+2005,SWD,Sweden,0.904
+2005,NOR,Norway,0.984
+2005,DEN,Denmark,1.053
+2005,ICE,Iceland,1.02
+2005,CAP,Cape Verde,-0.578
+2005,STP,Sao Tome and Principe,-0.604
+2005,GNB,Guinea-Bissau,-0.495
+2005,EQG,Equatorial Guinea,-0.24
+2005,GAM,Gambia,-1.048
+2005,MLI,Mali,-0.672
+2005,SEN,Senegal,-0.774
+2005,BEN,Benin,-0.599
+2005,MAA,Mauritania,-0.489
+2005,NIR,Niger,-0.585
+2005,CDI,Ivory Coast,-0.373
+2005,GUI,Guinea,-0.731
+2005,BFO,Burkina Faso,-0.558
+2005,LBR,Liberia,-0.392
+2005,SIE,Sierra Leone,-0.633
+2005,GHA,Ghana,-0.576
+2005,TOG,Togo,-0.762
+2005,CAO,Cameroon,-0.037
+2005,NIG,Nigeria,-0.577
+2005,GAB,Gabon,-0.772
+2005,CEN,Central African Republic,-0.326
+2005,CHA,Chad,-1.075
+2005,CON,Congo,-0.793
+2005,DRC,Democratic Republic of the Congo,-0.589
+2005,UGA,Uganda,-0.162
+2005,KEN,Kenya,-0.488
+2005,TAZ,Tanzania,-0.573
+2005,BUI,Burundi,-0.428
+2005,RWA,Rwanda,-0.551
+2005,SOM,Somalia,-0.786
+2005,DJI,Djibouti,-0.905
+2005,ETH,Ethiopia,-0.415
+2005,ERI,Eritrea,-0.648
+2005,ANG,Angola,-0.513
+2005,MZM,Mozambique,-0.674
+2005,ZAM,Zambia,-0.548
+2005,ZIM,Zimbabwe,-1.12
+2005,MAW,Malawi,-0.359
+2005,SAF,South Africa,-0.459
+2005,NAM,Namibia,-0.545
+2005,LES,Lesotho,-0.611
+2005,BOT,Botswana,-0.544
+2005,SWA,Swaziland,-0.595
+2005,MAG,Madagascar,-0.496
+2005,COM,Comoros,-1.063
+2005,MAS,Mauritius,-0.513
+2005,SEY,Seychelles,-0.561
+2005,MOR,Morocco,-1.037
+2005,ALG,Algeria,-0.985
+2005,TUN,Tunisia,-0.968
+2005,LIB,Libya,-1.433
+2005,SUD,Sudan,-1.25
+2005,IRN,Iran,-1.467
+2005,TUR,Turkey,0.647
+2005,IRQ,Iraq,-0.4
+2005,EGY,Egypt,-1.5
+2005,SYR,Syria,-1.122
+2005,LEB,Lebanon,-0.956
+2005,JOR,Jordan,-0.98
+2005,ISR,Israel,2.435
+2005,SAU,Saudi Arabia,-1.112
+2005,YAR,Yemen Arab Republic,-1.167
+2005,KUW,Kuwait,-1.093
+2005,BAH,Bahrain,-1.147
+2005,QAT,Qatar,-1.177
+2005,UAE,United Arab Emirates,-1.036
+2005,OMA,Oman,-1.213
+2005,AFG,Afghanistan,-0.826
+2005,TKM,Turkmenistan,-1.063
+2005,TAJ,Tajikistan,-0.271
+2005,KYR,Kyrgyzstan,-0.205
+2005,UZB,Uzbekistan,-0.268
+2005,KZK,Kazakhstan,-0.162
+2005,CHN,China,-0.783
+2005,MON,Mongolia,-0.48
+2005,PRK,North Korea,-1.444
+2005,ROK,South Korea,0.539
+2005,JPN,Japan,0.699
+2005,IND,India,-0.278
+2005,BHU,Bhutan,-0.518
+2005,PAK,Pakistan,-0.626
+2005,BNG,Bangladesh,-1.031
+2005,MYA,Myanmar,-1.265
+2005,SRI,Sri Lanka,-0.582
+2005,MAD,Maldives,-0.822
+2005,NEP,Nepal,-0.529
+2005,THI,Thailand,-0.331
+2005,CAM,Cambodia,-0.726
+2005,LAO,Laos,-0.813
+2005,DRV,Vietnam,-1.49
+2005,MAL,Malaysia,-1.065
+2005,SIN,Singapore,-0.316
+2005,BRU,Brunei,-0.951
+2005,PHI,Philippines,-0.503
+2005,INS,Indonesia,-1.137
+2005,ETM,East Timor,-0.235
+2005,AUL,Australia,1.425
+2005,PNG,Papua New Guinea,0.317
+2005,NEW,New Zealand,0.707
+2005,VAN,Vanuatu,0.255
+2005,SOL,Solomon Islands,0.06
+2005,NAU,Nauru,0.226
+2005,TUV,Tuvalu,0.152
+2005,FIJ,Fiji,-0.164
+2005,TON,Tonga,0.077
+2005,NAU,Nauru,0.705
+2005,MSI,Marshall Islands,1.732
+2005,PAL,Palau,2.007
+2005,FSM,Federated States of Micronesia,1.892
+2005,WSM,Samoa,0.24
+2006,USA,United States of America,2.879
+2006,CAN,Canada,1.412
+2006,BHM,Bahamas,-0.358
+2006,CUB,Cuba,-1.256
+2006,HAI,Haiti,-0.16
+2006,DOM,Dominican Republic,-0.003
+2006,JAM,Jamaica,-0.608
+2006,TRI,Trinidad and Tobago,-0.619
+2006,BAR,Barbados,-0.604
+2006,DMA,Dominica,-0.623
+2006,GRN,Grenada,-0.455
+2006,SLU,St. Lucia,-0.788
+2006,SVG,St. Vincent and the Grenadines,-0.557
+2006,AAB,Antigua & Barbuda,-0.596
+2006,SKN,St. Kitts and Nevis,-0.589
+2006,MEX,Mexico,-0.16
+2006,BLZ,Belize,-0.402
+2006,GUA,Guatemala,-0.124
+2006,HON,Honduras,-0.101
+2006,SAL,El Salvador,-0.269
+2006,NIC,Nicaragua,-0.137
+2006,COS,Costa Rica,-0.313
+2006,PAN,Panama,-0.257
+2006,COL,Colombia,-0.342
+2006,VEN,Venezuela,-1.155
+2006,GUY,Guyana,-0.603
+2006,SUR,Suriname,-0.67
+2006,ECU,Ecuador,-0.411
+2006,PER,Peru,-0.088
+2006,BRA,Brazil,-0.192
+2006,BOL,Bolivia,-0.333
+2006,PAR,Paraguay,-0.239
+2006,CHL,Chile,-0.131
+2006,ARG,Argentina,0.046
+2006,URU,Uruguay,-0.152
+2006,UKG,United Kingdom,1.533
+2006,IRE,Ireland,0.795
+2006,NTH,Netherlands,0.986
+2006,BEL,Belgium,0.973
+2006,LUX,Luxembourg,0.982
+2006,FRN,France,1.49
+2006,MNC,Monaco,0.977
+2006,LIE,Liechtenstein,0.832
+2006,SWZ,Switzerland,0.831
+2006,SPN,Spain,0.989
+2006,AND,Andorra,1.016
+2006,POR,Portugal,0.967
+2006,GFR,German Federal Republic,0.983
+2006,POL,Poland,1.005
+2006,AUS,Austria,0.835
+2006,HUN,Hungary,0.999
+2006,CZR,Czech Republic,1.082
+2006,SLO,Slovakia,0.973
+2006,ITA,Italy,0.862
+2006,SNM,San Marino,0.896
+2006,MLT,Malta,0.675
+2006,ALB,Albania,1.129
+2006,MNG,Montenegro,0.922
+2006,MAC,Macedonia,0.946
+2006,CRO,Croatia,0.943
+2006,YUG,Yugoslavia,0.937
+2006,BOS,Bosnia and Herzegovina,0.94
+2006,SLV,Slovenia,1
+2006,GRC,Greece,0.976
+2006,CYP,Cyprus,0.732
+2006,BUL,Bulgaria,0.966
+2006,MLD,Moldova,0.967
+2006,ROM,Romania,0.959
+2006,RUS,Russia,-0.071
+2006,EST,Estonia,0.952
+2006,LAT,Latvia,1.023
+2006,LIT,Lithuania,0.981
+2006,UKR,Ukraine,0.827
+2006,BLR,Belarus,-0.342
+2006,ARM,Armenia,0.098
+2006,GRG,Georgia,0.85
+2006,AZE,Azerbaijan,-0.117
+2006,FIN,Finland,0.958
+2006,SWD,Sweden,0.825
+2006,NOR,Norway,0.975
+2006,DEN,Denmark,1.001
+2006,ICE,Iceland,0.987
+2006,CAP,Cape Verde,-0.351
+2006,STP,Sao Tome and Principe,-0.648
+2006,GNB,Guinea-Bissau,-0.459
+2006,EQG,Equatorial Guinea,0.315
+2006,GAM,Gambia,-1.036
+2006,MLI,Mali,-0.693
+2006,SEN,Senegal,-0.771
+2006,BEN,Benin,-0.64
+2006,MAA,Mauritania,-0.744
+2006,NIR,Niger,-0.723
+2006,CDI,Ivory Coast,-0.177
+2006,GUI,Guinea,-0.896
+2006,BFO,Burkina Faso,-0.613
+2006,LBR,Liberia,-0.463
+2006,SIE,Sierra Leone,-0.676
+2006,GHA,Ghana,-0.538
+2006,TOG,Togo,-0.791
+2006,CAO,Cameroon,-0.026
+2006,NIG,Nigeria,-0.451
+2006,GAB,Gabon,-0.798
+2006,CEN,Central African Republic,-0.352
+2006,CHA,Chad,-0.544
+2006,CON,Congo,-0.741
+2006,DRC,Democratic Republic of the Congo,-0.914
+2006,UGA,Uganda,-0.009
+2006,KEN,Kenya,-0.42
+2006,TAZ,Tanzania,-0.294
+2006,BUI,Burundi,-0.096
+2006,RWA,Rwanda,-0.621
+2006,SOM,Somalia,-0.725
+2006,DJI,Djibouti,-0.846
+2006,ETH,Ethiopia,-0.419
+2006,ERI,Eritrea,-0.625
+2006,ANG,Angola,-0.446
+2006,MZM,Mozambique,-0.686
+2006,ZAM,Zambia,-0.668
+2006,ZIM,Zimbabwe,-1.187
+2006,MAW,Malawi,-0.065
+2006,SAF,South Africa,-0.503
+2006,NAM,Namibia,-0.644
+2006,LES,Lesotho,-0.594
+2006,BOT,Botswana,-0.43
+2006,SWA,Swaziland,-0.476
+2006,MAG,Madagascar,-0.412
+2006,COM,Comoros,-0.936
+2006,MAS,Mauritius,-0.457
+2006,SEY,Seychelles,-0.624
+2006,MOR,Morocco,-0.94
+2006,ALG,Algeria,-1.198
+2006,TUN,Tunisia,-1.074
+2006,LIB,Libya,-1.528
+2006,SUD,Sudan,-1.397
+2006,IRN,Iran,-1.523
+2006,TUR,Turkey,0.546
+2006,IRQ,Iraq,-0.665
+2006,EGY,Egypt,-1.553
+2006,SYR,Syria,-1.312
+2006,LEB,Lebanon,-0.932
+2006,JOR,Jordan,-0.735
+2006,ISR,Israel,2.454
+2006,SAU,Saudi Arabia,-0.921
+2006,YAR,Yemen Arab Republic,-1.082
+2006,KUW,Kuwait,-0.98
+2006,BAH,Bahrain,-1.07
+2006,QAT,Qatar,-1.11
+2006,UAE,United Arab Emirates,-0.932
+2006,OMA,Oman,-1.183
+2006,AFG,Afghanistan,-0.618
+2006,TKM,Turkmenistan,-0.941
+2006,TAJ,Tajikistan,-0.262
+2006,KYR,Kyrgyzstan,-0.252
+2006,UZB,Uzbekistan,-0.354
+2006,KZK,Kazakhstan,-0.21
+2006,CHN,China,-0.785
+2006,MON,Mongolia,-0.482
+2006,PRK,North Korea,-1.346
+2006,ROK,South Korea,0.629
+2006,JPN,Japan,0.793
+2006,IND,India,-0.434
+2006,BHU,Bhutan,-0.516
+2006,PAK,Pakistan,-0.581
+2006,BNG,Bangladesh,-0.973
+2006,MYA,Myanmar,-1.301
+2006,SRI,Sri Lanka,-0.697
+2006,MAD,Maldives,-0.646
+2006,NEP,Nepal,-0.452
+2006,THI,Thailand,-0.278
+2006,CAM,Cambodia,-0.816
+2006,LAO,Laos,-0.852
+2006,DRV,Vietnam,-1.523
+2006,MAL,Malaysia,-0.852
+2006,SIN,Singapore,-0.265
+2006,BRU,Brunei,-0.951
+2006,PHI,Philippines,-0.482
+2006,INS,Indonesia,-1.197
+2006,ETM,East Timor,-0.359
+2006,AUL,Australia,1.569
+2006,PNG,Papua New Guinea,0.272
+2006,NEW,New Zealand,0.677
+2006,VAN,Vanuatu,0.335
+2006,SOL,Solomon Islands,-0.053
+2006,NAU,Nauru,0.232
+2006,TUV,Tuvalu,0.369
+2006,FIJ,Fiji,0.131
+2006,TON,Tonga,0.234
+2006,NAU,Nauru,0.53
+2006,MSI,Marshall Islands,1.915
+2006,PAL,Palau,1.984
+2006,FSM,Federated States of Micronesia,1.772
+2006,WSM,Samoa,0.301
+2007,USA,United States of America,2.771
+2007,CAN,Canada,1.56
+2007,BHM,Bahamas,-0.321
+2007,CUB,Cuba,-1.322
+2007,HAI,Haiti,-0.475
+2007,DOM,Dominican Republic,-0.424
+2007,JAM,Jamaica,-0.663
+2007,TRI,Trinidad and Tobago,-0.658
+2007,BAR,Barbados,-0.665
+2007,DMA,Dominica,-0.649
+2007,GRN,Grenada,-0.412
+2007,SLU,St. Lucia,-0.731
+2007,SVG,St. Vincent and the Grenadines,-0.632
+2007,AAB,Antigua & Barbuda,-0.663
+2007,SKN,St. Kitts and Nevis,-0.662
+2007,MEX,Mexico,-0.181
+2007,BLZ,Belize,-0.45
+2007,GUA,Guatemala,-0.169
+2007,HON,Honduras,-0.222
+2007,SAL,El Salvador,-0.296
+2007,NIC,Nicaragua,-0.555
+2007,COS,Costa Rica,-0.26
+2007,PAN,Panama,-0.193
+2007,COL,Colombia,-0.242
+2007,VEN,Venezuela,-1.186
+2007,GUY,Guyana,-0.6
+2007,SUR,Suriname,-0.685
+2007,ECU,Ecuador,-0.477
+2007,PER,Peru,-0.065
+2007,BRA,Brazil,-0.22
+2007,BOL,Bolivia,-0.429
+2007,PAR,Paraguay,-0.217
+2007,CHL,Chile,-0.082
+2007,ARG,Argentina,-0.016
+2007,URU,Uruguay,-0.2
+2007,UKG,United Kingdom,1.625
+2007,IRE,Ireland,0.797
+2007,NTH,Netherlands,1.027
+2007,BEL,Belgium,0.978
+2007,LUX,Luxembourg,0.989
+2007,FRN,France,1.503
+2007,MNC,Monaco,0.96
+2007,LIE,Liechtenstein,0.775
+2007,SWZ,Switzerland,0.77
+2007,SPN,Spain,0.919
+2007,AND,Andorra,0.895
+2007,POR,Portugal,0.952
+2007,GFR,German Federal Republic,0.925
+2007,POL,Poland,1.019
+2007,AUS,Austria,0.744
+2007,HUN,Hungary,1.05
+2007,CZR,Czech Republic,1.094
+2007,SLO,Slovakia,1.002
+2007,ITA,Italy,0.893
+2007,SNM,San Marino,0.811
+2007,MLT,Malta,0.688
+2007,ALB,Albania,0.888
+2007,MNG,Montenegro,0.906
+2007,MAC,Macedonia,0.952
+2007,CRO,Croatia,0.95
+2007,YUG,Yugoslavia,0.813
+2007,BOS,Bosnia and Herzegovina,0.931
+2007,SLV,Slovenia,0.996
+2007,GRC,Greece,0.997
+2007,CYP,Cyprus,0.727
+2007,BUL,Bulgaria,0.933
+2007,MLD,Moldova,0.889
+2007,ROM,Romania,0.97
+2007,RUS,Russia,-0.013
+2007,EST,Estonia,0.962
+2007,LAT,Latvia,1.027
+2007,LIT,Lithuania,1.004
+2007,UKR,Ukraine,0.926
+2007,BLR,Belarus,-0.432
+2007,ARM,Armenia,0.091
+2007,GRG,Georgia,0.776
+2007,AZE,Azerbaijan,-0.103
+2007,FIN,Finland,0.918
+2007,SWD,Sweden,0.834
+2007,NOR,Norway,0.924
+2007,DEN,Denmark,0.988
+2007,ICE,Iceland,0.963
+2007,CAP,Cape Verde,-0.52
+2007,STP,Sao Tome and Principe,-0.634
+2007,GNB,Guinea-Bissau,-0.514
+2007,EQG,Equatorial Guinea,-0.045
+2007,GAM,Gambia,-1.095
+2007,MLI,Mali,-0.72
+2007,SEN,Senegal,-0.793
+2007,BEN,Benin,-0.594
+2007,MAA,Mauritania,-0.646
+2007,NIR,Niger,-0.765
+2007,CDI,Ivory Coast,-0.017
+2007,GUI,Guinea,-0.88
+2007,BFO,Burkina Faso,-0.657
+2007,LBR,Liberia,-0.412
+2007,SIE,Sierra Leone,-0.664
+2007,GHA,Ghana,-0.442
+2007,TOG,Togo,-0.778
+2007,CAO,Cameroon,0.041
+2007,NIG,Nigeria,-0.489
+2007,GAB,Gabon,-0.755
+2007,CEN,Central African Republic,-0.528
+2007,CHA,Chad,-0.59
+2007,CON,Congo,-0.676
+2007,DRC,Democratic Republic of the Congo,-0.517
+2007,UGA,Uganda,-0.423
+2007,KEN,Kenya,-0.443
+2007,TAZ,Tanzania,-0.421
+2007,BUI,Burundi,-0.185
+2007,RWA,Rwanda,-0.42
+2007,SOM,Somalia,-1.013
+2007,DJI,Djibouti,-0.884
+2007,ETH,Ethiopia,-0.527
+2007,ERI,Eritrea,-0.6
+2007,ANG,Angola,-0.32
+2007,MZM,Mozambique,-0.652
+2007,ZAM,Zambia,-0.711
+2007,ZIM,Zimbabwe,-1.331
+2007,MAW,Malawi,-0.267
+2007,SAF,South Africa,-0.551
+2007,NAM,Namibia,-0.579
+2007,LES,Lesotho,-0.662
+2007,BOT,Botswana,-0.513
+2007,SWA,Swaziland,-0.664
+2007,MAG,Madagascar,-0.348
+2007,COM,Comoros,-0.744
+2007,MAS,Mauritius,-0.442
+2007,SEY,Seychelles,-0.523
+2007,MOR,Morocco,-0.803
+2007,ALG,Algeria,-1.247
+2007,TUN,Tunisia,-0.763
+2007,LIB,Libya,-1.584
+2007,SUD,Sudan,-1.491
+2007,IRN,Iran,-1.559
+2007,TUR,Turkey,0.516
+2007,IRQ,Iraq,-0.68
+2007,EGY,Egypt,-1.541
+2007,SYR,Syria,-1.452
+2007,LEB,Lebanon,-0.861
+2007,JOR,Jordan,-0.773
+2007,ISR,Israel,2.306
+2007,SAU,Saudi Arabia,-0.891
+2007,YAR,Yemen Arab Republic,-1.028
+2007,KUW,Kuwait,-1.049
+2007,BAH,Bahrain,-0.959
+2007,QAT,Qatar,-1.111
+2007,UAE,United Arab Emirates,-0.855
+2007,OMA,Oman,-1.343
+2007,AFG,Afghanistan,-0.588
+2007,TKM,Turkmenistan,-0.966
+2007,TAJ,Tajikistan,-0.256
+2007,KYR,Kyrgyzstan,-0.372
+2007,UZB,Uzbekistan,-0.403
+2007,KZK,Kazakhstan,-0.121
+2007,CHN,China,-0.609
+2007,MON,Mongolia,-0.406
+2007,PRK,North Korea,-1.362
+2007,ROK,South Korea,0.58
+2007,JPN,Japan,0.605
+2007,IND,India,-0.432
+2007,BHU,Bhutan,-0.453
+2007,PAK,Pakistan,-0.553
+2007,BNG,Bangladesh,-0.989
+2007,MYA,Myanmar,-1.261
+2007,SRI,Sri Lanka,-0.659
+2007,MAD,Maldives,-0.722
+2007,NEP,Nepal,-0.483
+2007,THI,Thailand,-0.259
+2007,CAM,Cambodia,-0.702
+2007,LAO,Laos,-0.868
+2007,DRV,Vietnam,-1.398
+2007,MAL,Malaysia,-1.037
+2007,SIN,Singapore,-0.268
+2007,BRU,Brunei,-0.766
+2007,PHI,Philippines,-0.47
+2007,INS,Indonesia,-1.144
+2007,ETM,East Timor,-0.282
+2007,AUL,Australia,1.424
+2007,PNG,Papua New Guinea,-0.033
+2007,NEW,New Zealand,0.614
+2007,VAN,Vanuatu,0.31
+2007,SOL,Solomon Islands,-0.189
+2007,NAU,Nauru,0.314
+2007,TUV,Tuvalu,-0.09
+2007,FIJ,Fiji,-0.051
+2007,TON,Tonga,0.178
+2007,NAU,Nauru,0.624
+2007,MSI,Marshall Islands,1.737
+2007,PAL,Palau,1.827
+2007,FSM,Federated States of Micronesia,1.71
+2007,WSM,Samoa,0.145
+2008,USA,United States of America,2.752
+2008,CAN,Canada,1.611
+2008,BHM,Bahamas,-0.383
+2008,CUB,Cuba,-1.333
+2008,HAI,Haiti,-0.346
+2008,DOM,Dominican Republic,-0.461
+2008,JAM,Jamaica,-0.7
+2008,TRI,Trinidad and Tobago,-0.611
+2008,BAR,Barbados,-0.665
+2008,DMA,Dominica,-0.697
+2008,GRN,Grenada,-0.512
+2008,SLU,St. Lucia,-0.527
+2008,SVG,St. Vincent and the Grenadines,-0.623
+2008,AAB,Antigua & Barbuda,-0.687
+2008,SKN,St. Kitts and Nevis,-0.666
+2008,MEX,Mexico,-0.184
+2008,BLZ,Belize,-0.446
+2008,GUA,Guatemala,-0.231
+2008,HON,Honduras,-0.226
+2008,SAL,El Salvador,-0.153
+2008,NIC,Nicaragua,-0.779
+2008,COS,Costa Rica,-0.221
+2008,PAN,Panama,-0.125
+2008,COL,Colombia,-0.239
+2008,VEN,Venezuela,-1.248
+2008,GUY,Guyana,-0.672
+2008,SUR,Suriname,-0.598
+2008,ECU,Ecuador,-0.556
+2008,PER,Peru,-0.073
+2008,BRA,Brazil,-0.204
+2008,BOL,Bolivia,-0.458
+2008,PAR,Paraguay,-0.299
+2008,CHL,Chile,-0.067
+2008,ARG,Argentina,0.008
+2008,URU,Uruguay,-0.191
+2008,UKG,United Kingdom,1.588
+2008,IRE,Ireland,0.766
+2008,NTH,Netherlands,0.992
+2008,BEL,Belgium,0.93
+2008,LUX,Luxembourg,0.958
+2008,FRN,France,1.437
+2008,MNC,Monaco,0.937
+2008,LIE,Liechtenstein,0.74
+2008,SWZ,Switzerland,0.69
+2008,SPN,Spain,0.898
+2008,AND,Andorra,0.942
+2008,POR,Portugal,0.928
+2008,GFR,German Federal Republic,0.872
+2008,POL,Poland,1.028
+2008,AUS,Austria,0.756
+2008,HUN,Hungary,0.971
+2008,CZR,Czech Republic,1.04
+2008,SLO,Slovakia,0.948
+2008,ITA,Italy,0.875
+2008,SNM,San Marino,0.849
+2008,MLT,Malta,0.641
+2008,ALB,Albania,0.955
+2008,MNG,Montenegro,0.883
+2008,MAC,Macedonia,0.954
+2008,CRO,Croatia,0.948
+2008,YUG,Yugoslavia,0.597
+2008,BOS,Bosnia and Herzegovina,0.943
+2008,SLV,Slovenia,0.964
+2008,GRC,Greece,0.948
+2008,CYP,Cyprus,0.619
+2008,BUL,Bulgaria,0.942
+2008,MLD,Moldova,0.89
+2008,ROM,Romania,1.003
+2008,RUS,Russia,-0.084
+2008,EST,Estonia,0.958
+2008,LAT,Latvia,0.985
+2008,LIT,Lithuania,0.964
+2008,UKR,Ukraine,0.959
+2008,BLR,Belarus,-0.457
+2008,ARM,Armenia,0.13
+2008,GRG,Georgia,0.816
+2008,AZE,Azerbaijan,-0.275
+2008,FIN,Finland,0.88
+2008,SWD,Sweden,0.805
+2008,NOR,Norway,0.759
+2008,DEN,Denmark,1.015
+2008,ICE,Iceland,0.82
+2008,CAP,Cape Verde,-0.364
+2008,STP,Sao Tome and Principe,-0.625
+2008,GNB,Guinea-Bissau,-0.46
+2008,EQG,Equatorial Guinea,-0.244
+2008,GAM,Gambia,-1.039
+2008,MLI,Mali,-0.664
+2008,SEN,Senegal,-0.787
+2008,BEN,Benin,-0.536
+2008,MAA,Mauritania,-0.789
+2008,NIR,Niger,-0.789
+2008,CDI,Ivory Coast,-0.12
+2008,GUI,Guinea,-0.91
+2008,BFO,Burkina Faso,-0.561
+2008,LBR,Liberia,-0.309
+2008,SIE,Sierra Leone,-0.667
+2008,GHA,Ghana,-0.459
+2008,TOG,Togo,-0.637
+2008,CAO,Cameroon,0.122
+2008,NIG,Nigeria,-0.619
+2008,GAB,Gabon,-0.761
+2008,CEN,Central African Republic,-0.553
+2008,CHA,Chad,-0.644
+2008,CON,Congo,-0.638
+2008,DRC,Democratic Republic of the Congo,-0.54
+2008,UGA,Uganda,-0.598
+2008,KEN,Kenya,-0.539
+2008,TAZ,Tanzania,-0.469
+2008,BUI,Burundi,-0.268
+2008,RWA,Rwanda,-0.468
+2008,SOM,Somalia,-1.045
+2008,DJI,Djibouti,-0.939
+2008,ETH,Ethiopia,-0.487
+2008,ERI,Eritrea,-0.6
+2008,ANG,Angola,-0.53
+2008,MZM,Mozambique,-0.646
+2008,ZAM,Zambia,-0.633
+2008,ZIM,Zimbabwe,-1.353
+2008,MAW,Malawi,-0.453
+2008,SAF,South Africa,-0.56
+2008,NAM,Namibia,-0.631
+2008,LES,Lesotho,-0.665
+2008,BOT,Botswana,-0.309
+2008,SWA,Swaziland,-0.714
+2008,MAG,Madagascar,-0.408
+2008,COM,Comoros,-0.805
+2008,MAS,Mauritius,-0.443
+2008,SEY,Seychelles,-0.532
+2008,MOR,Morocco,-0.731
+2008,ALG,Algeria,-1.276
+2008,TUN,Tunisia,-0.888
+2008,LIB,Libya,-1.597
+2008,SUD,Sudan,-1.523
+2008,IRN,Iran,-1.734
+2008,TUR,Turkey,0.447
+2008,IRQ,Iraq,-0.705
+2008,EGY,Egypt,-1.634
+2008,SYR,Syria,-1.577
+2008,LEB,Lebanon,-0.837
+2008,JOR,Jordan,-0.757
+2008,ISR,Israel,2.303
+2008,SAU,Saudi Arabia,-0.946
+2008,YAR,Yemen Arab Republic,-1.126
+2008,KUW,Kuwait,-1.098
+2008,BAH,Bahrain,-1
+2008,QAT,Qatar,-1.163
+2008,UAE,United Arab Emirates,-0.956
+2008,OMA,Oman,-1.377
+2008,AFG,Afghanistan,-0.61
+2008,TKM,Turkmenistan,-0.871
+2008,TAJ,Tajikistan,-0.336
+2008,KYR,Kyrgyzstan,-0.256
+2008,UZB,Uzbekistan,-0.374
+2008,KZK,Kazakhstan,-0.098
+2008,CHN,China,-0.557
+2008,MON,Mongolia,-0.377
+2008,PRK,North Korea,-1.372
+2008,ROK,South Korea,0.609
+2008,JPN,Japan,0.508
+2008,IND,India,-0.536
+2008,BHU,Bhutan,-0.494
+2008,PAK,Pakistan,-0.669
+2008,BNG,Bangladesh,-0.883
+2008,MYA,Myanmar,-1.321
+2008,SRI,Sri Lanka,-0.836
+2008,MAD,Maldives,-0.617
+2008,NEP,Nepal,-0.504
+2008,THI,Thailand,-0.264
+2008,CAM,Cambodia,-0.711
+2008,LAO,Laos,-0.862
+2008,DRV,Vietnam,-1.377
+2008,MAL,Malaysia,-0.965
+2008,SIN,Singapore,-0.364
+2008,BRU,Brunei,-0.698
+2008,PHI,Philippines,-0.504
+2008,INS,Indonesia,-1.128
+2008,ETM,East Timor,-0.165
+2008,AUL,Australia,1.433
+2008,PNG,Papua New Guinea,-0.137
+2008,NEW,New Zealand,0.663
+2008,VAN,Vanuatu,0.129
+2008,SOL,Solomon Islands,-0.497
+2008,NAU,Nauru,0.383
+2008,TUV,Tuvalu,-0.189
+2008,FIJ,Fiji,0.137
+2008,TON,Tonga,0.189
+2008,NAU,Nauru,0.78
+2008,MSI,Marshall Islands,1.312
+2008,PAL,Palau,2.062
+2008,FSM,Federated States of Micronesia,1.873
+2008,WSM,Samoa,0.175
+2009,USA,United States of America,2.564
+2009,CAN,Canada,1.582
+2009,BHM,Bahamas,-0.446
+2009,CUB,Cuba,-1.496
+2009,HAI,Haiti,-0.445
+2009,DOM,Dominican Republic,-0.541
+2009,JAM,Jamaica,-0.542
+2009,TRI,Trinidad and Tobago,-0.547
+2009,BAR,Barbados,-0.673
+2009,DMA,Dominica,-0.687
+2009,GRN,Grenada,-0.525
+2009,SLU,St. Lucia,-0.384
+2009,SVG,St. Vincent and the Grenadines,-0.653
+2009,AAB,Antigua & Barbuda,-0.579
+2009,SKN,St. Kitts and Nevis,-0.547
+2009,MEX,Mexico,-0.15
+2009,BLZ,Belize,-0.435
+2009,GUA,Guatemala,-0.243
+2009,HON,Honduras,-0.255
+2009,SAL,El Salvador,-0.245
+2009,NIC,Nicaragua,-1.006
+2009,COS,Costa Rica,-0.221
+2009,PAN,Panama,0.154
+2009,COL,Colombia,-0.178
+2009,VEN,Venezuela,-1.33
+2009,GUY,Guyana,-0.602
+2009,SUR,Suriname,-0.639
+2009,ECU,Ecuador,-0.68
+2009,PER,Peru,-0.095
+2009,BRA,Brazil,-0.359
+2009,BOL,Bolivia,-0.71
+2009,PAR,Paraguay,-0.377
+2009,CHL,Chile,-0.123
+2009,ARG,Argentina,-0.083
+2009,URU,Uruguay,-0.205
+2009,UKG,United Kingdom,1.479
+2009,IRE,Ireland,0.779
+2009,NTH,Netherlands,1.084
+2009,BEL,Belgium,1.003
+2009,LUX,Luxembourg,0.921
+2009,FRN,France,1.312
+2009,MNC,Monaco,0.886
+2009,LIE,Liechtenstein,0.834
+2009,SWZ,Switzerland,0.755
+2009,SPN,Spain,0.888
+2009,AND,Andorra,0.872
+2009,POR,Portugal,0.863
+2009,GFR,German Federal Republic,1.041
+2009,POL,Poland,1.117
+2009,AUS,Austria,0.797
+2009,HUN,Hungary,0.998
+2009,CZR,Czech Republic,1.095
+2009,SLO,Slovakia,1.011
+2009,ITA,Italy,0.979
+2009,SNM,San Marino,0.855
+2009,MLT,Malta,0.663
+2009,ALB,Albania,0.731
+2009,MNG,Montenegro,0.904
+2009,MAC,Macedonia,0.983
+2009,CRO,Croatia,0.909
+2009,YUG,Yugoslavia,0.429
+2009,BOS,Bosnia and Herzegovina,0.716
+2009,SLV,Slovenia,0.874
+2009,GRC,Greece,0.916
+2009,CYP,Cyprus,0.633
+2009,BUL,Bulgaria,0.915
+2009,MLD,Moldova,0.901
+2009,ROM,Romania,0.976
+2009,RUS,Russia,-0.059
+2009,EST,Estonia,0.971
+2009,LAT,Latvia,0.938
+2009,LIT,Lithuania,1.013
+2009,UKR,Ukraine,0.935
+2009,BLR,Belarus,-0.418
+2009,ARM,Armenia,0.078
+2009,GRG,Georgia,0.881
+2009,AZE,Azerbaijan,-0.265
+2009,FIN,Finland,0.864
+2009,SWD,Sweden,0.855
+2009,NOR,Norway,0.805
+2009,DEN,Denmark,1.047
+2009,ICE,Iceland,0.887
+2009,CAP,Cape Verde,-0.475
+2009,STP,Sao Tome and Principe,-0.624
+2009,GNB,Guinea-Bissau,-0.539
+2009,EQG,Equatorial Guinea,-0.541
+2009,GAM,Gambia,-0.909
+2009,MLI,Mali,-0.753
+2009,SEN,Senegal,-0.787
+2009,BEN,Benin,-0.361
+2009,MAA,Mauritania,-0.955
+2009,NIR,Niger,-0.795
+2009,CDI,Ivory Coast,-0.128
+2009,GUI,Guinea,-0.849
+2009,BFO,Burkina Faso,-0.469
+2009,LBR,Liberia,-0.212
+2009,SIE,Sierra Leone,-0.54
+2009,GHA,Ghana,-0.458
+2009,TOG,Togo,-0.579
+2009,CAO,Cameroon,0.13
+2009,NIG,Nigeria,-0.687
+2009,GAB,Gabon,-0.717
+2009,CEN,Central African Republic,-0.566
+2009,CHA,Chad,-0.658
+2009,CON,Congo,-0.671
+2009,DRC,Democratic Republic of the Congo,-0.507
+2009,UGA,Uganda,-0.381
+2009,KEN,Kenya,-0.474
+2009,TAZ,Tanzania,-0.438
+2009,BUI,Burundi,-0.289
+2009,RWA,Rwanda,-0.473
+2009,SOM,Somalia,-1.163
+2009,DJI,Djibouti,-1.027
+2009,ETH,Ethiopia,-0.437
+2009,ERI,Eritrea,-0.663
+2009,ANG,Angola,-0.617
+2009,MZM,Mozambique,-0.661
+2009,ZAM,Zambia,-0.553
+2009,ZIM,Zimbabwe,-1.338
+2009,MAW,Malawi,-0.405
+2009,SAF,South Africa,-0.574
+2009,NAM,Namibia,-0.817
+2009,LES,Lesotho,-0.567
+2009,BOT,Botswana,-0.298
+2009,SWA,Swaziland,-0.693
+2009,MAG,Madagascar,-0.423
+2009,COM,Comoros,-0.559
+2009,MAS,Mauritius,-0.461
+2009,SEY,Seychelles,-0.552
+2009,MOR,Morocco,-0.758
+2009,ALG,Algeria,-1.242
+2009,TUN,Tunisia,-1.028
+2009,LIB,Libya,-1.505
+2009,SUD,Sudan,-1.579
+2009,IRN,Iran,-1.699
+2009,TUR,Turkey,0.447
+2009,IRQ,Iraq,-0.82
+2009,EGY,Egypt,-1.585
+2009,SYR,Syria,-1.652
+2009,LEB,Lebanon,-0.881
+2009,JOR,Jordan,-0.736
+2009,ISR,Israel,2.289
+2009,SAU,Saudi Arabia,-0.88
+2009,YAR,Yemen Arab Republic,-1.159
+2009,KUW,Kuwait,-1.155
+2009,BAH,Bahrain,-1.02
+2009,QAT,Qatar,-1.181
+2009,UAE,United Arab Emirates,-1.013
+2009,OMA,Oman,-1.363
+2009,AFG,Afghanistan,-0.699
+2009,TKM,Turkmenistan,-0.881
+2009,TAJ,Tajikistan,-0.393
+2009,KYR,Kyrgyzstan,-0.322
+2009,UZB,Uzbekistan,-0.282
+2009,KZK,Kazakhstan,-0.186
+2009,CHN,China,-0.592
+2009,MON,Mongolia,-0.398
+2009,PRK,North Korea,-1.492
+2009,ROK,South Korea,0.619
+2009,JPN,Japan,0.57
+2009,IND,India,-0.486
+2009,BHU,Bhutan,-0.55
+2009,PAK,Pakistan,-0.593
+2009,BNG,Bangladesh,-0.892
+2009,MYA,Myanmar,-1.317
+2009,SRI,Sri Lanka,-0.896
+2009,MAD,Maldives,-0.441
+2009,NEP,Nepal,-0.555
+2009,THI,Thailand,-0.463
+2009,CAM,Cambodia,-0.682
+2009,LAO,Laos,-0.892
+2009,DRV,Vietnam,-1.335
+2009,MAL,Malaysia,-1.063
+2009,SIN,Singapore,-0.434
+2009,BRU,Brunei,-0.877
+2009,PHI,Philippines,-0.527
+2009,INS,Indonesia,-1.108
+2009,ETM,East Timor,0.007
+2009,AUL,Australia,1.268
+2009,PNG,Papua New Guinea,-0.024
+2009,NEW,New Zealand,0.812
+2009,VAN,Vanuatu,0.343
+2009,SOL,Solomon Islands,-0.491
+2009,NAU,Nauru,0.437
+2009,TUV,Tuvalu,-0.21
+2009,FIJ,Fiji,0.22
+2009,TON,Tonga,0.222
+2009,NAU,Nauru,1.6
+2009,MSI,Marshall Islands,1.192
+2009,PAL,Palau,1.874
+2009,FSM,Federated States of Micronesia,1.953
+2009,WSM,Samoa,0.198
+2010,USA,United States of America,2.519
+2010,CAN,Canada,1.529
+2010,BHM,Bahamas,-0.355
+2010,CUB,Cuba,-1.511
+2010,HAI,Haiti,-0.599
+2010,DOM,Dominican Republic,-0.569
+2010,JAM,Jamaica,-0.548
+2010,TRI,Trinidad and Tobago,-0.61
+2010,BAR,Barbados,-0.607
+2010,DMA,Dominica,-0.648
+2010,GRN,Grenada,0.556
+2010,SLU,St. Lucia,-0.383
+2010,SVG,St. Vincent and the Grenadines,-0.676
+2010,AAB,Antigua & Barbuda,-0.567
+2010,SKN,St. Kitts and Nevis,-0.575
+2010,MEX,Mexico,-0.1
+2010,BLZ,Belize,-0.511
+2010,GUA,Guatemala,-0.22
+2010,HON,Honduras,-0.219
+2010,SAL,El Salvador,-0.218
+2010,NIC,Nicaragua,-1.078
+2010,COS,Costa Rica,-0.274
+2010,PAN,Panama,0.21
+2010,COL,Colombia,-0.167
+2010,VEN,Venezuela,-1.365
+2010,GUY,Guyana,-0.703
+2010,SUR,Suriname,-0.685
+2010,ECU,Ecuador,-0.691
+2010,PER,Peru,-0.111
+2010,BRA,Brazil,-0.463
+2010,BOL,Bolivia,-0.845
+2010,PAR,Paraguay,-0.392
+2010,CHL,Chile,-0.166
+2010,ARG,Argentina,-0.019
+2010,URU,Uruguay,-0.19
+2010,UKG,United Kingdom,1.611
+2010,IRE,Ireland,0.719
+2010,NTH,Netherlands,1.039
+2010,BEL,Belgium,1.049
+2010,LUX,Luxembourg,0.873
+2010,FRN,France,1.366
+2010,MNC,Monaco,0.873
+2010,LIE,Liechtenstein,0.796
+2010,SWZ,Switzerland,0.73
+2010,SPN,Spain,0.795
+2010,AND,Andorra,0.894
+2010,POR,Portugal,0.882
+2010,GFR,German Federal Republic,0.99
+2010,POL,Poland,1.044
+2010,AUS,Austria,0.73
+2010,HUN,Hungary,1.003
+2010,CZR,Czech Republic,1.069
+2010,SLO,Slovakia,0.99
+2010,ITA,Italy,0.883
+2010,SNM,San Marino,0.805
+2010,MLT,Malta,0.601
+2010,ALB,Albania,0.867
+2010,MNG,Montenegro,0.865
+2010,MAC,Macedonia,0.837
+2010,CRO,Croatia,0.94
+2010,YUG,Yugoslavia,0.438
+2010,BOS,Bosnia and Herzegovina,0.62
+2010,SLV,Slovenia,0.862
+2010,GRC,Greece,-0.116
+2010,CYP,Cyprus,0.574
+2010,BUL,Bulgaria,0.993
+2010,MLD,Moldova,0.868
+2010,ROM,Romania,0.961
+2010,RUS,Russia,-0.033
+2010,EST,Estonia,1.036
+2010,LAT,Latvia,1.036
+2010,LIT,Lithuania,1.046
+2010,UKR,Ukraine,0.837
+2010,BLR,Belarus,-0.374
+2010,ARM,Armenia,0.038
+2010,GRG,Georgia,0.857
+2010,AZE,Azerbaijan,-0.277
+2010,FIN,Finland,0.833
+2010,SWD,Sweden,0.85
+2010,NOR,Norway,0.816
+2010,DEN,Denmark,1.028
+2010,ICE,Iceland,0.839
+2010,CAP,Cape Verde,-0.418
+2010,STP,Sao Tome and Principe,-0.333
+2010,GNB,Guinea-Bissau,-0.56
+2010,EQG,Equatorial Guinea,-0.54
+2010,GAM,Gambia,-0.736
+2010,MLI,Mali,-0.742
+2010,SEN,Senegal,-0.783
+2010,BEN,Benin,-0.232
+2010,MAA,Mauritania,-0.958
+2010,NIR,Niger,-0.715
+2010,CDI,Ivory Coast,0.024
+2010,GUI,Guinea,-0.986
+2010,BFO,Burkina Faso,-0.607
+2010,LBR,Liberia,-0.29
+2010,SIE,Sierra Leone,-0.537
+2010,GHA,Ghana,-0.516
+2010,TOG,Togo,-0.531
+2010,CAO,Cameroon,0.192
+2010,NIG,Nigeria,-0.555
+2010,GAB,Gabon,-0.641
+2010,CEN,Central African Republic,-0.584
+2010,CHA,Chad,-0.655
+2010,CON,Congo,-0.592
+2010,DRC,Democratic Republic of the Congo,-0.374
+2010,UGA,Uganda,-0.591
+2010,KEN,Kenya,-0.569
+2010,TAZ,Tanzania,-0.503
+2010,BUI,Burundi,-0.312
+2010,RWA,Rwanda,-0.442
+2010,SOM,Somalia,-1.109
+2010,DJI,Djibouti,-0.698
+2010,ETH,Ethiopia,-0.511
+2010,ERI,Eritrea,-0.71
+2010,ANG,Angola,-0.683
+2010,MZM,Mozambique,-0.683
+2010,ZAM,Zambia,-0.554
+2010,ZIM,Zimbabwe,-1.389
+2010,MAW,Malawi,-0.448
+2010,SAF,South Africa,-0.55
+2010,NAM,Namibia,-0.789
+2010,LES,Lesotho,-0.612
+2010,BOT,Botswana,-0.362
+2010,SWA,Swaziland,-0.772
+2010,MAG,Madagascar,-0.447
+2010,COM,Comoros,-0.752
+2010,MAS,Mauritius,-0.506
+2010,SEY,Seychelles,-0.583
+2010,MOR,Morocco,-0.71
+2010,ALG,Algeria,-1.252
+2010,TUN,Tunisia,-0.992
+2010,LIB,Libya,-1.444
+2010,SUD,Sudan,-1.491
+2010,IRN,Iran,-1.87
+2010,TUR,Turkey,0.417
+2010,IRQ,Iraq,-0.793
+2010,EGY,Egypt,-1.544
+2010,SYR,Syria,-1.84
+2010,LEB,Lebanon,-1.006
+2010,JOR,Jordan,-0.694
+2010,ISR,Israel,2.35
+2010,SAU,Saudi Arabia,-0.904
+2010,YAR,Yemen Arab Republic,-1.064
+2010,KUW,Kuwait,-1.081
+2010,BAH,Bahrain,-0.976
+2010,QAT,Qatar,-1.187
+2010,UAE,United Arab Emirates,-0.988
+2010,OMA,Oman,-1.356
+2010,AFG,Afghanistan,-0.779
+2010,TKM,Turkmenistan,-0.869
+2010,TAJ,Tajikistan,-0.377
+2010,KYR,Kyrgyzstan,-0.258
+2010,UZB,Uzbekistan,-0.408
+2010,KZK,Kazakhstan,-0.311
+2010,CHN,China,-0.77
+2010,MON,Mongolia,-0.332
+2010,PRK,North Korea,-1.796
+2010,ROK,South Korea,0.582
+2010,JPN,Japan,0.63
+2010,IND,India,-0.518
+2010,BHU,Bhutan,-0.503
+2010,PAK,Pakistan,-0.746
+2010,BNG,Bangladesh,-0.947
+2010,MYA,Myanmar,-1.161
+2010,SRI,Sri Lanka,-0.953
+2010,MAD,Maldives,-0.447
+2010,NEP,Nepal,-0.574
+2010,THI,Thailand,-0.532
+2010,CAM,Cambodia,-0.773
+2010,LAO,Laos,-0.883
+2010,DRV,Vietnam,-1.351
+2010,MAL,Malaysia,-1.123
+2010,SIN,Singapore,-0.477
+2010,BRU,Brunei,-0.935
+2010,PHI,Philippines,-0.536
+2010,INS,Indonesia,-1.141
+2010,ETM,East Timor,-0.201
+2010,AUL,Australia,1.278
+2010,PNG,Papua New Guinea,-0.098
+2010,NEW,New Zealand,0.808
+2010,VAN,Vanuatu,-0.1
+2010,SOL,Solomon Islands,-0.418
+2010,NAU,Nauru,0.474
+2010,TUV,Tuvalu,-0.275
+2010,FIJ,Fiji,-0.003
+2010,TON,Tonga,0.223
+2010,NAU,Nauru,2.077
+2010,MSI,Marshall Islands,1.268
+2010,PAL,Palau,2.012
+2010,FSM,Federated States of Micronesia,1.93
+2010,WSM,Samoa,0.207
+2011,USA,United States of America,2.522
+2011,CAN,Canada,1.856
+2011,BHM,Bahamas,-0.368
+2011,CUB,Cuba,-1.713
+2011,HAI,Haiti,-0.276
+2011,DOM,Dominican Republic,-0.595
+2011,JAM,Jamaica,-0.491
+2011,TRI,Trinidad and Tobago,-0.645
+2011,BAR,Barbados,-0.495
+2011,DMA,Dominica,-0.699
+2011,GRN,Grenada,-0.237
+2011,SLU,St. Lucia,-0.347
+2011,SVG,St. Vincent and the Grenadines,-0.761
+2011,AAB,Antigua & Barbuda,-0.626
+2011,SKN,St. Kitts and Nevis,-0.645
+2011,MEX,Mexico,-0.1
+2011,BLZ,Belize,-0.458
+2011,GUA,Guatemala,-0.26
+2011,HON,Honduras,-0.136
+2011,SAL,El Salvador,-0.104
+2011,NIC,Nicaragua,-1.365
+2011,COS,Costa Rica,-0.217
+2011,PAN,Panama,0.218
+2011,COL,Colombia,-0.117
+2011,VEN,Venezuela,-1.662
+2011,GUY,Guyana,-0.673
+2011,SUR,Suriname,-0.735
+2011,ECU,Ecuador,-1.258
+2011,PER,Peru,-0.086
+2011,BRA,Brazil,-0.547
+2011,BOL,Bolivia,-1.183
+2011,PAR,Paraguay,-0.428
+2011,CHL,Chile,-0.16
+2011,ARG,Argentina,-0.088
+2011,URU,Uruguay,-0.363
+2011,UKG,United Kingdom,1.497
+2011,IRE,Ireland,0.79
+2011,NTH,Netherlands,1.08
+2011,BEL,Belgium,1.087
+2011,LUX,Luxembourg,0.882
+2011,FRN,France,1.267
+2011,MNC,Monaco,0.896
+2011,LIE,Liechtenstein,0.755
+2011,SWZ,Switzerland,0.652
+2011,SPN,Spain,0.898
+2011,AND,Andorra,0.867
+2011,POR,Portugal,0.883
+2011,GFR,German Federal Republic,0.955
+2011,POL,Poland,1.016
+2011,AUS,Austria,0.763
+2011,HUN,Hungary,0.929
+2011,CZR,Czech Republic,1.018
+2011,SLO,Slovakia,0.998
+2011,ITA,Italy,0.931
+2011,SNM,San Marino,0.866
+2011,MLT,Malta,0.633
+2011,ALB,Albania,0.963
+2011,MNG,Montenegro,0.849
+2011,MAC,Macedonia,0.899
+2011,CRO,Croatia,0.942
+2011,YUG,Yugoslavia,0.463
+2011,BOS,Bosnia and Herzegovina,0.774
+2011,SLV,Slovenia,0.882
+2011,GRC,Greece,0.676
+2011,CYP,Cyprus,0.615
+2011,BUL,Bulgaria,1.001
+2011,MLD,Moldova,0.905
+2011,ROM,Romania,0.964
+2011,RUS,Russia,-0.113
+2011,EST,Estonia,1.012
+2011,LAT,Latvia,1.01
+2011,LIT,Lithuania,1.017
+2011,UKR,Ukraine,0.705
+2011,BLR,Belarus,-0.48
+2011,ARM,Armenia,0.061
+2011,GRG,Georgia,0.826
+2011,AZE,Azerbaijan,-0.477
+2011,FIN,Finland,0.897
+2011,SWD,Sweden,0.873
+2011,NOR,Norway,0.828
+2011,DEN,Denmark,1.018
+2011,ICE,Iceland,0.83
+2011,CAP,Cape Verde,-0.431
+2011,STP,Sao Tome and Principe,-0.395
+2011,GNB,Guinea-Bissau,-0.563
+2011,EQG,Equatorial Guinea,-0.316
+2011,GAM,Gambia,-0.672
+2011,MLI,Mali,-0.753
+2011,SEN,Senegal,-0.624
+2011,BEN,Benin,-0.407
+2011,MAA,Mauritania,-0.861
+2011,NIR,Niger,-0.784
+2011,CDI,Ivory Coast,-0.106
+2011,GUI,Guinea,-0.797
+2011,BFO,Burkina Faso,-0.543
+2011,LBR,Liberia,-0.363
+2011,SIE,Sierra Leone,-0.551
+2011,GHA,Ghana,-0.545
+2011,TOG,Togo,-0.534
+2011,CAO,Cameroon,0.143
+2011,NIG,Nigeria,-0.604
+2011,GAB,Gabon,-0.578
+2011,CEN,Central African Republic,-0.425
+2011,CHA,Chad,-0.666
+2011,CON,Congo,-0.62
+2011,DRC,Democratic Republic of the Congo,-0.511
+2011,UGA,Uganda,-0.725
+2011,KEN,Kenya,-0.706
+2011,TAZ,Tanzania,-0.571
+2011,BUI,Burundi,-0.298
+2011,RWA,Rwanda,-0.368
+2011,SOM,Somalia,-1.041
+2011,DJI,Djibouti,-0.794
+2011,ETH,Ethiopia,-0.494
+2011,ERI,Eritrea,-0.63
+2011,ANG,Angola,-0.688
+2011,MZM,Mozambique,-0.712
+2011,ZAM,Zambia,-0.733
+2011,ZIM,Zimbabwe,-1.333
+2011,MAW,Malawi,-0.475
+2011,SAF,South Africa,-0.727
+2011,NAM,Namibia,-0.747
+2011,LES,Lesotho,-0.717
+2011,BOT,Botswana,-0.365
+2011,SWA,Swaziland,-0.875
+2011,MAG,Madagascar,-0.492
+2011,COM,Comoros,-0.817
+2011,MAS,Mauritius,-0.459
+2011,SEY,Seychelles,-0.476
+2011,MOR,Morocco,-0.718
+2011,ALG,Algeria,-1.306
+2011,TUN,Tunisia,-0.752
+2011,LIB,Libya,-1.052
+2011,SUD,Sudan,-1.388
+2011,SSUD,South Sudan,-0.054
+2011,IRN,Iran,-1.801
+2011,TUR,Turkey,0.477
+2011,IRQ,Iraq,-0.739
+2011,EGY,Egypt,-1.348
+2011,SYR,Syria,-1.738
+2011,LEB,Lebanon,-1.083
+2011,JOR,Jordan,-0.744
+2011,ISR,Israel,2.34
+2011,SAU,Saudi Arabia,-0.92
+2011,YAR,Yemen Arab Republic,-1.11
+2011,KUW,Kuwait,-1.012
+2011,BAH,Bahrain,-0.919
+2011,QAT,Qatar,-1.116
+2011,UAE,United Arab Emirates,-0.828
+2011,OMA,Oman,-1.335
+2011,AFG,Afghanistan,-0.691
+2011,TKM,Turkmenistan,-0.908
+2011,TAJ,Tajikistan,-0.3
+2011,KYR,Kyrgyzstan,-0.286
+2011,UZB,Uzbekistan,-0.46
+2011,KZK,Kazakhstan,-0.383
+2011,CHN,China,-0.72
+2011,MON,Mongolia,-0.307
+2011,PRK,North Korea,-1.78
+2011,ROK,South Korea,0.649
+2011,JPN,Japan,0.646
+2011,IND,India,-0.49
+2011,BHU,Bhutan,-0.57
+2011,PAK,Pakistan,-0.763
+2011,BNG,Bangladesh,-1.011
+2011,MYA,Myanmar,-1.277
+2011,SRI,Sri Lanka,-0.969
+2011,MAD,Maldives,-0.414
+2011,NEP,Nepal,-0.741
+2011,THI,Thailand,-0.488
+2011,CAM,Cambodia,-0.797
+2011,LAO,Laos,-0.916
+2011,DRV,Vietnam,-1.284
+2011,MAL,Malaysia,-0.922
+2011,SIN,Singapore,-0.63
+2011,BRU,Brunei,-0.988
+2011,PHI,Philippines,-0.418
+2011,INS,Indonesia,-0.967
+2011,ETM,East Timor,-0.235
+2011,AUL,Australia,1.266
+2011,PNG,Papua New Guinea,-0.076
+2011,NEW,New Zealand,0.797
+2011,VAN,Vanuatu,0.042
+2011,SOL,Solomon Islands,-0.406
+2011,NAU,Nauru,0.503
+2011,TUV,Tuvalu,-0.355
+2011,FIJ,Fiji,-0.146
+2011,TON,Tonga,0.227
+2011,NAU,Nauru,2.403
+2011,MSI,Marshall Islands,1.344
+2011,PAL,Palau,2.138
+2011,FSM,Federated States of Micronesia,2.11
+2011,WSM,Samoa,0.203
+2012,USA,United States of America,2.681
+2012,CAN,Canada,2.059
+2012,BHM,Bahamas,-0.384
+2012,CUB,Cuba,-1.682
+2012,HAI,Haiti,-0.383
+2012,DOM,Dominican Republic,-0.544
+2012,JAM,Jamaica,-0.556
+2012,TRI,Trinidad and Tobago,-0.638
+2012,BAR,Barbados,-0.479
+2012,DMA,Dominica,-0.75
+2012,GRN,Grenada,-0.478
+2012,SLU,St. Lucia,-0.581
+2012,SVG,St. Vincent and the Grenadines,-0.76
+2012,AAB,Antigua & Barbuda,-0.626
+2012,SKN,St. Kitts and Nevis,-0.617
+2012,MEX,Mexico,-0.107
+2012,BLZ,Belize,-0.496
+2012,GUA,Guatemala,-0.181
+2012,HON,Honduras,0.077
+2012,SAL,El Salvador,-0.053
+2012,NIC,Nicaragua,-1.516
+2012,COS,Costa Rica,-0.252
+2012,PAN,Panama,0.233
+2012,COL,Colombia,-0.07
+2012,VEN,Venezuela,-1.564
+2012,GUY,Guyana,-0.738
+2012,SUR,Suriname,-0.704
+2012,ECU,Ecuador,-1.216
+2012,PER,Peru,-0.112
+2012,BRA,Brazil,-0.602
+2012,BOL,Bolivia,-1.422
+2012,PAR,Paraguay,-0.281
+2012,CHL,Chile,-0.282
+2012,ARG,Argentina,-0.14
+2012,URU,Uruguay,-0.418
+2012,UKG,United Kingdom,1.53
+2012,IRE,Ireland,0.755
+2012,NTH,Netherlands,1.037
+2012,BEL,Belgium,1.034
+2012,LUX,Luxembourg,0.895
+2012,FRN,France,1.42
+2012,MNC,Monaco,0.955
+2012,LIE,Liechtenstein,0.804
+2012,SWZ,Switzerland,0.708
+2012,SPN,Spain,0.941
+2012,AND,Andorra,0.952
+2012,POR,Portugal,0.927
+2012,GFR,German Federal Republic,0.991
+2012,POL,Poland,1.042
+2012,AUS,Austria,0.748
+2012,HUN,Hungary,1.038
+2012,CZR,Czech Republic,1.173
+2012,SLO,Slovakia,1.052
+2012,ITA,Italy,0.923
+2012,SNM,San Marino,0.919
+2012,MLT,Malta,0.608
+2012,ALB,Albania,1
+2012,MNG,Montenegro,0.847
+2012,MAC,Macedonia,0.944
+2012,CRO,Croatia,1.011
+2012,YUG,Yugoslavia,0.527
+2012,BOS,Bosnia and Herzegovina,0.853
+2012,SLV,Slovenia,0.92
+2012,GRC,Greece,0.853
+2012,CYP,Cyprus,0.595
+2012,BUL,Bulgaria,0.979
+2012,MLD,Moldova,0.959
+2012,ROM,Romania,1.028
+2012,RUS,Russia,0.067
+2012,EST,Estonia,1.052
+2012,LAT,Latvia,1.061
+2012,LIT,Lithuania,1.061
+2012,UKR,Ukraine,0.81
+2012,BLR,Belarus,-0.406
+2012,ARM,Armenia,0.065
+2012,GRG,Georgia,0.862
+2012,AZE,Azerbaijan,-0.528
+2012,FIN,Finland,0.916
+2012,SWD,Sweden,0.846
+2012,NOR,Norway,0.898
+2012,DEN,Denmark,1.005
+2012,ICE,Iceland,0.836
+2012,CAP,Cape Verde,-0.459
+2012,STP,Sao Tome and Principe,-0.45
+2012,GNB,Guinea-Bissau,-0.547
+2012,EQG,Equatorial Guinea,-0.359
+2012,GAM,Gambia,-0.482
+2012,MLI,Mali,-0.681
+2012,SEN,Senegal,-0.573
+2012,BEN,Benin,-0.507
+2012,MAA,Mauritania,-0.852
+2012,NIR,Niger,-0.749
+2012,CDI,Ivory Coast,-0.36
+2012,GUI,Guinea,-0.75
+2012,BFO,Burkina Faso,-0.467
+2012,LBR,Liberia,-0.241
+2012,SIE,Sierra Leone,-0.553
+2012,GHA,Ghana,-0.569
+2012,TOG,Togo,-0.1
+2012,CAO,Cameroon,0.285
+2012,NIG,Nigeria,-0.653
+2012,GAB,Gabon,-0.402
+2012,CEN,Central African Republic,-0.541
+2012,CHA,Chad,-0.445
+2012,CON,Congo,-0.616
+2012,DRC,Democratic Republic of the Congo,-0.62
+2012,UGA,Uganda,-0.722
+2012,KEN,Kenya,-0.676
+2012,TAZ,Tanzania,-0.646
+2012,BUI,Burundi,-0.179
+2012,RWA,Rwanda,-0.224
+2012,SOM,Somalia,-0.885
+2012,DJI,Djibouti,-0.827
+2012,ETH,Ethiopia,-0.503
+2012,ERI,Eritrea,-0.731
+2012,ANG,Angola,-0.749
+2012,MZM,Mozambique,-0.501
+2012,ZAM,Zambia,-0.524
+2012,ZIM,Zimbabwe,-1.348
+2012,MAW,Malawi,-0.34
+2012,SAF,South Africa,-0.551
+2012,NAM,Namibia,-0.792
+2012,LES,Lesotho,-0.604
+2012,BOT,Botswana,-0.458
+2012,SWA,Swaziland,-0.871
+2012,MAG,Madagascar,-0.499
+2012,COM,Comoros,-0.882
+2012,MAS,Mauritius,-0.597
+2012,SEY,Seychelles,-0.355
+2012,MOR,Morocco,-0.735
+2012,ALG,Algeria,-1.165
+2012,TUN,Tunisia,-0.788
+2012,LIB,Libya,-0.918
+2012,SUD,Sudan,-1.328
+2012,SSUD,South Sudan,0.215
+2012,IRN,Iran,-1.955
+2012,TUR,Turkey,0.405
+2012,IRQ,Iraq,-0.827
+2012,EGY,Egypt,-1.441
+2012,SYR,Syria,-1.855
+2012,LEB,Lebanon,-1.162
+2012,JOR,Jordan,-0.766
+2012,ISR,Israel,2.456
+2012,SAU,Saudi Arabia,-1.104
+2012,YAR,Yemen Arab Republic,-1.188
+2012,KUW,Kuwait,-1.218
+2012,BAH,Bahrain,-1.001
+2012,QAT,Qatar,-1.267
+2012,UAE,United Arab Emirates,-1.004
+2012,OMA,Oman,-1.386
+2012,AFG,Afghanistan,-0.801
+2012,TKM,Turkmenistan,-0.914
+2012,TAJ,Tajikistan,-0.427
+2012,KYR,Kyrgyzstan,-0.22
+2012,UZB,Uzbekistan,-0.35
+2012,KZK,Kazakhstan,-0.401
+2012,CHN,China,-0.852
+2012,MON,Mongolia,-0.278
+2012,PRK,North Korea,-1.965
+2012,ROK,South Korea,0.726
+2012,JPN,Japan,0.644
+2012,IND,India,-0.645
+2012,BHU,Bhutan,-0.588
+2012,PAK,Pakistan,-0.914
+2012,BNG,Bangladesh,-0.946
+2012,MYA,Myanmar,-1.124
+2012,SRI,Sri Lanka,-0.95
+2012,MAD,Maldives,-0.567
+2012,NEP,Nepal,-0.661
+2012,THI,Thailand,-0.499
+2012,CAM,Cambodia,-0.743
+2012,LAO,Laos,-0.85
+2012,DRV,Vietnam,-1.266
+2012,MAL,Malaysia,-0.912
+2012,SIN,Singapore,-0.521
+2012,BRU,Brunei,-0.957
+2012,PHI,Philippines,-0.486
+2012,INS,Indonesia,-0.999
+2012,ETM,East Timor,-0.26
+2012,AUL,Australia,1.36
+2012,PNG,Papua New Guinea,0.214
+2012,NEW,New Zealand,0.739
+2012,VAN,Vanuatu,0.125
+2012,SOL,Solomon Islands,-0.463
+2012,NAU,Nauru,0.5
+2012,TUV,Tuvalu,-0.506
+2012,FIJ,Fiji,-0.117
+2012,TON,Tonga,0.29
+2012,NAU,Nauru,1.529
+2012,MSI,Marshall Islands,1.45
+2012,PAL,Palau,2.192
+2012,FSM,Federated States of Micronesia,2.033
+2012,WSM,Samoa,0.222
\ No newline at end of file
diff --git a/inst/examples/shiny/UN_Advanced/global.R b/inst/examples/shiny/UN_Advanced/global.R
new file mode 100644
index 0000000..9107282
--- /dev/null
+++ b/inst/examples/shiny/UN_Advanced/global.R
@@ -0,0 +1 @@
+ideal <- read.csv("Data/UN_IdealPoints.csv", stringsAsFactors = F)
diff --git a/inst/examples/shiny/UN_Advanced/server.R b/inst/examples/shiny/UN_Advanced/server.R
new file mode 100644
index 0000000..27f127d
--- /dev/null
+++ b/inst/examples/shiny/UN_Advanced/server.R
@@ -0,0 +1,56 @@
+#server script for United Nations Advanced Example
+library(shiny)
+library(plotly)
+library(dplyr)
+
+shinyServer(function(input, output, session) {
+
+ output$trendPlot <- renderPlotly({
+ if (length(input$name) == 0) {
+ print("Please select at least one country")
+ } else {
+ df_trend <- ideal[ideal$Name == input$name, ]
+
+ # Graph title
+ if (length(input$name) > 2) {
+ j_names_comma <- paste(input$name[-length(input$name)], collapse = ', ')
+ j_names <- paste0(j_names_comma, ", and ", input$name[length(input$name)])
+ } else {
+ j_names <- paste(input$name, collapse = ' and ')
+ }
+
+ graph_title <- paste("Ideal Points for ", j_names, sep="")
+
+ ggideal_point <- ggplot(df_trend) +
+ geom_line(aes(x = Year, y = Ideal.point, color = Name)) +
+ labs(x = "Year", y = "Ideology", title = graph_title) +
+ scale_colour_hue("clarity", l = 70, c = 150) + ggthemes::theme_few() +
+ theme(legend.position = "none")
+
+ # Convert ggplot object to plotly
+ ggplotly(ggideal_point) %>%
+ layout(
+ showlegend = FALSE,
+ # increase the size of the right margin to accommodate more room for the annotation labels
+ margin = list(r = 170)
+ )
+ }
+ })
+
+ output$termPlot <- renderPlot({
+ df_term <- ideal %>% filter(Name %in% input$name) %>%
+ group_by(Name) %>% summarise(terms = n())
+
+ trans_theme <- theme(
+ panel.grid.minor = element_blank(),
+ panel.grid.major = element_blank(),
+ panel.background = element_rect(fill = NA),
+ plot.background = element_rect(fill = NA)
+ )
+
+ ggplot(df_term, aes(x = reorder(Name, terms), y = terms))+
+ geom_bar(stat = "identity", fill = "#2980b9") + coord_flip() +
+ theme_bw() + trans_theme + labs(y = "Terms (in years)", x = "")
+
+ }, bg="transparent")
+})
diff --git a/inst/examples/shiny/UN_Advanced/ui.R b/inst/examples/shiny/UN_Advanced/ui.R
new file mode 100644
index 0000000..aa3feba
--- /dev/null
+++ b/inst/examples/shiny/UN_Advanced/ui.R
@@ -0,0 +1,26 @@
+#user interface for United Nations Advanced Example
+shinyUI(fluidPage(
+
+ # Application title
+ titlePanel("Ideal Points"),
+
+ # Sidebar with a slider input for number of bins
+
+ sidebarPanel(
+ h3("Ideal Points Estimation"),
+ # Select Justices name here
+ selectizeInput("name", label = "Country Name(s) of Interest",
+ choices = unique(ideal$Name), multiple = T,
+ options = list(maxItems = 5, placeholder = 'Select a name'),
+ selected = "United States of America"),
+ # Term plot
+ plotOutput("termPlot", height = 200),
+
+ helpText("Data: Bailey, Michael, Anton Strezhnev and Erik Voeten. Forthcoming. “Estimating Dynamic State Preferences from United Nations Voting Data.” Journal of Conflict Resolution. ")
+ ),
+
+ # Show a plot of the generated distribution
+ mainPanel(
+ plotlyOutput("trendPlot")
+ )
+))
diff --git a/inst/examples/shiny/UN_Simple/Data/UN_IdealPoints.csv b/inst/examples/shiny/UN_Simple/Data/UN_IdealPoints.csv
new file mode 100644
index 0000000..206a0f0
--- /dev/null
+++ b/inst/examples/shiny/UN_Simple/Data/UN_IdealPoints.csv
@@ -0,0 +1 @@
+Year,ID,Name,Ideal.point
1946,UKG,United Kingdom,2.158
1947,UKG,United Kingdom,2.119
1948,UKG,United Kingdom,2.074
1949,UKG,United Kingdom,2.039
1950,UKG,United Kingdom,1.574
1951,UKG,United Kingdom,1.832
1952,UKG,United Kingdom,2.106
1953,UKG,United Kingdom,2.193
1954,UKG,United Kingdom,2.391
1955,UKG,United Kingdom,2.209
1956,UKG,United Kingdom,2.247
1957,UKG,United Kingdom,2.225
1958,UKG,United Kingdom,2.389
1959,UKG,United Kingdom,2.067
1960,UKG,United Kingdom,1.899
1961,UKG,United Kingdom,1.844
1962,UKG,United Kingdom,2.002
1963,UKG,United Kingdom,2.072
1965,UKG,United Kingdom,2.006
1966,UKG,United Kingdom,2.108
1967,UKG,United Kingdom,2.192
1968,UKG,United Kingdom,2.108
1969,UKG,United Kingdom,2.024
1970,UKG,United Kingdom,1.85
1971,UKG,United Kingdom,1.599
1972,UKG,United Kingdom,1.965
1973,UKG,United Kingdom,2.118
1974,UKG,United Kingdom,1.777
1975,UKG,United Kingdom,1.859
1976,UKG,United Kingdom,2.071
1977,UKG,United Kingdom,1.957
1978,UKG,United Kingdom,2.018
1979,UKG,United Kingdom,2.028
1980,UKG,United Kingdom,1.976
1981,UKG,United Kingdom,1.925
1982,UKG,United Kingdom,1.955
1983,UKG,United Kingdom,1.971
1984,UKG,United Kingdom,2.047
1985,UKG,United Kingdom,2.04
1986,UKG,United Kingdom,2.103
1987,UKG,United Kingdom,2.03
1988,UKG,United Kingdom,2.052
1989,UKG,United Kingdom,2.105
1990,UKG,United Kingdom,2.108
1991,UKG,United Kingdom,1.992
1992,UKG,United Kingdom,1.863
1993,UKG,United Kingdom,1.904
1994,UKG,United Kingdom,1.986
1995,UKG,United Kingdom,2.228
1996,UKG,United Kingdom,2.034
1997,UKG,United Kingdom,1.977
1998,UKG,United Kingdom,1.759
1999,UKG,United Kingdom,1.687
2000,UKG,United Kingdom,1.689
2001,UKG,United Kingdom,1.626
2002,UKG,United Kingdom,1.674
2003,UKG,United Kingdom,1.67
2004,UKG,United Kingdom,1.635
2005,UKG,United Kingdom,1.671
2006,UKG,United Kingdom,1.533
2007,UKG,United Kingdom,1.625
2008,UKG,United Kingdom,1.588
2009,UKG,United Kingdom,1.479
2010,UKG,United Kingdom,1.611
2011,UKG,United Kingdom,1.497
2012,UKG,United Kingdom,1.53
1946,USA,United States of America,1.714
1947,USA,United States of America,1.813
1948,USA,United States of America,1.936
1949,USA,United States of America,1.877
1950,USA,United States of America,1.811
1951,USA,United States of America,1.828
1952,USA,United States of America,1.905
1953,USA,United States of America,1.693
1954,USA,United States of America,1.482
1955,USA,United States of America,1.719
1956,USA,United States of America,1.285
1957,USA,United States of America,1.247
1958,USA,United States of America,1.266
1959,USA,United States of America,1.547
1960,USA,United States of America,1.532
1961,USA,United States of America,1.698
1962,USA,United States of America,1.889
1963,USA,United States of America,1.852
1965,USA,United States of America,2.011
1966,USA,United States of America,2.069
1967,USA,United States of America,2.259
1968,USA,United States of America,2.213
1969,USA,United States of America,2.093
1970,USA,United States of America,2.132
1971,USA,United States of America,1.805
1972,USA,United States of America,2.04
1973,USA,United States of America,2.209
1974,USA,United States of America,2.031
1975,USA,United States of America,2.145
1976,USA,United States of America,2.393
1977,USA,United States of America,2.206
1978,USA,United States of America,2.228
1979,USA,United States of America,2.277
1980,USA,United States of America,2.318
1981,USA,United States of America,2.658
1982,USA,United States of America,2.568
1983,USA,United States of America,2.592
1984,USA,United States of America,2.737
1985,USA,United States of America,2.742
1986,USA,United States of America,2.851
1987,USA,United States of America,2.927
1988,USA,United States of America,2.902
1989,USA,United States of America,2.962
1990,USA,United States of America,2.893
1991,USA,United States of America,2.754
1992,USA,United States of America,2.766
1993,USA,United States of America,2.734
1994,USA,United States of America,2.688
1995,USA,United States of America,2.939
1996,USA,United States of America,3.014
1997,USA,United States of America,2.923
1998,USA,United States of America,2.854
1999,USA,United States of America,2.678
2000,USA,United States of America,2.611
2001,USA,United States of America,2.576
2002,USA,United States of America,2.628
2003,USA,United States of America,2.796
2004,USA,United States of America,2.771
2005,USA,United States of America,2.894
2006,USA,United States of America,2.879
2007,USA,United States of America,2.771
2008,USA,United States of America,2.752
2009,USA,United States of America,2.564
2010,USA,United States of America,2.519
2011,USA,United States of America,2.522
2012,USA,United States of America,2.681
1946,CAN,Canada,1.849
1947,CAN,Canada,1.987
1948,CAN,Canada,1.911
1949,CAN,Canada,1.672
1950,CAN,Canada,1.812
1951,CAN,Canada,1.916
1952,CAN,Canada,1.989
1953,CAN,Canada,1.745
1954,CAN,Canada,1.746
1955,CAN,Canada,1.714
1956,CAN,Canada,1.554
1957,CAN,Canada,1.608
1958,CAN,Canada,1.579
1959,CAN,Canada,1.096
1960,CAN,Canada,1.106
1961,CAN,Canada,1.467
1962,CAN,Canada,1.555
1963,CAN,Canada,1.61
1965,CAN,Canada,1.685
1966,CAN,Canada,1.774
1967,CAN,Canada,1.769
1968,CAN,Canada,1.577
1969,CAN,Canada,1.615
1970,CAN,Canada,1.577
1971,CAN,Canada,1.391
1972,CAN,Canada,1.549
1973,CAN,Canada,1.479
1974,CAN,Canada,1.471
1975,CAN,Canada,1.457
1976,CAN,Canada,1.501
1977,CAN,Canada,1.687
1978,CAN,Canada,1.768
1979,CAN,Canada,1.81
1980,CAN,Canada,1.824
1981,CAN,Canada,1.773
1982,CAN,Canada,1.656
1983,CAN,Canada,1.658
1984,CAN,Canada,1.703
1985,CAN,Canada,1.645
1986,CAN,Canada,1.555
1987,CAN,Canada,1.546
1988,CAN,Canada,1.403
1989,CAN,Canada,1.356
1990,CAN,Canada,1.286
1991,CAN,Canada,1.271
1992,CAN,Canada,1.234
1993,CAN,Canada,1.138
1994,CAN,Canada,1.145
1995,CAN,Canada,1.106
1996,CAN,Canada,1.196
1997,CAN,Canada,1.116
1998,CAN,Canada,1.178
1999,CAN,Canada,1.173
2000,CAN,Canada,1.112
2001,CAN,Canada,1.184
2002,CAN,Canada,1.129
2003,CAN,Canada,1.127
2004,CAN,Canada,1.236
2005,CAN,Canada,1.264
2006,CAN,Canada,1.412
2007,CAN,Canada,1.56
2008,CAN,Canada,1.611
2009,CAN,Canada,1.582
2010,CAN,Canada,1.529
2011,CAN,Canada,1.856
2012,CAN,Canada,2.059
\ No newline at end of file
diff --git a/inst/examples/shiny/UN_Simple/global.R b/inst/examples/shiny/UN_Simple/global.R
new file mode 100644
index 0000000..9107282
--- /dev/null
+++ b/inst/examples/shiny/UN_Simple/global.R
@@ -0,0 +1 @@
+ideal <- read.csv("Data/UN_IdealPoints.csv", stringsAsFactors = F)
diff --git a/inst/examples/shiny/UN_Simple/server.R b/inst/examples/shiny/UN_Simple/server.R
new file mode 100644
index 0000000..61746c4
--- /dev/null
+++ b/inst/examples/shiny/UN_Simple/server.R
@@ -0,0 +1,19 @@
+library(shiny)
+library(plotly)
+
+shinyServer(function(input, output, session) {
+
+ output$trendPlot <- renderPlotly({
+
+ if (length(input$name) == 0) {
+ print("Please select at least one country")
+ } else {
+ df_trend <- ideal[ideal$Name == input$name, ]
+ ggplot(df_trend) +
+ geom_line(aes(x = Year, y = Ideal.point, color = Name)) +
+ labs(x = "Year", y = "Ideology", title = "Ideal Points for Countries") +
+ scale_colour_hue("clarity", l = 70, c = 150) + ggthemes::theme_few()
+ }
+
+ })
+})
diff --git a/inst/examples/shiny/UN_Simple/ui.R b/inst/examples/shiny/UN_Simple/ui.R
new file mode 100644
index 0000000..ed7fa26
--- /dev/null
+++ b/inst/examples/shiny/UN_Simple/ui.R
@@ -0,0 +1,27 @@
+library(shiny)
+
+shinyUI(fluidPage(
+
+ # Application title
+ titlePanel("Ideal Points"),
+
+ sidebarPanel(
+ h3("Ideal Points Estimation"),
+ # Select Justices name here
+ selectizeInput("name",
+ label = "Country Name(s) of Interest",
+ choices = unique(ideal$Name),
+ multiple = T,
+ options = list(maxItems = 5, placeholder = 'Select a name'),
+ selected = "United States of America"),
+ # Term plot
+ plotOutput("termPlot", height = 200),
+ helpText("Data: Bailey, Michael, Anton Strezhnev and Erik Voeten. Forthcoming. 'Estimating Dynamic State Preferences from United Nations Voting Data.' Journal of Conflict Resolution. ")
+ ),
+
+ # Show a plot of the generated distribution
+ mainPanel(
+ plotlyOutput("trendPlot")
+ )
+)
+)
diff --git a/inst/examples/shiny/basic/DESCRIPTION b/inst/examples/shiny/basic/DESCRIPTION
new file mode 100644
index 0000000..ff7c2bd
--- /dev/null
+++ b/inst/examples/shiny/basic/DESCRIPTION
@@ -0,0 +1,4 @@
+Title: plotly/ggplot2/crosstalk/Shiny demo
+Author: Joe Cheng <joe at rstudio.com>
+DisplayMode: Normal
+Type: Shiny
diff --git a/inst/examples/shiny/basic/app.R b/inst/examples/shiny/basic/app.R
new file mode 100644
index 0000000..b5152d3
--- /dev/null
+++ b/inst/examples/shiny/basic/app.R
@@ -0,0 +1,51 @@
+# UPDATE: this sort of thing is now possible without shiny,
+# see inst/examples/crosstalk/07-binned-target.R
+library(plotly)
+library(shiny)
+library(crosstalk)
+library(dplyr)
+
+# Prepare mtcars
+sd <- mtcars %>%
+ mutate(gear = factor(gear)) %>%
+ mutate(cyl = factor(cyl)) %>%
+ SharedData$new(group = "A")
+
+ui <- fluidPage(
+ fillRow(height = 500,
+ plotlyOutput("p1"),
+ plotlyOutput("p2"),
+ plotOutput("plot1")
+ ),
+ filter_select("gears", "Gears", sd, ~gear),
+ filter_select("cyl", "Cylinders", sd, ~cyl)
+)
+
+server <- function(input, output, session) {
+
+ output$p1 <- renderPlotly({
+ plot_ly(sd, x = ~wt, y = ~mpg, color = ~gear, height = "100%") %>%
+ highlight("plotly_selected")
+ })
+
+ output$p2 <- renderPlotly({
+ plot_ly(sd, x = ~wt, y = ~disp, color = ~gear, height = "100%") %>%
+ highlight("plotly_selected")
+ })
+
+ output$plot1 <- renderPlot({
+
+ mtcars$selected_ <- if (any(sd$selection())) sd$selection() else FALSE
+
+ # Use ordered factor levels, otherwise the highlighted parts
+ # of the bars appear on the top, not the bottom
+ mtcars$selected_ <- as.character(mtcars$selected_)
+
+ ggplot(mtcars) +
+ # Specify TRUE/FALSE colors, and hide legend
+ scale_fill_manual(values = c("TRUE" = "#000000", "FALSE" = "#CCCCCC"), guide = FALSE) +
+ geom_bar(aes(x = factor(cyl), fill = selected_), color = "black")
+ })
+}
+
+shinyApp(ui, server)
diff --git a/inst/examples/shiny/event_data/DESCRIPTION b/inst/examples/shiny/event_data/DESCRIPTION
new file mode 100644
index 0000000..44b81b0
--- /dev/null
+++ b/inst/examples/shiny/event_data/DESCRIPTION
@@ -0,0 +1,8 @@
+Title: Passing plotly selections to shiny via crosstalk
+Author: Plotly, Inc.
+AuthorUrl: https://plot.ly/r/
+License: MIT
+DisplayMode: Showcase
+Tags: plotly, crosstalk, shiny
+Type: Shiny
+
diff --git a/inst/examples/shiny/event_data/app.R b/inst/examples/shiny/event_data/app.R
new file mode 100644
index 0000000..5127f02
--- /dev/null
+++ b/inst/examples/shiny/event_data/app.R
@@ -0,0 +1,43 @@
+library(shiny)
+library(plotly)
+
+ui <- fluidPage(
+ radioButtons("plotType", "Plot Type:", choices = c("ggplotly", "plotly")),
+ plotlyOutput("plot"),
+ verbatimTextOutput("hover"),
+ verbatimTextOutput("click"),
+ verbatimTextOutput("brush")
+)
+
+server <- function(input, output, session) {
+
+ nms <- row.names(mtcars)
+
+ output$plot <- renderPlotly({
+ if (identical(input$plotType, "ggplotly")) {
+ p <- ggplot(mtcars, aes(x = mpg, y = wt, key = nms)) + geom_point()
+ ggplotly(p) %>% layout(dragmode = "select")
+ } else {
+ plot_ly(mtcars, x = ~mpg, y = ~wt, key = nms) %>%
+ layout(dragmode = "select")
+ }
+ })
+
+ output$hover <- renderPrint({
+ d <- event_data("plotly_hover")
+ if (is.null(d)) "Hover events appear here (unhover to clear)" else d
+ })
+
+ output$click <- renderPrint({
+ d <- event_data("plotly_click")
+ if (is.null(d)) "Click events appear here (double-click to clear)" else d
+ })
+
+ output$brush <- renderPrint({
+ d <- event_data("plotly_selected")
+ if (is.null(d)) "Click and drag events (i.e., select/lasso) appear here (double-click to clear)" else d
+ })
+
+}
+
+shinyApp(ui, server, options = list(display.mode = "showcase"))
diff --git a/inst/examples/shiny/event_data_3D/app.R b/inst/examples/shiny/event_data_3D/app.R
new file mode 100644
index 0000000..1afbe06
--- /dev/null
+++ b/inst/examples/shiny/event_data_3D/app.R
@@ -0,0 +1,28 @@
+library(shiny)
+library(plotly)
+
+ui <- fluidPage(
+ plotlyOutput("plot"),
+ verbatimTextOutput("hover"),
+ verbatimTextOutput("click")
+)
+
+server <- function(input, output, session) {
+
+ output$plot <- renderPlotly({
+ plot_ly(x = rnorm(10), y = rnorm(10), z = rnorm(10), type = "scatter3d")
+ })
+
+ output$hover <- renderPrint({
+ d <- event_data("plotly_hover")
+ if (is.null(d)) "Hover events appear here (unhover to clear)" else d
+ })
+
+ output$click <- renderPrint({
+ d <- event_data("plotly_click")
+ if (is.null(d)) "Click events appear here (double-click to clear)" else d
+ })
+
+}
+
+shinyApp(ui, server, options = list(display.mode = "showcase"))
diff --git a/inst/examples/shiny/event_data_click/DESCRIPTION b/inst/examples/shiny/event_data_click/DESCRIPTION
new file mode 100644
index 0000000..44b81b0
--- /dev/null
+++ b/inst/examples/shiny/event_data_click/DESCRIPTION
@@ -0,0 +1,8 @@
+Title: Passing plotly selections to shiny via crosstalk
+Author: Plotly, Inc.
+AuthorUrl: https://plot.ly/r/
+License: MIT
+DisplayMode: Showcase
+Tags: plotly, crosstalk, shiny
+Type: Shiny
+
diff --git a/inst/examples/shiny/event_data_click/app.R b/inst/examples/shiny/event_data_click/app.R
new file mode 100644
index 0000000..cc3232d
--- /dev/null
+++ b/inst/examples/shiny/event_data_click/app.R
@@ -0,0 +1,53 @@
+library(plotly)
+library(shiny)
+
+# compute a correlation matrix
+correlation <- round(cor(mtcars), 3)
+nms <- names(mtcars)
+
+ui <- fluidPage(
+ mainPanel(
+ plotlyOutput("heat"),
+ plotlyOutput("scatterplot")
+ ),
+ verbatimTextOutput("selection")
+)
+
+server <- function(input, output, session) {
+ output$heat <- renderPlotly({
+ plot_ly(x = nms, y = nms, z = correlation,
+ key = correlation, type = "heatmap", source = "heatplot") %>%
+ layout(xaxis = list(title = ""),
+ yaxis = list(title = ""))
+ })
+
+ output$selection <- renderPrint({
+ s <- event_data("plotly_click", source = "heatplot")
+ if (length(s) == 0) {
+ "Click on a cell in the heatmap to display a scatterplot"
+ } else {
+ cat("You selected: \n\n")
+ as.list(s)
+ }
+ })
+
+ output$scatterplot <- renderPlotly({
+ s <- event_data("plotly_click", source = "heatplot")
+ if (length(s)) {
+ vars <- c(s[["x"]], s[["y"]])
+ d <- setNames(mtcars[vars], c("x", "y"))
+ yhat <- fitted(lm(y ~ x, data = d))
+ plot_ly(d, x = ~x) %>%
+ add_markers(y = ~y) %>%
+ add_lines(y = ~yhat) %>%
+ layout(xaxis = list(title = s[["x"]]),
+ yaxis = list(title = s[["y"]]),
+ showlegend = FALSE)
+ } else {
+ plotly_empty()
+ }
+ })
+
+}
+
+shinyApp(ui, server, options = list(display.mode = "showcase"))
diff --git a/inst/examples/shiny/event_data_click_map/app.R b/inst/examples/shiny/event_data_click_map/app.R
new file mode 100644
index 0000000..b993fa6
--- /dev/null
+++ b/inst/examples/shiny/event_data_click_map/app.R
@@ -0,0 +1,33 @@
+# git checkout feature/transmit
+# R CMD install ./
+
+library(shiny)
+library(plotly)
+
+ui <- fluidPage(
+ plotlyOutput("plot"),
+ verbatimTextOutput("click")
+)
+
+server <- function(input, output, session) {
+
+ output$plot <- renderPlotly({
+ # specify some map projection/options
+ g <- list(
+ scope = 'usa',
+ projection = list(type = 'albers usa'),
+ lakecolor = toRGB('white')
+ )
+ plot_ly(z = state.area, text = state.name, locations = state.abb,
+ type = 'choropleth', locationmode = 'USA-states') %>%
+ layout(geo = g)
+ })
+
+ output$click <- renderPrint({
+ d <- event_data("plotly_click")
+ if (is.null(d)) "Click on a state to view event data" else d
+ })
+
+}
+
+shinyApp(ui, server)
diff --git a/inst/examples/shiny/event_data_modules/app.R b/inst/examples/shiny/event_data_modules/app.R
new file mode 100644
index 0000000..e96a82c
--- /dev/null
+++ b/inst/examples/shiny/event_data_modules/app.R
@@ -0,0 +1,48 @@
+library(shiny)
+library(plotly)
+
+reusableUI <- function(id = NULL) {
+ ns <- NS(id)
+
+ fluidRow(
+ column(4, plotlyOutput(ns("p1"))),
+ column(4, plotlyOutput(ns("p2"))),
+ column(4, verbatimTextOutput(ns("ev1"))),
+ column(4, verbatimTextOutput(ns("ev2")))
+ )
+}
+
+viz <- function(input, output, session, src) {
+
+ # if you want, you can distinguish between events *within* a module
+ src2 <- paste0(src, "2")
+
+ output$p1 <- renderPlotly({
+ plot_ly(mtcars, x = ~mpg, y = ~disp,
+ key = row.names(mtcars), source = src)
+ })
+ output$p2 <- renderPlotly({
+ plot_ly(mtcars, x = ~mpg, y = ~disp,
+ key = row.names(mtcars), source = src2)
+ })
+ output$ev1 <- renderPrint({
+ event_data("plotly_hover", source = src)
+ })
+ output$ev2 <- renderPrint({
+ event_data("plotly_hover", source = src2)
+ })
+
+}
+
+ui <- fluidPage(
+ reusableUI("one"),
+ reusableUI("two")
+)
+
+server <- function(input, output, session) {
+ # use the src argument to namespace plotly events
+ callModule(viz, "one", src = "A")
+ callModule(viz, "two", src = "B")
+}
+
+shinyApp(ui, server)
diff --git a/inst/examples/shiny/event_data_select/app.R b/inst/examples/shiny/event_data_select/app.R
new file mode 100644
index 0000000..aaf8c32
--- /dev/null
+++ b/inst/examples/shiny/event_data_select/app.R
@@ -0,0 +1,82 @@
+library(plotly)
+library(shiny)
+
+# user interface
+ui <- fluidPage(
+ titlePanel("Linked highlighting with plotly and shiny"),
+ mainPanel(
+ htmltools::div(style = "display:inline-block", plotlyOutput("x", width = 400, height = 250)),
+ wellPanel(
+ style = "display:inline-block; vertical-align:top;",
+ sliderInput("xbins", "Number of x bins",
+ min = 1, max = 50, value = 20, width = 250),
+ sliderInput("ybins", "Number of y bins",
+ min = 1, max = 50, value = 20, width = 250)
+ ),
+ br(),
+ htmltools::div(style = "display:inline-block", plotlyOutput("xy", width = 400, height = 400)),
+ htmltools::div(style = "display:inline-block", plotlyOutput("y", width = 250, height = 400))
+ )
+)
+
+# marker objects
+m <- list(color = toRGB("black"))
+m2 <- list(color = toRGB("black", 0.2))
+
+server <- function(input, output, session) {
+
+ # convenience function for computing xbin/ybin object given a number of bins
+ compute_bins <- function(x, n) {
+ list(
+ start = min(x),
+ end = max(x),
+ size = (max(x) - min(x)) / n
+ )
+ }
+
+ # the 'x' histogram
+ output$x <- renderPlotly({
+ x <- cars$speed
+ xbins <- compute_bins(x, input$xbins)
+ p <- plot_ly(x = x, type = "histogram", autobinx = F,
+ xbins = xbins, marker = m2)
+ # obtain plotlyjs selection
+ s <- event_data("plotly_selected")
+ # if points are selected, subset the data, and highlight
+ if (length(s$x) > 0) {
+ p <- add_trace(p, x = s$x, type = "histogram", autobinx = F,
+ xbins = xbins, marker = m)
+ }
+ p %>%
+ config(displayModeBar = F, showLink = F) %>%
+ layout(showlegend = F, barmode = "overlay", yaxis = list(title = "count"),
+ xaxis = list(title = "", showticklabels = F))
+ })
+
+ # basically the same as 'x' histogram
+ output$y <- renderPlotly({
+ y <- cars$dist
+ ybins <- compute_bins(y, input$ybins)
+ p <- plot_ly(y = y, type = "histogram", autobiny = F,
+ ybins = ybins, marker = m2)
+ s <- event_data("plotly_selected")
+ if (length(s$y) > 0) {
+ p <- add_trace(p, y = s$y, type = "histogram", autobiny = F,
+ ybins = ybins, marker = m)
+ }
+ p %>%
+ config(displayModeBar = F, showLink = F) %>%
+ layout(showlegend = F, barmode = "overlay", xaxis = list(title = "count"),
+ yaxis = list(title = "", showticklabels = F))
+ })
+
+ output$xy <- renderPlotly({
+ cars %>%
+ plot_ly(x = ~speed, y = ~dist,
+ mode = "markers", marker = m) %>%
+ layout(dragmode = "select")
+ })
+
+}
+
+shinyApp(ui, server)
diff --git a/inst/examples/shiny/lmGadget/app.R b/inst/examples/shiny/lmGadget/app.R
new file mode 100644
index 0000000..f0ea2be
--- /dev/null
+++ b/inst/examples/shiny/lmGadget/app.R
@@ -0,0 +1,80 @@
+# Many thanks to RStudio for shiny gadgets
+# And special thanks to Winston Chang for the inspiration
+# https://gist.github.com/wch/c4b857d73493e6550cba
+library(shiny)
+library(miniUI)
+library(plotly)
+
+#' Shiny gadget for interactive linear model fitting
+#'
+#' Click on points to add/remove them from consideration
+#'
+#' @param dat a data.frame
+#' @param x a formula specifying the x variable
+#' @param y a formula specifying the y variable
+#' @param key a vector specifying unique attributes for each row
+
+lmGadget <- function(dat, x, y, key = row.names(dat)) {
+
+ ui <- miniPage(
+ gadgetTitleBar("Interactive lm"),
+ miniContentPanel(
+ fillRow(
+ flex = c(NA, 1),
+ fillCol(
+ width = "100px",
+ selectInput("degree", "Polynomial degree", c(1, 2, 3, 4))
+ ),
+ plotlyOutput("plot1", height = "100%")
+ )
+ )
+ )
+
+ # mechanism for managing selected points
+ init <- function() {
+ selected <- rep(FALSE, nrow(dat))
+ function(x) {
+ if (missing(x)) return(selected)
+ selected <<- xor(selected, x)
+ selected
+ }
+ }
+ selection <- init()
+
+ server <- function(input, output) {
+
+ output$plot1 <- renderPlotly({
+ req(input$degree)
+ d <- event_data("plotly_click")
+ selected <- selection(key %in% d[["key"]])
+ modelDat <- dat[!selected, ]
+ formula <- as.formula(
+ sprintf("%s ~ poly(%s, degree = %s)", as.character(y)[2], as.character(x)[2], input$degree)
+ )
+ m <- lm(formula, modelDat)
+ modelDat$yhat <- as.numeric(fitted(m))
+ mcolor <- rep(toRGB("black"), NROW(dat))
+ mcolor[selected] <- toRGB("grey90")
+
+ dat %>%
+ plot_ly(x = x, y = y) %>%
+ add_markers(key = key, marker = list(color = mcolor, size = 10)) %>%
+ add_lines(y = ~yhat, data = modelDat) %>%
+ layout(showlegend = FALSE)
+ })
+
+ # Return the most recent fitted model, when we press "done"
+ observeEvent(input$done, {
+ selected <- selection()
+ modelDat <- dat[!selected, ]
+ formula <- as.formula(
+ sprintf("%s ~ poly(%s, degree = %s)", as.character(y)[2], as.character(x)[2], input$degree)
+ )
+ stopApp(lm(formula, modelDat))
+ })
+ }
+
+ runGadget(ui, server)
+}
+
+m <- lmGadget(mtcars, x = ~wt, y = ~mpg)
diff --git a/inst/examples/shiny/proxy_mapbox/app.R b/inst/examples/shiny/proxy_mapbox/app.R
new file mode 100644
index 0000000..b707c74
--- /dev/null
+++ b/inst/examples/shiny/proxy_mapbox/app.R
@@ -0,0 +1,28 @@
+library(shiny)
+library(plotly)
+
+# get all the available mapbox styles
+mapStyles <- schema()$layout$layoutAttributes$mapbox$style$values
+
+ui <- fluidPage(
+ selectInput("style", "Select a mapbox style", mapStyles),
+ plotlyOutput("map")
+)
+
+server <- function(input, output, session) {
+
+ output$map <- renderPlotly({
+ plot_mapbox()
+ })
+
+ observeEvent(input$style, {
+ plotlyProxy("map", session) %>%
+ plotlyProxyInvoke(
+ "relayout",
+ list(mapbox = list(style = input$style))
+ )
+ })
+
+}
+
+shinyApp(ui, server)
diff --git a/inst/examples/shiny/proxy_relayout/app.R b/inst/examples/shiny/proxy_relayout/app.R
new file mode 100644
index 0000000..e818f58
--- /dev/null
+++ b/inst/examples/shiny/proxy_relayout/app.R
@@ -0,0 +1,43 @@
+library(shiny)
+library(plotly)
+
+ui <- fluidPage(
+ plotlyOutput("plot")
+)
+
+server <- function(input, output, session) {
+
+ p <- ggplot(txhousing) +
+ geom_line(aes(date, median, group = city))
+
+ output$plot <- renderPlotly({
+ ggplotly(p, dynamicTicks = TRUE) %>%
+ rangeslider()
+ })
+
+ observeEvent(event_data("plotly_relayout"), {
+ d <- event_data("plotly_relayout")
+ xmin <- if (length(d[["xaxis.range[0]"]])) d[["xaxis.range[0]"]] else d[["xaxis.range"]][1]
+ xmax <- if (length(d[["xaxis.range[1]"]])) d[["xaxis.range[1]"]] else d[["xaxis.range"]][2]
+ if (is.null(xmin) || is.null(xmax)) return(NULL)
+
+ # compute the y-range based on the new x-range
+ idx <- with(txhousing, xmin <= date & date <= xmax)
+ yrng <- extendrange(txhousing$median[idx])
+
+ plotlyProxy("plot", session) %>%
+ plotlyProxyInvoke("relayout", list(yaxis = list(range = yrng)))
+ })
+
+ yRange <- range(txhousing$median, na.rm = TRUE)
+ observeEvent(event_data("plotly_doubleclick"), {
+
+ plotlyProxy("plot", session) %>%
+ plotlyProxyInvoke("relayout", list(yaxis = list(range = yRange)))
+
+ })
+
+
+}
+
+shinyApp(ui, server)
diff --git a/inst/examples/shiny/proxy_restyle_canada/app.R b/inst/examples/shiny/proxy_restyle_canada/app.R
new file mode 100644
index 0000000..41d00ed
--- /dev/null
+++ b/inst/examples/shiny/proxy_restyle_canada/app.R
@@ -0,0 +1,36 @@
+library(shiny)
+library(plotly)
+
+ui <- fluidPage(
+ selectInput("color", "Canada's fillcolor", colors(), selected = "black"),
+ plotlyOutput("map")
+)
+
+server <- function(input, output, session) {
+
+ output$map <- renderPlotly({
+
+ map_data("world", "canada") %>%
+ group_by(group) %>%
+ plot_mapbox(x = ~long, y = ~lat, color = I("black")) %>%
+ add_polygons() %>%
+ layout(
+ mapbox = list(
+ center = list(lat = ~median(lat), lon = ~median(long))
+ )
+ )
+
+ })
+
+ observeEvent(input$color, {
+
+ plotlyProxy("map", session) %>%
+ plotlyProxyInvoke(
+ "restyle", list(fillcolor = toRGB(input$color))
+ )
+
+ })
+
+}
+
+shinyApp(ui, server)
diff --git a/inst/examples/shiny/proxy_restyle_economics/app.R b/inst/examples/shiny/proxy_restyle_economics/app.R
new file mode 100644
index 0000000..717aaef
--- /dev/null
+++ b/inst/examples/shiny/proxy_restyle_economics/app.R
@@ -0,0 +1,39 @@
+library(shiny)
+library(plotly)
+
+ui <- fluidPage(
+ sliderInput("marker", "Marker size", min = 0, max = 20, value = 8),
+ sliderInput("path", "Path size", min = 0, max = 30, value = 2),
+ plotlyOutput("p")
+)
+
+server <- function(input, output, session) {
+
+ output$p <- renderPlotly({
+ plot_ly(
+ economics, x = ~pce, y = ~psavert, z = ~unemploy,
+ color = ~as.numeric(date), mode = "markers+lines"
+ )
+ })
+
+ observeEvent(input$marker, {
+ plotlyProxy("p", session) %>%
+ plotlyProxyInvoke(
+ "restyle",
+ # could also do list(marker = list(size = input$marker))
+ # but that overwrites the existing marker definition
+ # https://github.com/plotly/plotly.js/issues/1866#issuecomment-314115744
+ list(marker.size = input$marker)
+ )
+ })
+
+ observeEvent(input$path, {
+ plotlyProxy("p", session) %>%
+ plotlyProxyInvoke(
+ "restyle", list(line.width = input$path)
+ )
+ })
+
+}
+
+shinyApp(ui, server)
diff --git a/inst/htmlwidgets/lib/plotlyjs/plotly-htmlwidgets.css b/inst/htmlwidgets/lib/plotlyjs/plotly-htmlwidgets.css
new file mode 100644
index 0000000..f35906d
--- /dev/null
+++ b/inst/htmlwidgets/lib/plotlyjs/plotly-htmlwidgets.css
@@ -0,0 +1,9 @@
+/*
+just here so that plotly works
+correctly with ioslides.
+see https://github.com/ropensci/plotly/issues/463
+*/
+
+slide:not(.current) .plotly.html-widget{
+ display: none;
+}
diff --git a/inst/htmlwidgets/lib/typedarray/LICENSE b/inst/htmlwidgets/lib/typedarray/LICENSE
new file mode 100644
index 0000000..ae3a855
--- /dev/null
+++ b/inst/htmlwidgets/lib/typedarray/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Plotly, Inc
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/inst/htmlwidgets/lib/typedarray/typedarray.min.js b/inst/htmlwidgets/lib/typedarray/typedarray.min.js
new file mode 100644
index 0000000..8e25a3c
--- /dev/null
+++ b/inst/htmlwidgets/lib/typedarray/typedarray.min.js
@@ -0,0 +1 @@
+(function(global){"use strict";var undefined=void 0;var MAX_ARRAY_LENGTH=1e5;function Type(v){switch(typeof v){case"undefined":return"undefined";case"boolean":return"boolean";case"number":return"number";case"string":return"string";default:return v===null?"null":"object"}}function Class(v){return Object.prototype.toString.call(v).replace(/^\[object *|\]$/g,"")}function IsCallable(o){return typeof o==="function"}function ToObject(v){if(v===null||v===undefined)throw TypeError();return Objec [...]
\ No newline at end of file
diff --git a/inst/htmlwidgets/plotly.js b/inst/htmlwidgets/plotly.js
new file mode 100644
index 0000000..eaa17a3
--- /dev/null
+++ b/inst/htmlwidgets/plotly.js
@@ -0,0 +1,823 @@
+
+HTMLWidgets.widget({
+ name: "plotly",
+ type: "output",
+
+ initialize: function(el, width, height) {
+ // when upgrading plotly.js,
+ // uncomment this console.log(), then do `load_all(); plot_ly()`
+ // open in chrome, right-click on console output: "save-as" -> "schema.json"
+ // Schema <- jsonlite::fromJSON("~/Downloads/schema.json")
+ // devtools::use_data(Schema, overwrite = T, internal = T)
+ // console.log(JSON.stringify(Plotly.PlotSchema.get()));
+
+ return {};
+ },
+
+ resize: function(el, width, height, instance) {
+ if (instance.autosize) {
+ var width = instance.width || width;
+ var height = instance.height || height;
+ Plotly.relayout(el.id, {width: width, height: height});
+ }
+ },
+
+ renderValue: function(el, x, instance) {
+
+ if (typeof(window) !== "undefined") {
+ // make sure plots don't get created outside the network (for on-prem)
+ window.PLOTLYENV = window.PLOTLYENV || {};
+ window.PLOTLYENV.BASE_URL = x.base_url;
+ }
+
+ var graphDiv = document.getElementById(el.id);
+
+ // TODO: move the control panel injection strategy inside here...
+ HTMLWidgets.addPostRenderHandler(function() {
+
+ // lower the z-index of the modebar to prevent it from highjacking hover
+ // (TODO: do this via CSS?)
+ // https://github.com/ropensci/plotly/issues/956
+ // https://www.w3schools.com/jsref/prop_style_zindex.asp
+ var modebars = document.querySelectorAll(".js-plotly-plot .plotly .modebar");
+ for (var i = 0; i < modebars.length; i++) {
+ modebars[i].style.zIndex = 1;
+ }
+ });
+
+ // inject a "control panel" holding selectize/dynamic color widget(s)
+ if (x.selectize || x.highlight.dynamic && !instance.plotly) {
+ var flex = document.createElement("div");
+ flex.class = "plotly-crosstalk-control-panel";
+ flex.style = "display: flex; flex-wrap: wrap";
+
+ // inject the colourpicker HTML container into the flexbox
+ if (x.highlight.dynamic) {
+ var pickerDiv = document.createElement("div");
+
+ var pickerInput = document.createElement("input");
+ pickerInput.id = el.id + "-colourpicker";
+ pickerInput.placeholder = "asdasd";
+
+ var pickerLabel = document.createElement("label");
+ pickerLabel.for = pickerInput.id;
+ pickerLabel.innerHTML = "Brush color ";
+
+ pickerDiv.appendChild(pickerLabel);
+ pickerDiv.appendChild(pickerInput);
+ flex.appendChild(pickerDiv);
+ }
+
+ // inject selectize HTML containers (one for every crosstalk group)
+ if (x.selectize) {
+ var ids = Object.keys(x.selectize);
+
+ for (var i = 0; i < ids.length; i++) {
+ var container = document.createElement("div");
+ container.id = ids[i];
+ container.style = "width: 80%; height: 10%";
+ container.class = "form-group crosstalk-input-plotly-highlight";
+
+ var label = document.createElement("label");
+ label.for = ids[i];
+ label.innerHTML = x.selectize[ids[i]].group;
+ label.class = "control-label";
+
+ var selectDiv = document.createElement("div");
+ var select = document.createElement("select");
+ select.multiple = true;
+
+ selectDiv.appendChild(select);
+ container.appendChild(label);
+ container.appendChild(selectDiv);
+ flex.appendChild(container);
+ }
+ }
+
+ // finally, insert the flexbox inside the htmlwidget container,
+ // but before the plotly graph div
+ graphDiv.parentElement.insertBefore(flex, graphDiv);
+
+ if (x.highlight.dynamic) {
+ var picker = $("#" + pickerInput.id);
+ var colors = x.highlight.color || [];
+ // TODO: let users specify options?
+ var opts = {
+ value: colors[0],
+ showColour: "both",
+ palette: "limited",
+ allowedCols: colors.join(" "),
+ width: "20%",
+ height: "10%"
+ };
+ picker.colourpicker({changeDelay: 0});
+ picker.colourpicker("settings", opts);
+ picker.colourpicker("value", opts.value);
+ // inform crosstalk about a change in the current selection colour
+ var grps = x.highlight.ctGroups || [];
+ for (var i = 0; i < grps.length; i++) {
+ crosstalk.group(grps[i]).var('plotlySelectionColour')
+ .set(picker.colourpicker('value'));
+ }
+ picker.on("change", function() {
+ for (var i = 0; i < grps.length; i++) {
+ crosstalk.group(grps[i]).var('plotlySelectionColour')
+ .set(picker.colourpicker('value'));
+ }
+ });
+ }
+ }
+
+ // remove "sendDataToCloud", unless user has specified they want it
+ x.config = x.config || {};
+ if (!x.config.cloud) {
+ x.config.modeBarButtonsToRemove = x.config.modeBarButtonsToRemove || [];
+ x.config.modeBarButtonsToRemove.push("sendDataToCloud");
+ }
+
+ // if no plot exists yet, create one with a particular configuration
+ if (!instance.plotly) {
+
+ var plot = Plotly.plot(graphDiv, x);
+ instance.plotly = true;
+ instance.autosize = x.layout.autosize || true;
+ instance.width = x.layout.width;
+ instance.height = x.layout.height;
+
+ } else {
+
+ // this is essentially equivalent to Plotly.newPlot(), but avoids creating
+ // a new webgl context
+ // https://github.com/plotly/plotly.js/blob/2b24f9def901831e61282076cf3f835598d56f0e/src/plot_api/plot_api.js#L531-L532
+
+ // TODO: restore crosstalk selections?
+ Plotly.purge(graphDiv);
+ // TODO: why is this necessary to get crosstalk working?
+ graphDiv.data = undefined;
+ graphDiv.layout = undefined;
+ var plot = Plotly.plot(graphDiv, x);
+
+ }
+
+ // Trigger plotly.js calls defined via `plotlyProxy()`
+ plot.then(function() {
+ if (HTMLWidgets.shinyMode) {
+ Shiny.addCustomMessageHandler("plotly-calls", function(msg) {
+ var gd = document.getElementById(msg.id);
+ if (!gd) {
+ throw new Error("Couldn't find plotly graph with id: " + msg.id);
+ }
+ if (!Plotly[msg.method]) {
+ throw new Error("Unknown method " + msg.method);
+ }
+ var args = [gd].concat(msg.args);
+ Plotly[msg.method].apply(null, args);
+ });
+ }
+ });
+
+ // Attach attributes (e.g., "key", "z") to plotly event data
+ function eventDataWithKey(eventData) {
+ if (eventData === undefined || !eventData.hasOwnProperty("points")) {
+ return null;
+ }
+ return eventData.points.map(function(pt) {
+ var obj = {
+ curveNumber: pt.curveNumber,
+ pointNumber: pt.pointNumber,
+ x: pt.x,
+ y: pt.y
+ };
+ /*
+ TL;DR: (I think) we have to select the graph div (again) to attach keys...
+
+ Why? Remember that crosstalk will dynamically add/delete traces
+ (see traceManager.prototype.updateSelection() below)
+ For this reason, we can't simply grab keys from x.data (like we did previously)
+ Moreover, we can't use _fullData, since that doesn't include
+ unofficial attributes. It's true that click/hover events fire with
+ pt.data, but drag events don't...
+ */
+ var gd = document.getElementById(el.id);
+ var trace = gd.data[pt.curveNumber];
+
+ // Add other attributes here, if desired
+ if (!trace._isSimpleKey) {
+ var attrsToAttach = ["key", "z"];
+ } else {
+ // simple keys fire the whole key
+ obj.key = trace.key;
+ var attrsToAttach = ["z"];
+ }
+
+ for (var i = 0; i < attrsToAttach.length; i++) {
+ var attr = trace[attrsToAttach[i]];
+ if (Array.isArray(attr)) {
+ // pointNumber can be an array (e.g., heatmaps)
+ // TODO: can pointNumber be 3D?
+ obj[attrsToAttach[i]] = typeof pt.pointNumber === "number" ?
+ attr[pt.pointNumber] : attr[pt.pointNumber[0]][pt.pointNumber[1]];
+ }
+ }
+ return obj;
+ });
+ }
+
+ // send user input event data to shiny
+ if (HTMLWidgets.shinyMode) {
+ // https://plot.ly/javascript/zoom-events/
+ graphDiv.on('plotly_relayout', function(d) {
+ Shiny.onInputChange(
+ ".clientValue-plotly_relayout-" + x.source,
+ JSON.stringify(d)
+ );
+ });
+ graphDiv.on('plotly_hover', function(d) {
+ Shiny.onInputChange(
+ ".clientValue-plotly_hover-" + x.source,
+ JSON.stringify(eventDataWithKey(d))
+ );
+ });
+ graphDiv.on('plotly_click', function(d) {
+ Shiny.onInputChange(
+ ".clientValue-plotly_click-" + x.source,
+ JSON.stringify(eventDataWithKey(d))
+ );
+ });
+ graphDiv.on('plotly_selected', function(d) {
+ Shiny.onInputChange(
+ ".clientValue-plotly_selected-" + x.source,
+ JSON.stringify(eventDataWithKey(d))
+ );
+ });
+ graphDiv.on('plotly_unhover', function(eventData) {
+ Shiny.onInputChange(".clientValue-plotly_hover-" + x.source, null);
+ });
+ graphDiv.on('plotly_doubleclick', function(eventData) {
+ Shiny.onInputChange(".clientValue-plotly_click-" + x.source, null);
+ });
+ // 'plotly_deselect' is code for doubleclick when in select mode
+ graphDiv.on('plotly_deselect', function(eventData) {
+ Shiny.onInputChange(".clientValue-plotly_selected-" + x.source, null);
+ Shiny.onInputChange(".clientValue-plotly_click-" + x.source, null);
+ });
+ }
+
+
+ // Given an array of {curveNumber: x, pointNumber: y} objects,
+ // return a hash of {
+ // set1: {value: [key1, key2, ...], _isSimpleKey: false},
+ // set2: {value: [key3, key4, ...], _isSimpleKey: false}
+ // }
+ function pointsToKeys(points) {
+ var keysBySet = {};
+ for (var i = 0; i < points.length; i++) {
+
+ var trace = graphDiv.data[points[i].curveNumber];
+ if (!trace.key || !trace.set) {
+ continue;
+ }
+
+ // set defaults for this keySet
+ // note that we don't track the nested property (yet) since we always
+ // emit the union -- http://cpsievert.github.io/talks/20161212b/#21
+ keysBySet[trace.set] = keysBySet[trace.set] || {
+ value: [],
+ _isSimpleKey: trace._isSimpleKey
+ };
+
+ // selecting a point of a "simple" trace means: select the
+ // entire key attached to this trace, which is useful for,
+ // say clicking on a fitted line to select corresponding observations
+ var key = trace._isSimpleKey ? trace.key : trace.key[points[i].pointNumber];
+ // http://stackoverflow.com/questions/10865025/merge-flatten-an-array-of-arrays-in-javascript
+ var keyFlat = trace._isNestedKey ? [].concat.apply([], key) : key;
+
+ // TODO: better to only add new values?
+ keysBySet[trace.set].value = keysBySet[trace.set].value.concat(keyFlat);
+ }
+
+ return keysBySet;
+ }
+
+
+ x.highlight.color = x.highlight.color || [];
+ // make sure highlight color is an array
+ if (!Array.isArray(x.highlight.color)) {
+ x.highlight.color = [x.highlight.color];
+ }
+
+ var traceManager = new TraceManager(graphDiv, x.highlight);
+
+ // Gather all *unique* sets.
+ var allSets = [];
+ for (var curveIdx = 0; curveIdx < x.data.length; curveIdx++) {
+ var newSet = x.data[curveIdx].set;
+ if (newSet) {
+ if (allSets.indexOf(newSet) === -1) {
+ allSets.push(newSet);
+ }
+ }
+ }
+
+ // register event listeners for all sets
+ for (var i = 0; i < allSets.length; i++) {
+
+ var set = allSets[i];
+ var selection = new crosstalk.SelectionHandle(set);
+ var filter = new crosstalk.FilterHandle(set);
+
+ var filterChange = function(e) {
+ removeBrush(el);
+ traceManager.updateFilter(set, e.value);
+ };
+ filter.on("change", filterChange);
+
+
+ var selectionChange = function(e) {
+
+ // array of "event objects" tracking the selection history
+ // this is used to avoid adding redundant selections
+ var selectionHistory = crosstalk.var("plotlySelectionHistory").get() || [];
+
+ // Construct an event object "defining" the current event.
+ var event = {
+ receiverID: traceManager.gd.id,
+ plotlySelectionColour: crosstalk.group(set).var("plotlySelectionColour").get()
+ };
+ event[set] = e.value;
+ // TODO: is there a smarter way to check object equality?
+ if (selectionHistory.length > 0) {
+ var ev = JSON.stringify(event);
+ for (var i = 0; i < selectionHistory.length; i++) {
+ var sel = JSON.stringify(selectionHistory[i]);
+ if (sel == ev) {
+ return;
+ }
+ }
+ }
+
+ // accumulate history for persistent selection
+ if (!x.highlight.persistent) {
+ selectionHistory = [event];
+ } else {
+ selectionHistory.push(event);
+ }
+ crosstalk.var("plotlySelectionHistory").set(selectionHistory);
+
+ // do the actual updating of traces, frames, and the selectize widget
+ traceManager.updateSelection(set, e.value);
+ // https://github.com/selectize/selectize.js/blob/master/docs/api.md#methods_items
+ if (x.selectize) {
+ if (!x.highlight.persistent || e.value === null) {
+ selectize.clear(true);
+ }
+ selectize.addItems(e.value, true);
+ selectize.close();
+ }
+ }
+ selection.on("change", selectionChange);
+
+ // Set a crosstalk variable selection value, triggering an update
+ graphDiv.on(x.highlight.on, function turnOn(e) {
+ if (e) {
+ var selectedKeys = pointsToKeys(e.points);
+ // Keys are group names, values are array of selected keys from group.
+ for (var set in selectedKeys) {
+ if (selectedKeys.hasOwnProperty(set)) {
+ selection.set(selectedKeys[set].value, {sender: el});
+ }
+ }
+ }
+ });
+
+ graphDiv.on(x.highlight.off, function turnOff(e) {
+ // remove any visual clues
+ removeBrush(el);
+ // remove any selection history
+ crosstalk.var("plotlySelectionHistory").set(null);
+ // trigger the actual removal of selection traces
+ selection.set(null, {sender: el});
+ });
+
+ // register a callback for selectize so that there is bi-directional
+ // communication between the widget and direct manipulation events
+ if (x.selectize) {
+ var selectizeID = Object.keys(x.selectize)[i];
+ var items = x.selectize[selectizeID].items;
+ var first = [{value: "", label: "(All)"}];
+ var opts = {
+ options: first.concat(items),
+ searchField: "label",
+ valueField: "value",
+ labelField: "label",
+ maxItems: 50
+ };
+ var select = $("#" + selectizeID).find("select")[0];
+ var selectize = $(select).selectize(opts)[0].selectize;
+ // NOTE: this callback is triggered when *directly* altering
+ // dropdown items
+ selectize.on("change", function() {
+ var currentItems = traceManager.groupSelections[set] || [];
+ if (!x.highlight.persistent) {
+ removeBrush(el);
+ for (var i = 0; i < currentItems.length; i++) {
+ selectize.removeItem(currentItems[i], true);
+ }
+ }
+ var newItems = selectize.items.filter(function(idx) {
+ return currentItems.indexOf(idx) < 0;
+ });
+ if (newItems.length > 0) {
+ traceManager.updateSelection(set, newItems);
+ } else {
+ // Item has been removed...
+ // TODO: this logic won't work for dynamically changing palette
+ traceManager.updateSelection(set, null);
+ traceManager.updateSelection(set, selectize.items);
+ }
+ });
+ }
+
+
+
+
+
+
+
+
+ }
+
+ } // end of renderValue
+}); // end of widget definition
+
+/**
+ * @param graphDiv The Plotly graph div
+ * @param highlight An object with options for updating selection(s)
+ */
+function TraceManager(graphDiv, highlight) {
+ // The Plotly graph div
+ this.gd = graphDiv;
+
+ // Preserve the original data.
+ // TODO: try using Lib.extendFlat() as done in
+ // https://github.com/plotly/plotly.js/pull/1136
+ this.origData = JSON.parse(JSON.stringify(graphDiv.data));
+
+ // avoid doing this over and over
+ this.origOpacity = [];
+ for (var i = 0; i < this.origData.length; i++) {
+ this.origOpacity[i] = this.origData[i].opacity || 1;
+ }
+
+ // key: group name, value: null or array of keys representing the
+ // most recently received selection for that group.
+ this.groupSelections = {};
+
+ // selection parameters (e.g., transient versus persistent selection)
+ this.highlight = highlight;
+}
+
+TraceManager.prototype.close = function() {
+ // TODO: Unhook all event handlers
+};
+
+TraceManager.prototype.updateFilter = function(group, keys) {
+
+ if (typeof(keys) === "undefined" || keys === null) {
+
+ this.gd.data = JSON.parse(JSON.stringify(this.origData));
+
+ } else {
+
+ var traces = [];
+ for (var i = 0; i < this.origData.length; i++) {
+ var trace = this.origData[i];
+ if (!trace.key || trace.set !== group) {
+ continue;
+ }
+ var matchFunc = getMatchFunc(trace);
+ var matches = matchFunc(trace.key, keys);
+
+ if (matches.length > 0) {
+ if (!trace._isSimpleKey) {
+ // subsetArrayAttrs doesn't mutate trace (it makes a modified clone)
+ trace = subsetArrayAttrs(trace, matches);
+ }
+ traces.push(trace);
+ }
+ }
+ }
+
+ this.gd.data = traces;
+ Plotly.redraw(this.gd);
+
+ // NOTE: we purposely do _not_ restore selection(s), since on filter,
+ // axis likely will update, changing the pixel -> data mapping, leading
+ // to a likely mismatch in the brush outline and highlighted marks
+
+};
+
+TraceManager.prototype.updateSelection = function(group, keys) {
+
+ if (keys !== null && !Array.isArray(keys)) {
+ throw new Error("Invalid keys argument; null or array expected");
+ }
+
+ // if selection has been cleared, or if this is transient
+ // selection, delete the "selection traces"
+ var nNewTraces = this.gd.data.length - this.origData.length;
+ if (keys === null || !this.highlight.persistent && nNewTraces > 0) {
+ var tracesToRemove = [];
+ for (var i = this.origData.length; i < this.gd.data.length; i++) {
+ tracesToRemove.push(i);
+ }
+ Plotly.deleteTraces(this.gd, tracesToRemove);
+ this.groupSelections[group] = keys;
+ } else {
+ // add to the groupSelection, rather than overwriting it
+ // TODO: can this be removed?
+ this.groupSelections[group] = this.groupSelections[group] || [];
+ for (var i = 0; i < keys.length; i++) {
+ var k = keys[i];
+ if (this.groupSelections[group].indexOf(k) < 0) {
+ this.groupSelections[group].push(k);
+ }
+ }
+ }
+
+ if (keys === null) {
+
+ Plotly.restyle(this.gd, {"opacity": this.origOpacity});
+
+ } else if (keys.length >= 1) {
+
+ // placeholder for new "selection traces"
+ var traces = [];
+ // this variable is set in R/highlight.R
+ var selectionColour = crosstalk.group(group).var("plotlySelectionColour").get() ||
+ this.highlight.color[0];
+
+ // selection brush attributes
+ var selectAttrs = Object.keys(this.highlight.selected);
+
+ for (var i = 0; i < this.origData.length; i++) {
+ // TODO: try using Lib.extendFlat() as done in
+ // https://github.com/plotly/plotly.js/pull/1136
+ var trace = JSON.parse(JSON.stringify(this.gd.data[i]));
+ if (!trace.key || trace.set !== group) {
+ continue;
+ }
+ // Get sorted array of matching indices in trace.key
+ var matchFunc = getMatchFunc(trace);
+ var matches = matchFunc(trace.key, keys);
+
+ if (matches.length > 0) {
+ // If this is a "simple" key, that means select the entire trace
+ if (!trace._isSimpleKey) {
+ trace = subsetArrayAttrs(trace, matches);
+ }
+ // Apply selection brush attributes (supplied from R)
+ // TODO: it would be neat to have a dropdown to dynamically specify these
+ for (var j = 0; j < selectAttrs.length; j++) {
+ var attr = selectAttrs[j];
+ trace[attr] = this.highlight.selected[attr];
+ }
+
+ // if it is defined, override color with the "dynamic brush color""
+ // TODO: DRY this up
+ var d = this.gd._fullData[i];
+ if (d.marker) {
+ trace.marker = trace.marker || {};
+ trace.marker.color = selectionColour || trace.marker.color || d.marker.color;
+
+ // adopt any user-defined styling for the selection
+ var selected = this.highlight.selected.marker || {};
+ var attrs = Object.keys(selected);
+ for (var j = 0; j < attrs.length; j++) {
+ trace.marker[attrs[j]] = selected[attrs[j]];
+ }
+ }
+
+ if (d.line) {
+ trace.line = trace.line || {};
+ trace.line.color = selectionColour || trace.line.color || d.line.color;
+
+ // adopt any user-defined styling for the selection
+ var selected = this.highlight.selected.line || {};
+ var attrs = Object.keys(selected);
+ for (var j = 0; j < attrs.length; j++) {
+ trace.line[attrs[j]] = selected[attrs[j]];
+ }
+ }
+
+ if (d.textfont) {
+ trace.textfont = trace.textfont || {};
+ trace.textfont.color = selectionColour || trace.textfont.color || d.textfont.color;
+
+ // adopt any user-defined styling for the selection
+ var selected = this.highlight.selected.textfont || {};
+ var attrs = Object.keys(selected);
+ for (var j = 0; j < attrs.length; j++) {
+ trace.textfont[attrs[j]] = selected[attrs[j]];
+ }
+ }
+ // attach a sensible name/legendgroup
+ trace.name = trace.name || keys.join("<br />");
+ trace.legendgroup = trace.legendgroup || keys.join("<br />");
+
+ // keep track of mapping between this new trace and the trace it targets
+ // (necessary for updating frames to reflect the selection traces)
+ trace._originalIndex = i;
+ trace._newIndex = this.gd._fullData.length + traces.length;
+ traces.push(trace);
+ }
+ }
+
+ if (traces.length > 0) {
+
+ Plotly.addTraces(this.gd, traces).then(function(gd) {
+ // incrementally add selection traces to frames
+ // (this is heavily inspired by Plotly.Plots.modifyFrames()
+ // in src/plots/plots.js)
+ var _hash = gd._transitionData._frameHash;
+ var _frames = gd._transitionData._frames || [];
+
+ for (var i = 0; i < _frames.length; i++) {
+
+ // add to _frames[i].traces *if* this frame references selected trace(s)
+ var newIndices = [];
+ for (var j = 0; j < traces.length; j++) {
+ var tr = traces[j];
+ if (_frames[i].traces.indexOf(tr._originalIndex) > -1) {
+ newIndices.push(tr._newIndex);
+ _frames[i].traces.push(tr._newIndex);
+ }
+ }
+
+ // nothing to do...
+ if (newIndices.length === 0) {
+ continue;
+ }
+
+ var ctr = 0;
+ var nFrameTraces = _frames[i].data.length;
+
+ for (var j = 0; j < nFrameTraces; j++) {
+ var frameTrace = _frames[i].data[j];
+ if (!frameTrace.key || frameTrace.set !== group) {
+ continue;
+ }
+
+ var matchFunc = getMatchFunc(frameTrace);
+ var matches = matchFunc(frameTrace.key, keys);
+
+ if (matches.length > 0) {
+ if (!trace._isSimpleKey) {
+ frameTrace = subsetArrayAttrs(frameTrace, matches);
+ }
+ var d = gd._fullData[newIndices[ctr]];
+ if (d.marker) {
+ frameTrace.marker = d.marker;
+ }
+ if (d.line) {
+ frameTrace.line = d.line;
+ }
+ if (d.textfont) {
+ frameTrace.textfont = d.textfont;
+ }
+ ctr = ctr + 1;
+ _frames[i].data.push(frameTrace);
+ }
+ }
+
+ // update gd._transitionData._frameHash
+ _hash[_frames[i].name] = _frames[i];
+ }
+
+ });
+
+ // dim traces that have a set matching the set of selection sets
+ var tracesToDim = [],
+ opacities = [],
+ sets = Object.keys(this.groupSelections),
+ n = this.origData.length;
+
+ for (var i = 0; i < n; i++) {
+ var opacity = this.origOpacity[i] || 1;
+ // have we already dimmed this trace? Or is this even worth doing?
+ if (opacity !== this.gd._fullData[i].opacity || this.highlight.opacityDim === 1) {
+ continue;
+ }
+ // is this set an element of the set of selection sets?
+ var matches = findMatches(sets, [this.gd.data[i].set]);
+ if (matches.length) {
+ tracesToDim.push(i);
+ opacities.push(opacity * this.highlight.opacityDim);
+ }
+ }
+
+ if (tracesToDim.length > 0) {
+ Plotly.restyle(this.gd, {"opacity": opacities}, tracesToDim);
+ }
+
+ }
+
+ }
+};
+
+/*
+Note: in all of these match functions, we assume needleSet (i.e. the selected keys)
+is a 1D (or flat) array. The real difference is the meaning of haystack.
+findMatches() does the usual thing you'd expect for
+linked brushing on a scatterplot matrix. findSimpleMatches() returns a match iff
+haystack is a subset of the needleSet. findNestedMatches() returns
+*/
+
+function getMatchFunc(trace) {
+ return (trace._isNestedKey) ? findNestedMatches :
+ (trace._isSimpleKey) ? findSimpleMatches : findMatches;
+}
+
+// find matches for "flat" keys
+function findMatches(haystack, needleSet) {
+ var matches = [];
+ haystack.forEach(function(obj, i) {
+ if (obj === null || needleSet.indexOf(obj) >= 0) {
+ matches.push(i);
+ }
+ });
+ return matches;
+}
+
+// find matches for "simple" keys
+function findSimpleMatches(haystack, needleSet) {
+ var match = haystack.every(function(val) {
+ return val === null || needleSet.indexOf(val) >= 0;
+ });
+ // yes, this doesn't make much sense other than conforming
+ // to the output type of the other match functions
+ return (match) ? [0] : []
+}
+
+// find matches for a "nested" haystack (2D arrays)
+function findNestedMatches(haystack, needleSet) {
+ var matches = [];
+ for (var i = 0; i < haystack.length; i++) {
+ var hay = haystack[i];
+ var match = hay.every(function(val) {
+ return val === null || needleSet.indexOf(val) >= 0;
+ });
+ if (match) {
+ matches.push(i);
+ }
+ }
+ return matches;
+}
+
+function isPlainObject(obj) {
+ return (
+ Object.prototype.toString.call(obj) === '[object Object]' &&
+ Object.getPrototypeOf(obj) === Object.prototype
+ );
+}
+
+function subsetArrayAttrs(obj, indices) {
+ var newObj = {};
+ Object.keys(obj).forEach(function(k) {
+ var val = obj[k];
+
+ if (k.charAt(0) === "_") {
+ newObj[k] = val;
+ } else if (k === "transforms" && Array.isArray(val)) {
+ newObj[k] = val.map(function(transform) {
+ return subsetArrayAttrs(transform, indices);
+ });
+ } else if (k === "colorscale" && Array.isArray(val)) {
+ newObj[k] = val;
+ } else if (isPlainObject(val)) {
+ newObj[k] = subsetArrayAttrs(val, indices);
+ } else if (Array.isArray(val)) {
+ newObj[k] = subsetArray(val, indices);
+ } else {
+ newObj[k] = val;
+ }
+ });
+ return newObj;
+}
+
+function subsetArray(arr, indices) {
+ var result = [];
+ for (var i = 0; i < indices.length; i++) {
+ result.push(arr[indices[i]]);
+ }
+ return result;
+}
+
+// Convenience function for removing plotly's brush
+function removeBrush(el) {
+ var outlines = el.querySelectorAll(".select-outline");
+ for (var i = 0; i < outlines.length; i++) {
+ outlines[i].remove();
+ }
+}
diff --git a/inst/htmlwidgets/plotly.yaml b/inst/htmlwidgets/plotly.yaml
new file mode 100644
index 0000000..e69de29
diff --git a/inst/plotlyjs.R b/inst/plotlyjs.R
new file mode 100644
index 0000000..7f622c5
--- /dev/null
+++ b/inst/plotlyjs.R
@@ -0,0 +1,56 @@
+library(httr)
+# download latest GitHub release
+x <- GET('https://api.github.com/repos/plotly/plotly.js/releases/latest')
+zip <- content(x)$zipball_url
+# for a particular version:
+# zip <- "https://github.com/plotly/plotly.js/archive/v1.25.0.zip"
+tmp <- tempfile(fileext = ".zip")
+download.file(zip, tmp)
+unzip(tmp)
+# update the plotly.js bundle
+p <- Sys.glob("*plotly.js*/dist/plotly.min.js")
+file.copy(p, "inst/htmlwidgets/lib/plotlyjs/plotly-latest.min.js", overwrite = T)
+l <- Sys.glob("*plotly.js*/LICENSE")
+file.copy(l, "inst/htmlwidgets/lib/plotlyjs/LICENSE", overwrite = T)
+unlink("*plotly.js*", recursive = T)
+message("Manually update plotly.yaml with this version")
+basename(zip)
+
+
+# download latest build from master
+#download.file(
+# "https://raw.githubusercontent.com/plotly/plotly.js/master/dist/plotly.min.js",
+# destfile = "inst/htmlwidgets/lib/plotlyjs/plotly-latest.min.js"
+#)
+
+
+#' Update plotly's internal traces information
+#'
+#' Note this script requires RSelenium and phantomjs and shouldn't be
+#' run other than package maintainers
+#'
+#'
+#' @param path a path pointing to the source of plotly's R package
+
+update_attributes <- function(path = ".") {
+ if (!is.na(Sys.getenv("RSTUDIO", NA))) {
+ stop("This function must be called outside of RStudio")
+ }
+ devtools::load_all(path)
+ pjs <- RSelenium::phantom()
+ on.exit(pjs$stop(), add = TRUE)
+ on.exit(unlink("index.html"), add = TRUE)
+ remDr <- RSelenium::remoteDriver(browserName = "phantomjs")
+ Sys.sleep(5)
+ remDr$open(silent = TRUE)
+ htmlwidgets::saveWidget(plotly::plot_ly(), "index.html")
+ remDr$navigate(file.path(getwd(), "index.html"))
+ Schema <- remDr$executeScript("return Plotly.PlotSchema.get()")
+ devtools::use_data(Schema, overwrite = TRUE, internal = TRUE)
+}
+
+update_attributes()
+
+# For some reason this fails as of plotly.js v1.17.0, probably because
+# Plotly is a global anymore. Anyway, see plotly.js for another hack to
+# get at the schema.
diff --git a/inst/stars.R b/inst/stars.R
new file mode 100644
index 0000000..6404ad7
--- /dev/null
+++ b/inst/stars.R
@@ -0,0 +1,22 @@
+library(jsonlite)
+library(ggplot2)
+
+stars <- fromJSON(
+ "https://raw.githubusercontent.com/cpsievert/starline/master/data/stars.json",
+ simplifyDataFrame = FALSE
+)
+
+dats <- lapply(stars$repos, function(r) {
+ starz <- unlist(r$stars$dates)
+ starz <- starz[sort(names(starz))]
+ data.frame(
+ date = as.Date(names(starz)),
+ value = cumsum(starz),
+ repo = r$uri,
+ stringsAsFactors = FALSE
+ )
+})
+
+d <- dplyr::bind_rows(dats)
+ggplot(d, aes(date, value, color = repo)) +
+ geom_line() + ylab("Number of stars")
diff --git a/man/add_annotations.Rd b/man/add_annotations.Rd
new file mode 100644
index 0000000..487c585
--- /dev/null
+++ b/man/add_annotations.Rd
@@ -0,0 +1,39 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add.R
+\name{add_annotations}
+\alias{add_annotations}
+\title{Add an annotation(s) to a plot}
+\usage{
+add_annotations(p, text = NULL, ..., data = NULL, inherit = TRUE)
+}
+\arguments{
+\item{p}{a plotly object}
+
+\item{text}{annotation text (required).}
+
+\item{...}{these arguments are documented at
+\url{https://github.com/plotly/plotly.js/blob/master/src/components/annotations/attributes.js}}
+
+\item{data}{a data frame.}
+
+\item{inherit}{inherit attributes from \code{\link[=plot_ly]{plot_ly()}}?}
+}
+\description{
+Add an annotation(s) to a plot
+}
+\examples{
+
+# single annotation
+plot_ly(mtcars, x = ~wt, y = ~mpg) \%>\%
+ slice(which.max(mpg)) \%>\%
+ add_annotations(text = "Good mileage")
+
+# multiple annotations
+plot_ly(mtcars, x = ~wt, y = ~mpg) \%>\%
+ filter(gear == 5) \%>\%
+ add_annotations("five cylinder", ax = 40)
+
+}
+\author{
+Carson Sievert
+}
diff --git a/man/add_data.Rd b/man/add_data.Rd
new file mode 100644
index 0000000..8e496f3
--- /dev/null
+++ b/man/add_data.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add.R
+\name{add_data}
+\alias{add_data}
+\title{Add data to a plotly visualization}
+\usage{
+add_data(p, data = NULL)
+}
+\arguments{
+\item{p}{a plotly visualization}
+
+\item{data}{a data frame.}
+}
+\description{
+Add data to a plotly visualization
+}
+\examples{
+
+plot_ly() \%>\% add_data(economics) \%>\% add_trace(x = ~date, y = ~pce)
+}
diff --git a/man/add_fun.Rd b/man/add_fun.Rd
new file mode 100644
index 0000000..e3ee483
--- /dev/null
+++ b/man/add_fun.Rd
@@ -0,0 +1,45 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add.R
+\name{add_fun}
+\alias{add_fun}
+\title{Apply function to plot, without modifying data}
+\usage{
+add_fun(p, fun, ...)
+}
+\arguments{
+\item{p}{a plotly object.}
+
+\item{fun}{a function. Should take a plotly object as input and return a
+modified plotly object.}
+
+\item{...}{arguments passed to \code{fun}.}
+}
+\description{
+Useful when you need two or more layers that apply a summary statistic
+to the original data.
+}
+\examples{
+
+txhousing \%>\%
+ group_by(city) \%>\%
+ plot_ly(x = ~date, y = ~median) \%>\%
+ add_lines(alpha = 0.2, name = "Texan Cities") \%>\%
+ add_fun(function(plot) {
+ plot \%>\% filter(city == "Houston") \%>\% add_lines(name = "Houston")
+ }) \%>\%
+ add_fun(function(plot) {
+ plot \%>\% filter(city == "San Antonio") \%>\% add_lines(name = "San Antonio")
+ })
+
+plot_ly(mtcars, x = ~wt, y = ~mpg) \%>\%
+ add_markers() \%>\%
+ add_fun(function(p) {
+ p \%>\% slice(which.max(mpg)) \%>\%
+ add_annotations("Good mileage")
+ }) \%>\%
+ add_fun(function(p) {
+ p \%>\% slice(which.min(mpg)) \%>\%
+ add_annotations(text = "Bad mileage")
+ })
+
+}
diff --git a/man/add_trace.Rd b/man/add_trace.Rd
new file mode 100644
index 0000000..ff96744
--- /dev/null
+++ b/man/add_trace.Rd
@@ -0,0 +1,183 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/add.R
+\name{add_trace}
+\alias{add_trace}
+\alias{add_markers}
+\alias{add_text}
+\alias{add_paths}
+\alias{add_lines}
+\alias{add_segments}
+\alias{add_polygons}
+\alias{add_ribbons}
+\alias{add_area}
+\alias{add_pie}
+\alias{add_bars}
+\alias{add_histogram}
+\alias{add_histogram2d}
+\alias{add_histogram2dcontour}
+\alias{add_heatmap}
+\alias{add_contour}
+\alias{add_boxplot}
+\alias{add_surface}
+\alias{add_mesh}
+\alias{add_scattergeo}
+\alias{add_choropleth}
+\title{Add trace(s) to a plotly visualization}
+\usage{
+add_trace(p, ..., data = NULL, inherit = TRUE)
+
+add_markers(p, x = NULL, y = NULL, z = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_text(p, x = NULL, y = NULL, z = NULL, text = NULL, ...,
+ data = NULL, inherit = TRUE)
+
+add_paths(p, x = NULL, y = NULL, z = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_lines(p, x = NULL, y = NULL, z = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_segments(p, x = NULL, y = NULL, xend = NULL, yend = NULL, ...,
+ data = NULL, inherit = TRUE)
+
+add_polygons(p, x = NULL, y = NULL, ..., data = NULL, inherit = TRUE)
+
+add_ribbons(p, x = NULL, ymin = NULL, ymax = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_area(p, r = NULL, t = NULL, ..., data = NULL, inherit = TRUE)
+
+add_pie(p, values = NULL, labels = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_bars(p, x = NULL, y = NULL, ..., data = NULL, inherit = TRUE)
+
+add_histogram(p, x = NULL, y = NULL, ..., data = NULL, inherit = TRUE)
+
+add_histogram2d(p, x = NULL, y = NULL, z = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_histogram2dcontour(p, x = NULL, y = NULL, z = NULL, ...,
+ data = NULL, inherit = TRUE)
+
+add_heatmap(p, x = NULL, y = NULL, z = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_contour(p, z = NULL, ..., data = NULL, inherit = TRUE)
+
+add_boxplot(p, x = NULL, y = NULL, ..., data = NULL, inherit = TRUE)
+
+add_surface(p, z = NULL, ..., data = NULL, inherit = TRUE)
+
+add_mesh(p, x = NULL, y = NULL, z = NULL, ..., data = NULL,
+ inherit = TRUE)
+
+add_scattergeo(p, ...)
+
+add_choropleth(p, z = NULL, ..., data = NULL, inherit = TRUE)
+}
+\arguments{
+\item{p}{a plotly object}
+
+\item{...}{These arguments are documented at \url{https://plot.ly/r/reference/}
+Note that acceptable arguments depend on the value of \code{type}.}
+
+\item{data}{A data frame (optional) or \link[crosstalk:SharedData]{crosstalk::SharedData} object.}
+
+\item{inherit}{inherit attributes from \code{\link[=plot_ly]{plot_ly()}}?}
+
+\item{x}{the x variable.}
+
+\item{y}{the y variable.}
+
+\item{z}{a numeric matrix}
+
+\item{text}{textual labels.}
+
+\item{xend}{"final" x position (in this context, x represents "start")}
+
+\item{yend}{"final" y position (in this context, y represents "start")}
+
+\item{ymin}{a variable used to define the lower boundary of a polygon.}
+
+\item{ymax}{a variable used to define the upper boundary of a polygon.}
+
+\item{r}{For polar chart only. Sets the radial coordinates.}
+
+\item{t}{For polar chart only. Sets the radial coordinates.}
+
+\item{values}{the value to associated with each slice of the pie.}
+
+\item{labels}{the labels (categories) corresponding to \code{values}.}
+}
+\description{
+Add trace(s) to a plotly visualization
+}
+\examples{
+
+p <- plot_ly(economics, x = ~date, y = ~uempmed)
+p
+p \%>\% add_markers()
+p \%>\% add_lines()
+p \%>\% add_text(text = ".")
+
+# attributes declared in plot_ly() carry over to downstream traces,
+# but can be overwritten
+plot_ly(economics, x = ~date, y = ~uempmed, color = I("red")) \%>\%
+ add_lines() \%>\%
+ add_markers(color = ~pop) \%>\%
+ layout(showlegend = FALSE)
+
+txhousing \%>\%
+ group_by(city) \%>\%
+ plot_ly(x = ~date, y = ~median) \%>\%
+ add_lines(fill = "black")
+
+ggplot2::map_data("world", "canada") \%>\%
+ group_by(group) \%>\%
+ plot_ly(x = ~long, y = ~lat) \%>\%
+ add_polygons(hoverinfo = "none") \%>\%
+ add_markers(text = ~paste(name, "<br />", pop), hoverinfo = "text",
+ data = maps::canada.cities) \%>\%
+ layout(showlegend = FALSE)
+
+plot_ly(economics, x = ~date) \%>\%
+ add_ribbons(ymin = ~pce - 1e3, ymax = ~pce + 1e3)
+p <- plot_ly(plotly::wind, r = ~r, t = ~t) \%>\% add_area(color = ~nms)
+layout(p, radialaxis = list(ticksuffix = "\%"), orientation = 270)
+ds <- data.frame(
+ labels = c("A", "B", "C"),
+ values = c(10, 40, 60)
+)
+
+plot_ly(ds, labels = ~labels, values = ~values) \%>\%
+ add_pie() \%>\%
+ layout(title = "Basic Pie Chart using Plotly")
+library(dplyr)
+mtcars \%>\%
+ count(vs) \%>\%
+ plot_ly(x = ~vs, y = ~n) \%>\%
+ add_bars()
+
+plot_ly(x = ~rnorm(100)) \%>\% add_histogram()
+plot_ly(x = ~LETTERS, y = ~LETTERS) \%>\% add_histogram2d()
+z <- as.matrix(table(LETTERS, LETTERS))
+plot_ly(x = ~LETTERS, y = ~LETTERS, z = ~z) \%>\% add_histogram2d()
+plot_ly(MASS::geyser, x = ~waiting, y = ~duration) \%>\%
+add_histogram2dcontour()
+plot_ly(z = ~volcano) \%>\% add_heatmap()
+plot_ly(z = ~volcano) \%>\% add_contour()
+plot_ly(mtcars, x = ~factor(vs), y = ~mpg) \%>\% add_boxplot()
+plot_ly(z = ~volcano) \%>\% add_surface()
+plot_ly(x = c(0, 0, 1), y = c(0, 1, 0), z = c(0, 0, 0)) \%>\% add_mesh()
+}
+\references{
+\url{https://plot.ly/r/reference/}
+}
+\seealso{
+\code{\link[=plot_ly]{plot_ly()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/animation.Rd b/man/animation.Rd
new file mode 100644
index 0000000..d2937ae
--- /dev/null
+++ b/man/animation.Rd
@@ -0,0 +1,93 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/animate.R
+\name{animation_opts}
+\alias{animation_opts}
+\alias{animation}
+\alias{animation_slider}
+\alias{animation_button}
+\title{Animation configuration options}
+\usage{
+animation_opts(p, frame = 500, transition = frame, easing = "linear",
+ redraw = TRUE, mode = "immediate")
+
+animation_slider(p, hide = FALSE, ...)
+
+animation_button(p, ...)
+}
+\arguments{
+\item{p}{a plotly object.}
+
+\item{frame}{The amount of time between frames (in milliseconds).
+Note that this amount should include the \code{transition}.}
+
+\item{transition}{The duration of the smooth transition between
+frames (in milliseconds).}
+
+\item{easing}{The type of transition easing. See the list of options here
+\url{https://github.com/plotly/plotly.js/blob/master/src/plots/animation_attributes.js}}
+
+\item{redraw}{Trigger a redraw of the plot at completion of the transition?
+A redraw may significantly impact performance, but may be necessary to
+update graphical elements that can't be transitioned.}
+
+\item{mode}{Describes how a new animate call interacts with currently-running
+animations. If \code{immediate}, current animations are interrupted and
+the new animation is started. If \code{next}, the current frame is allowed
+to complete, after which the new animation is started. If \code{afterall}
+all existing frames are animated to completion before the new animation
+is started.}
+
+\item{hide}{remove the animation slider?}
+
+\item{...}{for \code{animation_slider}, attributes are passed to a special
+layout.sliders object tied to the animation frames.
+The definition of these attributes may be found here
+\url{https://github.com/plotly/plotly.js/blob/master/src/components/sliders/attributes.js}
+For \code{animation_button}, arguments are passed to a special
+layout.updatemenus button object tied to the animation
+\url{https://github.com/plotly/plotly.js/blob/master/src/components/updatemenus/attributes.js}}
+}
+\description{
+Animations can be created by either using the \code{frame} argument in
+\code{\link[=plot_ly]{plot_ly()}} or the (unofficial) \code{frame} ggplot2 aesthetic in
+\code{\link[=ggplotly]{ggplotly()}}. By default, animations populate a play button
+and slider component for controlling the state of the animation
+(to pause an animation, click on a relevant location on the slider bar).
+Both the play button and slider component transition between frames according
+rules specified by \code{\link[=animation_opts]{animation_opts()}}.
+}
+\examples{
+
+df <- data.frame(
+ x = c(1, 2, 2, 1, 1, 2),
+ y = c(1, 2, 2, 1, 1, 2),
+ z = c(1, 1, 2, 2, 3, 3)
+)
+plot_ly(df) \%>\%
+ add_markers(x = 1.5, y = 1.5) \%>\%
+ add_markers(x = ~x, y = ~y, frame = ~z)
+
+# it's a good idea to remove smooth transitions when there is
+# no relationship between objects in each view
+plot_ly(mtcars, x = ~wt, y = ~mpg, frame = ~cyl) \%>\%
+ animation_opts(transition = 0)
+
+# works the same way with ggplotly
+if (interactive()) {
+ p <- ggplot(txhousing, aes(month, median)) +
+ geom_line(aes(group = year), alpha = 0.3) +
+ geom_smooth() +
+ geom_line(aes(frame = year, ids = month), color = "red") +
+ facet_wrap(~ city)
+
+ ggplotly(p, width = 1200, height = 900) \%>\%
+ animation_opts(1000)
+}
+
+
+#' # for more, see https://cpsievert.github.io/plotly_book/key-frame-animations.html
+
+}
+\author{
+Carson Sievert
+}
diff --git a/man/api.Rd b/man/api.Rd
new file mode 100644
index 0000000..f4a4093
--- /dev/null
+++ b/man/api.Rd
@@ -0,0 +1,151 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/api_exports.R
+\name{api_create}
+\alias{api_create}
+\alias{api_create.plotly}
+\alias{api_create.ggplot}
+\alias{api_create.data.frame}
+\alias{api_download_plot}
+\alias{api_download_grid}
+\alias{api}
+\title{Tools for working with plotly's REST API (v2)}
+\usage{
+api_create(x = last_plot(), filename = NULL, fileopt = c("overwrite",
+ "new"), sharing = c("public", "private", "secret"), ...)
+
+\method{api_create}{plotly}(x = last_plot(), filename = NULL,
+ fileopt = "overwrite", sharing = "public", ...)
+
+\method{api_create}{ggplot}(x = last_plot(), filename = NULL,
+ fileopt = "overwrite", sharing = "public", ...)
+
+\method{api_create}{data.frame}(x, filename = NULL, fileopt = "overwrite",
+ sharing = "public", ...)
+
+api_download_plot(id, username)
+
+api_download_grid(id, username)
+
+api(endpoint = "/", verb = "GET", body = NULL, ...)
+}
+\arguments{
+\item{x}{An R object to hosted on plotly's web platform.
+Can be a plotly/ggplot2 object or a \link{data.frame}.}
+
+\item{filename}{character vector naming file(s). If \code{x} is a plot,
+can be a vector of length 2 naming both the plot AND the underlying grid.}
+
+\item{fileopt}{character string describing whether to "overwrite" existing
+files or ensure "new" file(s) are always created.}
+
+\item{sharing}{If 'public', anyone can view this graph. It will appear in
+your profile and can appear in search engines. You do not need to be
+logged in to Plotly to view this chart.
+If 'private', only you can view this plot. It will not appear in the
+Plotly feed, your profile, or search engines. You must be logged in to
+Plotly to view this graph. You can privately share this graph with other
+Plotly users in your online Plotly account and they will need to be logged
+in to view this plot.
+If 'secret', anyone with this secret link can view this chart. It will
+not appear in the Plotly feed, your profile, or search engines.
+If it is embedded inside a webpage or an IPython notebook, anybody who is
+viewing that page will be able to view the graph.
+You do not need to be logged in to view this plot.}
+
+\item{...}{For \code{api()}, these arguments are passed onto
+\code{\link[httr:VERB]{httr::VERB()}}. For \code{api_create()}, these arguments are
+included in the body of the HTTP request.}
+
+\item{id}{a filename id.}
+
+\item{username}{a plotly username.}
+
+\item{endpoint}{the endpoint (i.e., location) for the request.
+To see a list of all available endpoints, call \code{api()}.
+Any relevant query parameters should be included here (see examples).}
+
+\item{verb}{name of the HTTP verb to use (as in, \code{\link[httr:VERB]{httr::VERB()}}).}
+
+\item{body}{body of the HTTP request(as in, \code{\link[httr:VERB]{httr::VERB()}}).
+If this value is not already converted to JSON
+(via \code{\link[jsonlite:toJSON]{jsonlite::toJSON()}}), it uses the internal \code{to_JSON()}
+to ensure values are "automatically unboxed" (i.e., vec.}
+}
+\description{
+Convenience functions for working with version 2 of plotly's REST API.
+Upload R objects to a plotly account via \code{api_create()} and download
+plotly objects via \code{api_download_plot()}/\code{api_download_grid()}.
+For anything else, use \code{api()}.
+}
+\examples{
+
+\dontrun{
+
+# ------------------------------------------------------------
+# api_create() makes it easy to upload ggplot2/plotly objects
+# and/or data frames to your plotly account
+# ------------------------------------------------------------
+
+# A data frame creates a plotly "grid". Printing one will take you
+# to the it's web address so you can start creating!
+(m <- api_create(mtcars))
+
+# A plotly/ggplot2 object create a plotly "plot".
+p <- plot_ly(mtcars, x = ~factor(vs))
+(r <- api_create(p))
+
+# api_create() returns metadata about the remote "file". Here is
+# one way you could use that metadata to download a plot for local use:
+fileID <- strsplit(r$file$fid, ":")[[1]]
+layout(
+ api_download_plot(fileID[2], fileID[1]),
+ title = sprintf("Local version of <a href='\%s'>this</a> plot", r$file$web_url)
+)
+
+------------------------------------------------------------
+# The api() function provides a low-level interface for performing
+# any action at any endpoint! It always returns a list.
+# ------------------------------------------------------------
+
+# list all the endpoints
+api()
+
+# search the entire platform!
+# see https://api.plot.ly/v2/search
+api("search?q=overdose")
+api("search?q=plottype:pie trump fake")
+
+# these examples will require a user account
+usr <- Sys.getenv("plotly_username", NA)
+if (!is.na(usr)) {
+ # your account info https://api.plot.ly/v2/#users
+ api(sprintf("users/\%s", usr))
+ # your folders/files https://api.plot.ly/v2/folders#user
+ api(sprintf("folders/home?user=\%s", usr))
+}
+
+# Retrieve a specific file https://api.plot.ly/v2/files#retrieve
+api("files/cpsievert:14681")
+
+# change the filename https://api.plot.ly/v2/files#update
+# (note: this won't work unless you have proper credentials to the relevant account)
+api("files/cpsievert:14681", "PATCH", list(filename = "toy file"))
+
+# Copy a file https://api.plot.ly/v2/files#lookup
+api("files/cpsievert:14681/copy", "POST")
+
+# Create a folder https://api.plot.ly/v2/folders#create
+api("folders", "POST", list(path = "/starts/at/root/and/ends/here"))
+
+}
+
+}
+\references{
+\url{https://api.plot.ly/v2}
+}
+\seealso{
+\code{\link[=signup]{signup()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/as.widget.Rd b/man/as.widget.Rd
new file mode 100644
index 0000000..99281b4
--- /dev/null
+++ b/man/as.widget.Rd
@@ -0,0 +1,18 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/deprecated.R
+\name{as.widget}
+\alias{as.widget}
+\title{Convert a plotly object to an htmlwidget object}
+\usage{
+as.widget(x, ...)
+}
+\arguments{
+\item{x}{a plotly object.}
+
+\item{...}{other options passed onto \code{htmlwidgets::createWidget}}
+}
+\description{
+This function was deprecated in 4.0.0,
+as plotly objects are now htmlwidget objects,
+so there is no need to convert them.
+}
diff --git a/man/as_widget.Rd b/man/as_widget.Rd
new file mode 100644
index 0000000..ec17ef2
--- /dev/null
+++ b/man/as_widget.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly.R
+\name{as_widget}
+\alias{as_widget}
+\title{Convert a list to a plotly htmlwidget object}
+\usage{
+as_widget(x, ...)
+}
+\arguments{
+\item{x}{a plotly object.}
+
+\item{...}{other options passed onto \code{htmlwidgets::createWidget}}
+}
+\description{
+Convert a list to a plotly htmlwidget object
+}
+\examples{
+
+trace <- list(x = 1, y = 1)
+obj <- list(data = list(trace), layout = list(title = "my plot"))
+as_widget(obj)
+
+}
diff --git a/man/attrs_selected.Rd b/man/attrs_selected.Rd
new file mode 100644
index 0000000..995d6bf
--- /dev/null
+++ b/man/attrs_selected.Rd
@@ -0,0 +1,20 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/highlight.R
+\name{attrs_selected}
+\alias{attrs_selected}
+\title{Specify attributes of selection traces}
+\usage{
+attrs_selected(opacity = 1, ...)
+}
+\arguments{
+\item{opacity}{a number between 0 and 1 specifying the overall opacity of
+the selected trace}
+
+\item{...}{other trace attributes attached to the selection trace.}
+}
+\description{
+By default the name of the selection trace derives from the selected values.
+}
+\author{
+Carson Sievert
+}
diff --git a/man/bbox.Rd b/man/bbox.Rd
new file mode 100644
index 0000000..00fd4b3
--- /dev/null
+++ b/man/bbox.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ggplotly.R
+\name{bbox}
+\alias{bbox}
+\title{Estimate bounding box of a rotated string}
+\usage{
+bbox(txt = "foo", angle = 0, size = 12)
+}
+\arguments{
+\item{txt}{a character string of length 1}
+
+\item{angle}{sets the angle of the tick labels with respect to the
+horizontal (e.g., \code{tickangle} of -90 draws the tick labels vertically)}
+
+\item{size}{vertical size of a character}
+}
+\description{
+Estimate bounding box of a rotated string
+}
+\references{
+https://www.dropbox.com/s/nc6968prgw8ne4w/bbox.pdf?dl=0
+}
diff --git a/man/colorbar.Rd b/man/colorbar.Rd
new file mode 100644
index 0000000..dc328e4
--- /dev/null
+++ b/man/colorbar.Rd
@@ -0,0 +1,44 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/helpers.R
+\name{colorbar}
+\alias{colorbar}
+\title{Modify the colorbar}
+\usage{
+colorbar(p, ..., limits = NULL, which = 1)
+}
+\arguments{
+\item{p}{a plotly object}
+
+\item{...}{arguments are documented here
+\url{https://plot.ly/r/reference/#scatter-marker-colorbar}.}
+
+\item{limits}{numeric vector of length 2. Set the extent of the colorbar scale.}
+
+\item{which}{colorbar to modify? Should only be relevant for subplots with
+multiple colorbars.}
+}
+\description{
+Modify the colorbar
+}
+\examples{
+
+p <- plot_ly(mtcars, x = ~wt, y = ~mpg, color = ~cyl)
+
+# pass any colorbar attribute --
+# https://plot.ly/r/reference/#scatter-marker-colorbar
+colorbar(p, len = 0.5)
+
+# Expand the limits of the colorbar
+colorbar(p, limits = c(0, 20))
+# values outside the colorbar limits are considered "missing"
+colorbar(p, limits = c(5, 6))
+
+# also works on colorbars generated via a z value
+corr <- cor(diamonds[vapply(diamonds, is.numeric, logical(1))])
+plot_ly(x = rownames(corr), y = colnames(corr), z = corr) \%>\%
+ add_heatmap() \%>\%
+ colorbar(limits = c(-1, 1))
+}
+\author{
+Carson Sievert
+}
diff --git a/man/config.Rd b/man/config.Rd
new file mode 100644
index 0000000..41b09bb
--- /dev/null
+++ b/man/config.Rd
@@ -0,0 +1,29 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/layout.R
+\name{config}
+\alias{config}
+\title{Set the default configuration for plotly}
+\usage{
+config(p, ..., collaborate = TRUE, cloud = FALSE)
+}
+\arguments{
+\item{p}{a plotly object}
+
+\item{...}{these arguments are documented at
+\url{https://github.com/plotly/plotly.js/blob/master/src/plot_api/plot_config.js}}
+
+\item{collaborate}{include the collaborate mode bar button (unique to the R pkg)?}
+
+\item{cloud}{include the send data to cloud button?}
+}
+\description{
+Set the default configuration for plotly
+}
+\examples{
+
+config(plot_ly(), displaylogo = FALSE, collaborate = FALSE)
+
+}
+\author{
+Carson Sievert
+}
diff --git a/man/embed_notebook.Rd b/man/embed_notebook.Rd
new file mode 100644
index 0000000..181cfef
--- /dev/null
+++ b/man/embed_notebook.Rd
@@ -0,0 +1,25 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/embed.R
+\name{embed_notebook}
+\alias{embed_notebook}
+\title{Embed a plot as an iframe into a Jupyter Notebook}
+\usage{
+embed_notebook(x, width = NULL, height = NULL, file = NULL)
+}
+\arguments{
+\item{x}{a plotly object}
+
+\item{width}{attribute of the iframe. If \code{NULL}, the width in
+\code{plot_ly} is used. If that is also \code{NULL}, '100\%' is the default.}
+
+\item{height}{attribute of the iframe. If \code{NULL}, the height in
+\code{plot_ly} is used. If that is also \code{NULL}, '400px' is the default.}
+
+\item{file}{deprecated.}
+}
+\description{
+Embed a plot as an iframe into a Jupyter Notebook
+}
+\author{
+Carson Sievert
+}
diff --git a/man/event_data.Rd b/man/event_data.Rd
new file mode 100644
index 0000000..f5c405d
--- /dev/null
+++ b/man/event_data.Rd
@@ -0,0 +1,31 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/shiny.R
+\name{event_data}
+\alias{event_data}
+\title{Access plotly user input event data in shiny}
+\usage{
+event_data(event = c("plotly_hover", "plotly_click", "plotly_selected",
+ "plotly_relayout"), source = "A",
+ session = shiny::getDefaultReactiveDomain())
+}
+\arguments{
+\item{event}{The type of plotly event. Currently 'plotly_hover',
+'plotly_click', 'plotly_selected', and 'plotly_relayout' are supported.}
+
+\item{source}{a character string of length 1. Match the value of this string
+with the source argument in \code{\link[=plot_ly]{plot_ly()}} to retrieve the
+event data corresponding to a specific plot (shiny apps can have multiple plots).}
+
+\item{session}{a shiny session object (the default should almost always be used).}
+}
+\description{
+This function must be called within a reactive shiny context.
+}
+\examples{
+\dontrun{
+plotly_example("shiny", "event_data")
+}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/export.Rd b/man/export.Rd
new file mode 100644
index 0000000..5962b3b
--- /dev/null
+++ b/man/export.Rd
@@ -0,0 +1,64 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/export.R
+\name{export}
+\alias{export}
+\title{Export a plotly graph to a static file}
+\usage{
+export(p = last_plot(), file = "plotly.png", selenium = NULL, ...)
+}
+\arguments{
+\item{p}{a plotly or ggplot object.}
+
+\item{file}{a filename. The file type is inferred from the file extension.
+Valid extensions include 'jpeg' | 'png' | 'webp' | 'svg' | 'pdf'}
+
+\item{selenium}{used only when \code{p} is a WebGL plot or the output
+format is 'webp' or 'svg'. Should be an object of class "rsClientServer"
+returned by \code{RSelenium::rsDriver} (see examples).}
+
+\item{...}{if \code{p} is non-WebGL and the output file format is
+jpeg/png/pdf arguments are passed along to \code{webshot::webshot()}.
+Otherwise, they are ignored.}
+}
+\description{
+Export a plotly graph to a static file
+}
+\details{
+For SVG plots, a screenshot is taken via \code{webshot::webshot()}.
+Since \code{phantomjs} (and hence \code{webshot}) does not support WebGL,
+the RSelenium package is used for exporting WebGL plots.
+}
+\examples{
+# The webshot package handles non-WebGL conversion to jpeg/png/pdf
+\dontrun{
+export(plot_ly(economics, x = ~date, y = ~pce))
+export(plot_ly(economics, x = ~date, y = ~pce), "plot.pdf")
+
+# svg/webp output or WebGL conversion can be done via RSelenium
+if (requireNamespace("RSelenium")) {
+ rD <- RSelenium::rsDriver(browser = "chrome")
+ export(
+ plot_ly(economics, x = ~date, y = ~pce), "plot.svg", rD
+ )
+ export(
+ plot_ly(economics, x = ~date, y = ~pce, z = ~pop), "yay.svg", rD
+ )
+}
+
+# If you can't get a selenium server running, another option is to
+# use Plotly.downloadImage() via htmlwidgets::onRender()...
+# Downloading images won't work inside RStudio, but you can set the viewer
+# option to NULL to prompt your default web browser
+options(viewer = NULL)
+plot_ly(economics, x = ~date, y = ~pce, z = ~pop) \%>\%
+ htmlwidgets::onRender(
+ "function(el, x) {
+ var gd = document.getElementById(el.id);
+ Plotly.downloadImage(gd, {format: 'png', width: 600, height: 400, filename: 'plot'});
+ }"
+ )
+}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/geom2trace.Rd b/man/geom2trace.Rd
new file mode 100644
index 0000000..a3fd2c1
--- /dev/null
+++ b/man/geom2trace.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/layers2traces.R
+\name{geom2trace}
+\alias{geom2trace}
+\title{Convert a "basic" geoms to a plotly.js trace.}
+\usage{
+geom2trace(data, params, p)
+}
+\arguments{
+\item{data}{the data returned by \code{plotly::to_basic}.}
+
+\item{params}{parameters for the geom, statistic, and 'constant' aesthetics}
+
+\item{p}{a ggplot2 object (the conversion may depend on scales, for instance).}
+}
+\description{
+This function makes it possible to convert ggplot2 geoms that
+are not included with ggplot2 itself. Users shouldn't need to use
+this function. It exists purely to allow other package authors to write
+their own conversion method(s).
+}
diff --git a/man/get_figure.Rd b/man/get_figure.Rd
new file mode 100644
index 0000000..c48f46f
--- /dev/null
+++ b/man/get_figure.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/deprecated.R
+\name{get_figure}
+\alias{get_figure}
+\title{Request a figure object}
+\usage{
+get_figure(username, id)
+}
+\arguments{
+\item{username}{corresponding username for the figure.}
+
+\item{id}{of the Plotly figure.}
+}
+\description{
+Deprecated: see \code{\link[=api_download_plot]{api_download_plot()}}.
+}
diff --git a/man/get_l.Rd b/man/get_l.Rd
new file mode 100644
index 0000000..d4aeaa9
--- /dev/null
+++ b/man/get_l.Rd
@@ -0,0 +1,14 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/sf.R
+\name{get_l}
+\alias{get_l}
+\title{Obtain number of points comprising a geometry}
+\usage{
+get_l(g)
+}
+\arguments{
+\item{g}{an sf geometry}
+}
+\description{
+Exported for internal reasons. Not intended for general use.
+}
diff --git a/man/get_x.Rd b/man/get_x.Rd
new file mode 100644
index 0000000..f980cee
--- /dev/null
+++ b/man/get_x.Rd
@@ -0,0 +1,14 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/sf.R
+\name{get_x}
+\alias{get_x}
+\title{Obtain x coordinates of sf geometry/geometries}
+\usage{
+get_x(g)
+}
+\arguments{
+\item{g}{an sf geometry}
+}
+\description{
+Exported for internal reasons. Not intended for general use.
+}
diff --git a/man/get_y.Rd b/man/get_y.Rd
new file mode 100644
index 0000000..e29a5b8
--- /dev/null
+++ b/man/get_y.Rd
@@ -0,0 +1,14 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/sf.R
+\name{get_y}
+\alias{get_y}
+\title{Obtain y coordinates of sf geometry/geometries}
+\usage{
+get_y(g)
+}
+\arguments{
+\item{g}{an sf geometry}
+}
+\description{
+Exported for internal reasons. Not intended for general use.
+}
diff --git a/man/gg2list.Rd b/man/gg2list.Rd
new file mode 100644
index 0000000..4ef3af3
--- /dev/null
+++ b/man/gg2list.Rd
@@ -0,0 +1,42 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ggplotly.R
+\name{gg2list}
+\alias{gg2list}
+\title{Convert a ggplot to a list.}
+\usage{
+gg2list(p, width = NULL, height = NULL, tooltip = "all",
+ dynamicTicks = FALSE, layerData = 1, originalData = TRUE,
+ source = "A", ...)
+}
+\arguments{
+\item{p}{ggplot2 plot.}
+
+\item{width}{Width of the plot in pixels (optional, defaults to automatic sizing).}
+
+\item{height}{Height of the plot in pixels (optional, defaults to automatic sizing).}
+
+\item{tooltip}{a character vector specifying which aesthetic tooltips to show in the
+tooltip. The default, "all", means show all the aesthetic tooltips
+(including the unofficial "text" aesthetic).}
+
+\item{dynamicTicks}{accepts the following values: \code{FALSE}, \code{TRUE}, \code{"x"}, or \code{"y"}.
+Dynamic ticks are useful for updating ticks in response to zoom/pan/filter
+interactions; however, there is no guarantee they reproduce axis tick text
+as they would appear in the static ggplot2 image.}
+
+\item{layerData}{data from which layer should be returned?}
+
+\item{originalData}{should the "original" or "scaled" data be returned?}
+
+\item{source}{a character string of length 1. Match the value of this string
+with the source argument in \code{\link[=event_data]{event_data()}} to retrieve the
+event data corresponding to a specific plot (shiny apps can have multiple plots).}
+
+\item{...}{currently not used}
+}
+\value{
+a 'built' plotly object (list with names "data" and "layout").
+}
+\description{
+Convert a ggplot to a list.
+}
diff --git a/man/ggplotly.Rd b/man/ggplotly.Rd
new file mode 100644
index 0000000..752d8d8
--- /dev/null
+++ b/man/ggplotly.Rd
@@ -0,0 +1,90 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/ggplotly.R
+\name{ggplotly}
+\alias{ggplotly}
+\title{Convert ggplot2 to plotly}
+\usage{
+ggplotly(p = ggplot2::last_plot(), width = NULL, height = NULL,
+ tooltip = "all", dynamicTicks = FALSE, layerData = 1,
+ originalData = TRUE, source = "A", ...)
+}
+\arguments{
+\item{p}{a ggplot object.}
+
+\item{width}{Width of the plot in pixels (optional, defaults to automatic sizing).}
+
+\item{height}{Height of the plot in pixels (optional, defaults to automatic sizing).}
+
+\item{tooltip}{a character vector specifying which aesthetic mappings to show
+in the tooltip. The default, "all", means show all the aesthetic mappings
+(including the unofficial "text" aesthetic). The order of variables here will
+also control the order they appear. For example, use
+\code{tooltip = c("y", "x", "colour")} if you want y first, x second, and
+colour last.}
+
+\item{dynamicTicks}{should plotly.js dynamically generate axis tick labels?
+Dynamic ticks are useful for updating ticks in response to zoom/pan
+interactions; however, they can not always reproduce labels as they
+would appear in the static ggplot2 image.}
+
+\item{layerData}{data from which layer should be returned?}
+
+\item{originalData}{should the "original" or "scaled" data be returned?}
+
+\item{source}{a character string of length 1. Match the value of this string
+with the source argument in \code{\link[=event_data]{event_data()}} to retrieve the
+event data corresponding to a specific plot (shiny apps can have multiple plots).}
+
+\item{...}{arguments passed onto methods.}
+}
+\description{
+This function converts a \code{\link[ggplot2:ggplot]{ggplot2::ggplot()}} object to a
+plotly object.
+}
+\details{
+Conversion of relative sizes depends on the size of the current
+graphics device (if no device is open, width/height of a new (off-screen)
+device defaults to 640/480). In other words, \code{height} and
+\code{width} must be specified at runtime to ensure sizing is correct.
+}
+\examples{
+\dontrun{
+# simple example
+ggiris <- qplot(Petal.Width, Sepal.Length, data = iris, color = Species)
+ggplotly(ggiris)
+
+data(canada.cities, package = "maps")
+viz <- ggplot(canada.cities, aes(long, lat)) +
+ borders(regions = "canada") +
+ coord_equal() +
+ geom_point(aes(text = name, size = pop), colour = "red", alpha = 1/2)
+ggplotly(viz, tooltip = c("text", "size"))
+
+
+# highlighting lines
+demo("highlight-ggplotly", package = "plotly")
+
+# client-side linked brushing
+library(crosstalk)
+d <- SharedData$new(mtcars)
+subplot(
+ qplot(data = d, x = mpg, y = wt),
+ qplot(data = d, x = mpg, y = vs)
+)
+
+# client-side linked brushing in a scatterplot matrix
+SharedData$new(iris) \%>\%
+ GGally::ggpairs(aes(colour = Species), columns = 1:4) \%>\%
+ ggplotly(tooltip = c("x", "y", "colour"))
+}
+
+}
+\references{
+\url{https://plot.ly/ggplot2}
+}
+\seealso{
+\code{\link[=plot_ly]{plot_ly()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/group2NA.Rd b/man/group2NA.Rd
new file mode 100644
index 0000000..e219fb0
--- /dev/null
+++ b/man/group2NA.Rd
@@ -0,0 +1,58 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/group2NA.R
+\name{group2NA}
+\alias{group2NA}
+\title{Separate groups with missing values}
+\usage{
+group2NA(data, groupNames = "group", nested = NULL, ordered = NULL,
+ retrace.first = inherits(data, "GeomPolygon"))
+}
+\arguments{
+\item{data}{a data frame.}
+
+\item{groupNames}{character vector of grouping variable(s)}
+
+\item{nested}{other variables that group should be nested
+(i.e., ordered) within.}
+
+\item{ordered}{a variable to arrange by (within nested & groupNames). This
+is useful primarily for ordering by x}
+
+\item{retrace.first}{should the first row of each group be appended to the
+last row? This is useful for enclosing polygons with lines.}
+}
+\value{
+a data.frame with rows ordered by: \code{nested},
+then \code{groupNames}, then \code{ordered}. As long as \code{groupNames}
+contains valid variable names, new rows will also be inserted to separate
+the groups.
+}
+\description{
+This function is used internally by plotly, but may also be useful to some
+power users. The details section explains when and why this function is useful.
+}
+\details{
+If a group of scatter traces share the same non-positional characteristics
+(i.e., color, fill, etc), it is more efficient to draw them as a single trace
+with missing values that separate the groups (instead of multiple traces),
+In this case, one should also take care to make sure
+\href{https://plot.ly/r/reference/#scatter-connectgaps}{connectgaps}
+is set to \code{FALSE}.
+}
+\examples{
+
+# note the insertion of new rows with missing values
+group2NA(mtcars, "vs", "cyl")
+
+# need to group lines by city somehow!
+plot_ly(txhousing, x = ~date, y = ~median) \%>\% add_lines()
+
+# instead of using group_by(), you could use group2NA()
+tx <- group2NA(txhousing, "city")
+plot_ly(tx, x = ~date, y = ~median) \%>\% add_lines()
+
+# add_lines() will ensure paths are sorted by x, but this is equivalent
+tx <- group2NA(txhousing, "city", ordered = "date")
+plot_ly(tx, x = ~date, y = ~median) \%>\% add_paths()
+
+}
diff --git a/man/hide_colorbar.Rd b/man/hide_colorbar.Rd
new file mode 100644
index 0000000..1ac269c
--- /dev/null
+++ b/man/hide_colorbar.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/helpers.R
+\name{hide_colorbar}
+\alias{hide_colorbar}
+\title{Hide color bar(s)}
+\usage{
+hide_colorbar(p)
+}
+\arguments{
+\item{p}{a plotly object.}
+}
+\description{
+Hide color bar(s)
+}
+\examples{
+
+p <- plot_ly(mtcars, x = ~wt, y = ~cyl, color = ~cyl)
+hide_colorbar(p)
+
+}
+\seealso{
+\code{\link[=hide_legend]{hide_legend()}}
+}
diff --git a/man/hide_guides.Rd b/man/hide_guides.Rd
new file mode 100644
index 0000000..c76d88f
--- /dev/null
+++ b/man/hide_guides.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/helpers.R
+\name{hide_guides}
+\alias{hide_guides}
+\title{Hide guides (legends and colorbars)}
+\usage{
+hide_guides(p)
+}
+\arguments{
+\item{p}{a plotly object.}
+}
+\description{
+Hide guides (legends and colorbars)
+}
+\seealso{
+\code{\link[=hide_legend]{hide_legend()}}, \code{\link[=hide_colorbar]{hide_colorbar()}}
+}
diff --git a/man/hide_legend.Rd b/man/hide_legend.Rd
new file mode 100644
index 0000000..e9e699e
--- /dev/null
+++ b/man/hide_legend.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/helpers.R
+\name{hide_legend}
+\alias{hide_legend}
+\title{Hide legend}
+\usage{
+hide_legend(p)
+}
+\arguments{
+\item{p}{a plotly object.}
+}
+\description{
+Hide legend
+}
+\examples{
+
+p <- plot_ly(mtcars, x = ~wt, y = ~cyl, color = ~factor(cyl))
+hide_legend(p)
+}
+\seealso{
+\code{\link[=hide_colorbar]{hide_colorbar()}}
+}
diff --git a/man/highlight.Rd b/man/highlight.Rd
new file mode 100644
index 0000000..fa1ba11
--- /dev/null
+++ b/man/highlight.Rd
@@ -0,0 +1,107 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/highlight.R
+\name{highlight}
+\alias{highlight}
+\title{Query graphical elements in multiple linked views}
+\usage{
+highlight(p, on = "plotly_click", off, persistent = getOption("persistent",
+ FALSE), dynamic = FALSE, color = NULL, selectize = FALSE,
+ defaultValues = NULL, opacityDim = getOption("opacityDim", 0.2),
+ selected = attrs_selected(), ...)
+}
+\arguments{
+\item{p}{a plotly visualization.}
+
+\item{on}{turn on a selection on which event(s)? To disable on events
+altogether, use \code{NULL}. Currently the following are supported:
+\itemize{
+\item \code{'plotly_click'}
+\item \code{'plotly_hover'}
+\item \code{'plotly_selected'}: triggered through rectangular
+(layout.dragmode = 'select') or lasso (layout.dragmode = 'lasso') brush.
+Currently only works for scatter traces with mode 'markers'.
+}}
+
+\item{off}{turn off a selection on which event(s)? To disable off
+events altogether, use \code{NULL}. Currently the following are supported:
+\itemize{
+\item \code{'plotly_doubleclick'}: triggered on a double mouse click while
+(layout.dragmode = 'zoom') or (layout.dragmode = 'pan')
+\item \code{'plotly_deselect'}: triggered on a double mouse click while
+(layout.dragmode = 'select') or (layout.dragmode = 'lasso')
+\item \code{'plotly_relayout'}: triggered whenever axes are rescaled
+(i.e., clicking the home button in the modebar) or whenever the height/width
+of the plot changes.
+}}
+
+\item{persistent}{should selections persist (i.e., accumulate)?}
+
+\item{dynamic}{should a widget for changing selection colors be included?}
+
+\item{color}{character string of color(s) to use for
+highlighting selections. See \code{\link[=toRGB]{toRGB()}} for valid color
+specifications. If \code{NULL} (the default), the color of selected marks
+are not altered.}
+
+\item{selectize}{provide a selectize.js widget for selecting keys? Note that
+the label used for this widget derives from the groupName of the SharedData object.}
+
+\item{defaultValues}{a vector of values for setting a "default selection".
+These values should match the key attribute.}
+
+\item{opacityDim}{a number between 0 and 1 used to reduce the
+opacity of non-selected traces (by multiplying with the existing opacity).}
+
+\item{selected}{attributes of the selection, see \code{\link[=attrs_selected]{attrs_selected()}}.}
+
+\item{...}{currently not supported.}
+}
+\description{
+This function sets a variety of options for brushing (i.e., highlighting)
+multiple plots. These options are primarily designed for linking
+multiple plotly graphs, and may not behave as expected when linking
+plotly to another htmlwidget package via crosstalk. In some cases,
+other htmlwidgets will respect these options, such as persistent selection
+in leaflet (see \code{demo("highlight-leaflet", package = "plotly")}).
+}
+\examples{
+
+# These examples are designed to show you how to highlight/brush a *single*
+# view. For examples of multiple linked views, see `demo(package = "plotly")`
+
+
+library(crosstalk)
+d <- SharedData$new(txhousing, ~city)
+p <- ggplot(d, aes(date, median, group = city)) + geom_line()
+gg <- ggplotly(p, tooltip = "city")
+highlight(gg, persistent = TRUE, dynamic = TRUE)
+
+# supply custom colors to the brush
+cols <- toRGB(RColorBrewer::brewer.pal(3, "Dark2"), 0.5)
+highlight(
+ gg, on = "plotly_hover", color = cols, persistent = TRUE, dynamic = TRUE
+)
+
+# Use attrs_selected() for complete control over the selection appearance
+# note any relevant colors you specify here should override the color argument
+s <- attrs_selected(
+ showlegend = TRUE,
+ mode = "lines+markers",
+ marker = list(symbol = "x")
+)
+
+highlight(
+ layout(gg, showlegend = TRUE),
+ selected = s, persistent = TRUE
+)
+
+}
+\references{
+\url{https://cpsievert.github.io/plotly_book/linking-views-without-shiny.html}
+}
+\seealso{
+\code{\link[=attrs_selected]{attrs_selected()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/hobbs.Rd b/man/hobbs.Rd
new file mode 100644
index 0000000..1cebf90
--- /dev/null
+++ b/man/hobbs.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/data.R
+\docType{data}
+\name{hobbs}
+\alias{hobbs}
+\title{Hobbs data}
+\format{A data frame with three variables: \code{r}, \code{t},
+\code{nms}.}
+\usage{
+hobbs
+}
+\description{
+Description TBD.
+}
+\keyword{datasets}
diff --git a/man/knit_print.api_grid.Rd b/man/knit_print.api_grid.Rd
new file mode 100644
index 0000000..f773f17
--- /dev/null
+++ b/man/knit_print.api_grid.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{knit_print.api_grid}
+\alias{knit_print.api_grid}
+\title{Embed a plotly grid as an iframe in a knitr doc}
+\usage{
+knit_print.api_grid(x, options, ...)
+}
+\arguments{
+\item{x}{a plotly figure object}
+
+\item{options}{knitr options.}
+
+\item{...}{placeholder.}
+}
+\description{
+Embed a plotly grid as an iframe in a knitr doc
+}
+\references{
+https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd
+}
diff --git a/man/knit_print.api_grid_local.Rd b/man/knit_print.api_grid_local.Rd
new file mode 100644
index 0000000..7df00e2
--- /dev/null
+++ b/man/knit_print.api_grid_local.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{knit_print.api_grid_local}
+\alias{knit_print.api_grid_local}
+\title{Embed a plotly grid as an iframe in a knitr doc}
+\usage{
+knit_print.api_grid_local(x, options, ...)
+}
+\arguments{
+\item{x}{a plotly figure object}
+
+\item{options}{knitr options.}
+
+\item{...}{placeholder.}
+}
+\description{
+Embed a plotly grid as an iframe in a knitr doc
+}
+\references{
+https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd
+}
diff --git a/man/knit_print.api_plot.Rd b/man/knit_print.api_plot.Rd
new file mode 100644
index 0000000..c86a13a
--- /dev/null
+++ b/man/knit_print.api_plot.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{knit_print.api_plot}
+\alias{knit_print.api_plot}
+\title{Embed a plotly figure as an iframe in a knitr doc}
+\usage{
+knit_print.api_plot(x, options, ...)
+}
+\arguments{
+\item{x}{a plotly figure object}
+
+\item{options}{knitr options.}
+
+\item{...}{placeholder.}
+}
+\description{
+Embed a plotly figure as an iframe in a knitr doc
+}
+\references{
+https://github.com/yihui/knitr/blob/master/vignettes/knit_print.Rmd
+}
diff --git a/man/last_plot.Rd b/man/last_plot.Rd
new file mode 100644
index 0000000..640b044
--- /dev/null
+++ b/man/last_plot.Rd
@@ -0,0 +1,14 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/last_plot.R
+\name{last_plot}
+\alias{last_plot}
+\title{Retrieve the last plot to be modified or created.}
+\usage{
+last_plot()
+}
+\description{
+Retrieve the last plot to be modified or created.
+}
+\seealso{
+\code{\link[ggplot2:last_plot]{ggplot2::last_plot()}}
+}
diff --git a/man/layout.Rd b/man/layout.Rd
new file mode 100644
index 0000000..74b69ad
--- /dev/null
+++ b/man/layout.Rd
@@ -0,0 +1,23 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/layout.R
+\name{layout}
+\alias{layout}
+\title{Modify the layout of a plotly visualization}
+\usage{
+layout(p, ..., data = NULL)
+}
+\arguments{
+\item{p}{A plotly object.}
+
+\item{...}{Arguments to the layout object. For documentation,
+see \url{https://plot.ly/r/reference/#Layout_and_layout_style_objects}}
+
+\item{data}{A data frame to associate with this layout (optional). If not
+provided, arguments are evaluated using the data frame in \code{\link[=plot_ly]{plot_ly()}}.}
+}
+\description{
+Modify the layout of a plotly visualization
+}
+\author{
+Carson Sievert
+}
diff --git a/man/mic.Rd b/man/mic.Rd
new file mode 100644
index 0000000..274d9b2
--- /dev/null
+++ b/man/mic.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/data.R
+\docType{data}
+\name{mic}
+\alias{mic}
+\title{Mic data}
+\format{A data frame with three variables: \code{r}, \code{t},
+\code{nms}.}
+\usage{
+mic
+}
+\description{
+Description TBD.
+}
+\keyword{datasets}
diff --git a/man/offline.Rd b/man/offline.Rd
new file mode 100644
index 0000000..70ff93c
--- /dev/null
+++ b/man/offline.Rd
@@ -0,0 +1,31 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/deprecated.R
+\name{offline}
+\alias{offline}
+\title{Plotly Offline}
+\usage{
+offline(p, height, width, out_dir, open_browser)
+}
+\arguments{
+\item{p}{a plotly object}
+
+\item{height}{A valid CSS unit. (like "100\%", "600px", "auto") or a number,
+which will be coerced to a string and have "px" appended.}
+
+\item{width}{A valid CSS unit. (like "100\%", "600px", "auto") or a number,
+which will be coerced to a string and have "px" appended.}
+
+\item{out_dir}{a directory to place the visualization.
+If \code{NULL}, a temporary directory is used when the offline object is printed.}
+
+\item{open_browser}{open the visualization after creating it?}
+}
+\value{
+a plotly object of class "offline"
+}
+\description{
+Deprecated in version 2.0 (offline plots are now the default)
+}
+\author{
+Carson Sievert
+}
diff --git a/man/plot_dendro.Rd b/man/plot_dendro.Rd
new file mode 100644
index 0000000..af80b0d
--- /dev/null
+++ b/man/plot_dendro.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly.R
+\name{plot_dendro}
+\alias{plot_dendro}
+\title{Plot an interactive dendrogram}
+\usage{
+plot_dendro(d, set = "A", xmin = -50, height = 500, width = 500, ...)
+}
+\arguments{
+\item{d}{a dendrogram object}
+
+\item{set}{defines a crosstalk group}
+
+\item{xmin}{minimum of the range of the x-scale}
+
+\item{height}{height}
+
+\item{width}{width}
+
+\item{...}{arguments supplied to \code{\link[=subplot]{subplot()}}}
+}
+\description{
+This function takes advantage of nested key selections to implement an
+interactive dendrogram. Selecting a node selects all the labels (i.e. leafs)
+under that node.
+}
+\examples{
+
+hc <- hclust(dist(USArrests), "ave")
+dend1 <- as.dendrogram(hc)
+plot_dendro(dend1, height = 600) \%>\%
+ hide_legend() \%>\%
+ highlight(persistent = TRUE, dynamic = TRUE)
+
+}
+\seealso{
+\code{\link[=plot_ly]{plot_ly()}}, \code{\link[=plot_mapbox]{plot_mapbox()}}, \code{\link[=ggplotly]{ggplotly()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/plot_geo.Rd b/man/plot_geo.Rd
new file mode 100644
index 0000000..5669676
--- /dev/null
+++ b/man/plot_geo.Rd
@@ -0,0 +1,33 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly.R
+\name{plot_geo}
+\alias{plot_geo}
+\title{Initiate a plotly-geo object}
+\usage{
+plot_geo(data = data.frame(), ...)
+}
+\arguments{
+\item{data}{A data frame (optional).}
+
+\item{...}{arguments passed along to \code{\link[=plot_ly]{plot_ly()}}.}
+}
+\description{
+Use this function instead of \code{\link[=plot_ly]{plot_ly()}} to initialize
+a plotly-geo object. This enforces the entire plot so use
+the scattergeo trace type, and enables higher level geometries
+like \code{\link[=add_polygons]{add_polygons()}} to work
+}
+\examples{
+
+map_data("world", "canada") \%>\%
+ group_by(group) \%>\%
+ plot_geo(x = ~long, y = ~lat) \%>\%
+ add_markers(size = I(1))
+
+}
+\seealso{
+\code{\link[=plot_ly]{plot_ly()}}, \code{\link[=plot_mapbox]{plot_mapbox()}}, \code{\link[=ggplotly]{ggplotly()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/plot_ly.Rd b/man/plot_ly.Rd
new file mode 100644
index 0000000..cc1b67b
--- /dev/null
+++ b/man/plot_ly.Rd
@@ -0,0 +1,125 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly.R
+\name{plot_ly}
+\alias{plot_ly}
+\title{Initiate a plotly visualization}
+\usage{
+plot_ly(data = data.frame(), ..., type = NULL, color, colors = NULL,
+ alpha = 1, symbol, symbols = NULL, size, sizes = c(10, 100), linetype,
+ linetypes = NULL, split, frame, width = NULL, height = NULL,
+ source = "A")
+}
+\arguments{
+\item{data}{A data frame (optional) or \link[crosstalk:SharedData]{crosstalk::SharedData} object.}
+
+\item{...}{These arguments are documented at \url{https://plot.ly/r/reference/}
+Note that acceptable arguments depend on the value of \code{type}.}
+
+\item{type}{A character string describing the type of trace.}
+
+\item{color}{A formula containing a name or expression.
+Values are scaled and mapped to color codes based on the value of
+\code{colors} and \code{alpha}. To avoid scaling, wrap with \code{\link[=I]{I()}},
+and provide value(s) that can be converted to rgb color codes by
+\code{\link[grDevices:col2rgb]{grDevices::col2rgb()}}.}
+
+\item{colors}{Either a colorbrewer2.org palette name (e.g. "YlOrRd" or "Blues"),
+or a vector of colors to interpolate in hexadecimal "#RRGGBB" format,
+or a color interpolation function like \code{colorRamp()}.}
+
+\item{alpha}{A number between 0 and 1 specifying the alpha channel applied to color.}
+
+\item{symbol}{A formula containing a name or expression.
+Values are scaled and mapped to symbols based on the value of \code{symbols}.
+To avoid scaling, wrap with \code{\link[=I]{I()}}, and provide valid
+\code{\link[=pch]{pch()}} values and/or valid plotly symbol(s) as a string}
+
+\item{symbols}{A character vector of symbol types.
+Either valid \link{pch} or plotly symbol codes may be supplied.}
+
+\item{size}{A formula containing a name or expression yielding a numeric vector.
+Values are scaled according to the range specified in \code{sizes}.}
+
+\item{sizes}{A numeric vector of length 2 used to scale sizes to pixels.}
+
+\item{linetype}{A formula containing a name or expression.
+Values are scaled and mapped to linetypes based on the value of
+\code{linetypes}. To avoid scaling, wrap with \code{\link[=I]{I()}}.}
+
+\item{linetypes}{A character vector of line types.
+Either valid \link{par} (lty) or plotly dash codes may be supplied.}
+
+\item{split}{A formula containing a name or expression. Similar to
+\code{\link[=group_by]{group_by()}}, but ensures at least one trace for each unique
+value. This replaces the functionality of the (now deprecated)
+\code{group} argument.}
+
+\item{frame}{A formula containing a name or expression. The resulting value
+is used to split data into frames, and then animated.}
+
+\item{width}{Width in pixels (optional, defaults to automatic sizing).}
+
+\item{height}{Height in pixels (optional, defaults to automatic sizing).}
+
+\item{source}{a character string of length 1. Match the value of this string
+with the source argument in \code{\link[=event_data]{event_data()}} to retrieve the
+event data corresponding to a specific plot (shiny apps can have multiple plots).}
+}
+\description{
+Transform data into a plotly visualization.
+}
+\details{
+There are a number of "visual properties" that aren't included in the official
+Reference section (see below).
+}
+\examples{
+\dontrun{
+
+# plot_ly() tries to create a sensible plot based on the information you
+# give it. If you don't provide a trace type, plot_ly() will infer one.
+plot_ly(economics, x = ~pop)
+plot_ly(economics, x = ~date, y = ~pop)
+# plot_ly() doesn't require data frame(s), which allows one to take
+# advantage of trace type(s) designed specifically for numeric matrices
+plot_ly(z = ~volcano)
+plot_ly(z = ~volcano, type = "surface")
+
+# plotly has a functional interface: every plotly function takes a plotly
+# object as it's first input argument and returns a modified plotly object
+add_lines(plot_ly(economics, x = ~date, y = ~unemploy/pop))
+
+# To make code more readable, plotly imports the pipe operator from magrittr
+economics \%>\% plot_ly(x = ~date, y = ~unemploy/pop) \%>\% add_lines()
+
+# Attributes defined via plot_ly() set 'global' attributes that
+# are carried onto subsequent traces, but those may be over-written
+plot_ly(economics, x = ~date, color = I("black")) \%>\%
+ add_lines(y = ~uempmed) \%>\%
+ add_lines(y = ~psavert, color = I("red"))
+
+# Attributes are documented in the figure reference -> https://plot.ly/r/reference
+# You might notice plot_ly() has named arguments that aren't in this figure
+# reference. These arguments make it easier to map abstract data values to
+# visual attributes.
+p <- plot_ly(iris, x = ~Sepal.Width, y = ~Sepal.Length)
+add_markers(p, color = ~Petal.Length, size = ~Petal.Length)
+add_markers(p, color = ~Species)
+add_markers(p, color = ~Species, colors = "Set1")
+add_markers(p, symbol = ~Species)
+add_paths(p, linetype = ~Species)
+
+}
+
+}
+\seealso{
+\itemize{
+\item For initializing a plotly-geo object: \code{\link[=plot_geo]{plot_geo()}}.
+\item For initializing a plotly-mapbox object: \code{\link[=plot_mapbox]{plot_mapbox()}}.
+\item For translating a ggplot2 object to a plotly object: \code{\link[=ggplotly]{ggplotly()}}.
+\item For modifying any plotly object: \code{\link[=layout]{layout()}}, \code{\link[=add_trace]{add_trace()}}, \code{\link[=style]{style()}}
+\item
+}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/plot_mapbox.Rd b/man/plot_mapbox.Rd
new file mode 100644
index 0000000..23ad16f
--- /dev/null
+++ b/man/plot_mapbox.Rd
@@ -0,0 +1,42 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly.R
+\name{plot_mapbox}
+\alias{plot_mapbox}
+\title{Initiate a plotly-mapbox object}
+\usage{
+plot_mapbox(data = data.frame(), ...)
+}
+\arguments{
+\item{data}{A data frame (optional).}
+
+\item{...}{arguments passed along to \code{\link[=plot_ly]{plot_ly()}}. They should be
+valid scattermapbox attributes - \url{https://plot.ly/r/reference/#scattermapbox}.
+Note that x/y can also be used in place of lat/lon.}
+}
+\description{
+Use this function instead of \code{\link[=plot_ly]{plot_ly()}} to initialize
+a plotly-mapbox object. This enforces the entire plot so use
+the scattermapbox trace type, and enables higher level geometries
+like \code{\link[=add_polygons]{add_polygons()}} to work
+}
+\examples{
+\dontrun{
+
+map_data("world", "canada") \%>\%
+ group_by(group) \%>\%
+ plot_mapbox(x = ~long, y = ~lat) \%>\%
+ add_polygons() \%>\%
+ layout(
+ mapbox = list(
+ center = list(lat = ~median(lat), lon = ~median(long))
+ )
+ )
+}
+
+}
+\seealso{
+\code{\link[=plot_ly]{plot_ly()}}, \code{\link[=plot_geo]{plot_geo()}}, \code{\link[=ggplotly]{ggplotly()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/plotly-shiny.Rd b/man/plotly-shiny.Rd
new file mode 100644
index 0000000..e610c54
--- /dev/null
+++ b/man/plotly-shiny.Rd
@@ -0,0 +1,33 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/shiny.R
+\name{plotly-shiny}
+\alias{plotly-shiny}
+\alias{plotlyOutput}
+\alias{renderPlotly}
+\title{Shiny bindings for plotly}
+\usage{
+plotlyOutput(outputId, width = "100\%", height = "400px", inline = FALSE)
+
+renderPlotly(expr, env = parent.frame(), quoted = FALSE)
+}
+\arguments{
+\item{outputId}{output variable to read from}
+
+\item{width, height}{Must be a valid CSS unit (like \code{"100\%"},
+\code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
+string and have \code{"px"} appended.}
+
+\item{inline}{use an inline (\code{span()}) or block container
+(\code{div()}) for the output}
+
+\item{expr}{An expression that generates a plotly}
+
+\item{env}{The environment in which to evaluate \code{expr}.}
+
+\item{quoted}{Is \code{expr} a quoted expression (with \code{quote()})? This
+is useful if you want to save an expression in a variable.}
+}
+\description{
+Output and render functions for using plotly within Shiny
+applications and interactive Rmd documents.
+}
diff --git a/man/plotly.Rd b/man/plotly.Rd
new file mode 100644
index 0000000..1343ed7
--- /dev/null
+++ b/man/plotly.Rd
@@ -0,0 +1,25 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/deprecated.R
+\name{plotly}
+\alias{plotly}
+\title{Main interface to plotly}
+\usage{
+plotly(username, key)
+}
+\arguments{
+\item{username}{plotly username}
+
+\item{key}{plotly API key}
+}
+\description{
+This function is now deprecated. It used to provide a way to store plotly
+account credentials, but that can now be done with environment variables.
+For more details and examples, see \url{https://plot.ly/r/getting-started/}.
+
+If you're here looking for an intro/overview of the package, see the
+\href{https://github.com/ropensci/plotly/#getting-started}{readme}
+}
+\seealso{
+\code{\link[=ggplotly]{ggplotly()}}, \code{\link[=plot_ly]{plot_ly()}}, \code{\link[=signup]{signup()}}
+}
+\keyword{internal}
diff --git a/man/plotlyProxy.Rd b/man/plotlyProxy.Rd
new file mode 100644
index 0000000..a044ba6
--- /dev/null
+++ b/man/plotlyProxy.Rd
@@ -0,0 +1,43 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/proxy.R
+\name{plotlyProxy}
+\alias{plotlyProxy}
+\alias{plotlyProxyInvoke}
+\title{Modify a plotly object inside a shiny app}
+\usage{
+plotlyProxy(outputId, session = shiny::getDefaultReactiveDomain(),
+ deferUntilFlush = TRUE)
+
+plotlyProxyInvoke(p, method, ...)
+}
+\arguments{
+\item{outputId}{single-element character vector indicating the output ID
+map to modify (if invoked from a Shiny module, the namespace will be added
+automatically)}
+
+\item{session}{the Shiny session object to which the map belongs; usually the
+default value will suffice.}
+
+\item{deferUntilFlush}{indicates whether actions performed against this
+instance should be carried out right away, or whether they should be held
+until after the next time all of the outputs are updated.}
+
+\item{p}{a plotly proxy object (created with \code{plotlyProxy})}
+
+\item{method}{a plotlyjs method to invoke. For a list of options,
+visit the \href{https://plot.ly/javascript/plotlyjs-function-reference}{plotlyjs function reference}}
+
+\item{...}{unnamed arguments passed onto the plotly.js method}
+}
+\description{
+Modify a plotly object inside a shiny app
+}
+\examples{
+
+
+if (require("shiny") && interactive()) {
+ plotly_example("shiny", "proxy_relayout")
+ plotly_example("shiny", "proxy_mapbox")
+}
+
+}
diff --git a/man/plotly_IMAGE.Rd b/man/plotly_IMAGE.Rd
new file mode 100644
index 0000000..c7cd66d
--- /dev/null
+++ b/man/plotly_IMAGE.Rd
@@ -0,0 +1,38 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly_IMAGE.R
+\name{plotly_IMAGE}
+\alias{plotly_IMAGE}
+\title{Create a static image}
+\usage{
+plotly_IMAGE(x, width = 1000, height = 500, format = "png", scale = 1,
+ out_file, ...)
+}
+\arguments{
+\item{x}{either a plotly object or a list.}
+
+\item{width}{Image width in pixels}
+
+\item{height}{Image height in pixels}
+
+\item{format}{The desired image format 'png', 'jpeg', 'svg', 'pdf', 'eps', or 'webp'}
+
+\item{scale}{Both png and jpeg formats will be scaled beyond the specified width and height by this number.}
+
+\item{out_file}{A filename for writing the image to a file.}
+
+\item{...}{arguments passed onto \code{httr::POST}}
+}
+\description{
+The images endpoint turns a plot (which may be given in multiple forms)
+into an image of the desired format.
+}
+\examples{
+\dontrun{
+p <- plot_ly(x = 1:10)
+Png <- plotly_IMAGE(p, out_file = "plotly-test-image.png")
+Jpeg <- plotly_IMAGE(p, format = "jpeg", out_file = "plotly-test-image.jpeg")
+Svg <- plotly_IMAGE(p, format = "svg", out_file = "plotly-test-image.svg")
+Pdf <- plotly_IMAGE(p, format = "pdf", out_file = "plotly-test-image.pdf")
+}
+
+}
diff --git a/man/plotly_POST.Rd b/man/plotly_POST.Rd
new file mode 100644
index 0000000..c1d9755
--- /dev/null
+++ b/man/plotly_POST.Rd
@@ -0,0 +1,43 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/deprecated.R
+\name{plotly_POST}
+\alias{plotly_POST}
+\title{Create/Modify plotly graphs}
+\usage{
+plotly_POST(x = last_plot(), filename = NULL, fileopt = "overwrite",
+ sharing = c("public", "private", "secret"), ...)
+}
+\arguments{
+\item{x}{either a ggplot object, a plotly object, or a list.}
+
+\item{filename}{character string describing the name of the plot in your
+plotly account. Use / to specify directories. If a directory path does not
+exist it will be created. If this argument is not specified and the title
+of the plot exists, that will be used for the filename.}
+
+\item{fileopt}{character string describing whether to create a "new" plotly,
+"overwrite" an existing plotly, "append" data to existing plotly,
+or "extend" it.}
+
+\item{sharing}{If 'public', anyone can view this graph. It will appear in
+your profile and can appear in search engines. You do not need to be
+logged in to Plotly to view this chart.
+If 'private', only you can view this plot. It will not appear in the
+Plotly feed, your profile, or search engines. You must be logged in to
+Plotly to view this graph. You can privately share this graph with other
+Plotly users in your online Plotly account and they will need to be logged
+in to view this plot.
+If 'secret', anyone with this secret link can view this chart. It will
+not appear in the Plotly feed, your profile, or search engines.
+If it is embedded inside a webpage or an IPython notebook, anybody who is
+viewing that page will be able to view the graph.
+You do not need to be logged in to view this plot.}
+
+\item{...}{not used}
+}
+\description{
+Deprecated: see \code{\link[=api_create]{api_create()}}.
+}
+\seealso{
+\code{\link[=plot_ly]{plot_ly()}}, \code{\link[=signup]{signup()}}
+}
diff --git a/man/plotly_build.Rd b/man/plotly_build.Rd
new file mode 100644
index 0000000..2d86e8e
--- /dev/null
+++ b/man/plotly_build.Rd
@@ -0,0 +1,28 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly_build.R
+\name{plotly_build}
+\alias{plotly_build}
+\title{'Build' (i.e., evaluate) a plotly object}
+\usage{
+plotly_build(p, registerFrames = TRUE)
+}
+\arguments{
+\item{p}{a ggplot object, or a plotly object, or a list.}
+
+\item{registerFrames}{should a frame trace attribute be interpreted as frames in an animation?}
+}
+\description{
+This generic function creates the list object sent to plotly.js
+for rendering. Using this function can be useful for overriding defaults
+provided by \code{ggplotly}/\code{plot_ly} or for debugging rendering
+errors.
+}
+\examples{
+
+p <- plot_ly(economics, x = ~date, y = ~pce)
+# the unevaluated plotly object
+str(p)
+# the evaluated data
+str(plotly_build(p)$x$data)
+
+}
diff --git a/man/plotly_data.Rd b/man/plotly_data.Rd
new file mode 100644
index 0000000..1a6d682
--- /dev/null
+++ b/man/plotly_data.Rd
@@ -0,0 +1,113 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly_data.R
+\name{plotly_data}
+\alias{plotly_data}
+\alias{groups.plotly}
+\alias{ungroup.plotly}
+\alias{group_by_.plotly}
+\alias{summarise_.plotly}
+\alias{mutate_.plotly}
+\alias{do_.plotly}
+\alias{arrange_.plotly}
+\alias{select_.plotly}
+\alias{filter_.plotly}
+\alias{distinct_.plotly}
+\alias{slice_.plotly}
+\alias{rename_.plotly}
+\alias{transmute_.plotly}
+\title{Obtain data associated with a plotly graph}
+\usage{
+plotly_data(p, id = p$x$cur_data)
+
+\method{groups}{plotly}(x)
+
+\method{ungroup}{plotly}(x, ...)
+
+\method{group_by_}{plotly}(.data, ..., .dots, add = FALSE)
+
+\method{summarise_}{plotly}(.data, ..., .dots)
+
+\method{mutate_}{plotly}(.data, ..., .dots)
+
+\method{do_}{plotly}(.data, ..., .dots)
+
+\method{arrange_}{plotly}(.data, ..., .dots)
+
+\method{select_}{plotly}(.data, ..., .dots)
+
+\method{filter_}{plotly}(.data, ..., .dots)
+
+\method{distinct_}{plotly}(.data, ..., .dots)
+
+\method{slice_}{plotly}(.data, ..., .dots)
+
+\method{rename_}{plotly}(.data, ..., .dots)
+
+\method{transmute_}{plotly}(.data, ..., .dots)
+}
+\arguments{
+\item{p}{a plotly visualization}
+
+\item{id}{a character string or number referencing an "attribute layer".}
+
+\item{x}{a plotly visualization}
+
+\item{...}{stuff passed onto the relevant method}
+
+\item{.data}{a plotly visualization}
+
+\item{.dots}{Used to work around non-standard evaluation. See vignette("nse") for details}
+
+\item{add}{By default, when add = FALSE, group_by will override existing groups.
+To instead add to the existing groups, use add = TRUE}
+}
+\description{
+\code{plotly_data()} returns data associated with
+a plotly visualization (if there are multiple data frames, by default,
+it returns the most recent one).
+}
+\examples{
+
+# use group_by() to define groups of visual markings
+p <- txhousing \%>\%
+ group_by(city) \%>\%
+ plot_ly(x = ~date, y = ~sales)
+p
+
+# plotly objects preserve data groupings
+groups(p)
+plotly_data(p)
+
+# dplyr verbs operate on plotly objects as if they were data frames
+p <- economics \%>\%
+ plot_ly(x = ~date, y = ~unemploy / pop) \%>\%
+ add_lines() \%>\%
+ mutate(rate = unemploy / pop) \%>\%
+ filter(rate == max(rate))
+plotly_data(p)
+add_markers(p)
+layout(p, annotations = list(x = ~date, y = ~rate, text = "peak"))
+
+# use group_by() + do() + subplot() for trellis displays
+d <- group_by(mpg, drv)
+plots <- do(d, p = plot_ly(., x = ~cty, name = ~drv))
+subplot(plots[["p"]], nrows = 3, shareX = TRUE)
+
+# arrange displays by their mean
+means <- summarise(d, mn = mean(cty, na.rm = TRUE))
+means \%>\%
+ dplyr::left_join(plots) \%>\%
+ arrange(mn) \%>\%
+ subplot(nrows = NROW(.), shareX = TRUE)
+
+# more dplyr verbs applied to plotly objects
+p <- mtcars \%>\%
+ plot_ly(x = ~wt, y = ~mpg, name = "scatter trace") \%>\%
+ add_markers()
+p \%>\% slice(1) \%>\% plotly_data()
+p \%>\% slice(1) \%>\% add_markers(name = "first observation")
+p \%>\% filter(cyl == 4) \%>\% plotly_data()
+p \%>\% filter(cyl == 4) \%>\% add_markers(name = "four cylinders")
+
+
+}
diff --git a/man/plotly_empty.Rd b/man/plotly_empty.Rd
new file mode 100644
index 0000000..297e8c9
--- /dev/null
+++ b/man/plotly_empty.Rd
@@ -0,0 +1,14 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/helpers.R
+\name{plotly_empty}
+\alias{plotly_empty}
+\title{Create a complete empty plotly graph.}
+\usage{
+plotly_empty(...)
+}
+\arguments{
+\item{...}{arguments passed onto \code{\link[=plot_ly]{plot_ly()}}}
+}
+\description{
+Useful when used with \code{\link[=subplot]{subplot()}}
+}
diff --git a/man/plotly_example.Rd b/man/plotly_example.Rd
new file mode 100644
index 0000000..790fd6a
--- /dev/null
+++ b/man/plotly_example.Rd
@@ -0,0 +1,22 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly_example.R
+\name{plotly_example}
+\alias{plotly_example}
+\title{Run a plotly example(s)}
+\usage{
+plotly_example(type = c("demo", "shiny", "rmd"), name, ...)
+}
+\arguments{
+\item{type}{the type of example}
+
+\item{name}{the name of the example (valid names depend on \code{type}).}
+
+\item{...}{arguments passed onto the suitable method.}
+}
+\description{
+Provides a unified interface for running demos, shiny apps, and Rmd documents
+which are bundled with the package.
+}
+\author{
+Carson Sievert
+}
diff --git a/man/plotly_json.Rd b/man/plotly_json.Rd
new file mode 100644
index 0000000..890b3f5
--- /dev/null
+++ b/man/plotly_json.Rd
@@ -0,0 +1,24 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/dev.R
+\name{plotly_json}
+\alias{plotly_json}
+\title{Inspect JSON sent to plotly.js}
+\usage{
+plotly_json(p = last_plot(), jsonedit = interactive(), ...)
+}
+\arguments{
+\item{p}{a plotly or ggplot object.}
+
+\item{jsonedit}{use \code{listviewer::jsonedit} to view the JSON?}
+
+\item{...}{other options passed onto \code{listviewer::jsonedit}}
+}
+\description{
+This function is useful for obtaining/viewing/debugging JSON
+sent to plotly.js.
+}
+\examples{
+
+plotly_json(plot_ly())
+plotly_json(plot_ly(), FALSE)
+}
diff --git a/man/print.api.Rd b/man/print.api.Rd
new file mode 100644
index 0000000..cd3ba48
--- /dev/null
+++ b/man/print.api.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{print.api}
+\alias{print.api}
+\title{Print method for a 'generic' API response}
+\usage{
+\method{print}{api}(x, ...)
+}
+\arguments{
+\item{x}{a list.}
+
+\item{...}{additional arguments (currently ignored)}
+}
+\description{
+Print method for a 'generic' API response
+}
diff --git a/man/print.api_grid.Rd b/man/print.api_grid.Rd
new file mode 100644
index 0000000..c72e772
--- /dev/null
+++ b/man/print.api_grid.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{print.api_grid}
+\alias{print.api_grid}
+\title{Print a plotly grid object}
+\usage{
+\method{print}{api_grid}(x, ...)
+}
+\arguments{
+\item{x}{a plotly grid object}
+
+\item{...}{additional arguments (currently ignored)}
+}
+\description{
+Print a plotly grid object
+}
diff --git a/man/print.api_grid_local.Rd b/man/print.api_grid_local.Rd
new file mode 100644
index 0000000..f5d7e65
--- /dev/null
+++ b/man/print.api_grid_local.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{print.api_grid_local}
+\alias{print.api_grid_local}
+\title{Print a plotly grid object}
+\usage{
+\method{print}{api_grid_local}(x, ...)
+}
+\arguments{
+\item{x}{a plotly grid object}
+
+\item{...}{additional arguments (currently ignored)}
+}
+\description{
+Print a plotly grid object
+}
diff --git a/man/print.api_plot.Rd b/man/print.api_plot.Rd
new file mode 100644
index 0000000..d480db8
--- /dev/null
+++ b/man/print.api_plot.Rd
@@ -0,0 +1,16 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/print.R
+\name{print.api_plot}
+\alias{print.api_plot}
+\title{Print a plot on plotly's platform}
+\usage{
+\method{print}{api_plot}(x, ...)
+}
+\arguments{
+\item{x}{a plotly figure object}
+
+\item{...}{additional arguments (currently ignored)}
+}
+\description{
+Print a plot on plotly's platform
+}
diff --git a/man/rangeslider.Rd b/man/rangeslider.Rd
new file mode 100644
index 0000000..be1a800
--- /dev/null
+++ b/man/rangeslider.Rd
@@ -0,0 +1,41 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/layout.R
+\name{rangeslider}
+\alias{rangeslider}
+\title{Add a range slider to the x-axis}
+\usage{
+rangeslider(p, start = NULL, end = NULL, ...)
+}
+\arguments{
+\item{p}{plotly object.}
+
+\item{start}{a start date/value.}
+
+\item{end}{an end date/value.}
+
+\item{...}{these arguments are documented here
+\url{https://plot.ly/r/reference/#layout-xaxis-rangeslider}}
+}
+\description{
+Add a range slider to the x-axis
+}
+\examples{
+
+plot_ly(x = time(USAccDeaths), y = USAccDeaths) \%>\%
+ add_lines() \%>\%
+ rangeslider()
+
+d <- tibble::tibble(
+ time = seq(as.Date("2016-01-01"), as.Date("2016-08-31"), by = "days"),
+ y = rnorm(seq_along(time))
+ )
+
+plot_ly(d, x = ~time, y = ~y) \%>\%
+ add_lines() \%>\%
+ rangeslider(d$time[5], d$time[50])
+
+
+}
+\author{
+Carson Sievert
+}
diff --git a/man/raster2uri.Rd b/man/raster2uri.Rd
new file mode 100644
index 0000000..764bb7c
--- /dev/null
+++ b/man/raster2uri.Rd
@@ -0,0 +1,39 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/helpers.R
+\name{raster2uri}
+\alias{raster2uri}
+\title{Convert a raster object to a data URI}
+\usage{
+raster2uri(r, ...)
+}
+\arguments{
+\item{r}{an object coercable to a raster object via \code{\link[=as.raster]{as.raster()}}}
+
+\item{...}{arguments passed onto \code{\link[=as.raster]{as.raster()}}.}
+}
+\description{
+Convenient embedding images via \code{\link[=layout]{layout()}}
+\href{images}{https://plot.ly/r/reference/#layout-images}.
+}
+\examples{
+
+# a red gradient (from ?as.raster)
+r <- as.raster(matrix(hcl(0, 80, seq(50, 80, 10)), nrow = 4, ncol = 5))
+plot(r)
+
+# embed the raster as an image
+plot_ly(x = 1, y = 1) \%>\%
+ layout(
+ images = list(list(
+ source = raster2uri(r),
+ xref = "paper",
+ yref = "paper",
+ x = 0, y = 0,
+ sizex = 0.5, sizey = 0.5,
+ xanchor = "left", yanchor = "bottom"
+ ))
+ )
+}
+\author{
+Carson Sievert
+}
diff --git a/man/reexports.Rd b/man/reexports.Rd
new file mode 100644
index 0000000..887979e
--- /dev/null
+++ b/man/reexports.Rd
@@ -0,0 +1,42 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/imports.R, R/pipe.R
+\docType{import}
+\name{mutate}
+\alias{mutate}
+\alias{mutate_}
+\alias{transmute}
+\alias{transmute_}
+\alias{select}
+\alias{select_}
+\alias{rename}
+\alias{rename_}
+\alias{group_by}
+\alias{group_by_}
+\alias{groups}
+\alias{ungroup}
+\alias{summarise}
+\alias{summarise_}
+\alias{do}
+\alias{do_}
+\alias{arrange}
+\alias{arrange_}
+\alias{distinct}
+\alias{distinct_}
+\alias{slice}
+\alias{slice_}
+\alias{filter}
+\alias{filter_}
+\alias{reexports}
+\alias{\%>\%}
+\title{Objects exported from other packages}
+\keyword{internal}
+\description{
+These objects are imported from other packages. Follow the links
+below to see their documentation.
+
+\describe{
+ \item{dplyr}{\code{\link[dplyr]{mutate}}, \code{\link[dplyr]{mutate_}}, \code{\link[dplyr]{transmute}}, \code{\link[dplyr]{transmute_}}, \code{\link[dplyr]{select}}, \code{\link[dplyr]{select_}}, \code{\link[dplyr]{rename}}, \code{\link[dplyr]{rename_}}, \code{\link[dplyr]{group_by}}, \code{\link[dplyr]{group_by_}}, \code{\link[dplyr]{groups}}, \code{\link[dplyr]{ungroup}}, \code{\link[dplyr]{summarise}}, \code{\link[dplyr]{summarise_}}, \code{\link[dplyr]{do}}, \code{\link[dplyr]{do_} [...]
+
+ \item{magrittr}{\code{\link[magrittr]{\%>\%}}}
+}}
+
diff --git a/man/remove_typedarray_polyfill.Rd b/man/remove_typedarray_polyfill.Rd
new file mode 100644
index 0000000..dc29214
--- /dev/null
+++ b/man/remove_typedarray_polyfill.Rd
@@ -0,0 +1,32 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/plotly.R
+\name{remove_typedarray_polyfill}
+\alias{remove_typedarray_polyfill}
+\title{Remove TypedArray polyfill}
+\usage{
+remove_typedarray_polyfill(p)
+}
+\arguments{
+\item{p}{a plotly object}
+}
+\description{
+By default, plotly.js' TypedArray polyfill is included as a dependency, so
+printing "just works" in any context. Many users won't need this polyfill,
+so this function may be used to remove it and thus reduce the size of the page.
+}
+\details{
+The polyfill seems to be only relevant for those rendering plots
+via phantomjs and RStudio on some Windows platforms.
+}
+\examples{
+
+\dontrun{
+p1 <- plot_ly()
+p2 <- remove_typedarray_polyfill(p1)
+t1 <- tempfile(fileext = ".html")
+htmlwidgets::saveWidget(p1, t1)
+file.info(t1)$size
+htmlwidgets::saveWidget(p2, t1)
+file.info(t1)$size
+}
+}
diff --git a/man/schema.Rd b/man/schema.Rd
new file mode 100644
index 0000000..9dbc71d
--- /dev/null
+++ b/man/schema.Rd
@@ -0,0 +1,32 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/dev.R
+\name{schema}
+\alias{schema}
+\title{Acquire (and optionally display) plotly's plot schema}
+\usage{
+schema(jsonedit = interactive(), ...)
+}
+\arguments{
+\item{jsonedit}{use \code{listviewer::jsonedit} to view the JSON?}
+
+\item{...}{other options passed onto \code{listviewer::jsonedit}}
+}
+\description{
+The schema contains valid attributes names, their value type,
+default values (if any), and min/max values (if applicable).
+}
+\examples{
+s <- schema()
+
+# retrieve acceptable `layout.mapbox.style` values
+if (!is.na(Sys.getenv('MAPBOX_TOKEN', NA))) {
+ styles <- s$layout$layoutAttributes$mapbox$style$values
+ subplot(
+ plot_mapbox() \%>\% layout(mapbox = list(style = styles[3])),
+ plot_mapbox() \%>\% layout(mapbox = list(style = styles[5]))
+ )
+}
+
+
+
+}
diff --git a/man/showRGB.Rd b/man/showRGB.Rd
new file mode 100644
index 0000000..6434c91
--- /dev/null
+++ b/man/showRGB.Rd
@@ -0,0 +1,24 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/toRGB.R
+\name{showRGB}
+\alias{showRGB}
+\title{View colors already formatted by toRGB()}
+\usage{
+showRGB(x, ...)
+}
+\arguments{
+\item{x}{character string specifying color(s).}
+
+\item{...}{arguments passed along to \code{scales::show_col}.}
+}
+\description{
+Useful for viewing colors after they've been converted to plotly.js'
+color format -- "rgba(255, 255, 255, 1)"
+}
+\examples{
+
+showRGB(toRGB(colors()), labels = FALSE)
+}
+\author{
+Carson Sievert
+}
diff --git a/man/signup.Rd b/man/signup.Rd
new file mode 100644
index 0000000..21fdc29
--- /dev/null
+++ b/man/signup.Rd
@@ -0,0 +1,49 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/signup.R
+\name{signup}
+\alias{signup}
+\title{Create a new plotly account.}
+\usage{
+signup(username, email, save = TRUE)
+}
+\arguments{
+\item{username}{Desired username.}
+
+\item{email}{Desired email.}
+
+\item{save}{If request is successful, should the username & API key be
+automatically stored as an environment variable in a .Rprofile?}
+}
+\value{
+\itemize{
+\item api_key key to use with the api
+\item tmp_pw temporary password to access your plotly account
+}
+}
+\description{
+A sign up interface to plotly through the R Console.
+}
+\examples{
+\dontrun{
+# You need a plotly username and API key to communicate with the plotly API.
+
+# If you don't already have an API key, you can obtain one with a valid
+# username and email via signup().
+s <- signup('anna.lyst', 'anna.lyst at plot.ly')
+
+# If you already have a username and API key, please create the following
+# environment variables:
+Sys.setenv("plotly_username" = "me")
+Sys.setenv("plotly_api_key" = "mykey")
+# You can also change the default domain if you have a plotly server.
+Sys.setenv("plotly_domain" = "http://mydomain.com")
+
+# If you want to automatically load these environment variables when you
+# start R, you can put them inside your ~/.Rprofile
+# (see help(.Rprofile) for more details)
+
+}
+}
+\references{
+https://plot.ly/rest/
+}
diff --git a/man/style.Rd b/man/style.Rd
new file mode 100644
index 0000000..b418df3
--- /dev/null
+++ b/man/style.Rd
@@ -0,0 +1,33 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/style.R
+\name{style}
+\alias{style}
+\title{Modify trace(s)}
+\usage{
+style(p, ..., traces = NULL)
+}
+\arguments{
+\item{p}{A plotly visualization.}
+
+\item{...}{Visual properties.}
+
+\item{traces}{numeric vector. Which traces should be modified? By default,
+attributes place in \code{...} will be applied to every trace.}
+}
+\description{
+Modify trace(s) of an existing plotly visualization. Useful when used in
+conjunction with \code{\link[=get_figure]{get_figure()}}.
+}
+\examples{
+
+p <- qplot(data = mtcars, wt, mpg, geom = c("point", "smooth"))
+# keep the hover info for points, but remove it for the line/ribbon
+style(p, hoverinfo = "none", traces = c(2, 3))
+
+}
+\seealso{
+\code{\link[=api_download_plot]{api_download_plot()}}
+}
+\author{
+Carson Sievert
+}
diff --git a/man/subplot.Rd b/man/subplot.Rd
new file mode 100644
index 0000000..6832235
--- /dev/null
+++ b/man/subplot.Rd
@@ -0,0 +1,87 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/subplots.R
+\name{subplot}
+\alias{subplot}
+\title{View multiple plots in a single view}
+\usage{
+subplot(..., nrows = 1, widths = NULL, heights = NULL, margin = 0.02,
+ shareX = FALSE, shareY = FALSE, titleX = shareX, titleY = shareY,
+ which_layout = "merge")
+}
+\arguments{
+\item{...}{One of the following
+\itemize{
+\item any number of plotly/ggplot2 objects.
+\item a list of plotly/ggplot2 objects.
+\item a tibble with one list-column of plotly/ggplot2 objects.
+}}
+
+\item{nrows}{number of rows for laying out plots in a grid-like structure.
+Only used if no domain is already specified.}
+
+\item{widths}{relative width of each column on a 0-1 scale. By default all
+columns have an equal relative width.}
+
+\item{heights}{relative height of each row on a 0-1 scale. By default all
+rows have an equal relative height.}
+
+\item{margin}{either a single value or four values (all between 0 and 1).
+If four values are provided, the first is used as the left margin, the second
+is used as the right margin, the third is used as the top margin, and the
+fourth is used as the bottom margin.
+If a single value is provided, it will be used as all four margins.}
+
+\item{shareX}{should the x-axis be shared amongst the subplots?}
+
+\item{shareY}{should the y-axis be shared amongst the subplots?}
+
+\item{titleX}{should x-axis titles be retained?}
+
+\item{titleY}{should y-axis titles be retained?}
+
+\item{which_layout}{adopt the layout of which plot? If the default value of
+"merge" is used, layout options found later in the sequence of plots will
+override options found earlier in the sequence. This argument also accepts a
+numeric vector specifying which plots to consider when merging.}
+}
+\value{
+A plotly object
+}
+\description{
+View multiple plots in a single view
+}
+\examples{
+
+# pass any number of plotly objects to subplot()
+p1 <- plot_ly(economics, x = ~date, y = ~uempmed)
+p2 <- plot_ly(economics, x = ~date, y = ~unemploy)
+subplot(p1, p2, p1, p2, nrows = 2, margin = 0.05)
+
+#' # anchor multiple traces on the same legend entry
+ p1 <- add_lines(p1, color = I("black"), name = "1st", legendgroup = "1st")
+ p2 <- add_lines(p2, color = I("red"), name = "2nd", legendgroup = "2nd")
+
+ subplot(
+ p1, style(p1, showlegend = FALSE),
+ p2, style(p2, showlegend = FALSE),
+ nrows = 2, margin = 0.05
+ )
+
+# or pass a list
+economics_long \%>\%
+ split(.$variable) \%>\%
+ lapply(function(d) plot_ly(d, x = ~date, y = ~value)) \%>\%
+ subplot(nrows = NROW(.), shareX = TRUE)
+
+# or pass a tibble with a list-column of plotly objects
+economics_long \%>\%
+ group_by(variable) \%>\%
+ do(p = plot_ly(., x = ~date, y = ~value)) \%>\%
+ subplot(nrows = NROW(.), shareX = TRUE)
+
+# learn more at https://cpsievert.github.io/plotly_book/subplot.html
+
+}
+\author{
+Carson Sievert
+}
diff --git a/man/toRGB.Rd b/man/toRGB.Rd
new file mode 100644
index 0000000..1380dc7
--- /dev/null
+++ b/man/toRGB.Rd
@@ -0,0 +1,38 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/toRGB.R
+\name{toRGB}
+\alias{toRGB}
+\title{Convert R colours to RGBA hexadecimal colour values}
+\usage{
+toRGB(x, alpha = 1)
+}
+\arguments{
+\item{x}{see the \code{col} argument in \code{col2rgb} for valid specifications}
+
+\item{alpha}{alpha channel on 0-1 scale}
+}
+\value{
+hexadecimal colour value (if is.na(x), return "transparent" for compatibility with Plotly)
+}
+\description{
+Convert R colours to RGBA hexadecimal colour values
+}
+\examples{
+
+toRGB("steelblue")
+# [1] "rgba(70,130,180,1)"
+
+m <- list(
+ color = toRGB("red"),
+ line = list(
+ color = toRGB("black"),
+ width = 19
+ )
+)
+
+plot_ly(x = 1, y = 1, marker = m)
+
+}
+\seealso{
+\code{\link[=showRGB]{showRGB()}}
+}
diff --git a/man/toWebGL.Rd b/man/toWebGL.Rd
new file mode 100644
index 0000000..3d155ee
--- /dev/null
+++ b/man/toWebGL.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/helpers.R
+\name{toWebGL}
+\alias{toWebGL}
+\title{Convert trace types to WebGL}
+\usage{
+toWebGL(p)
+}
+\arguments{
+\item{p}{a plotly or ggplot object.}
+}
+\description{
+Convert trace types to WebGL
+}
+\examples{
+
+# currently no bargl trace type
+toWebGL(qplot(1:10))
+toWebGL(qplot(1:10, 1:10))
+
+}
diff --git a/man/to_basic.Rd b/man/to_basic.Rd
new file mode 100644
index 0000000..abd2f2a
--- /dev/null
+++ b/man/to_basic.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/layers2traces.R
+\name{to_basic}
+\alias{to_basic}
+\title{Convert a geom to a "basic" geom.}
+\usage{
+to_basic(data, prestats_data, layout, params, p, ...)
+}
+\arguments{
+\item{data}{the data returned by \code{ggplot2::ggplot_build()}.}
+
+\item{prestats_data}{the data before statistics are computed.}
+
+\item{layout}{the panel layout.}
+
+\item{params}{parameters for the geom, statistic, and 'constant' aesthetics}
+
+\item{p}{a ggplot2 object (the conversion may depend on scales, for instance).}
+
+\item{...}{currently ignored}
+}
+\description{
+This function makes it possible to convert ggplot2 geoms that
+are not included with ggplot2 itself. Users shouldn't need to use
+this function. It exists purely to allow other package authors to write
+their own conversion method(s).
+}
diff --git a/man/wind.Rd b/man/wind.Rd
new file mode 100644
index 0000000..e82edbd
--- /dev/null
+++ b/man/wind.Rd
@@ -0,0 +1,15 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/data.R
+\docType{data}
+\name{wind}
+\alias{wind}
+\title{Wind data}
+\format{A data frame with three variables: \code{r}, \code{t},
+\code{nms}.}
+\usage{
+wind
+}
+\description{
+Description TBD.
+}
+\keyword{datasets}
diff --git a/tests/testthat.R b/tests/testthat.R
new file mode 100644
index 0000000..ff1a9eb
--- /dev/null
+++ b/tests/testthat.R
@@ -0,0 +1,191 @@
+library("testthat")
+library("plotly")
+library("RSclient")
+
+# report any differences in plot (list) hashes if this is a Travis pull request
+report_diffs <- grepl("^[0-9]+$", Sys.getenv("TRAVIS_PULL_REQUEST"))
+# build a ggplot2 comparison table?
+build_table <- Sys.getenv("PLOTLY_TABLE") == "TRUE"
+
+# stuff that should be done once (not everytime save_outputs() is called)
+if (report_diffs || build_table) {
+ message("Spinning up an independent R session with plotly's master branch installed")
+ Rserve::Rserve(args = "--vanilla --RS-enable-remote")
+ conn <- RSconnect()
+ # ensure the seed is the same for randomized tests
+ set.seed(1)
+ RSeval(conn, "set.seed(1)")
+ # we don't make assumptions about ggplot2 versioning,
+ # but it is _strongly_ recommended to use the CRAN version (of ggplot2)
+ RSeval(conn, "devtools::install_github('ropensci/plotly')")
+ RSeval(conn, "library(plotly)")
+ if (report_diffs) {
+ # hash of the version being tested
+ this_hash <- substr(Sys.getenv("TRAVIS_COMMIT"), 1, 7)
+ # hash of version to compare with (master)
+ master_hash <- RSeval(conn, "packageDescription('plotly')$GithubSHA1")
+ master_hash <- substr(master_hash, 1, 7)
+ # plotly-test-table repo hosts the diff pages & keeps track of previous versions
+ table_dir <- normalizePath("../../plotly-test-table", mustWork = T)
+ # Make sure we have appropriate versions of plotlyjs
+ # (see plotly-test-table/template/template/index.html)
+ file.copy(
+ file.path("..", "inst", "htmlwidgets", "lib", "plotlyjs", "plotly-latest.min.js"),
+ file.path(table_dir, "template", "New.min.js"),
+ overwrite = TRUE
+ )
+ download.file(
+ "https://raw.githubusercontent.com/ropensci/plotly/master/inst/htmlwidgets/lib/plotlyjs/plotly-latest.min.js",
+ file.path(table_dir, "template", "Old.min.js"),
+ method = "curl"
+ )
+ # directory for placing test differences
+ this_dir <- file.path(table_dir, this_hash)
+ if (dir.exists(this_dir)) {
+ message("Tests were already run on this commit. Nuking the old results...")
+ unlink(this_dir, recursive = T)
+ }
+ master_dir <- file.path(table_dir, master_hash)
+ # csv file that tracks plot hashes
+ hash_file <- file.path(table_dir, "hashes.csv")
+ if (!file.exists(hash_file)) {
+ file.create(hash_file)
+ cat("commit,test,hash\n", file = hash_file, append = T)
+ }
+ hash_info <- utils::read.csv(hash_file)
+ master_info <- hash_info[hash_info$commit %in% master_hash, ]
+ }
+}
+
+# Some tests make plot.ly HTTP requests and require a valid user account
+# (see test-plotly-filename.R). For security reasons, these tests should be
+# skipped on pull requests (the .travis.yml file uses encrypted credentials
+# & encrypted environment vars cannot be accessed on pull request builds)
+skip_if_not_master <- function() {
+ is_pr <- grepl("^[0-9]+$", Sys.getenv("TRAVIS_PULL_REQUEST"))
+ is_r_release <- Sys.getenv("TRAVIS_R_VERSION_STRING", "release") == "release"
+ if (!is_pr && is_r_release) return(invisible(TRUE))
+ skip("plot.ly API calls are only tested on the master build on r-release")
+}
+
+# This function is called within testthat/test-*.R files.
+# It takes a ggplot or plotly object as input, and it returns a figure
+# object (aka the data behind the plot).
+save_outputs <- function(gg, name) {
+ print(paste("Running test:", name))
+ p <- plotly_build(gg)$x[c("data", "layout")]
+ has_diff <- if (report_diffs) {
+ # save a hash of the R object
+ plot_hash <- digest::digest(p)
+ info <- paste(this_hash, name, plot_hash, sep = ",")
+ cat(paste(info, "\n"), file = hash_file, append = T)
+ test_info <- master_info[master_info$test %in% name, ]
+ # is the plot hash different from master?
+ !isTRUE(plot_hash == test_info$hash)
+ } else FALSE
+ if (has_diff || build_table) {
+ RSassign(conn, gg)
+ pm <- RSeval(conn, "tryCatch(plotly::plotly_build(gg)$x[c('data', 'layout')], error = function(e) e$message)")
+ if (build_table) {
+ # save pngs of ggplot
+ filename <- paste0(gsub("\\s+", "-", name), ".png")
+ ggFile <- paste("ggplot", filename, sep = "-")
+ res <- tryCatch(ggsave(ggFile, gg),
+ error = function(e) {
+ err <- qplot() +
+ annotate('text', label = paste('Error:', e$message),
+ x = 1, y = 1, color = 'red')
+ ggsave(ggFile, err, width = 3, height = 2, units = 'in')
+ })
+ img <- function(x, f) {
+ tryCatch(plotly_IMAGE(x, out_file = f, width = 300, height = 400),
+ error = function(e) {
+ err <- qplot() +
+ annotate('text', label = paste('Error:', e$message),
+ x = 1, y = 1, color = 'red')
+ # TODO: convert pixels to inches?
+ ggsave(plotlyFile, err, width = 3, height = 2, units = 'in')
+ })
+ }
+ # save _this_ plotly version
+ plotlyFile <- paste("plotly", this_hash, filename, sep = "-")
+ res <- img(p, plotlyFile)
+ # save _master_ plotly version
+ plotlyFile <- paste("plotly", "master", filename, sep = "-")
+ RSassign(conn, plotlyFile, "plotlyFile")
+ RSassign(conn, img, "img")
+ res <- RSeval(conn, "img(p, plotlyFile)")
+ }
+ # it could be that the hash didn't exist, so make sure they're different
+ # before build a diff page
+ if (plot_hash != digest::digest(pm)) {
+ test_dir <- file.path(this_dir, gsub("\\s+", "-", name))
+ if (dir.exists(test_dir)) stop(shQuote(name), " has already been used to save_outputs() in another test.")
+ dir.create(test_dir, recursive = T)
+ # copy over diffing template
+ file.copy(
+ dir(file.path(table_dir, "template", "template"), full.names = TRUE), test_dir
+ )
+ # overwrite the default JSON
+ writeLines(
+ paste("New =", plotly:::to_JSON(p)),
+ file.path(test_dir, "New.json")
+ )
+ writeLines(
+ paste("Old =", plotly:::to_JSON(pm)),
+ file.path(test_dir, "Old.json")
+ )
+ }
+ }
+ p
+}
+
+# use me just like testthat::test_check()
+test_run <- function(...) {
+ # shut down the other R session on exit
+ if (report_diffs || build_table) {
+ on.exit(RSshutdown(conn))
+ on.exit(RSclose(conn), add = TRUE)
+ }
+ test_check(...)
+}
+
+if (packageVersion("ggplot2") > "2.2.1") {
+ test_run("plotly")
+}
+
+# now, actually build the table (if necessary)
+if (build_table) {
+ imgfy <- function(pat) {
+ sprintf(
+ "<img src='%s' width='%s' height='%s' />",
+ dir(pattern = pat), width, height
+ )
+ }
+ imgs <- data.frame(
+ imgfy("^ggplot-"),
+ imgfy(paste0("^plotly-", this_hash)),
+ imgfy("^plotly-master")
+ )
+ imgs <- cbind(
+ sub("\\.png", "", sub("^[a-z]+1-", "", dir(pattern = "^ggplot-"))),
+ imgs
+ )
+ setNames(imgs, c("ggplot2", this_hash, "master"))
+
+ html <- sprintf(
+ '<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset=\"utf-8\"/>
+ <style type=\"text/css\" media=\"screen\"> table td tr { border:1px solid #FF0000;} </style>
+ </head>
+ <body>
+ %s
+ </body>
+ </html>', as.character(knitr::kable(imgs, format = "html", escape = FALSE)))
+ tbl <- file.path(tmpDir, "index.html")
+ writeLines(html, tbl)
+ browseURL(tbl)
+ invisible(tbl)
+}
diff --git a/tests/testthat/test-animate-highlight.R b/tests/testthat/test-animate-highlight.R
new file mode 100644
index 0000000..2404062
--- /dev/null
+++ b/tests/testthat/test-animate-highlight.R
@@ -0,0 +1,318 @@
+context("highlighting and animation")
+
+m <- crosstalk::SharedData$new(mtcars, ~vs)
+
+test_that("SharedData produces key/set in plot_ly", {
+ m <- crosstalk::SharedData$new(mtcars, ~vs)
+ p <- plot_ly(m, x = ~wt, y = ~mpg) %>% add_markers()
+ tr <- plotly_build(p)$x$data[[1]]
+
+ expect_true(all(tr$key == m$key()))
+ expect_identical(tr$set, m$groupName())
+ expect_false(tr$`_isNestedKey` %||% FALSE)
+ expect_false(tr$`_isSimpleKey` %||% FALSE)
+})
+
+test_that("SharedData produces key/set in ggplotly", {
+ p <- ggplot(m, aes(x = wt, y = mpg)) + geom_point()
+ tr <- plotly_build(p)$x$data[[1]]
+
+ expect_true(all(tr$key == m$key()))
+ expect_type(tr$set, "character")
+ expect_length(tr$set, 1)
+ expect_false(tr$`_isNestedKey` %||% FALSE)
+ expect_false(tr$`_isSimpleKey` %||% FALSE)
+})
+
+test_that("SharedData produces key/set in ggpairs", {
+ p <- GGally::ggpairs(m, columns = 1:3)
+ l <- plotly_build(p)$x
+
+ for (i in seq_along(l$data)) {
+ tr <- l$data[[i]]
+ if (tr$mode != "markers") next
+ expect_true(all(tr$key == m$key()))
+ expect_identical(tr$set, m$groupName())
+ expect_false(tr$`_isNestedKey` %||% FALSE)
+ expect_false(tr$`_isSimpleKey` %||% FALSE)
+ }
+
+})
+
+
+test_that("When key is equivalent to group, produce simple keys", {
+ gg <- ggplot(m, aes(wt, mpg, color = factor(vs))) +
+ geom_point() +
+ geom_smooth(se = FALSE)
+
+ # for interactive testing -- `highlight(gg, "plotly_click")`
+
+ l <- plotly_build(gg)$x
+
+ for (i in seq_along(l$data)) {
+ tr <- l$data[[i]]
+ expect_false(tr$`_isNestedKey` %||% FALSE)
+
+ if (tr$mode == "markers") {
+ # clicking on a single point should select the whole group in a efficient
+ # (i.e., no trace subsetting occurs for simple keys) manner
+ expect_true(tr$key == tr$name)
+ expect_true(tr$`_isSimpleKey`)
+ } else {
+ # TODO: shouldn't key be a length 1 here?
+ expect_true(tr$name %in% tr$key)
+ expect_true(tr$`_isSimpleKey`)
+ }
+ }
+
+})
+
+
+m2 <- crosstalk::SharedData$new(mtcars)
+
+test_that("When key is nested within group, produce simple key", {
+ gg <- ggplot(m2, aes(wt, mpg, color = factor(vs))) +
+ geom_point() +
+ geom_smooth(se = FALSE)
+
+ # for interactive testing -- `highlight(gg, "plotly_click")`
+
+ l <- plotly_build(gg)$x
+
+ for (i in seq_along(l$data)) {
+ tr <- l$data[[i]]
+
+ key <- m2$key()[mtcars$vs == tr$name]
+ expect_true(all(tr$key == key))
+
+ if (tr$mode == "markers") {
+ expect_false(tr$`_isSimpleKey` %||% FALSE)
+ expect_false(tr$`_isNestedKey` %||% FALSE)
+ } else {
+ expect_true(tr$`_isSimpleKey`)
+ expect_false(tr$`_isNestedKey` %||% FALSE)
+ }
+ }
+
+})
+
+
+test_that("Key structure is passed along to frame data", {
+ p <- ggplot(m2, aes(wt, mpg, color = factor(vs), frame = am)) +
+ geom_point() +
+ geom_smooth(se = FALSE)
+
+ # TODO: why doesn't the highlight update on the second frame?
+ # animation_opts(p, 0, redraw = T) %>% highlight("plotly_click")
+
+ l <- suppressWarnings(plotly_build(p)$x)
+
+ for (i in seq_along(l$data)) {
+ tr <- l$data[[i]]
+
+ key <- m2$key()[mtcars$vs == tr$name & mtcars$am == tr$frame]
+ expect_true(all(tr$key == key))
+ }
+
+ # the fitted line of every frame should have a simple key
+ for (i in seq_along(l$frames)) {
+ fr <- l$frames[[i]]
+ for (j in seq_along(fr$data)) {
+ tr <- fr$data[[j]]
+ if (tr$mode != "lines") next
+ expect_true(tr$`_isSimpleKey`)
+ }
+ }
+
+})
+
+
+
+test_that("can handle inconsistent # of traces across frames & supply default colors", {
+ d <- data.frame(
+ y = rnorm(20),
+ score = c(1,1,1,1,2,2,2,2,3,3,3,3,1,1,1,1,2,2,2,2),
+ population = c(rep(1, 12), rep(2, 8))
+ )
+
+ p <- plot_ly(d, y = ~y, split = ~as.factor(score), frame = ~population) %>%
+ add_boxplot()
+
+ l <- plotly_build(p)$x
+
+ expect_length(l$data, 3)
+
+ # default colors are the plotly.js defaults
+ cols <- sapply(l$data, function(x) x$line$color)
+ defaultCols <- toRGB(traceColorDefaults()[1:3])
+ expect_equivalent(cols, defaultCols)
+
+ # trace names reflect the split/score (i.e., frames are removed)
+ nms <- sapply(l$data, "[[", "name")
+ expect_equivalent(nms, levels(as.factor(d$score)))
+
+ # 2 frames: both with 3 traces
+ expect_length(l$frames, 2)
+ expect_length(l$frames[[1]]$data, 3)
+ expect_length(l$frames[[2]]$data, 3)
+
+ # make sure the frames are targetting the right traces
+ expect_equivalent(l$frames[[1]]$traces, 0:2)
+ expect_equivalent(l$frames[[2]]$traces, 0:2)
+
+ # 1st frame has all 3 traces visible; 2nd frame has 2 visible
+ expect_true(
+ unique(sapply(l$frames[[1]]$data, "[[", "visible"))
+ )
+ expect_identical(
+ sapply(l$frames[[2]]$data, "[[", "visible"),
+ c(TRUE, TRUE, FALSE)
+ )
+
+ # ensure the default colors remain consistent throughout the animation
+ cols <- sapply(l$frames[[1]]$data, function(x) x$line$color)
+ expect_equivalent(cols, defaultCols)
+ cols <- sapply(l$frames[[2]]$data, function(x) x$line$color)
+ expect_equivalent(cols, defaultCols)
+
+ # ensure the animation defaults are supplied
+ buttonArgs <- l$layout$updatemenus[[1]]$buttons[[1]]$args[[2]]
+ defaults <- animation_opts_defaults()
+ expect_identical(
+ buttonArgs[names(defaults)], defaults
+ )
+
+ # step values reflect the frame values
+ steps <- l$layout$sliders[[1]]$steps
+ expect_equivalent(
+ unlist(lapply(steps, function(s) s$args[[1]])),
+ c("1", "2")
+ )
+
+ # all the slider steps reflect the animation default
+ res <- lapply(steps, function(s) {
+ expect_identical(s$args[[2]], defaults)
+ })
+
+})
+
+test_that("can change animation defaults", {
+
+ data(mtcars)
+
+ p <- plot_ly(mtcars, x = ~wt, y = ~mpg, frame = ~cyl) %>%
+ animation_opts(frame = 1200, transition = 1000, easing = "elastic") %>%
+ animation_button(
+ x = 1, xanchor = "right", y = 0, yanchor = "bottom"
+ ) %>%
+ animation_slider(
+ currentvalue = list(prefix = "YEAR ", font = list(color="red"))
+ )
+
+ l <- plotly_build(p)$x
+
+ expect_length(l$data, 1)
+ expect_length(l$frames, 3)
+
+ cyl <- as.character(unique(sort(mtcars$cyl)))
+ for (i in seq_along(l$frames)) {
+ f <- l$frames[[i]]
+ expect_equivalent(f$name, cyl[[i]])
+ expect_length(f$data, 1)
+ }
+
+ # the expectation for animation option values
+ aniOpts <- modify_list(
+ rapply(animation_opts_defaults(), unclass, how = "list"),
+ list(
+ frame = list(duration = 1200),
+ transition = list(duration = 1000, easing = "elastic")
+ )
+ )
+
+ # ensure the animation options are supplied
+ buttonArgs <- l$layout$updatemenus[[1]]$buttons[[1]]$args[[2]]
+ expect_equivalent(
+ buttonArgs[names(aniOpts)], aniOpts
+ )
+
+ # step values reflect the frame values
+ steps <- l$layout$sliders[[1]]$steps
+ expect_equivalent(
+ unlist(lapply(steps, function(s) s$args[[1]])), cyl
+ )
+
+ # all the slider steps reflect the animation options
+ res <- lapply(steps, function(s) {
+ expect_identical(
+ s$args[[2]], aniOpts
+ )
+ })
+
+})
+
+test_that("simple animation targeting works", {
+
+ df <- data.frame(
+ x = c(1, 2, 2, 1, 1, 2),
+ y = c(1, 2, 2, 1, 1, 2),
+ z = c(1, 1, 2, 2, 3, 3)
+ )
+ p <- plot_ly(df) %>%
+ add_markers(x = 1.5, y = 1.5) %>%
+ add_markers(x = ~x, y = ~y, frame = ~z)
+
+ l <- plotly_build(p)$x
+
+
+ expect_length(l$data, 2)
+ for (i in seq_along(l$data)) {
+ tr <- l$data[[i]]
+ # trace names are empty
+ expect_equivalent(tr$name %||% "no-name", "no-name")
+ # color defaults are retained
+ expect_equivalent(tr$marker$color, toRGB(traceColorDefaults()[[i]]))
+ }
+
+ # frame trace names are empty
+ expect_length(l$frames, 3)
+ for (i in seq_along(l$frames)) {
+ f <- l$frames[[i]]
+ for (j in seq_along(f$data)) {
+ tr <- f$data[[j]]
+ # trace names are empty
+ expect_equivalent(tr$name %||% "no-name", "no-name")
+ # color defaults are retained
+ expect_equivalent(tr$marker$color, toRGB(traceColorDefaults()[[2]]))
+ }
+ }
+
+ # since all trace types are scatter, redraw = FALSE
+ buttonArgs <- l$layout$updatemenus[[1]]$buttons[[1]]$args
+ expect_false(buttonArgs[[2]]$frame$redraw)
+
+ steps <- l$layout$sliders[[1]]$steps
+ res <- lapply(steps, function(s) {
+ expect_false(s$args[[2]]$frame$redraw)
+ })
+})
+
+test_that("animation frames are boxed up correctly", {
+ dallas <- subset(txhousing, city == "Dallas" & month == 1)
+ p <- ggplot(dallas) +
+ geom_point(aes(x = volume, y = sales, frame = year))
+ l <- plotly_build(p)$x
+
+ for (i in seq_along(l$frames)) {
+ traces <- l$frames[[i]]$data
+ for (j in seq_along(traces)) {
+ x <- traces[[j]]$x
+ y <- traces[[j]]$y
+ expect_true(length(x) > 1 || inherits(x, "AsIs"))
+ expect_true(length(y) > 1 || inherits(y, "AsIs"))
+ }
+ }
+
+})
+
+
diff --git a/tests/testthat/test-api.R b/tests/testthat/test-api.R
new file mode 100644
index 0000000..0f37a76
--- /dev/null
+++ b/tests/testthat/test-api.R
@@ -0,0 +1,149 @@
+context("api")
+
+test_that("api() returns endpoints", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ res <- api()
+ expect_true(length(res) > 1)
+ expect_true(all(c("plots", "grids", "folders") %in% names(res)))
+})
+
+test_that("Can search with white-space", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ res <- api("search?q=overdose drugs")
+ expect_true(length(res) > 1)
+})
+
+test_that("Changing a filename works", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ id <- plotly:::new_id()
+ f <- api("files/cpsievert:14680", "PATCH", list(filename = id))
+ expect_equivalent(f$filename, id)
+})
+
+
+test_that("Downloading plots works", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ # https://plot.ly/~cpsievert/200
+ p <- api_download_plot(200, "cpsievert")
+ expect_is(p, "htmlwidget")
+ expect_is(p, "plotly")
+
+ l <- plotly_build(p)$x
+ expect_length(l$data, 1)
+
+ # This file is a grid, not a plot https://plot.ly/~cpsievert/14681
+ expect_error(
+ api_download_plot(14681, "cpsievert"), "grid"
+ )
+})
+
+
+test_that("Downloading grids works", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ g <- api_download_grid(14681, "cpsievert")
+ expect_is(g, "api_file")
+ expect_is(
+ tibble::as_tibble(g$preview), "data.frame"
+ )
+
+ # This file is a plot, not a grid https://plot.ly/~cpsievert/14681
+ expect_error(
+ api_download_grid(200, "cpsievert"), "plot"
+ )
+})
+
+
+test_that("Creating produces a new file by default", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ expect_new <- function(obj) {
+ old <- api("folders/home?user=cpsievert")
+ new_obj <- api_create(obj)
+ Sys.sleep(3)
+ new <- api("folders/home?user=cpsievert")
+ n <- if (plotly:::is.plot(new_obj)) 2 else 1
+ expect_equivalent(old$children$count + n, new$children$count)
+ }
+
+ expect_new(mtcars)
+ # even if plot has multiple traces, only one grid should be created
+ p1 <- plot_ly(mtcars, x = ~mpg, y = ~wt)
+ p2 <- add_markers(p1, color = ~factor(cyl))
+ p3 <- add_markers(p1, color = ~factor(cyl), frame = ~factor(vs))
+ expect_new(p1)
+ expect_new(p2)
+ expect_new(p3)
+})
+
+
+test_that("Can overwrite a grid", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ id <- new_id()
+ m <- api_create(mtcars, id)
+ m2 <- api_create(iris, id)
+ expect_true(identical(m$embed_url, m2$embed_url))
+ expect_false(identical(m$cols, m2$cols))
+})
+
+test_that("Can overwrite a plot", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ id <- new_id()
+ p <- plot_ly()
+ m <- api_create(p, id)
+ m2 <- api_create(layout(p, title = "test"), id)
+ expect_true(identical(m$embed_url, m2$embed_url))
+ expect_false(identical(m$figure$layout$title, m2$figure$layout$title))
+})
+
+test_that("Can create plots with non-trivial src attributes", {
+ skip_on_cran()
+ skip_if_not_master()
+
+ expect_srcified <- function(x) {
+ expect_length(strsplit(x, ":")[[1]], 3)
+ }
+
+ # src-ifies data arrays, but not arrayOk of length 1
+ p <- plot_ly(x = 1:10, y = 1:10, marker = list(color = "red"))
+ res <- api_create(p)
+ trace <- res$figure$data[[1]]
+ expect_srcified(trace$xsrc)
+ expect_srcified(trace$ysrc)
+ expect_true(trace$marker$color == "red")
+
+ # can src-ify data[i].marker.color
+ p <- plot_ly(x = 1:10, y = 1:10, color = 1:10)
+ res <- api_create(p)
+ trace <- res$figure$data[[1]]
+ expect_srcified(trace$marker$colorsrc)
+
+ # can src-ify frames[i].data[i].marker.color
+ res <- p %>%
+ add_markers(frame = rep(1:2, 5)) %>%
+ api_create()
+ trace <- res$figure$frames[[1]]$data[[1]]
+ expect_srcified(trace$marker$colorsrc)
+
+ # can src-ify layout.xaxis.tickvals
+ res <- api_create(qplot(1:10))
+ expect_srcified(res$figure$layout$xaxis$tickvalssrc)
+
+})
+
+
+
diff --git a/tests/testthat/test-cookbook-axes.R b/tests/testthat/test-cookbook-axes.R
new file mode 100644
index 0000000..a016ed6
--- /dev/null
+++ b/tests/testthat/test-cookbook-axes.R
@@ -0,0 +1,196 @@
+context("cookbook axes")
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("cookbook-axes-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+bp <- ggplot(PlantGrowth, aes(x = group, y = weight)) +
+ geom_boxplot()
+
+# Reverse the order of a discrete-valued axis
+# Get the levels of the factor
+flevels <- levels(PlantGrowth$group)
+# "ctrl" "trt1" "trt2"
+# Reverse the order
+flevels <- rev(flevels)
+# "trt2" "trt1" "ctrl"
+bp.flevels <- bp + scale_x_discrete(limits = flevels)
+
+test_that("factor levels determine tick order", {
+ info <- expect_traces(bp.flevels, 1, "flevels")
+ expect_equivalent(info$layout$xaxis$ticktext, c("trt2", "trt1", "ctrl"))
+})
+
+## These two do the same thing; all data points outside the graphing
+## range are dropped, resulting in a misleading box plot.
+bp.ylim.hide <- bp + ylim(5, 7.5)
+test_that("ylim hides points", {
+ info <- expect_traces(bp.ylim.hide, 1, "ylim.hide")
+})
+
+bp.scale.hide <- bp + scale_y_continuous(limits = c(5, 7.5))
+test_that("scale_y(limits) hides points", {
+ info <- expect_traces(bp.scale.hide, 1, "scale.hide")
+ expect_equivalent(range(info$layout$yaxis$tickvals), c(5, 7.5))
+ y <- unlist(lapply(info$data, "[[", "y"))
+ expect_true(all(5 <= y & y <= 7.5, na.rm = TRUE))
+})
+
+bp.coord <- bp + coord_cartesian(ylim = c(5, 7.5))
+test_that("Using coord_cartesian zooms into the area", {
+ info <- expect_traces(bp.coord, 1, "coord-ylim")
+ expect_equivalent(range(info$layout$yaxis$tickvals), c(5, 7.5))
+ y <- unlist(lapply(info$data, "[[", "y"))
+ expect_false(all(5 <= y & y <= 7.5))
+})
+
+# Create some noisy exponentially-distributed data
+dat <- data.frame(
+ xval = c(0.26932812,-0.05341404,0.36977717,0.91504712,0.46329006,0.37956526, 0.93290644,0.75558976,0.67633497,0.48655293,0.79478162,0.55109982, 0.51681398,0.81073512,0.49406579,0.93919618,0.90472008,0.98732256, 0.94379876,0.95790909,0.54614241,1.13356941,1.13299144,1.18159277, 1.16428407,1.22955005,1.21030897,1.23314811,1.53822718,1.53674330, 1.80020468,1.40774011,1.74573515,1.26651625,2.06607711,1.50237263, 1.38480531,1.83625381,2.35275649,1.99004291,2.80396442,2.20863240, 2.42998876, [...]
+ yval = c(1.177512e+01,7.303113e+00,6.109053e+00,2.545169e+01,3.366341e+01,1.042255e+01,2.703767e+01,1.178223e+01,4.495965e+01,1.614609e+01,4.003015e+01,1.038442e+02,4.024992e+01,4.163942e+01,9.108197e+01,3.116299e+01,2.558871e+02,7.482977e+01,2.502789e+01,5.923683e+01,3.967814e+01,9.207318e+01,1.298618e+02,1.138197e+02,1.804303e+02,3.363494e+02,3.197204e+02,4.968737e+02,1.783433e+02,4.765546e+02,4.486885e+02,6.736079e+02,4.289288e+02,3.433946e+02,5.658634e+02,4.667053e+02,5.257803e+02, [...]
+)
+
+sp <- ggplot(dat, aes(xval, yval)) + geom_point()
+
+test_that("A scatterplot with regular (linear) axis scaling", {
+ info <- expect_traces(sp, 1, "linear-axes")
+})
+
+library(scales)
+sp.log2.scale <- sp + scale_y_continuous(trans = log2_trans())
+
+test_that("log2 scaling of the y axis (with visually-equal spacing)", {
+ info <- expect_traces(sp.log2.scale, 1, "log2-scale")
+})
+
+sp.log2.coord <- sp + coord_trans(y = "log2")
+
+test_that("log2 coordinate transformation with visually-diminishing spacing", {
+ info <- expect_traces(sp.log2.coord, 1, "log2-coord")
+})
+
+sp.labels <- sp +
+ scale_y_continuous(trans = log2_trans(),
+ breaks = trans_breaks("log2", function(x) 2^x),
+ labels = trans_format("log2", math_format(2^.x)))
+
+test_that("log2 transform with labels", {
+ info <- expect_traces(sp.labels, 1, "log2-labels")
+})
+
+sp.log10 <- sp + scale_y_log10()
+
+test_that("scale_y_log10", {
+ info <- expect_traces(sp.log10, 1, "scale_y_log10")
+})
+
+sp.log10.labels <- sp +
+ scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
+ labels = trans_format("log10", math_format(10^.x)))
+
+test_that("log10 with exponents on tick labels", {
+ info <- expect_traces(sp.log10.labels, 1, "scale_y_log10-labels")
+})
+
+no.x.title <- bp +
+ theme(axis.title.x = element_blank()) + # Remove x-axis label
+ ylab("Weight (Kg)") # Set y-axis label
+
+test_that("coord_fixed(ratio)", {
+ info <- expect_traces(no.x.title, 1, "no-x-title")
+ expect_identical(info$layout$xaxis$title, "")
+})
+
+# Also possible to set the axis label with the scale
+# Note that vertical space is still reserved for x"s label
+
+bp.scale.name <- bp + scale_x_discrete(name="") +
+ scale_y_continuous(name="Weight (Kg)")
+
+test_that("scale(name)", {
+ info <- expect_traces(bp.scale.name, 1, "scale-name")
+})
+
+# Change font options:
+# X-axis label: bold, red, and 20 points
+# X-axis tick marks: rotate 90 degrees CCW, move to the left a bit (using vjust,
+# since the labels are rotated), and 16 points
+
+bp.fonts <- bp +
+ theme(
+ axis.title.x = element_text(face = "bold", colour = "#990000", size = 20),
+ axis.text.x = element_text(angle = 90, vjust = 0.5, size = 16)
+ )
+
+test_that("element_text face, colour, size, angle, vjust, size", {
+ info <- expect_traces(bp.fonts, 1, "fonts")
+ expect_equivalent(info$layout$xaxis$tickangle, -90)
+ expect_match(info$layout$xaxis$title, "<b>", fixed = TRUE)
+ expect_match(info$layout$xaxis$title, "</b>", fixed = TRUE)
+})
+
+# Label formatters
+library(scales) # Need the scales package
+
+label.funs <- bp +
+ scale_y_continuous(labels = percent) +
+ scale_x_discrete(labels = abbreviate)
+
+test_that("In this particular case, x scale has no effect", {
+ info <- expect_traces(label.funs, 1, "label-funs")
+})
+
+# Self-defined formatting function for times.
+timeHMS_formatter <- function(x) {
+ h <- floor(x/60)
+ m <- floor(x %% 60)
+ s <- round(60*(x %% 1)) # Round to nearest second
+ lab <- sprintf("%02d:%02d:%02d", h, m, s) # Format the strings as HH:MM:SS
+ lab <- gsub("^00:", "", lab) # Remove leading 00: if present
+ lab <- gsub("^0", "", lab) # Remove leading 0 if present
+}
+
+custom.formatter <- bp + scale_y_continuous(label = timeHMS_formatter)
+
+test_that("custom HMS formatter function", {
+ info <- expect_traces(custom.formatter, 1, "custom-formatter")
+})
+
+blank.minor.major <- bp +
+ theme(panel.grid.minor = element_blank(),
+ panel.grid.major = element_blank())
+
+test_that("Hide all the gridlines", {
+ info <- expect_traces(blank.minor.major, 1, "blank-minor-major")
+})
+
+blank.minor <- bp +
+ theme(panel.grid.minor = element_blank())
+
+test_that("Hide just the minor gridlines", {
+ info <- expect_traces(blank.minor, 1, "blank-minor")
+})
+
+blank.x <- bp +
+ theme(panel.grid.minor.x = element_blank(),
+ panel.grid.major.x = element_blank())
+
+test_that("Hide all the horizontal gridlines", {
+ info <- expect_traces(blank.x, 1, "blank-x")
+})
+
+blank.y <- bp +
+ theme(panel.grid.minor.y = element_blank(),
+ panel.grid.major.y = element_blank())
+
+test_that("Hide all the vertical gridlines", {
+ info <- expect_traces(blank.y, 1, "blank-y")
+})
diff --git a/tests/testthat/test-cookbook-lines.R b/tests/testthat/test-cookbook-lines.R
new file mode 100644
index 0000000..64800d6
--- /dev/null
+++ b/tests/testthat/test-cookbook-lines.R
@@ -0,0 +1,205 @@
+context("cookbook lines")
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("cookbook-axes-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+# Some sample data
+df <- data.frame(
+ cond = c("control", "treatment"),
+ result = c(10, 11.5),
+ hline = c(9, 12)
+)
+
+# Basic bar plot
+bp <- ggplot(df, aes(x = cond, y = result)) +
+ geom_bar(position = "dodge", stat = "identity")
+
+test_that("geom_bar -> 1 trace", {
+ info <- expect_traces(bp, 1, "basic-bar")
+})
+
+# Add a horizontal line
+temp <- bp + geom_hline(aes(yintercept = 12))
+test_that("bar + hline = 2 traces", {
+ info <- expect_traces(temp, 2, "basic-horizontal-line")
+})
+
+# Make the line red and dashed
+temp <- bp + geom_hline(aes(yintercept=12), colour="#990000", linetype="dashed")
+test_that("bar + red dashed hline", {
+ info <- expect_traces(temp, 2, "dashed-red-line")
+ hline.info <- info$data[[2]]
+ expect_identical(hline.info$line$color, toRGB("#990000"))
+ expect_identical(hline.info$line$dash, "dash")
+})
+
+
+# Need to re-specify bp, because the data has changed
+bp <- ggplot(df, aes(x=cond, y=result)) +
+ geom_bar(position=position_dodge(), stat="identity")
+
+bp.err <- bp +
+ geom_errorbar(aes(y = hline, ymax = hline, ymin = hline),
+ colour = "#AA0000")
+test_that("Draw with separate lines for each bar", {
+ expect_traces(bp.err, 2, "bar-error-wide")
+})
+
+bp.err.narrow <- bp +
+ geom_errorbar(width = 0.5, aes(y = hline, ymax = hline, ymin = hline),
+ colour = "#AA0000")
+test_that("Make the lines narrower", {
+ info <- expect_traces(bp.err.narrow, 2, "bar-error-narrow")
+})
+
+
+# Can get the same result, even if we get the hline values from a second data frame
+# Define data frame with hline
+df.hlines <- data.frame(
+ cond = c("control","treatment"),
+ hline = c(9,12)
+)
+
+
+bp.err.diff <- bp +
+ geom_errorbar(data = df.hlines, aes(y = hline, ymax = hline, ymin = hline),
+ colour = "#AA0000")
+test_that("The bar graph are from df, but the lines are from df.hlines", {
+ info <- expect_traces(bp.err.diff, 2, "bar-error-diff")
+})
+
+df <- read.table(header=T, text="
+ cond group result hline
+ control A 10 9
+treatment A 11.5 12
+ control B 12 9
+treatment B 14 12
+")
+
+bp <- ggplot(df, aes(x = cond, y = result, fill = group)) +
+ geom_bar(position = position_dodge(), stat = "identity")
+
+test_that("bar dodged colored -> 1 trace", {
+ info <- expect_traces(bp, 2, "bar-dodge-color")
+})
+
+bp.err <-
+ bp + geom_errorbar(aes(y = hline, ymax = hline, ymin = hline),
+ linetype = "dashed")
+
+test_that("The error bars get plotted over one another", {
+ info <- expect_traces(bp.err, 4, "bar-dodge-color-error")
+})
+
+df <- read.table(header = TRUE, text = "
+ cond group result hline
+ control A 10 11
+treatment A 11.5 12
+ control B 12 12.5
+treatment B 14 15
+")
+
+bp <- ggplot(df, aes(x = cond, y = result, fill = group)) +
+ geom_bar(position = position_dodge(), stat = "identity")
+
+bp.err4 <- bp +
+ geom_errorbar(aes(y = hline, ymax = hline + 1, ymin = hline - 1),
+ linetype = "dashed", position = position_dodge())
+
+test_that("4 error bars", {
+ info <- expect_traces(bp.err4, 4, "bar-dodge-color-err4")
+})
+
+df <- read.table(header = T, text = "
+ cond xval yval
+ control 11.5 10.8
+ control 9.3 12.9
+ control 8.0 9.9
+ control 11.5 10.1
+ control 8.6 8.3
+ control 9.9 9.5
+ control 8.8 8.7
+ control 11.7 10.1
+ control 9.7 9.3
+ control 9.8 12.0
+ treatment 10.4 10.6
+ treatment 12.1 8.6
+ treatment 11.2 11.0
+ treatment 10.0 8.8
+ treatment 12.9 9.5
+ treatment 9.1 10.0
+ treatment 13.4 9.6
+ treatment 11.6 9.8
+ treatment 11.5 9.8
+ treatment 12.0 10.6
+")
+sp <- ggplot(df, aes(x = xval, y = yval, colour = cond)) + geom_point()
+
+test_that("basic scatterplot", {
+ info <- expect_traces(sp, 2, "scatter-basic")
+})
+
+temp <- sp + geom_hline(aes(yintercept=10))
+test_that("Add a horizontal line", {
+ info <- expect_traces(temp, 3, "scatter-hline")
+})
+
+temp <- sp +
+ geom_hline(aes(yintercept = 10)) +
+ geom_vline(aes(xintercept = 11.5),
+ colour = "#BB0000", linetype = "dashed")
+test_that("Add a red dashed vertical line", {
+ info <- expect_traces(temp, 4, "scatter-hline-vline")
+ expect_true(info$layout$showlegend)
+ mode <- sapply(info$data, "[[", "mode")
+ line.traces <- info$data[mode == "lines"]
+ expect_equivalent(length(line.traces), 2)
+ dash <- sapply(line.traces, function(tr)tr$line$dash)
+ dash.traces <- line.traces[dash == "dash"]
+ expect_equivalent(length(dash.traces), 1)
+ dash.trace <- dash.traces[[1]]
+ expect_identical(dash.trace$line$color, toRGB("#BB0000"))
+})
+
+# Facet, based on cond
+spf <- sp + facet_grid(. ~ cond)
+test_that("scatter facet -> 2 traces", {
+ info <- expect_traces(spf, 2, "scatter-facet")
+ expect_true(info$data[[1]]$xaxis != info$data[[2]]$xaxis)
+ expect_true(info$data[[1]]$yaxis == info$data[[2]]$yaxis)
+ # only one yaxis
+ expect_equivalent(sum(grepl("yaxis", names(info$layout))), 1)
+})
+
+temp <- spf + geom_hline(aes(yintercept=10))
+test_that("geom_hline -> 2 more traces", {
+ info <- expect_traces(temp, 4, "scatter-facet-hline")
+
+ expect_true(info$layout$showlegend)
+ has.name <- sapply(info$data, function(tr) isTRUE(nchar(tr$name) > 0))
+ expect_equivalent(sum(has.name), 2)
+})
+
+df.vlines <- data.frame(cond = levels(df$cond), xval = c(10,11.5))
+# cond xval
+# control 10.0
+# treatment 11.5
+
+spf.vline <-
+ spf +
+ geom_hline(aes(yintercept = 10)) +
+ geom_vline(aes(xintercept = xval),
+ data = df.vlines,
+ colour = "#990000", linetype = "dashed")
+test_that("geom_vline -> 2 more traces", {
+ info <- expect_traces(spf.vline, 6, "scatter-facet-hline-vline")
+})
diff --git a/tests/testthat/test-cookbook-scatterplots.R b/tests/testthat/test-cookbook-scatterplots.R
new file mode 100644
index 0000000..ec83271
--- /dev/null
+++ b/tests/testthat/test-cookbook-scatterplots.R
@@ -0,0 +1,82 @@
+# Make some noisily increasing data
+dat <- data.frame(
+ cond = rep(c("A", "B"), each = 10),
+ xvar = c(1.475957, -3.423712, 1.966129, 5.575364, 2.954719, 2.768286, 3.507499, 6.945000, 12.135050, 10.231673, 13.040393, 12.231689, 13.506993, 13.590874, 15.455178, 28.431185, 17.758937, 24.730797, 22.954238, 21.122766),
+ yvar = c(-1.315387, 3.323239, 4.452183, 4.597885, 5.697203, 5.991221, 5.764561, 10.163165, 14.805634, 11.447913, 12.163597, 10.930851, 13.491366, 11.800783, 19.246991, 13.870457, 11.031923, 22.700302, 24.877547, 22.520114)
+)
+# cond xvar yvar
+# A -4.252354091 3.473157275
+# A 1.702317971 0.005939612
+# ...
+# B 17.793359218 19.718587761
+# B 19.319909163 19.647899863
+g <- ggplot(dat, aes(x=xvar, y=yvar)) +
+ geom_point(shape=1) # Use hollow circles
+save_outputs(g, "scatterplots-hollow")
+
+g <- ggplot(dat, aes(x=xvar, y=yvar)) +
+ geom_point(shape=1) +
+ geom_smooth(method=lm) # Add linear regression line
+save_outputs(g, "scatterplots-smooth-lm")
+
+g <- ggplot(dat, aes(x=xvar, y=yvar)) +
+ geom_point(shape=1) +
+ geom_smooth(method=lm, se=FALSE) # Don't add shaded confidence region
+save_outputs(g, "scatterplots-smooth-lm-se-false")
+
+
+g <- ggplot(dat, aes(x=xvar, y=yvar)) +
+ geom_point(shape=1) + # Use hollow circles
+ geom_smooth() # Add a loess smoothed fit curve with confidence region
+save_outputs(g, "scatterplots-loess")
+
+# Set color by cond
+g <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1)
+save_outputs(g, "scatterplots-color")
+
+# # Same, but with different colors and add regression lines
+g <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1) +
+ scale_colour_hue(l=50) + # Use a slightly darker palette than normal
+ geom_smooth(method=lm, se=FALSE)
+save_outputs(g, "scatterplots-scale-color-hue")
+
+# Extend the regression lines beyond the domain of the data
+g <- ggplot(dat, aes(x=xvar, y=yvar, color=cond)) + geom_point(shape=1) +
+ scale_colour_hue(l=50) +
+ geom_smooth(method=lm, se=FALSE, fullrange=T)
+save_outputs(g, "scatterplots-full-range")
+
+# Set shape by cond
+g <- ggplot(dat, aes(x=xvar, y=yvar, shape=cond)) + geom_point()
+save_outputs(g, "scatterplots-shape")
+
+# Same, but with different shapes
+g <- ggplot(dat, aes(x=xvar, y=yvar, shape=cond)) + geom_point() +
+ scale_shape_manual(values=c(1,2)) # Use a hollow circle and triangle
+save_outputs(g, "scatterplots-shape-manual")
+
+# Round xvar and yvar to the nearest 5
+dat$xrnd <- round(dat$xvar/5)*5
+dat$yrnd <- round(dat$yvar/5)*5
+
+# Make each dot partially transparent, with 1/4 opacity
+# For heavy overplotting, try using smaller values
+g <- ggplot(dat, aes(x=xrnd, y=yrnd)) +
+ geom_point(shape=19, # Use solid circles
+ alpha=1/4) # 1/4 opacity
+save_outputs(g, "scatterplots-overlap")
+
+# Jitter the points
+# Jitter range is 1 on the x-axis, .5 on the y-axis
+g <- ggplot(dat, aes(x=xrnd, y=yrnd)) +
+ geom_point(shape=1, # Use hollow circles
+ position=position_jitter(width=1,height=.5))
+save_outputs(g, "scatterplots-jitter")
+
+# Jitter the points using geom_jitter
+# Jitter range is 1 on the x-axis, .5 on the y-axis
+g <- ggplot(dat, aes(x = xrnd, y = yrnd)) +
+ geom_jitter(shape = 1, # Use hollow circles
+ width = 1, height = 0.5)
+save_outputs(g, "scatterplots-geom_jitter")
+
diff --git a/tests/testthat/test-ggplot-abline.R b/tests/testthat/test-ggplot-abline.R
new file mode 100644
index 0000000..40e7e38
--- /dev/null
+++ b/tests/testthat/test-ggplot-abline.R
@@ -0,0 +1,46 @@
+context("Abline")
+
+# 'Abline' refers to the line coefficients, as in y = a b * x
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("cookbook-axes-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data=has.data, layout=L$layout)
+}
+
+test_that("Second trace be the a-b line", {
+ x <- seq(0, 3.5, by = 0.5)
+ y <- x * 0.95
+ df <- data.frame(x, y)
+
+ gg <- ggplot(df) + geom_point(aes(x, y, size = x)) +
+ geom_abline(intercept = 1.1, slope = 0.9, colour = "red", size = 4)
+
+ L <- expect_traces(gg, 2, "single-abline")
+
+ dat <- L$data[[2]]
+ expect_true(dat$x[1] <= 0)
+ expect_true(dat$x[2] >= 3.5)
+ expect_identical(dat$mode, "lines")
+ expect_identical(dat$showlegend, FALSE)
+})
+
+test_that("abline aesthetics", {
+ df <- data.frame(
+ m = c(2, -3, 0.1),
+ b = c(1, 0, -1)
+ )
+ p <- ggplot(df) + xlim(c(-5, 5)) + ylim(c(-5, 5)) +
+ geom_abline(aes(intercept = b, slope = m))
+
+ L <- expect_traces(p, 1, "multiple-abline")
+ expect_identical(range(L$layout$xaxis$tickvals), c(-5, 5))
+ expect_identical(range(L$layout$yaxis$tickvals), c(-5, 5))
+})
+
diff --git a/tests/testthat/test-ggplot-annotation_.R b/tests/testthat/test-ggplot-annotation_.R
new file mode 100644
index 0000000..b0f58ee
--- /dev/null
+++ b/tests/testthat/test-ggplot-annotation_.R
@@ -0,0 +1,59 @@
+context("annotation_")
+
+
+# Generate data
+rainbow <- matrix(hcl(seq(0, 360, length.out = 50 * 50), 80, 70), nrow = 50)
+p <- ggplot(mtcars, aes(mpg, wt)) +
+ geom_point() + facet_wrap(~vs) +
+ annotation_raster(rainbow, 15, 20, 3, 4)
+
+
+test_that("Basic annotation_raster() example works", {
+
+ l <- plotly_build(p)$x
+
+ # currently we add a blank geom for each raster
+ # not sure if that is the best thing to do...
+ expect_length(l$data, 4)
+
+ expect_length(l$layout$images, 2)
+ expect_equivalent(l$layout$images[[1]]$xref, "x")
+ expect_equivalent(l$layout$images[[1]]$yref, "y")
+ expect_equivalent(l$layout$images[[2]]$xref, "x2")
+ expect_equivalent(l$layout$images[[2]]$yref, "y")
+
+ for (i in 1:2) {
+ expect_equivalent(l$layout$images[[i]]$layer, "above")
+ expect_equivalent(l$layout$images[[i]]$xanchor, "left")
+ expect_equivalent(l$layout$images[[i]]$yanchor, "bottom")
+ expect_equivalent(l$layout$images[[i]]$sizex, 5)
+ expect_equivalent(l$layout$images[[i]]$sizey, 1)
+ expect_equivalent(l$layout$images[[i]][["x"]], 15)
+ expect_equivalent(l$layout$images[[i]][["y"]], 3)
+ expect_equivalent(l$layout$images[[i]]$sizing, "stretch")
+ }
+
+ # TODO: how to test the data URI content?
+
+})
+
+
+
+usamap <- map_data("state")
+seal.sub <- subset(seals, long > -130 & lat < 45 & lat > 40)
+p <- ggplot(seal.sub, aes(x = long, y = lat)) +
+ annotation_map(usamap, fill = "NA", colour = "grey50") +
+ geom_segment(aes(xend = long + delta_long, yend = lat + delta_lat))
+
+
+test_that("Basic annotation_map() example works", {
+
+ l <- plotly_build(p)$x
+
+ expect_equivalent(l$data[[1]]$type, "scatter")
+ expect_equivalent(l$data[[1]]$fill, "toself")
+ expect_equivalent(l$data[[1]]$fillcolor, "transparent")
+ expect_equivalent(l$data[[1]]$hoveron, "fills")
+ expect_false(l$data[[1]]$showlegend)
+
+})
diff --git a/tests/testthat/test-ggplot-area.R b/tests/testthat/test-ggplot-area.R
new file mode 100644
index 0000000..35c5cca
--- /dev/null
+++ b/tests/testthat/test-ggplot-area.R
@@ -0,0 +1,70 @@
+context("Area")
+
+# Test that the order of traces is correct
+# Expect traces function
+expect_traces <- function(gg, n_traces, name) {
+ stopifnot(is.numeric(n_traces))
+ save_outputs(gg, paste0("area-", name))
+ L <- gg2list(gg)
+ all_traces <- L$data
+ no_data <- sapply(all_traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has_data <- all_traces[!no_data]
+ expect_equivalent(length(has_data), n_traces)
+ list(data = has_data, layout = L$layout)
+}
+
+huron <- data.frame(year = 1875:1972, level = as.vector(LakeHuron))
+# like getAnywhere(round_any.numeric)
+huron$decade <- floor(huron$year / 10) * 10
+
+ar <- ggplot(huron) + geom_area(aes(x = year, y = level))
+
+test_that("sanity check for geom_area", {
+ L <- expect_traces(ar, 1, "simple")
+ expect_identical(L$data[[1]]$type, "scatter")
+ expect_identical(L$data[[1]]$mode, "lines")
+ expect_identical(L$data[[1]]$fill, "toself")
+ expect_true(
+ L$data[[1]]$fillcolor ==
+ toRGB(GeomArea$default_aes$fill, GeomArea$default_aes$alpha)
+ )
+})
+
+# Test alpha transparency in fill color
+gg <- ggplot(huron) + geom_area(aes(x = year, y = level), alpha = 0.4)
+
+test_that("transparency alpha in geom_area is converted", {
+ L <- expect_traces(gg, 1, "area-fillcolor")
+ expect_true(L$data[[1]]$line$color == "transparent")
+ expect_true(
+ L$data[[1]]$fillcolor ==
+ toRGB(GeomArea$default_aes$fill, 0.4)
+ )
+})
+
+
+# Generate data
+df <- aggregate(price ~ cut + carat, data = diamonds, FUN = length)
+names(df)[3] <- "n"
+temp <- aggregate(n ~ carat, data = df, FUN = sum)
+names(temp)[2] <- "sum.n"
+df <- merge(x = df, y = temp, all.x = TRUE)
+df$freq <- df$n / df$sum.n
+# Generate ggplot object
+p <- ggplot(data = df, aes(x = carat, y = freq, fill = cut)) +
+ geom_area()
+# Test
+test_that("traces are ordered correctly in geom_area", {
+ info <- expect_traces(p, 5, "traces_order")
+ tr <- info$data[[1]]
+ la <- info$layout
+ expect_identical(tr$type, "scatter")
+ # check trace order
+ trace.names <- levels(df$cut)
+ for (i in 1:5){
+ expect_true(grepl(trace.names[i], info$data[[i]]$name))
+ }
+})
+
diff --git a/tests/testthat/test-ggplot-bar.R b/tests/testthat/test-ggplot-bar.R
new file mode 100644
index 0000000..05f0e71
--- /dev/null
+++ b/tests/testthat/test-ggplot-bar.R
@@ -0,0 +1,176 @@
+context("bar")
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("bar-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+researchers <- data.frame(
+ country = c("Canada", "Canada", "Germany", "USA"),
+ name = c("Warren", "Andreanne", "Stefan", "Toby"),
+ papers = c(23, 14, 37, 20),
+ field = c("Math", "Bio", "Bio", "Math")
+)
+
+gg <- ggplot(researchers, aes(country, papers, fill = field))
+
+test_that("position_dodge()", {
+ gg.dodge <- gg + geom_bar(stat = "identity", position = "dodge")
+ info <- expect_traces(gg.dodge, 2, "dodge")
+ expect_identical(info$layout$barmode, "relative")
+
+ l <- ggplotly(gg.dodge, dynamicTicks = "x")$x
+ expect_identical(l$layout$barmode, "dodge")
+ expect_equivalent(l$data[[1]]$x, c("Canada", "Germany"))
+ expect_equivalent(l$data[[1]]$name, "Bio")
+ expect_equivalent(l$data[[2]]$x, c("Canada", "USA"))
+ expect_equivalent(l$data[[2]]$name, "Math")
+})
+
+test_that("position_stack()", {
+ gg.stack <- gg + geom_bar(stat = "identity", position = "stack")
+ info <- expect_traces(gg.stack, 2, "stack")
+ expect_identical(info$layout$barmode, "relative")
+
+ l <- ggplotly(gg.stack, dynamicTicks = T)$x
+ expect_identical(l$layout$barmode, "relative")
+})
+
+test_that("position_identity()", {
+ gg.identity <- gg + geom_bar(stat = "identity", position = "identity")
+ info <- expect_traces(gg.identity, 2, "identity")
+ expect_identical(info$layout$barmode, "relative")
+
+ l <- ggplotly(gg.identity, dynamicTicks = T)$x
+ expect_identical(l$layout$barmode, "relative")
+})
+
+test_that("dates work well with bar charts", {
+ researchers$month <- c("2012-01-01", "2012-01-01", "2012-02-01", "2012-02-01")
+ researchers$month <- as.Date(researchers$month)
+ gd <- ggplot(researchers, aes(month, papers, fill = field)) +
+ geom_bar(stat = "identity")
+ info <- expect_traces(gd, 2, "dates")
+
+ # by default, date axes are linear...
+ expect_equivalent(info$layout$xaxis$type, "linear")
+ expect_equivalent(
+ info$data[[1]]$x,
+ as.numeric(unique(researchers$month))
+ )
+
+ # different story for dynamicTicks...
+ l <- ggplotly(gd, dynamicTicks = TRUE)$x
+ expect_equivalent(l$layout$xaxis$type, "date")
+ expect_equivalent(l$layout$xaxis$tickmode, "auto")
+ expect_is(l$layout$xaxis$range, "Date")
+ for (attr in c("x", "width")) {
+ expect_is(l$data[[1]][[attr]], "Date")
+ }
+
+})
+
+## http://www.cookbook-r.com/Graphs/Bar_and_line_graphs_%28ggplot2%29/
+df <- data.frame(
+ time = factor(c("Lunch","Dinner"), levels = c("Lunch","Dinner")),
+ total_bill = c(14.89, 17.23)
+)
+
+test_that("Very basic bar graph", {
+ gg <- ggplot(data = df, aes(x = time, y = total_bill)) +
+ geom_bar(stat = "identity")
+ info <- expect_traces(gg, 1, "nocolor")
+ tr <- info$data[[1]]
+ expect_identical(tr$type, "bar")
+ expect_equivalent(tr$y, df$total_bill)
+})
+
+test_that("Map the time of day to different fill colors", {
+ gg <- ggplot(data = df, aes(x = time, y = total_bill, fill = time)) +
+ geom_bar(stat = "identity")
+ info <- expect_traces(gg, 2, "color")
+ # is the color of the two bars the same?
+ expect_false(
+ identical(info$data[[1]]$marker$color, info$data[[2]]$marker$color)
+ )
+ expect_true(info$layout$showlegend)
+})
+
+test_that("Add a black outline", {
+ gg <- ggplot(data = df, aes(x = time, y = total_bill, fill = time)) +
+ geom_bar(colour = "black", stat = "identity")
+ info <- expect_traces(gg, 2, "black-outline")
+ for(tr in info$data){
+ expect_true(is.character(tr$marker$color))
+ expect_identical(tr$marker$line$color, toRGB("black"))
+ expect_true(tr$showlegend)
+ }
+ expect_true(info$layout$showlegend)
+})
+
+
+test_that('guides(colour="none") does not affect fill legend', {
+ gg <- ggplot(data = df, aes(x = time, y = total_bill, fill = time)) +
+ geom_bar(color = "black", stat = "identity") +
+ guides(colour = "none")
+ info <- expect_traces(gg, 2, "aes-fill-guides-color-none")
+ expect_true(info$layout$showlegend)
+})
+
+test_that("guides(fill=FALSE) does not affect colour legend", {
+ gg <- ggplot(data = df, aes(x = time, y = total_bill, colour = time)) +
+ geom_bar(fill = "grey", stat = "identity") +
+ guides(fill = FALSE)
+ info <- expect_traces(gg, 2, "aes-colour-guides-fill-FALSE")
+ for(tr in info$data){
+ expect_equivalent(tr$marker$color, toRGB("grey"))
+ expect_true(is.character(tr$marker$line$color))
+ expect_true(tr$showlegend)
+ }
+ expect_match(info$layout$annotations[[1]]$text, "time")
+ expect_true(info$layout$showlegend)
+})
+
+
+base <- ggplot(mtcars, aes(factor(vs), fill = factor(cyl)))
+
+test_that("geom_bar() stacks counts", {
+ info <- expect_traces(base + geom_bar(), 3, "position-stack")
+ expect_identical(info$layout$barmode, "relative")
+ trs <- info$data
+ # sum of y values for each trace
+ test <- as.numeric(sort(sapply(trs, function(x) sum(x$y))))
+ true <- as.numeric(sort(table(mtcars$cyl)))
+ expect_identical(test, true)
+})
+
+test_that("geom_bar(position = 'fill') stacks proportions", {
+ info <- expect_traces(base + geom_bar(position = "fill"), 3, "position-fill")
+ expect_identical(info$layout$barmode, "relative")
+ trs <- info$data
+ # sum of y-values *conditioned* on a x-value
+ prop <- sum(sapply(sapply(trs, "[[", "y"), "[", 1))
+ expect_identical(prop, 1)
+})
+
+d <- diamonds[1:50, ]
+gbar <- ggplot(d, aes(cut, price)) + geom_bar(stat = "identity")
+
+test_that("Using identity with multiple y for a given x works ", {
+ info <- expect_traces(gbar, 1, "category-names")
+})
+
+p <- ggplot(mtcars, aes(factor(cyl))) + geom_bar() + coord_flip()
+
+test_that("geom_bar() + coord_flip() works", {
+ info <- expect_traces(p, 1, "coord-flip")
+ expect_identical(info$data[[1]]$orientation, "h")
+})
+
diff --git a/tests/testthat/test-ggplot-blank.R b/tests/testthat/test-ggplot-blank.R
new file mode 100644
index 0000000..3f98069
--- /dev/null
+++ b/tests/testthat/test-ggplot-blank.R
@@ -0,0 +1,14 @@
+context("blank")
+
+test_that("geom_blank", {
+ l <- ggplotly(qplot())$x
+
+ expect_length(l$data, 1)
+ expect_false(l$data[[1]]$visible)
+
+ l <- ggplotly(ggplot())$x
+
+ expect_length(l$data, 1)
+ expect_false(l$data[[1]]$visible)
+
+})
diff --git a/tests/testthat/test-ggplot-boxplot.R b/tests/testthat/test-ggplot-boxplot.R
new file mode 100644
index 0000000..86cf6b9
--- /dev/null
+++ b/tests/testthat/test-ggplot-boxplot.R
@@ -0,0 +1,86 @@
+context("Boxplot")
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ save_outputs(gg, paste0("boxplot-", name))
+ L <- gg2list(gg)
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(traces=has.data, layout=L$layout)
+}
+
+test_that("geom_boxplot gives a boxplot", {
+ gg <- ggplot(mtcars, aes(factor(cyl), mpg)) + geom_boxplot()
+
+ L <- save_outputs(gg, "boxplot")
+
+ expect_length(L$data, 1)
+ expect_true(L$data[[1]]$type == "box")
+ expect_true(L$data[[1]]$orientation %||% "v" == "v")
+})
+
+test_that("geom_boxplot with coord_flip", {
+ p <- ggplot(diamonds, aes(cut, price)) +
+ geom_boxplot() +
+ coord_flip()
+
+ L <- plotly_build(p)$x
+
+ expect_length(L$data, 1)
+ expect_true(L$data[[1]]$orientation == "h")
+ expect_equivalent(sort(L$data[[1]]$x), sort(diamonds[["price"]]))
+})
+
+
+test_that("you can make a boxplot for a distribution of datetimes", {
+ dist <- c(10, 20, 33, 40, 11, 12, 11)
+ dist <- as.POSIXct(paste0("2014-09-19 10:00:", dist))
+ df <- data.frame(y=dist)
+ df$x <- 0
+
+ bp <- ggplot(df) + geom_boxplot(aes(x, y))
+
+ L <- save_outputs(bp, "boxplot-datetime")
+
+ expect_equivalent(length(L$data), 1) # 1 trace
+ expect_equivalent(L$data[[1]]$type, "box")
+ expect_equivalent(L$data[[1]]$y, as.numeric(df$y))
+})
+
+# check legend shows up when each box-and-whiskers has a fill
+# make ggplot2
+m <- ggplot(mtcars, aes(factor(cyl), mpg))
+p <- m + geom_boxplot(aes(fill = factor(cyl)))
+# tests
+test_that("legends for boxplot", {
+ info <- expect_traces(p, 3, "legends_for_fill")
+ tr <- info$traces
+ la <- info$layout
+ expect_identical(tr[[1]]$type, "box")
+ # check legend exists
+ expect_identical(la$showlegend, TRUE)
+ # check legend for each fill exists
+ for (i in 1:3) {
+ expect_identical(tr[[i]]$showlegend, TRUE)
+ }
+})
+
+dat <- data.frame(
+ cond = factor(rep(c("A", "B", "C", "D"), each = 200)),
+ col = factor(rep(c("C1", "C2"), each = 400)),
+ rating = c(rnorm(200), rnorm(200, mean=.8), rnorm(200, mean=.4), rnorm(200, mean=.2))
+)
+g <- ggplot(dat, aes(x = cond, y = rating)) +
+ geom_boxplot(outlier.shape = NA, aes(fill = col))
+
+test_that("correct # of unique fillcolors", {
+ L <- save_outputs(g, "boxplot-fillcolor")
+ expect_equivalent(length(L$data), 2)
+ expect_identical(L$data[[1]]$type, "box")
+ fills <- sapply(L$data, "[[", "fillcolor")
+ expect_equivalent(length(unique(fills)), length(unique(dat$col)))
+})
diff --git a/tests/testthat/test-ggplot-col.R b/tests/testthat/test-ggplot-col.R
new file mode 100644
index 0000000..64eba35
--- /dev/null
+++ b/tests/testthat/test-ggplot-col.R
@@ -0,0 +1,29 @@
+context("geom_col")
+
+# https://gist.github.com/jsonbecker/0cc702804512fdf29c7f9067adfc17d0#gistcomment-1935427
+df <- data.frame(
+ type = c(rep('Elementary', 2), rep('Middle', 2), rep('High', 2)),
+ included = rep(c('included','excluded'), 3),
+ dollars = c(1000, 2500, 4000, 1000, 3000, 2800)
+)
+
+p <- df %>%
+ group_by(type) %>%
+ mutate(prop = dollars / sum(dollars)) %>%
+ ggplot(aes(type, prop, fill = included)) +
+ geom_col() +
+ geom_text(aes(label = scales::percent(prop)), position = position_stack(vjust = 0.5))
+
+test_that("geom_col is supported", {
+ l <- save_outputs(p, "col")
+ barDat <- l$data[sapply(l$data, "[[", "type") %in% "bar"]
+ expect_equivalent(
+ unlist(lapply(barDat, "[[", "x")),
+ c(1, 2, 3, 1, 2, 3)
+ )
+ expect_equal(
+ unlist(lapply(barDat, "[[", "y")),
+ c(0.7142857, 0.4827586, 0.2, 0.2857143, 0.5172414, 0.8),
+ tolerance = 0.0001
+ )
+})
diff --git a/tests/testthat/test-ggplot-contour.R b/tests/testthat/test-ggplot-contour.R
new file mode 100644
index 0000000..ead33f9
--- /dev/null
+++ b/tests/testthat/test-ggplot-contour.R
@@ -0,0 +1,15 @@
+context("Contour")
+
+volcano3d <- reshape2::melt(volcano)
+names(volcano3d) <- c("x", "y", "z")
+# Draw a contour plot using geom_contour
+gg <- ggplot(volcano3d) + geom_contour(aes(x=x, y=y, z=z))
+L <- save_outputs(gg, "contour")
+
+test_that("geom_contour is translated to a path", {
+ expect_equivalent(length(L$data), 1)
+ expect_identical(L$data[[1]]$type, "scatter")
+ expect_identical(L$data[[1]]$mode, "lines")
+})
+
+
diff --git a/tests/testthat/test-ggplot-coord-fixed.R b/tests/testthat/test-ggplot-coord-fixed.R
new file mode 100644
index 0000000..08088d1
--- /dev/null
+++ b/tests/testthat/test-ggplot-coord-fixed.R
@@ -0,0 +1,96 @@
+context("coord_fixed")
+
+test_that("single-panel fixed coordinates", {
+
+ base <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
+
+ l <- ggplotly(base + coord_equal())$x
+
+ expect_equal(l$layout$yaxis$scaleanchor, "x")
+ expect_equal(l$layout$yaxis$scaleratio, 1)
+
+ l2 <- ggplotly(base + coord_fixed(0.5))$x
+
+ expect_equal(l2$layout$yaxis$scaleanchor, "x")
+ expect_equal(l2$layout$yaxis$scaleratio, 0.5)
+
+})
+
+test_that("multi-panel fixed coordinates", {
+
+ base <- ggplot(mtcars, aes(wt, mpg)) +
+ geom_point()
+
+ # fixed scales
+ p <- base +
+ coord_equal() +
+ facet_wrap(~cyl)
+
+ l <- ggplotly(p)$x
+
+ expect_equal(l$layout$xaxis$scaleanchor, "y")
+ expect_equal(l$layout$xaxis$scaleratio, 1)
+ expect_equal(l$layout$xaxis2$scaleanchor, "y")
+ expect_equal(l$layout$xaxis2$scaleratio, 1)
+ expect_equal(l$layout$xaxis3$scaleanchor, "y")
+ expect_equal(l$layout$xaxis3$scaleratio, 1)
+
+ # free scales
+ # NOTE: ggplot2 doesn't even do the "correct" thing here, but we do!
+ p <- base +
+ coord_equal() +
+ facet_wrap(~cyl, scales = "free")
+
+ l <- ggplotly(p)$x
+
+ expect_equal(l$layout$yaxis$scaleanchor, "x")
+ expect_equal(l$layout$yaxis$scaleratio, 1)
+ expect_equal(l$layout$yaxis2$scaleanchor, "x2")
+ expect_equal(l$layout$yaxis2$scaleratio, 1)
+ expect_equal(l$layout$yaxis3$scaleanchor, "x3")
+ expect_equal(l$layout$yaxis3$scaleratio, 1)
+
+ # fixed scales (2 rows)
+ p <- base +
+ coord_equal() +
+ facet_wrap(~cyl, ncol = 2)
+
+ l <- ggplotly(p)$x
+
+ expect_equal(l$layout$yaxis$scaleanchor, "x")
+ expect_equal(l$layout$yaxis$scaleratio, 1)
+ expect_equal(l$layout$yaxis2$scaleanchor, "x")
+ expect_equal(l$layout$yaxis2$scaleratio, 1)
+ expect_equal(l$layout$xaxis$scaleanchor, "y2")
+ expect_equal(l$layout$xaxis$scaleratio, 1)
+
+ # facet_grid
+ p <- base +
+ coord_equal() +
+ facet_grid(~cyl)
+
+ l <- ggplotly(p)$x
+
+ expect_equal(l$layout$xaxis$scaleanchor, "y")
+ expect_equal(l$layout$xaxis$scaleratio, 1)
+ expect_equal(l$layout$xaxis2$scaleanchor, "y")
+ expect_equal(l$layout$xaxis2$scaleratio, 1)
+ expect_equal(l$layout$xaxis3$scaleanchor, "y")
+ expect_equal(l$layout$xaxis3$scaleratio, 1)
+
+})
+
+# TODO: why does this fail on Travis?
+# test_that("coord_map", {
+#
+# nz <- map_data("nz")
+# nzmap <- ggplot(nz, aes(x = long, y = lat, group = group)) +
+# geom_polygon(fill = "white", colour = "black") +
+# coord_map()
+# l <- ggplotly(nzmap)$x
+#
+# expect_equal(l$layout$xaxis$scaleanchor, "y")
+# expect_equal(
+# l$layout$xaxis$scaleratio, 0.7023914, tolerance = 0.0001
+# )
+# })
diff --git a/tests/testthat/test-ggplot-crossbar.R b/tests/testthat/test-ggplot-crossbar.R
new file mode 100644
index 0000000..a5e628f
--- /dev/null
+++ b/tests/testthat/test-ggplot-crossbar.R
@@ -0,0 +1,49 @@
+context("crossbar")
+
+
+d <- data.frame(
+ trt = factor(c(1, 1, 2, 2)),
+ resp = c(1, 5, 3, 4),
+ group = factor(c(1, 2, 1, 2)),
+ upper = c(1.1, 5.3, 3.3, 4.2),
+ lower = c(0.8, 4.6, 2.4, 3.6)
+)
+
+p <- ggplot(d, aes(trt, resp)) +
+ geom_crossbar(aes(ymin = lower, ymax = upper), width = 0.2)
+
+
+test_that("Basic geom_crossbar() works", {
+
+ l <- plotly_build(p)$x
+
+ # one trace for each rect and one trace for middle segments,
+ # but does it _really_ matter?
+ expect_length(l$data, 5)
+
+})
+
+
+p <- ggplot(d, aes(trt, resp, color = group, linetype = group)) +
+ geom_crossbar(aes(ymin = lower, ymax = upper), width = 0.2) +
+ scale_colour_manual(values = c("red", "purple"))
+
+test_that("geom_crossbar() with aesthetics", {
+
+ l <- plotly_build(p)$x
+
+ # one trace for each rect and 2 traces for middle segments...
+ expect_length(l$data, 6)
+
+ colors <- vapply(l$data, function(x) x$line$color, character(1))
+ dashes <- vapply(l$data, function(x) x$line$dash, character(1))
+
+ expect_equivalent(
+ unique(colors), toRGB(c("red", "purple"))
+ )
+
+ expect_equivalent(
+ unique(dashes), lty2dash(1:2)
+ )
+
+})
diff --git a/tests/testthat/test-ggplot-date.R b/tests/testthat/test-ggplot-date.R
new file mode 100644
index 0000000..e33dd20
--- /dev/null
+++ b/tests/testthat/test-ggplot-date.R
@@ -0,0 +1,37 @@
+context("date")
+
+test_that("datetimes are converted to e.g. 2013-01-02 05:00:00", {
+ in.str <- c("17 Mar 1983 06:33:44 AM",
+ "17 Mar 1984 01:59:55 PM")
+ time.obj <- strptime(in.str, "%d %b %Y %I:%M:%S %p")
+ out.str <- strftime(time.obj, "%Y-%m-%d %H:%M:%S")
+ df <- rbind(data.frame(who = "me", time.obj, dollars = c(1.1, 5.6)),
+ data.frame(who = "you", time.obj, dollars = c(10.2, 0)))
+ gg <- qplot(time.obj, dollars, data = df, color = who, geom = "line")
+ info <- save_outputs(gg, "date-strings")
+ expect_equivalent(length(info$data), 2)
+ for(trace in info$data[1:2]){
+ expect_equivalent(as.numeric(time.obj), trace$x)
+ }
+})
+
+test_that("class Date is supported", {
+ df <- data.frame(
+ x = c("2013-01-01", "2013-01-02", "2013-01-03"),
+ y = c(2, 3, 2.5)
+ )
+ df$x <- as.Date(df$x)
+ gg <- ggplot(df) + geom_line(aes(x = x, y = y))
+ info <- save_outputs(gg, "date-class-Date")
+ expect_equivalent(length(info$data), 1)
+})
+
+test_that("scale_x_date and irregular time series work", {
+ df <- data.frame(
+ date = seq(as.Date("2121-12-12"), len = 100, by = "1 day")[sample(100, 50)],
+ price = runif(50)
+ )
+ df <- df[order(df$date), ]
+ dt <- qplot(date, price, data = df, geom = "line") + theme(aspect.ratio = 1/4)
+ info <- save_outputs(dt, "date-irregular-time-series")
+})
diff --git a/tests/testthat/test-ggplot-density.R b/tests/testthat/test-ggplot-density.R
new file mode 100644
index 0000000..c938b6d
--- /dev/null
+++ b/tests/testthat/test-ggplot-density.R
@@ -0,0 +1,74 @@
+context("Probability density")
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("density-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data=has.data, layout=L$layout)
+}
+
+# Draw a probability density estimation using geom_density
+base <- ggplot(mtcars, aes(wt))
+
+test_that("geom_density() is translated to area chart", {
+ info <- expect_traces(base + geom_density(), 1, "simple")
+ tr <- info$data[[1]]
+ expect_identical(tr$type, "scatter")
+ expect_identical(tr$mode, "lines")
+ expect_identical(tr$fill, "toself")
+})
+
+test_that("geom_density() respects fill aesthetic", {
+ gg <- base + geom_density(aes(fill = factor(vs)), alpha = 0.3)
+ info <- expect_traces(gg, 2, "fill")
+ trs <- info$data
+ expect_identical(
+ unique(sapply(trs, "[[", "type")), "scatter"
+ )
+ expect_identical(
+ unique(sapply(trs, "[[", "fill")), "toself"
+ )
+ # check legend exists
+ expect_true(info$layout$showlegend, TRUE)
+ # check legend for each fill exists
+ expect_true(all(sapply(trs, "[[", "showlegend")))
+})
+
+test_that("geom_density() respects colour aesthetic", {
+ info <- expect_traces(base + geom_density(aes(colour=factor(vs))), 2, "color")
+ trs <- info$data
+ expect_identical(
+ unique(sapply(trs, "[[", "type")), "scatter"
+ )
+ expect_identical(
+ unique(sapply(trs, "[[", "fill")), "toself"
+ )
+})
+
+g <- base +
+ geom_histogram(aes(y = ..density..), binwidth = 0.5, fill = "pink") +
+ geom_density(fill = "lightblue", alpha = 0.1)
+
+test_that("geom_histogram(aes(y = ..density..)) + geom_density() works", {
+ info <- expect_traces(g, 2, "histogram")
+ trs <- info$data
+ type <- unique(sapply(trs, "[[", "type"))
+ expect_identical(sort(type), c("bar", "scatter"))
+})
+
+# Check if the traces are in the correct order when position = stack
+# Generate ggplot object
+p <- ggplot(data = mtcars, aes(x = mpg, fill = factor(cyl))) +
+ geom_density(position = "stack")
+
+test_that("traces are ordered correctly in geom_density", {
+ info <- expect_traces(p, 3, "traces_order")
+ nms <- as.character(sapply(info$data, "[[", "name"))
+ expect_identical(nms, c("4", "6", "8"))
+})
+
diff --git a/tests/testthat/test-ggplot-density2d.R b/tests/testthat/test-ggplot-density2d.R
new file mode 100644
index 0000000..d32ab9c
--- /dev/null
+++ b/tests/testthat/test-ggplot-density2d.R
@@ -0,0 +1,72 @@
+context("Density2d")
+
+# Draw a 2d density estimation using geom_density2d
+m <- ggplot(MASS::geyser, aes(x=duration, y=waiting)) +
+ geom_point(alpha = 0.4) +
+ geom_density2d()
+L <- save_outputs(m, "density2d")
+
+test_that("geom_density2d translates to path(s)", {
+ expect_equivalent(length(L$data), 2)
+ expect_identical(L$data[[2]]$type, "scatter")
+ expect_identical(L$data[[2]]$mode, "lines")
+})
+
+faithful$col <- factor(sample(1:20, nrow(faithful), replace = T))
+m <- ggplot(faithful, aes(x = eruptions, y = waiting)) +
+ stat_density_2d(aes(fill = ..level..), geom = "polygon") +
+ geom_point(aes(colour = col)) +
+ xlim(0.5, 6) + ylim(40, 110)
+
+L <- save_outputs(m, "density2dfill")
+
+test_that("StatDensity2d with GeomPolygon translates to filled path(s)", {
+ # only the marker traces should be shown in the legend
+ legends <- unlist(lapply(L$data, "[[", "showlegend"))
+ points <- L$data[legends]
+ # make sure we have 20 traces of points
+ expect_equivalent(length(points), 20)
+ expect_identical(
+ unique(unlist(lapply(points, "[[", "type"))), "scatter"
+ )
+ expect_identical(
+ unique(unlist(lapply(points, "[[", "mode"))), "markers"
+ )
+ # the other traces should be the colorbar and polygons
+ notPoints <- L$data[!legends]
+ polygons <- notPoints[-length(notPoints)]
+ colorbar <- notPoints[[length(notPoints)]]
+ expect_identical(
+ unique(unlist(lapply(polygons, "[[", "type"))), "scatter"
+ )
+ expect_identical(
+ unique(unlist(lapply(polygons, "[[", "mode"))), "lines"
+ )
+ expect_identical(
+ unique(unlist(lapply(polygons, "[[", "fill"))), "toself"
+ )
+ # split on fill for polygons
+ # (you can't have two polygons with different fill in a single trace)
+ expect_true(
+ length(unique(unlist(lapply(polygons, "[[", "fillcolor")))) > 1
+ )
+ # ensure the legend/guide are placed correctly
+ expect_true(L$layout$legend$y < 0.5)
+ expect_true(L$layout$legend$yanchor == "top")
+ expect_true(colorbar$marker$colorbar$y == 1)
+ expect_true(colorbar$marker$colorbar$yanchor == "top")
+ expect_true(colorbar$marker$colorbar$len == 0.5)
+
+ #test some properties that shouldn't be sensitive to ggplot2 defaults
+ expect_true(colorbar$marker$colorbar$title == "level")
+
+ # are the hidden colorbar markers on the correct range?
+ for (xy in c("x", "y")) {
+ rng <- L$layout[[paste0(xy, "axis")]]$range
+ expect_true(
+ all(min(rng) <= colorbar[[xy]] & colorbar[[xy]] <= max(rng))
+ )
+ }
+
+})
+
diff --git a/tests/testthat/test-ggplot-device.R b/tests/testthat/test-ggplot-device.R
new file mode 100644
index 0000000..a0351ad
--- /dev/null
+++ b/tests/testthat/test-ggplot-device.R
@@ -0,0 +1,8 @@
+context("device")
+
+test_that("ggplotly doesn't leave a new device open", {
+ devList1 <- dev.list()
+ p <- ggplotly(qplot(1:10))
+ devList2 <- dev.list()
+ expect_true(length(devList1) == length(devList2))
+})
diff --git a/tests/testthat/test-ggplot-dynamicTicks.R b/tests/testthat/test-ggplot-dynamicTicks.R
new file mode 100644
index 0000000..c1bf910
--- /dev/null
+++ b/tests/testthat/test-ggplot-dynamicTicks.R
@@ -0,0 +1,106 @@
+context("dynamicTicks")
+
+# note: test-ggplot-lines.R has some tests for date dynamicTicks
+
+test_that("Discrete axis maps to categorical type", {
+ g <- ggplot(mpg, aes(class, color = class)) + geom_bar()
+ p <- ggplotly(g, dynamicTicks = "x")
+
+ classes <- getLevels(mpg[["class"]])
+
+ axisActual <- with(
+ p$x$layout$xaxis, list(type, tickmode, categoryorder, categoryarray)
+ )
+ axisExpect <- list("category", "auto", "array", classes)
+ expect_equivalent(axisActual, axisExpect)
+ # trace data reflects the "domain" values
+ expect_equivalent(
+ sort(sapply(p$x$data, "[[", "x")), classes
+ )
+
+ # y-axis is left as expected
+ axisActual <- with(
+ p$x$layout$yaxis, list(type, tickmode)
+ )
+ axisExpect <- list("linear", "array")
+ expect_equivalent(axisActual, axisExpect)
+})
+
+test_that("Categorical axis reflects custom scale mapping", {
+
+ lims <- c("2seater", "suv")
+
+ g <- ggplot(mpg, aes(class, color = class)) +
+ geom_bar() +
+ scale_x_discrete(limits = lims)
+ p <- ggplotly(g, dynamicTicks = "x")
+
+ axisActual <- with(
+ p$x$layout$xaxis, list(type, tickmode, categoryorder, categoryarray)
+ )
+ axisExpect <- list("category", "auto", "array", lims)
+ expect_equivalent(axisActual, axisExpect)
+ expect_equivalent(
+ sort(sapply(p$x$data, "[[", "x")), sort(lims)
+ )
+
+ labs <- c("small", "large")
+ g <- ggplot(mpg, aes(class, color = class)) +
+ geom_bar() +
+ scale_x_discrete(limits = lims, labels = labs)
+ p <- ggplotly(g, dynamicTicks = "x")
+
+ axisActual <- with(
+ p$x$layout$xaxis, list(type, tickmode, categoryorder, categoryarray)
+ )
+ axisExpect <- list("category", "auto", "array", labs)
+ expect_equivalent(axisActual, axisExpect)
+ expect_equivalent(
+ sort(sapply(p$x$data, "[[", "x")), sort(labs)
+ )
+
+})
+
+test_that("Time axis inverse transforms correctly", {
+
+ d <- data.frame(
+ x = seq(Sys.Date(), Sys.Date() + 9, length.out = 10),
+ y = rnorm(10)
+ )
+
+ l <- ggplotly(ggplot(d, aes(x, y)) + geom_line(), dynamicTicks = TRUE)$x
+
+ expect_length(l$data, 1)
+ expect_equivalent(l$layout$xaxis$type, "date")
+ expect_equivalent(l$layout$xaxis$tickmode, "auto")
+ expect_is(l$layout$xaxis$range, "Date")
+ expect_equivalent(l$data[[1]][["x"]], d$x)
+
+ d2 <- data.frame(
+ x = seq(Sys.time(), Sys.time() + 9000, length.out = 10),
+ y = rnorm(10)
+ )
+
+ l2 <- ggplotly(ggplot(d2, aes(x, y)) + geom_line(), dynamicTicks = TRUE)$x
+
+ expect_length(l2$data, 1)
+ expect_equivalent(l2$layout$xaxis$type, "date")
+ expect_equivalent(l2$layout$xaxis$tickmode, "auto")
+ expect_is(l2$layout$xaxis$range, "POSIXt")
+ expect_equivalent(l2$data[[1]][["x"]], d2$x)
+
+})
+
+
+test_that("Inverse maps colorbar data", {
+
+ p <- ggplot(mpg, aes(hwy, manufacturer)) +
+ stat_bin2d(aes(fill = ..density..), binwidth = c(3,1))
+
+ l <- ggplotly(p, dynamicTicks = TRUE)$x
+
+ expect_length(l$data, 2)
+ expect_true(l$data[[2]]$y %in% unique(mpg$manufacturer))
+
+})
+
diff --git a/tests/testthat/test-ggplot-errorbar-horizontal.R b/tests/testthat/test-ggplot-errorbar-horizontal.R
new file mode 100644
index 0000000..83b5f43
--- /dev/null
+++ b/tests/testthat/test-ggplot-errorbar-horizontal.R
@@ -0,0 +1,23 @@
+context("geom_errorbarh")
+
+test_that("geom_errorbarh gives horizontal errorbars", {
+
+ df <- data.frame(
+ trt = factor(c(1, 1, 2, 2)),
+ resp = c(1, 5, 3, 4),
+ group = factor(c(1, 2, 1, 2)),
+ se = c(0.1, 0.3, 0.3, 0.4)
+ )
+ g <- ggplot(df, aes(resp, trt, colour=group)) + geom_point()
+ # Define the limits of the horizontal errorbars
+ g <- g + geom_errorbarh(aes(xmax = resp + se, xmin = resp - se))
+
+ L <- save_outputs(g, "errorbar-horizontal")
+
+ # Expect scatter plot and its error bars to have the same color
+ expect_equivalent(L$data[[1]]$marker$color, L$data[[3]]$error_x$color)
+ expect_equivalent(L$data[[2]]$marker$color, L$data[[4]]$error_x$color)
+ # Expect given errorbar values
+ expect_equivalent(L$data[[3]]$error_x$array, c(0.1, 0.3))
+ expect_equivalent(L$data[[4]]$error_x$array, c(0.3, 0.4))
+})
diff --git a/tests/testthat/test-ggplot-errorbar.R b/tests/testthat/test-ggplot-errorbar.R
new file mode 100644
index 0000000..8162ff8
--- /dev/null
+++ b/tests/testthat/test-ggplot-errorbar.R
@@ -0,0 +1,42 @@
+context("Errorbar")
+
+test_that("geom_errorbar gives errorbars", {
+
+ d <- dplyr::summarise(
+ dplyr::group_by_(mtcars, "cyl"),
+ q1 = quantile(mpg, 0.25),
+ m = mean(mpg),
+ q3 = quantile(mpg, 0.75)
+ )
+
+ g <- ggplot(d, aes(x = cyl, y = m)) + geom_line() +
+ geom_errorbar(aes(ymin = q1, ymax = q3))
+
+ L <- save_outputs(g, "errorbar")
+
+ # 1 trace should have error_y
+ idx <- vapply(L$data, function(x) is.null(x$error_y), logical(1))
+ expect_true(sum(idx) == 1)
+ # right data for errorbar ymax
+ expect_equivalent(L$data[!idx][[1]]$error_y$array, d$q3 - d$m)
+ expect_equivalent(L$data[!idx][[1]]$error_y$arrayminus, d$m - d$q1)
+})
+
+df <- data.frame(
+ trt = factor(c(1, 1, 2, 2)),
+ resp = c(1, 5, 3, 4),
+ group = factor(c(1, 2, 3, 4)),
+ upper = c(1.1, 5.3, 3.3, 4.2),
+ lower = c(0.8, 4.6, 2.4, 3.6)
+)
+
+p <- ggplot(df, aes(trt, resp, colour = group))
+g <- p + geom_errorbar(aes(ymin = lower, ymax = upper))
+
+test_that("geom_errorbar boxes an array of length 1", {
+ L <- save_outputs(g, "errorbar-unique-groups")
+ expect_equivalent(L$data[[1]]$error_y$array, I(0.1))
+ expect_equivalent(L$data[[1]]$error_y$arrayminus, I(0.2))
+})
+
+# TODO fix and add a test for width of errorbars
diff --git a/tests/testthat/test-ggplot-facets.R b/tests/testthat/test-ggplot-facets.R
new file mode 100644
index 0000000..be6aa9c
--- /dev/null
+++ b/tests/testthat/test-ggplot-facets.R
@@ -0,0 +1,133 @@
+context("Facets")
+
+test_that("6 facets becomes 6 panels", {
+ data(barley, package = "lattice")
+ gg <- qplot(yield, variety, data = barley,
+ color = year, facets = site ~ ., pch = I(1))+
+ theme_bw() +
+ theme(panel.spacing = grid::unit(0, "cm"))
+ info <- save_outputs(gg, "barley")
+ # two legend entries, but two groups
+ expect_equivalent(sum(sapply(info$data, "[[", "showlegend")), 2)
+ expect_identical(
+ sort(unique(sapply(info$data, "[[", "legendgroup"))), c("1931", "1932")
+ )
+ expect_identical(
+ sort(unique(sapply(info$data, "[[", "name"))), c("1931", "1932")
+ )
+})
+
+test_that("3 facets becomes 3 panels", {
+ df <- data.frame(
+ x = runif(99),
+ y = runif(99),
+ z = rep(c('a','b','c'), 33)
+ )
+ gg <- qplot(x, y, data = df, facets = z ~ ., pch = I(1)) +
+ theme_bw() +
+ theme(panel.spacing = grid::unit(0, "cm"))
+ info <- save_outputs(gg, "3-panels")
+ yaxes <- sapply(info$data, "[[", "yaxis")
+ xaxes <- sapply(info$data, "[[", "xaxis")
+ expect_true(all(c("y", "y2", "y3") %in% yaxes))
+ expect_true(all(xaxes == "x"))
+})
+
+# expect a certain number of _unique_ [x/y] axes
+expect_axes <- function(info, n, axis = "x") {
+ pattern <- paste0("^", axis, "axis([0-9]+)?$")
+ axes <- with(info, layout[grepl(pattern, names(layout))])
+ n.axes <- length(axes)
+ ranges <- do.call("rbind", lapply(axes, function(x) x$range))
+ expect_identical(nrow(unique(ranges)), as.integer(n))
+}
+
+no_panels <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
+
+test_that("facet_wrap(..., scales = 'free') creates interior scales", {
+ free_both <- no_panels + facet_wrap(~ am + vs, scales = "free")
+ info <- save_outputs(free_both, "facet_wrap_free")
+ expect_axes(info, 4L)
+ expect_axes(info, 4L, "y")
+
+ free_y <- no_panels + facet_wrap(~am+vs, scales = "free_y")
+ info <- save_outputs(free_y, "facet_wrap_free_y")
+ expect_axes(info, 1L)
+ expect_axes(info, 4L, "y")
+
+ free_x <- no_panels + facet_wrap(~am+vs, scales = "free_x")
+ info <- save_outputs(free_x, "facet_wrap_free_x")
+ expect_axes(info, 4L)
+ expect_axes(info, 1L, "y")
+})
+
+test_that("facet_grid(..., scales = 'free') doesnt create interior scales.", {
+ free_both <- no_panels + facet_grid(vs ~ am, scales = "free")
+ info <- save_outputs(free_both, "facet_grid_free")
+ expect_axes(info, 2L)
+ expect_axes(info, 2L, "y")
+
+ free_y <- no_panels + facet_grid(vs~am, scales = "free_y")
+ info <- save_outputs(free_y, "facet_grid_free_y")
+ expect_axes(info, 1L)
+ expect_axes(info, 2L, "y")
+
+ free_x <- no_panels + facet_grid(vs~am, scales = "free_x")
+ info <- save_outputs(free_x, "facet_grid_free_x")
+ expect_axes(info, 2L)
+ expect_axes(info, 1L, "y")
+})
+
+gg <- ggplot(mtcars, aes(mpg, wt)) +
+ geom_point() + geom_line() +
+ facet_wrap(~ cyl, scales = "free", ncol = 2)
+
+test_that("facet_wrap(..., scales = 'free') can handle multiple traces on each panel", {
+ info <- save_outputs(gg, "facet_wrap_free_mult")
+ yaxes <- unique(sapply(info$data, "[[", "yaxis"))
+ for (i in yaxes) {
+ dat <- info$data[sapply(info$data, "[[", "yaxis") %in% i]
+ modes <- sort(sapply(dat, "[[", "mode"))
+ expect_true(all(modes %in% c("lines", "markers")))
+ }
+})
+
+test_that("facet_wrap() doesn't create interior scales", {
+ g <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + facet_wrap(~cyl)
+ info <- save_outputs(g, "facet_wrap")
+ expect_equivalent(unique(unlist(lapply(info$data, "[[", "yaxis"))), "y")
+})
+
+
+g <- ggplot(mtcars, aes(mpg, wt)) +
+ geom_point() +
+ facet_wrap( ~ am, labeller = label_both)
+
+test_that("facet_wrap translates simple labeller function", {
+ info <- save_outputs(g, "facet_wrap-labeller")
+ txt <- sapply(info$layout$annotations, "[[", "text")
+ expect_true(all(c("am: 0", "am: 1") %in% txt))
+})
+
+g <- ggplot(mtcars, aes(mpg, wt)) +
+ geom_point() +
+ facet_grid(vs ~ am, labeller = label_both)
+
+test_that("facet_grid translates simple labeller function", {
+ info <- save_outputs(g, "facet_grid-labeller")
+ txt <- sapply(info$layout$annotations, "[[", "text")
+ expect_true(
+ all(c("am: 0", "am: 1", "vs: 0", "vs: 1") %in% txt)
+ )
+})
+
+p <- economics %>% tidyr::gather(variable, value, -date) %>%
+ qplot(data = ., date, value) +
+ facet_wrap(~variable, scale = "free_y", ncol = 2)
+
+test_that("when y scales are free, x-axes are still anchored on exterior", {
+ info <- save_outputs(p, "facet_wrap-free_y")
+ xaxes <- info$layout[grep("^xaxis", names(info$layout))]
+ yaxes <- info$layout[grep("^yaxis", names(info$layout))]
+ expect_equivalent(unique(sapply(xaxes, "[[", "anchor")), "y5")
+})
diff --git a/tests/testthat/test-ggplot-ggplotly.R b/tests/testthat/test-ggplot-ggplotly.R
new file mode 100644
index 0000000..d2c5276
--- /dev/null
+++ b/tests/testthat/test-ggplot-ggplotly.R
@@ -0,0 +1,31 @@
+context("ggplotly+plotly")
+
+p <- ggplot(txhousing, aes(x = date, y = median, group = city)) +
+ geom_line(alpha = 0.3)
+
+test_that("ggplotly returns original data with special attributes", {
+ dat <- ggplotly(p) %>% plotly_data()
+ expect_equivalent(dat, p$data)
+ expect_equivalent(as.character(dplyr::groups(dat)), "city")
+})
+
+test_that("can filter data returned by ggplotly", {
+ dat <- ggplotly(p) %>% filter(city == "Houston") %>% plotly_data()
+ expect_equivalent(dat, subset(p$data, city == "Houston"))
+ expect_equivalent(as.character(dplyr::groups(dat)), "city")
+})
+
+test_that("can add traces with original _and_ scaled data", {
+ l1 <- ggplotly(p) %>% add_lines() %>% plotly_build()
+ expect_equivalent(length(l1$x$data), 2)
+ l2 <- ggplotly(p, originalData = FALSE) %>%
+ add_lines() %>% plotly_build()
+ # ideally we'd test that the two plots have the same data, but
+ # for some reason R CMD check throws an error which I can't replicate :(
+ expect_equivalent(length(l2$x$data), 2)
+})
+
+test_that("can access ggplot data in layout()", {
+ l <- ggplotly(p) %>% layout(title = ~range(date))
+ expect_equivalent(plotly_build(l)$x$layout$title, range(txhousing$date))
+})
diff --git a/tests/testthat/test-ggplot-heatmap.R b/tests/testthat/test-ggplot-heatmap.R
new file mode 100644
index 0000000..da853c6
--- /dev/null
+++ b/tests/testthat/test-ggplot-heatmap.R
@@ -0,0 +1,62 @@
+context("Heatmap")
+
+wdays <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
+dtimes <- c("Morning", "Afternoon", "Evening")
+workweek <- matrix(
+ c(1, 20, 30, 20, 1, 60, 30, 60, 1, 50, 80, -10, 1, 30, 20),
+ nrow = 5, ncol = 3, byrow = TRUE,
+ dimnames = list(day = wdays, time = dtimes)
+)
+ww <- reshape2::melt(workweek)
+ww$day <- factor(ww$day, wdays)
+ww$time <- factor(ww$time, dtimes)
+# Plot a heatmap using geom_tile
+hm <- ggplot(ww) + geom_tile(aes(x = day, y = time, fill = value))
+
+test_that("geom_tile is translated to type=heatmap", {
+ L <- save_outputs(hm, "heatmap")
+ # one trace is for the colorbar
+ expect_equivalent(length(L$data), 2)
+ expect_equivalent(L$data[[1]]$type, "heatmap")
+ expect_equivalent(L$layout$xaxis$ticktext, wdays)
+ expect_equivalent(L$layout$yaxis$ticktext, dtimes)
+ # show bin value on hover (but without x/y since they are discrete)
+ expect_true(
+ L$data[[1]]$hoverinfo == "text"
+ )
+ expect_true(
+ all(grepl("value:\\s+[-]?[0-9]+$", c(L$data[[1]]$text)))
+ )
+})
+
+d <- expand.grid(
+ x = seq(0, 1, .005),
+ y = seq(0, 1, .005)
+)
+d$z <- with(d, (1 - y) * x / ((1 - y) * x + y * (1 - x)))
+p <- ggplot(data = d, aes(x, y)) +
+ geom_tile(aes(fill = z)) +
+ scale_fill_gradient2(low = '#67001f', mid = 'white', high = '#053061', midpoint = .5)
+
+test_that("geom_tile() scale_fill_gradient2()", {
+ L <- save_outputs(p, "heatmap-midpoint")
+ # one trace is for the colorbar
+ expect_equivalent(length(L$data), 2)
+ expect_equivalent(L$data[[1]]$type, "heatmap")
+})
+
+tidy_cor <- function(x) {
+ co <- as.data.frame(cor(x[vapply(x, is.numeric, logical(1))]))
+ co$var1 <- row.names(co)
+ tidyr::gather(co, var2, cor, -var1)
+}
+d <- tidy_cor(mtcars)
+p <- ggplot(d, aes(var1, var2, fill = cor)) + geom_tile()
+
+test_that("geom_tile() with discrete x/y", {
+ L <- save_outputs(p, "heatmap-discrete")
+ # one trace is for the colorbar
+ expect_equivalent(length(L$data), 2)
+ expect_equivalent(L$data[[1]]$type, "heatmap")
+})
+
diff --git a/tests/testthat/test-ggplot-hex.R b/tests/testthat/test-ggplot-hex.R
new file mode 100644
index 0000000..d94cccd
--- /dev/null
+++ b/tests/testthat/test-ggplot-hex.R
@@ -0,0 +1,22 @@
+context("hex")
+
+d <- ggplot(diamonds, aes(carat, price))
+
+test_that("geom_hex", {
+ g <- d + geom_hex()
+ l <- save_outputs(g, "hex-basic")
+ expect_true(length(l$data) > 1)
+})
+
+
+test_that("geom_hex with bins", {
+ g <- d + geom_hex(bins = 10)
+ l <- save_outputs(g, "hex-bins")
+ expect_true(length(l$data) > 1)
+})
+
+test_that("geom_hex with binwidth", {
+ g <- d + geom_hex(binwidth = c(1, 1000))
+ l <- save_outputs(g, "hex-binwidth")
+ expect_true(length(l$data) > 1)
+})
diff --git a/tests/testthat/test-ggplot-histogram.R b/tests/testthat/test-ggplot-histogram.R
new file mode 100644
index 0000000..7995abd
--- /dev/null
+++ b/tests/testthat/test-ggplot-histogram.R
@@ -0,0 +1,232 @@
+context("Histogram")
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("histogram-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+base <- ggplot(mtcars, aes(wt))
+
+test_that("geom_histogram() is a bar chart of counts with no bargap", {
+ info <- expect_traces(base + geom_histogram(), 1, "counts")
+ tr <- info$data[[1]]
+ expect_identical(tr$type, "bar")
+ expect_equivalent(sum(tr$y), nrow(mtcars))
+ expect_equivalent(info$layout$barmode, "relative")
+})
+
+test_that("geom_histogram(aes(y = ..density..)) displays a density", {
+ info <- expect_traces(base + geom_histogram(aes(y=..density..)), 1, "density")
+ tr <- info$data[[1]]
+ expect_identical(tr$type, "bar")
+ #default binwidth
+ bw <- (max(tr$x) - min(tr$x))/30
+ area <- sum(tr$y) * bw
+ # the "area" of the plot (should be 1).
+ # note this also serves as a check for the default binwidth
+ expect_equal(area, 1, tolerance = 0.1)
+})
+
+test_that("geom_histogram(aes(fill = ..count..)) works", {
+ info <- expect_traces(base + geom_histogram(aes(fill = ..count..)), 6, "fill")
+ # grab just the bar traces (there should also be a colorbar)
+ bars <- info$data[sapply(info$data, "[[", "type") == "bar"]
+ # each traces should have the same value of y
+ for (i in seq_along(bars)) {
+ ys <- bars[[i]]$y
+ expect_equivalent(length(unique(ys)), 1)
+ }
+})
+
+test_that("Histogram with fixed colour/fill works", {
+ gg <- base + geom_histogram(colour = "darkgreen", fill = "white")
+ info <- expect_traces(gg, 1, "fixed-fill-color")
+ tr <- info$data[[1]]
+ expect_true(tr$marker$color == "rgba(255,255,255,1)")
+ expect_true(tr$marker$line$color == "rgba(0,100,0,1)")
+})
+
+test_that("Specify histogram binwidth", {
+ gg <- base + geom_histogram(aes(y=..density..), binwidth = 0.3)
+ info <- expect_traces(gg, 1, "density-binwidth")
+ tr <- info$data[[1]]
+ area <- sum(tr$y) * 0.3
+ expect_equivalent(area, 1, 0.1)
+})
+
+test_that("geom_histogram(aes(fill = factor(...))) is a stacked by default", {
+ gg <- base + geom_histogram(aes(fill = factor(vs)))
+ info <- expect_traces(gg, 2, "fill-factor")
+ expect_equivalent(info$layout$barmode, "relative")
+})
+
+test_that("geom_histogram(aes(fill = factor(...))) respects position_identity()", {
+ gg <- base + geom_histogram(
+ aes(fill = factor(vs)), alpha = 0.3, position = "identity"
+ )
+ info <- expect_traces(gg, 2, "fill-factor-identity")
+ expect_equivalent(info$layout$barmode, "relative")
+})
+
+test_that("geom_histogram(aes(fill = factor(...))) respects position_dodge()", {
+ gg <- base + geom_histogram(
+ aes(fill = factor(vs)), alpha = 0.3, position = "dodge"
+ )
+ info <- expect_traces(gg, 2, "fill-factor-dodge")
+ expect_equivalent(info$layout$barmode, "relative")
+})
+
+test_that("geom_histogram() with facets", {
+ gg <- base + geom_histogram(aes(fill = factor(vs)), alpha = 0.3) +
+ facet_wrap(~am)
+ info <- expect_traces(gg, 4, "fill-factor-facets")
+ trs <- info$data
+ type <- unique(sapply(trs, "[[", "type"))
+ gap <- unique(sapply(trs, "[[", "bargap"))
+ barmode <- unique(sapply(trs, "[[", "barmode"))
+ expect_identical(type, "bar")
+ expect_equivalent(info$layout$barmode, "relative")
+})
+
+test_that("vline overlaid histogram", {
+ gg <- base + geom_histogram() +
+ geom_vline(aes(xintercept=mean(wt)), color="red", linetype="dashed", size=1)
+ info <- expect_traces(gg, 2, "vline")
+ trs <- info$data
+ type <- unique(sapply(trs, "[[", "type"))
+ expect_identical(sort(type), c("bar", "scatter"))
+})
+
+# Non-numeric (date) data
+noram <- data.frame(
+ month = c("2012-01-01", "2012-02-01", "2012-01-01", "2012-01-01",
+ "2012-03-01", "2012-02-01")
+)
+noram$month <- as.Date(noram$month)
+
+test_that("dates work well with histograms", {
+ hist <- ggplot(noram, aes(month)) + geom_histogram()
+ info <- expect_traces(hist, 1, "dates")
+})
+
+# Non-numeric (date) data, specifying binwidth
+killed <- data.frame(date=c("2014-12-24",
+ "2014-12-23",
+ "2014-12-22",
+ "2014-12-22",
+ "2014-12-22",
+ "2014-12-18",
+ "2014-12-22",
+ "2014-12-21",
+ "2014-12-21",
+ "2014-12-21",
+ "2014-12-20",
+ "2014-12-19",
+ "2014-12-18",
+ "2014-12-18",
+ "2014-12-17",
+ "2014-12-17",
+ "2013-12-20",
+ "2014-04-25",
+ "2014-12-01",
+ "2014-12-17",
+ "2014-12-17",
+ "2014-12-17",
+ "2014-12-17",
+ "2014-12-17",
+ "2014-12-17",
+ "2014-12-15",
+ "2014-12-15",
+ "2014-12-15",
+ "2014-12-14",
+ "2014-12-14",
+ "2014-12-14",
+ "2014-12-13",
+ "2014-12-13",
+ "2013-05-18",
+ "2014-12-13",
+ "2014-12-12",
+ "2014-12-12",
+ "2014-12-11",
+ "2014-12-10",
+ "2014-12-10",
+ "2014-12-10",
+ "2014-12-10",
+ "2014-12-09",
+ "2014-12-09",
+ "2014-12-09",
+ "2014-12-09",
+ "2014-12-08",
+ "2014-12-08",
+ "2014-12-08",
+ "2014-12-07",
+ "2014-12-08",
+ "2014-12-07",
+ "2014-05-01",
+ "2014-12-05",
+ "2014-12-05",
+ "2014-12-05",
+ "2014-12-04",
+ "2014-12-04",
+ "2014-12-04",
+ "2014-07-13",
+ "2014-12-02",
+ "2014-12-03",
+ "2014-12-03",
+ "2014-12-02",
+ "2014-12-02",
+ "2014-12-01",
+ "2014-12-01",
+ "2014-12-01",
+ "2014-04-02",
+ "2014-11-30",
+ "2014-11-30",
+ "2014-11-29",
+ "2014-11-28",
+ "2014-11-29",
+ "2014-11-27",
+ "2014-11-28",
+ "2014-11-27",
+ "2014-11-26",
+ "2014-11-25",
+ "2014-11-26",
+ "2014-11-25",
+ "2014-11-25",
+ "2014-11-24",
+ "2014-11-24",
+ "2014-11-23",
+ "2014-11-23",
+ "2014-11-24",
+ "2014-11-23",
+ "2014-11-22",
+ "2014-11-23",
+ "2014-11-22",
+ "2014-11-22",
+ "2014-11-21",
+ "2014-11-21",
+ "2014-11-21",
+ "2014-11-20",
+ "2014-11-20",
+ "2014-11-20",
+ "2014-11-19"))
+
+test_that("datetime binning for class POSIXt works in histograms", {
+ kP <- killed
+ kP$date <- as.POSIXct(kP$date)
+ histP <- ggplot(kP, aes(x = date)) + geom_histogram(binwidth = 2592000)
+ info <- expect_traces(histP, 1, "POSIXt-bins")
+})
+
+test_that("datetime binning for class Date works in histograms", {
+ kD <- killed
+ kD$date <- as.Date(kD$date)
+ histD <- ggplot(kD, aes(x = date)) + geom_histogram(binwidth = 30)
+ info <- expect_traces(histD, 1, "Date-bins")
+})
diff --git a/tests/testthat/test-ggplot-hline.R b/tests/testthat/test-ggplot-hline.R
new file mode 100644
index 0000000..4e03db2
--- /dev/null
+++ b/tests/testthat/test-ggplot-hline.R
@@ -0,0 +1,77 @@
+context("Hline")
+# Horizontal line
+
+x <- seq(0, 3.5, by = 0.5)
+y <- x * 0.95
+df <- data.frame(x, y)
+gg <- ggplot(df) + geom_point(aes(x, y))
+
+test_that("second trace be the hline", {
+ p <- gg + geom_hline(yintercept = 1.1, colour = "green", size = 3)
+
+ L <- save_outputs(p, "hline")
+ expect_equivalent(length(L$data), 2)
+ l <- L$data[[2]]
+ expect_equivalent(unique(l$y), 1.1)
+ expect_true(min(l$x) < min(x))
+ expect_true(max(l$x[2]) > max(x))
+ expect_identical(l$mode, "lines")
+ expect_true(l$line$color == "rgba(0,255,0,1)")
+})
+
+test_that("vector yintercept results in multiple horizontal lines", {
+ p <- gg + geom_hline(yintercept = 1:3, colour = "red", size = 3)
+
+ L <- save_outputs(p, "hline-multiple")
+ expect_equivalent(length(L$data), 2)
+ l <- L$data[[2]]
+ ys <- l$y
+ expect_equivalent(ys, c(1, 1, NA, 2, 2, NA, 3, 3))
+ xs <- l$x
+ expect_true(min(xs, na.rm = TRUE) < min(x))
+ expect_true(max(xs, na.rm = TRUE) > max(x))
+ expect_identical(l$mode, "lines")
+ expect_true(l$line$color == "rgba(255,0,0,1)")
+
+})
+
+test_that("hline can be drawn over range of factors", {
+ df <- data.frame(
+ cond = c("control", "treatment"),
+ result = c(10, 11.5)
+ )
+ gg <- ggplot(df, aes(x = cond, y = result)) +
+ geom_bar(position = "dodge", stat = "identity") +
+ geom_hline(aes(yintercept = 12))
+ L <- save_outputs(gg, "hline-factor")
+ expect_equivalent(length(L$data), 2) # 1 trace for bar chart, 1 trace for hline
+})
+
+
+test_that("hline/vline/abline split on linetype/colour/size", {
+ d <- tibble::tibble(
+ x = seq(0, 3.5, by = 0.5),
+ y = x * 0.95
+ )
+ gg <- ggplot(d, aes(x, y)) +
+ geom_vline(xintercept = c(2.5, 3, 3.5), linetype = 1:3) +
+ geom_hline(yintercept = c(2.5, 3, 3.5), size = 1:3) +
+ geom_abline(slope = -1, intercept = c(2.5, 3, 3.5), colour = 1:3)
+
+ l <- plotly_build(gg)$x
+ expect_length(l$data, 9)
+
+ expect_equivalent(
+ vapply(l$data, function(x) x$line$dash, character(1)),
+ lty2dash(c(1:3, rep(1, 6)))
+ )
+
+ expect_equivalent(
+ unique(vapply(l$data, function(x) x$line$color, character(1))),
+ c("rgba(0,0,0,1)", "rgba(255,0,0,1)", "rgba(0,205,0,1)")
+ )
+
+ expect_length(
+ unique(vapply(l$data, function(x) x$line$width, numeric(1))), 4
+ )
+})
diff --git a/tests/testthat/test-ggplot-jitter.R b/tests/testthat/test-ggplot-jitter.R
new file mode 100644
index 0000000..32f3dbe
--- /dev/null
+++ b/tests/testthat/test-ggplot-jitter.R
@@ -0,0 +1,26 @@
+context("geom_jitter")
+
+# Expect trace function
+expect_traces <- function(gg, n_traces, name) {
+ stopifnot(is.numeric(n_traces))
+ save_outputs(gg, paste0("jitter-", name))
+ L <- gg2list(gg)
+ all_traces <- L$data
+ no_data <- sapply(all_traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has_data <- all_traces[!no_data]
+ expect_equivalent(length(has_data), n_traces)
+ list(traces = has_data, layout = L$layout)
+}
+
+p <- ggplot(mpg, aes(cyl, hwy)) + geom_jitter()
+
+test_that("geom_jitter is working", {
+ info <- expect_traces(p, 1, "basic")
+ tr <- info$traces[[1]]
+ expect_identical(tr$type, "scatter")
+ # default jitter is 40% of the resolution of the data.
+ diffs <- abs(mpg$cyl - tr$x)
+ expect_true(all(0 < diffs & diffs < 0.4))
+})
diff --git a/tests/testthat/test-ggplot-labels.R b/tests/testthat/test-ggplot-labels.R
new file mode 100644
index 0000000..723b934
--- /dev/null
+++ b/tests/testthat/test-ggplot-labels.R
@@ -0,0 +1,36 @@
+context("labels")
+
+test_that("ggtitle is translated correctly", {
+ ggiris <- ggplot(iris) +
+ geom_point(aes(Petal.Width, Sepal.Width)) +
+ ggtitle("My amazing plot!")
+ info <- save_outputs(ggiris, "labels-ggtitle")
+ expect_identical(info$layout$title, "My amazing plot!")
+})
+
+test_that("ylab is translated correctly", {
+ ggiris <- ggplot(iris) +
+ geom_point(aes(Petal.Width, Sepal.Width)) +
+ ylab("sepal width")
+ info <- save_outputs(ggiris, "labels-ylab")
+ labs <- c(info$layout$xaxis$title, info$layout$yaxis$title)
+ expect_identical(labs, c("Petal.Width", "sepal width"))
+})
+
+# TODO: why is this failing on R-devel???
+#test_that("scale_x_continuous(name) is translated correctly", {
+# ggiris <- ggplot(iris) +
+# geom_point(aes(Petal.Width, Sepal.Width)) +
+# scale_x_continuous("petal width")
+# info <- save_outputs(ggiris, "labels-scale_x_continuous_name")
+# labs <- unlist(lapply(info$layout$annotations, "[[", "text"))
+# expect_identical(sort(labs), c("petal width", "Sepal.Width"))
+#})
+
+test_that("angled ticks are translated correctly", {
+ ggiris <- ggplot(iris) +
+ geom_point(aes(Petal.Width, Sepal.Width)) +
+ theme(axis.text.x = element_text(angle = 45))
+ info <- save_outputs(ggiris, "labels-angles")
+ expect_identical(info$layout$xaxis$tickangle, -45)
+})
diff --git a/tests/testthat/test-ggplot-legend.R b/tests/testthat/test-ggplot-legend.R
new file mode 100644
index 0000000..e68fdb2
--- /dev/null
+++ b/tests/testthat/test-ggplot-legend.R
@@ -0,0 +1,97 @@
+context("legends")
+
+expect_traces <- function(gg, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("legend-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+p <- ggplot(mtcars, aes(x = mpg, y = wt, color = factor(vs), shape = factor(cyl))) +
+ geom_point()
+
+test_that("Discrete colour and shape get merged into one legend", {
+ info <- save_outputs(p, "scatter_legend")
+ expect_equivalent(length(info$data), 5)
+ expect_true(info$layout$showlegend)
+ # 5 legend entries
+ expect_equivalent(sum(sapply(info$data, "[[", "showlegend")), 5)
+ # verify entries are sorted correctly
+ nms <- sapply(info$data, "[[", "name")
+ d <- unique(mtcars[c("vs", "cyl")])
+ d <- d[order(d$vs, d$cyl), ]
+ expect_identical(
+ nms, paste0("(", d$vs, ",", d$cyl, ")")
+ )
+ a <- info$layout$annotations
+ expect_match(a[[1]]$text, "^factor\\(vs\\)")
+ expect_match(a[[1]]$text, "factor\\(cyl\\)$")
+ expect_true(a[[1]]$y > info$layout$legend$y)
+})
+
+
+test_that("legend vanishes when theme(legend.position = 'none'')", {
+ info <- expect_traces(p + theme(legend.position = "none"), 5, "hide")
+ expect_identical(info$layout$showlegend, FALSE)
+})
+
+p <- ggplot(mtcars, aes(x = mpg, y = wt, color = factor(vs))) +
+ geom_point()
+
+# TODO: better support for scale_*_discrete()
+#test_that("trace order respects scale_color_discrete()", {
+# g <- p + scale_color_discrete(breaks = c(1, 0))
+# info <- expect_traces(g, 2, "iris-default")
+# nms <- unlist(lapply(info$data, "[[", "name"))
+# expect_true(all(nms == c("factor(vs): 1", "factor(vs): 0")))
+#})
+#
+#test_that("missing breaks translates to showlegend=FALSE", {
+# g <- p + scale_color_discrete(breaks = 1)
+# info <- expect_traces(two.legend.entries, 3, "iris-trace-showlegend-FALSE")
+# expect_equivalent(sum(sapply(info, "[[", "showlegend")), 1)
+#})
+
+# test of legend position
+test_that("very long legend items", {
+ long_items <- data.frame(
+ cat1 = sample(x = LETTERS[1:10],
+ size = 100, replace = TRUE),
+ cat2 = sample(x = c("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCC"),
+ size = 100, replace = TRUE)
+ )
+ p_long_items <- ggplot(long_items, aes(cat1, fill = cat2)) +
+ geom_bar(position = "dodge")
+ info <- expect_traces(p_long_items, 3, "very-long-legend-items")
+})
+
+iris$All <- "All species"
+p <- qplot(data = iris, x = Sepal.Length, y = Sepal.Width, color = All)
+
+test_that("legend is created with discrete mapping regardless of unique values", {
+ info <- expect_traces(p, 1, "one-entry")
+ expect_true(info$data[[1]]$showlegend)
+ expect_true(info$layout$showlegend)
+ expect_equivalent(length(info$layout$annotations), 1)
+})
+
+test_that("can hide legend", {
+ info <- expect_traces(hide_legend(p), 1, "hide-legend")
+ expect_false(info$layout$showlegend)
+ expect_null(info$layout$annotations %||% NULL)
+})
+
+
+# test of legend position
+test_that("many legend items", {
+ p <- ggplot(midwest, aes(category, fill = category)) + geom_bar()
+ info <- expect_traces(p, length(unique(midwest$category)), "many legend items")
+})
+
diff --git a/tests/testthat/test-ggplot-lines.R b/tests/testthat/test-ggplot-lines.R
new file mode 100644
index 0000000..2a86f9b
--- /dev/null
+++ b/tests/testthat/test-ggplot-lines.R
@@ -0,0 +1,120 @@
+context("lines")
+
+test_that("6 different automatic lty converted to plotly's 6 types", {
+ d <- expand.grid(x=1:6, y=1:6)
+ gg <- ggplot() +
+ geom_line(aes(x=x, y=y, group=x, linetype=as.factor(x)), data=d)
+ expected <-
+ c("solid",
+ "dash",
+ "dot",
+ "dashdot",
+ "longdash",
+ "longdashdot")
+ info <- save_outputs(gg, "linetype-types")
+ generated <- sapply(info$data[1:6], function(L) L$line$dash)
+ expect_true(all(generated %in% expected))
+ expect_true(all(expected %in% generated))
+})
+
+test_that("different colored lines become different colored traces", {
+ ## http://stackoverflow.com/questions/2564258/plot-2-graphs-in-same-plot-in-r/19039094#19039094
+
+ ## original data in a 'wide' format
+ x <- seq(-2, 2, 0.05)
+ y1 <- pnorm(x)
+ y2 <- pnorm(x, 1, 1)
+ df <- rbind(data.frame(x, variable="y1", value=y1),
+ data.frame(x, variable="y2", value=y2))
+ ## plot, using the aesthetics argument 'colour'
+ gg <- ggplot(data = df, aes(x = x, y = value, colour = variable))+
+ geom_line()+
+ scale_color_manual(values=c(y1="blue", y2="red"))
+ info <- save_outputs(gg, "linetype-colors")
+ expect_equivalent(length(info$data), 2)
+ expect_identical(info$data[[1]]$line$color, toRGB("blue"))
+ n <- length(x)
+ expect_identical(info$data[[1]]$y[1:n], y1)
+ expect_identical(info$data[[1]]$x[1:n], x)
+ expect_identical(info$data[[2]]$line$color, toRGB("red"))
+ expect_identical(info$data[[2]]$y[1:n], y2)
+ expect_identical(info$data[[2]]$x[1:n], x)
+})
+
+
+
+test_that("Translates both dates and datetimes (with dynamic ticks) correctly", {
+
+ dates <- seq(
+ as.Date("2002-01-01"),
+ by = "1 month",
+ length.out = 100
+ )
+
+ d <- data.frame(
+ value = rnorm(100),
+ date = dates
+ )
+
+ p <- ggplot(d, aes(date, value)) + geom_line()
+ l <- plotly_build(ggplotly(p, dynamicTicks = TRUE))$x
+
+ d2 <- data.frame(
+ value = rnorm(100),
+ date = as.POSIXct(dates)
+ )
+
+ p2 <- ggplot(d2, aes(date, value)) + geom_line()
+ l2 <- plotly_build(ggplotly(p2, dynamicTicks = TRUE))$x
+
+ # since these are dynamic ticks, let plotly.js generate the ticks
+ axisType <- with(l$layout$xaxis, list(type, tickmode, autorange))
+ expect_equivalent(axisType, list("date", "auto", TRUE))
+ axisType2 <- with(l2$layout$xaxis, list(type, tickmode, autorange))
+ expect_equivalent(axisType2, list("date", "auto", TRUE))
+
+ # range and data have been reverse transformed
+ expect_is(l$layout$xaxis$range, "Date")
+ expect_is(l$data[[1]]$x, "Date")
+ expect_is(l2$layout$xaxis$range, "POSIXct")
+ expect_is(l2$data[[1]]$x, "POSIXct")
+
+ # check the hovertext
+ dates1 <- sapply(strsplit(l$data[[1]]$text, br()), "[[", 1)
+ dates2 <- sapply(strsplit(l2$data[[1]]$text, br()), "[[", 1)
+ expect_equivalent(paste("date:", d$date), dates1)
+ expect_equivalent(paste("date:", d2$date), dates2)
+})
+
+test_that("geom_linerange() without a y aesthetic translates to a path", {
+ d <- data.frame(
+ x = 1:5,
+ ymax = 1:5,
+ ymin = 0
+ )
+
+ p <- ggplot(d, aes(x, ymax = ymax, ymin = ymin)) +
+ geom_linerange()
+
+ l <- plotly_build(p)$x
+
+ expect_length(l$data, 1)
+ expect_equivalent(l$data[[1]]$type, "scatter")
+ expect_equivalent(
+ l$data[[1]]$x,
+ c(1, 1, NA, 2, 2, NA, 3, 3, NA, 4, 4, NA, 5, 5)
+ )
+ expect_equivalent(
+ l$data[[1]]$y,
+ c(0, 1, NA, 0, 2, NA, 0, 3, NA, 0, 4, NA, 0, 5)
+ )
+ expect_equivalent(
+ unlist(l$data[[1]]$text),
+ c(
+ 'x: 1<br />ymin: 0', 'x: 1<br />ymax: 1', NA, 'x: 2<br />ymin: 0',
+ 'x: 2<br />ymax: 2', NA, 'x: 3<br />ymin: 0', 'x: 3<br />ymax: 3', NA,
+ 'x: 4<br />ymin: 0', 'x: 4<br />ymax: 4', NA, 'x: 5<br />ymin: 0', 'x: 5<br />ymax: 5'
+ )
+ )
+
+})
diff --git a/tests/testthat/test-ggplot-map.R b/tests/testthat/test-ggplot-map.R
new file mode 100644
index 0000000..aba9b13
--- /dev/null
+++ b/tests/testthat/test-ggplot-map.R
@@ -0,0 +1,16 @@
+context("maps")
+
+crimes <- data.frame(state = tolower(rownames(USArrests)), USArrests)
+crimesm <- tidyr::gather(crimes, variable, value, -state)
+states_map <- map_data("state")
+g <- ggplot(crimesm, aes(map_id = state)) +
+ geom_map(aes(fill = value), map = states_map) +
+ expand_limits(x = states_map$long, y = states_map$lat) +
+ facet_wrap( ~ variable)
+
+
+test_that("basic geom_map works", {
+ l <- save_outputs(g, "map-facet")
+ expect_true(length(l$data) > 1)
+})
+
diff --git a/tests/testthat/test-ggplot-path.R b/tests/testthat/test-ggplot-path.R
new file mode 100644
index 0000000..adac165
--- /dev/null
+++ b/tests/testthat/test-ggplot-path.R
@@ -0,0 +1,79 @@
+context("path")
+
+test_that("lines are different from paths", {
+ df <- data.frame(
+ x = c(1, 3, 2),
+ y = c(0, 0, 1)
+ )
+ p <- qplot(x, y, data = df, geom = "path")
+ info <- save_outputs(p, "path-lines-diff-from-paths")
+ expect_identical(info$data[[1]]$x[1:3], c(1, 3, 2))
+ expect_identical(info$data[[1]]$y[1:3], c(0, 0, 1))
+})
+
+two.paths <- data.frame(
+ x = c(1, 2, 1, 2),
+ y = c(1, 1, 2, 2)
+)
+
+test_that("paths with different colors become different traces", {
+ ## Numeric color.
+ gg <- ggplot() +
+ geom_path(aes(x, y, group = y, color = y), data = two.paths)
+ info <- save_outputs(gg, "path-colors")
+ # one trace is for the colorbar
+ expect_equivalent(length(info$data), 3)
+ expect_identical(info$data[[1]]$x[1:2], c(1,2))
+ expect_identical(info$data[[2]]$x[1:2], c(1,2))
+ expect_identical(info$data[[1]]$y[1:2], c(1,1))
+ expect_identical(info$data[[2]]$y[1:2], c(2,2))
+ ## Categorical color.
+ gg <- ggplot() +
+ geom_path(aes(x, y, group = y, color = paste0("FOO", y)), data = two.paths)
+ info <- save_outputs(gg, "path-colors2")
+ expect_equivalent(length(info$data), 2)
+ expect_identical(info$data[[1]]$x[1:2], c(1,2))
+ expect_identical(info$data[[2]]$x[1:2], c(1,2))
+ expect_identical(info$data[[1]]$y[1:2], c(1,1))
+ expect_identical(info$data[[2]]$y[1:2], c(2,2))
+})
+
+four.paths <- rbind(
+ data.frame(two.paths, g = "positive"),
+ data.frame(-two.paths, g = "negative")
+)
+
+test_that("paths with the same color but different groups stay together", {
+ gg <- ggplot() +
+ geom_path(aes(x, y, group = y, color = g), data = four.paths)
+ info <- save_outputs(gg, "path-colored-groups-stay-together")
+ expect_equivalent(length(info$data), 2)
+ expect_identical(info$data[[1]]$name, "positive")
+ expect_identical(info$data[[2]]$name, "negative")
+ expect_true(any(is.na(info$data[[1]]$x)))
+ expect_true(any(is.na(info$data[[1]]$y)))
+ expect_true(any(is.na(info$data[[2]]$x)))
+ expect_true(any(is.na(info$data[[2]]$y)))
+})
+
+test_that("lines & points are merged into markers+lines traces", {
+ df1 <- data.frame(
+ sex = factor(c("Female", "Female", "Male", "Male")),
+ time = factor(c("Lunch", "Dinner", "Lunch", "Dinner"),
+ levels = c("Lunch", "Dinner")),
+ total_bill = c(13.53, 16.81, 16.24, 17.42)
+ )
+ gg <- ggplot(data = df1, aes(x=time, y=total_bill, group=sex, shape=sex)) +
+ geom_line() +
+ geom_point()
+ info <- save_outputs(gg, "path-line-symbols")
+ expect_equivalent(length(info$data), 2) # 2 traces
+ expect_equivalent(info$data[[1]]$name, "Female")
+ expect_equivalent(info$data[[1]]$marker$symbol, "circle")
+ expect_equivalent(info$data[[2]]$name, "Male")
+ expect_equivalent(info$data[[2]]$marker$symbol, "triangle-up")
+ expect_match(info$data[[1]]$mode, "lines")
+ expect_match(info$data[[1]]$mode, "markers")
+ expect_match(info$data[[2]]$mode, "lines")
+ expect_match(info$data[[2]]$mode, "markers")
+})
diff --git a/tests/testthat/test-ggplot-point.R b/tests/testthat/test-ggplot-point.R
new file mode 100644
index 0000000..ee52d2c
--- /dev/null
+++ b/tests/testthat/test-ggplot-point.R
@@ -0,0 +1,59 @@
+context("geom_point")
+
+expect_traces <- function(gg, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("smooth-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(traces=has.data, layout=L$layout)
+}
+
+test_that("geom_point size & alpha translate to a single trace", {
+ gg <- ggplot(mtcars, aes(cyl, wt)) +
+ geom_point(aes(size = gear, alpha = cyl))
+ info <- save_outputs(gg, "point-size-alpha")
+ expect_equivalent(length(info$data), 1)
+ mkr <- info$data[[1]]$marker
+ expect_equivalent(length(mkr$size), nrow(mtcars))
+ expect_equivalent(length(mkr$opacity), nrow(mtcars))
+})
+
+test_that("marker color is non-transparent for open shapes", {
+ p <- ggplot(mtcars, aes(mpg, wt)) + geom_point(pch = 2)
+ info <- save_outputs(p, "open-shapes")
+ expect_true(
+ grepl("open$", info$data[[1]]$marker$symbol)
+ )
+ expect_true(
+ info$data[[1]]$marker$color == toRGB(GeomPoint$default_aes$colour)
+ )
+})
+
+test_that("marker color inherits from fill, when appropriate", {
+ df_shapes <- data.frame(shape = factor(0:24))
+ p <- ggplot(df_shapes, aes(shape = shape)) +
+ geom_point(aes(shape = shape, x = 0, y = 0), size = 5, fill = "red") +
+ facet_wrap(~shape) +
+ scale_shape_manual(values = df_shapes$shape, guide = "none")
+ l <- save_outputs(p, "all-shapes")
+ expect_equivalent(length(l$data), 25)
+ markerColors <- sapply(l$data, function(x) x$marker$color)
+ lineColors <- sapply(l$data, function(x) x$marker$line$color)
+ expect_true(all(markerColors[1:20] == lineColors[1:20]))
+ expect_true(all(markerColors[21:25] != lineColors[21:25]))
+})
+
+
+test_that("can plot on sub-second time scale", {
+ d <- data.frame(
+ x = Sys.time() + 1e-3 * c(1:9, 5000),
+ y = rnorm(10)
+ )
+ g <- ggplot(d, aes(x, y)) + geom_point()
+ info <- save_outputs(g, "point-size-alpha2")
+ expect_equivalent(info$data[[1]]$x, as.numeric(d$x))
+})
diff --git a/tests/testthat/test-ggplot-polygons.R b/tests/testthat/test-ggplot-polygons.R
new file mode 100644
index 0000000..2aa5ccf
--- /dev/null
+++ b/tests/testthat/test-ggplot-polygons.R
@@ -0,0 +1,204 @@
+context("polygon")
+
+expect_traces <- function(gg, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("polygon-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+poly.df <- data.frame(
+ x = c(0, 1, 1, 0, 2, 3, 3, 2) + 10,
+ y = c(0, 0, 1, 1, 0, 0, 1, 1),
+ g = c(1, 1, 1, 1, 2, 2, 2, 2),
+ lab = rep(c("left", "right"), each = 4)
+)
+
+test_that("polygons with different hovertext must be different traces ", {
+ gg <- ggplot(poly.df) + geom_polygon(aes(x, y, group = lab))
+ info <- expect_traces(gg, 2, "black")
+ expect_equivalent(info$data[[1]]$x, c(10, 11, 11, 10, 10))
+ expect_equivalent(info$data[[2]]$x, c(12, 13, 13, 12, 12))
+ expect_equivalent(info$data[[1]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(info$data[[2]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(unique(sapply(info$data, "[[", "fill")), "toself")
+ expect_equivalent(unique(sapply(info$data, "[[", "hoveron")), "fills")
+ expect_equivalent(sapply(info$data, "[[", "text"), c("lab: left", "lab: right"))
+})
+
+test_that("polygons with identical fill and hovertext generate one trace", {
+ gg <- ggplot(poly.df) + geom_polygon(aes(x, y, group = lab))
+ info <- plotly_build(ggplotly(gg, tooltip = NULL))$x
+ expect_equivalent(length(info$data), 1)
+ expect_equivalent(info$data[[1]]$x, c(10, 11, 11, 10, 10, NA, 12, 13, 13, 12, 12))
+ expect_equivalent(info$data[[1]]$y, c(0, 0, 1, 1, 0, NA, 0, 0, 1, 1, 0))
+ expect_equivalent(info$data[[1]]$fill, "toself")
+ expect_equivalent(info$data[[1]]$hoveron, "fills")
+ expect_equivalent(nchar(info$data[[1]]$text), 0)
+})
+
+blue.color <- rgb(0.23, 0.45, 0.67)
+
+test_that("polygons with different color become separate traces", {
+ gg <- ggplot(poly.df) +
+ geom_polygon(aes(x, y, color = lab), fill = "grey") +
+ scale_color_manual(values = c(left = blue.color, right = "springgreen3"))
+ info <- expect_traces(gg, 2, "aes-color")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_equivalent(tr$fillcolor, toRGB("grey"))
+ expect_equivalent(tr$fill, "toself")
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(traces.by.name[[1]]$x, c(10, 11, 11, 10, 10))
+ expect_equivalent(traces.by.name[[1]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[2]]$x, c(12, 13, 13, 12, 12))
+ expect_equivalent(traces.by.name[[2]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[1]]$line$color, toRGB(blue.color))
+ expect_equivalent(traces.by.name[[2]]$line$color, toRGB("springgreen3"))
+})
+
+test_that("geom_polygon(aes(fill)) -> fillcolor + line$color transparent", {
+ gg <- ggplot(poly.df) +
+ geom_polygon(aes(x, y, fill = lab)) +
+ scale_fill_manual(values = c(left = blue.color, right = "springgreen3"))
+ info <- expect_traces(gg, 2, "aes-fill")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$line$color == "transparent")
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(traces.by.name[[1]]$x, c(10, 11, 11, 10, 10))
+ expect_equivalent(traces.by.name[[1]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[2]]$x, c(12, 13, 13, 12, 12))
+ expect_equivalent(traces.by.name[[2]]$y, c(0, 0, 1, 1, 0))
+ expect_true(traces.by.name[[1]]$fillcolor == toRGB(blue.color))
+ expect_true(traces.by.name[[2]]$fillcolor == toRGB("springgreen3"))
+})
+
+test_that("geom_polygon(aes(fill), color) -> line$color", {
+ gg <- ggplot(poly.df) +
+ geom_polygon(aes(x, y, fill = lab), color = "black")+
+ scale_fill_manual(values = c(left = blue.color, right = "springgreen3"))
+ info <- expect_traces(gg, 2, "color-aes-fill")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$line$color == toRGB("black"))
+ expect_true(tr$fill == "toself")
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(traces.by.name[[1]]$x, c(10, 11, 11, 10, 10))
+ expect_equivalent(traces.by.name[[1]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[2]]$x, c(12, 13, 13, 12, 12))
+ expect_equivalent(traces.by.name[[2]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[1]]$fillcolor, toRGB(blue.color))
+ expect_equivalent(traces.by.name[[2]]$fillcolor, toRGB("springgreen3"))
+})
+
+test_that("geom_polygon(aes(linetype), fill, color)", {
+ gg <- ggplot(poly.df) +
+ geom_polygon(aes(x, y, linetype = lab), fill = "red", colour = "blue")+
+ scale_linetype_manual(values = c(left = "dotted", right = "dashed"))
+ info <- expect_traces(gg, 2, "color-fill-aes-linetype")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$fillcolor == toRGB("red"))
+ expect_true(tr$line$color == toRGB("blue"))
+ expect_true(tr$fill == "toself")
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(traces.by.name[[1]]$x, c(10, 11, 11, 10, 10))
+ expect_equivalent(traces.by.name[[1]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[1]]$line$dash, "dot")
+ expect_equivalent(traces.by.name[[2]]$x, c(12, 13, 13, 12, 12))
+ expect_equivalent(traces.by.name[[1]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[2]]$line$dash, "dash")
+})
+
+test_that("geom_polygon(aes(size), fill, colour)", {
+ gg <- ggplot(poly.df) +
+ geom_polygon(aes(x, y, size = lab), fill = "orange", colour = "black") +
+ scale_size_manual(values = c(left = 2, right = 3))
+ info <- expect_traces(gg, 2, "color-fill-aes-size")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$fillcolor == toRGB("orange"))
+ expect_true(tr$line$color == toRGB("black"))
+ expect_true(tr$fill == "toself")
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(traces.by.name[[1]]$x, c(10, 11, 11, 10, 10))
+ expect_equivalent(traces.by.name[[1]]$y, c(0, 0, 1, 1, 0))
+ expect_equivalent(traces.by.name[[2]]$x, c(12, 13, 13, 12, 12))
+ expect_equivalent(traces.by.name[[2]]$y, c(0, 0, 1, 1, 0))
+ expect_false(traces.by.name[[1]]$line$width ==
+ traces.by.name[[2]]$line$width)
+})
+
+test_that("borders become one trace with NA", {
+ gg <- ggplot(maps::canada.cities, aes(long, lat)) +
+ borders(regions = "canada")
+ info <- save_outputs(gg, "polygons-canada-borders")
+ expect_equivalent(length(info$data), 1)
+ expect_true(any(is.na(info$data[[1]]$x)))
+ expect_equivalent(nchar(info$data[[1]]$text), 0)
+})
+
+x <- c(0, -1, 2, -2, 1)
+y <- c(2, 0, 1, 1, 0)
+stars <-rbind(
+ data.frame(x, y, group = "left"),
+ data.frame(x = x + 10, y, group = "right")
+)
+star.group <- ggplot(stars) +
+ geom_polygon(aes(x, y, group = group))
+
+test_that("geom_polygon(aes(group)) -> 1 trace", {
+ info <- expect_traces(star.group, 1, "star-group")
+ tr <- info$data[[1]]
+ expect_equivalent(tr$fill, "toself")
+ expect_equivalent(
+ tr$x, c(0, -1, 2, -2, 1, 0, NA, 10, 9, 12, 8, 11, 10)
+ )
+ expect_equivalent(
+ tr$y, c(2, 0, 1, 1, 0, 2, NA, 2, 0, 1, 1, 0, 2)
+ )
+})
+
+star.group.color <- ggplot(stars) +
+ geom_polygon(aes(x, y, group = group), color = "red")
+
+test_that("geom_polygon(aes(group), color) -> 1 trace", {
+ info <- expect_traces(star.group.color, 1, "star-group-color")
+ tr <- info$data[[1]]
+ expect_true(tr$fill == "toself")
+ expect_true(tr$line$color == toRGB("red"))
+ expect_equivalent(
+ tr$x, c(0, -1, 2, -2, 1, 0, NA, 10, 9, 12, 8, 11, 10)
+ )
+ expect_equivalent(
+ tr$y, c(2, 0, 1, 1, 0, 2, NA, 2, 0, 1, 1, 0, 2)
+ )
+})
+
+star.fill.color <- ggplot(stars) +
+ geom_polygon(aes(x, y, group = group, fill = group), color = "black")
+
+test_that("geom_polygon(aes(group, fill), color) -> 2 trace", {
+ info <- expect_traces(star.fill.color, 2, "star-fill-color")
+ tr <- info$data[[1]]
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$line$color == toRGB("black"))
+ expect_true(tr$fill == "toself")
+ expect_equivalent(tr$y, c(2, 0, 1, 1, 0, 2))
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(traces.by.name[[1]]$x, c(0, -1, 2, -2, 1, 0))
+ expect_equivalent(traces.by.name[[2]]$x, c(10, 9, 12, 8, 11, 10))
+})
diff --git a/tests/testthat/test-ggplot-quantile.R b/tests/testthat/test-ggplot-quantile.R
new file mode 100644
index 0000000..d129b9d
--- /dev/null
+++ b/tests/testthat/test-ggplot-quantile.R
@@ -0,0 +1,53 @@
+context("quantile")
+
+
+
+test_that("Basic geom_quantile() works", {
+
+ skip_if_not_installed("quantreg")
+
+ p <- ggplot(mpg, aes(displ, 1 / hwy)) +
+ geom_point() +
+ geom_quantile()
+
+ l <- plotly_build(p)$x
+
+ expect_length(l$data, 4)
+
+ for (i in 2:4) {
+ tr <- l$data[[i]]
+ expect_equivalent(tr$type, "scatter")
+ expect_equivalent(tr$mode, "lines")
+ expect_equivalent(
+ tr$line$color, toRGB(GeomQuantile$default_aes[["colour"]])
+ )
+ }
+
+})
+
+test_that("Can specify gpar() in geom_quantile()", {
+
+ skip_if_not_installed("quantreg")
+
+ # TODO: implement lineend/linejoin/linemitre?
+
+ p <- ggplot(mpg, aes(displ, 1 / hwy)) +
+ geom_point() +
+ geom_quantile(colour = "red", alpha = 0.5)
+
+ l <- plotly_build(p)$x
+
+ expect_length(l$data, 4)
+
+ for (i in 2:4) {
+ tr <- l$data[[i]]
+ expect_equivalent(tr$type, "scatter")
+ expect_equivalent(tr$mode, "lines")
+ expect_equivalent(
+ tr$line$color, toRGB("red", 0.5)
+ )
+ }
+
+
+})
+
diff --git a/tests/testthat/test-ggplot-rect.R b/tests/testthat/test-ggplot-rect.R
new file mode 100644
index 0000000..7398208
--- /dev/null
+++ b/tests/testthat/test-ggplot-rect.R
@@ -0,0 +1,141 @@
+context("geom_rect")
+
+expect_traces <- function(gg, n.traces, name) {
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("rect-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data=has.data, layout=L$layout)
+}
+
+df <- data.frame(
+ x = sample(10, 20, replace = TRUE),
+ y = sample(10, 20, replace = TRUE)
+)
+
+gg <- ggplot(df, aes(xmin = x, xmax = x + 1, ymin = y, ymax = y + 2)) +
+ geom_rect()
+
+test_that('geom_rect becomes 1 trace with mode="lines" fill="toself"', {
+ info <- expect_traces(gg, 1, "black")
+ tr <- info$data[[1]]
+ expect_identical(tr$fill, "toself")
+ expect_identical(tr$type, "scatter")
+ expect_identical(tr$mode, "lines")
+ for(xy in c("x", "y")) {
+ expect_true(anyNA(tr[[xy]]))
+ }
+})
+
+df4 <- data.frame(
+ x = 1:4,
+ status = c("cool", "not", "not", "cool")
+)
+
+gg4 <- ggplot(df4, aes(xmin = x, xmax = x + 0.5, ymin = 0, ymax = 1)) +
+ geom_rect()
+
+test_that('trace contains NA back to 1st rect', {
+ info <- expect_traces(gg4, 1, "black4")
+ tr <- info$data[[1]]
+ expect_identical(tr$fill, "toself")
+ expect_identical(tr$type, "scatter")
+ expect_identical(tr$mode, "lines")
+ expected.x <- c(1, 1, 1.5, 1.5, 1, NA,
+ 2, 2, 2.5, 2.5, 2, NA,
+ 3, 3, 3.5, 3.5, 3, NA,
+ 4, 4, 4.5, 4.5, 4)
+ expect_equivalent(tr$x, expected.x)
+ expected.y <- c(0, 1, 1, 0, 0, NA,
+ 0, 1, 1, 0, 0, NA,
+ 0, 1, 1, 0, 0, NA,
+ 0, 1, 1, 0, 0)
+ expect_equivalent(tr$y, expected.y)
+})
+
+rect.color <- ggplot(df4, aes(xmin = x, xmax = x + 0.5, ymin = 0, ymax = 1)) +
+ geom_rect(aes(color = status), fill="grey")
+
+test_that('rect color', {
+ info <- expect_traces(rect.color, 2, "color")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$fillcolor == toRGB("grey"))
+ expect_true(tr$fill == "toself")
+ expect_equivalent(tr$y, c(0, 1, 1, 0, 0, NA, 0, 1, 1, 0, 0))
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(
+ traces.by.name[[1]]$x, c(1, 1, 1.5, 1.5, 1, NA, 4, 4, 4.5, 4.5, 4)
+ )
+ expect_equivalent(
+ traces.by.name[[2]]$x,c(2, 2, 2.5, 2.5, 2, NA, 3, 3, 3.5, 3.5, 3)
+ )
+ expect_false(
+ traces.by.name[[1]]$line$color == traces.by.name[[2]]$line$color
+ )
+})
+
+rect.fill <- ggplot(df4, aes(xmin = x, xmax = x + 0.5, ymin = 0, ymax = 1)) +
+ geom_rect(aes(fill = status))
+
+test_that('rect color', {
+ info <- expect_traces(rect.fill, 2, "fill")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$line$color == "transparent")
+ expect_true(tr$fill == "toself")
+ expect_equivalent(tr$y, c(0, 1, 1, 0, 0, NA, 0, 1, 1, 0, 0))
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(
+ traces.by.name[[1]]$x, c(1, 1, 1.5, 1.5, 1, NA, 4, 4, 4.5, 4.5, 4)
+ )
+ expect_equivalent(
+ traces.by.name[[2]]$x, c(2, 2, 2.5, 2.5, 2, NA, 3, 3, 3.5, 3.5, 3)
+ )
+ expect_false(
+ traces.by.name[[1]]$fillcolor == traces.by.name[[2]]$fillcolor
+ )
+})
+
+rect.fill.color <-
+ ggplot(df4, aes(xmin = x, xmax = x + 0.5, ymin = 0, ymax = 1)) +
+ geom_rect(aes(fill = status), color="black")
+
+test_that('rect aes(fill) with constant color', {
+ info <- expect_traces(rect.fill.color, 2, "fill-color")
+ traces.by.name <- list()
+ for(tr in info$data){
+ expect_true(tr$line$color == toRGB("black"))
+ expect_true(tr$fill == "toself")
+ expect_equivalent(
+ tr$y, c(0, 1, 1, 0, 0, NA, 0, 1, 1, 0, 0)
+ )
+ traces.by.name[[tr$name]] <- tr
+ }
+ expect_equivalent(
+ traces.by.name[[1]]$x, c(1, 1, 1.5, 1.5, 1, NA, 4, 4, 4.5, 4.5, 4)
+ )
+ expect_equivalent(
+ traces.by.name[[2]]$x, c(2, 2, 2.5, 2.5, 2, NA, 3, 3, 3.5, 3.5, 3)
+ )
+ expect_false(
+ traces.by.name[[1]]$fillcolor == traces.by.name[[2]]$fillcolor
+ )
+})
+
+
+p <- ggplot(data = data.frame(x1 = 1, x2 = 2, y1 = 1, y2 = 2)) +
+ geom_rect(aes(xmin = x1, xmax = x2, ymin = y1, ymax = y2),
+ fill = "#00000011", color = "black")
+
+test_that('Specifying alpha in hex color code works', {
+ info <- expect_traces(p, 1, "fill-hex-alpha")
+ expect_match(info$data[[1]]$fillcolor, "rgba\\(0,0,0,0\\.0[6]+")
+})
+
diff --git a/tests/testthat/test-ggplot-ribbon.R b/tests/testthat/test-ggplot-ribbon.R
new file mode 100644
index 0000000..1722e8d
--- /dev/null
+++ b/tests/testthat/test-ggplot-ribbon.R
@@ -0,0 +1,50 @@
+context("ribbon")
+
+expect_traces <- function(gg, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("ribbon-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(traces=has.data, layout=L$layout)
+}
+
+huron <- data.frame(year = 1875:1972, level = as.vector(LakeHuron))
+huron$decade <- with(huron, round(year/10) * 10)
+huron$diff <- huron$year - huron$decade
+
+p1 <- ggplot(data = huron) +
+ geom_ribbon(aes(x = year, ymin = level-1, ymax = level+1),
+ alpha = 0.1)
+
+test_that("geom_ribbon() creates 1 trace & respects alpha transparency", {
+ info <- expect_traces(p1, 1, "alpha")
+ tr <- info$traces[[1]]
+ expect_match(tr$fillcolor, "0.1)", fixed=TRUE)
+})
+
+p2 <- ggplot(data = huron, aes(group = factor(decade))) +
+ geom_ribbon(aes(x = diff, ymin = level-0.1, ymax = level+0.1))
+
+test_that("geom_ribbon() with group aesthetic produces 1 trace", {
+ info <- expect_traces(p2, 1, "group")
+})
+
+p3 <- ggplot(data = huron, aes(colour = factor(decade))) +
+ geom_ribbon(aes(x = diff, ymin = level-0.1, ymax = level+0.1))
+
+test_that("geom_ribbon() with colour aesthetic produces multiple traces", {
+ # 10 traces -- one for each decade
+ info <- expect_traces(p3, 10, "colour")
+})
+
+p4 <- ggplot(data = huron, aes(fill = factor(decade))) +
+ geom_ribbon(aes(x = diff, ymin = level-0.1, ymax = level+0.1))
+
+test_that("geom_ribbon() with fill aesthetic produces multiple traces", {
+ # 10 traces -- one for each decade
+ info <- expect_traces(p4, 10, "fill")
+})
diff --git a/tests/testthat/test-ggplot-rug.R b/tests/testthat/test-ggplot-rug.R
new file mode 100644
index 0000000..f3ddf5d
--- /dev/null
+++ b/tests/testthat/test-ggplot-rug.R
@@ -0,0 +1,58 @@
+context("rug")
+
+
+base <- ggplot(mtcars, aes(wt, mpg)) +
+ geom_point()
+
+test_that("Basic geom_rug() works", {
+
+ p <- base + geom_rug(sides = "b")
+ l <- plotly_build(p)$x
+ expect_length(l$data, 2)
+ expect_equivalent(l$data[[2]]$mode, "lines")
+
+ # default should be "bl" (bottom-left)
+ p <- base + geom_rug()
+ l <- plotly_build(p)$x
+ expect_length(l$data, 3)
+ for (i in 2:3) {
+ expect_equivalent(l$data[[i]]$mode, "lines")
+ }
+
+ p <- base + geom_rug(sides = "trbl")
+ l <- plotly_build(p)$x
+ expect_length(l$data, 5)
+ for (i in 2:5) {
+ expect_equivalent(l$data[[i]]$mode, "lines")
+ }
+
+})
+
+test_that("geom_rug() with facets", {
+
+ p <- base + geom_rug() +
+ facet_wrap(~vs, scales = "free")
+ l <- plotly_build(p)$x
+ expect_length(l$data, 6)
+
+})
+
+base <- base + facet_wrap(~vs, scales = "free")
+
+test_that("geom_rug() with graphical parameters", {
+
+ p <- base +
+ geom_rug(alpha = 0.5, color = "red", linetype = 2) +
+ facet_wrap(~vs, scales = "free_y")
+
+ l <- plotly_build(p)$x
+ expect_length(l$data, 6)
+
+ modes <- sapply(l$data, "[[", "mode")
+ rug <- l$data[modes %in% "lines"]
+ for (i in seq_along(rug)) {
+ expect_equivalent(rug[[i]]$line$color, toRGB("red", 0.5))
+ expect_equivalent(rug[[i]]$line$dash, lty2dash(2))
+ }
+
+})
diff --git a/tests/testthat/test-ggplot-segment.R b/tests/testthat/test-ggplot-segment.R
new file mode 100644
index 0000000..92736ab
--- /dev/null
+++ b/tests/testthat/test-ggplot-segment.R
@@ -0,0 +1,48 @@
+context("segment")
+
+test_that("segments become one path", {
+ seg.df <- data.frame(
+ x = c(0, 0),
+ y = c(0, 1),
+ xend = c(1, 1),
+ yend = c(0, 1)
+ )
+ gg <- ggplot() +
+ geom_segment(aes(x, y, xend = xend, yend = yend), data = seg.df)
+ info <- save_outputs(gg, "segment")
+ tr <- info$data[[1]]
+ expect_true(any(is.na(tr$x)))
+ expect_true(any(is.na(tr$y)))
+})
+
+test_that("with non-numeric data, we can have more than one segment", {
+ df <- data.frame(donation = c(102.35377, 98.80028, 102.34715, 103.71195,
+ 107.74814, 92.21549, 103.54709, 93.52689,
+ 104.32014, 93.23326, 123.76597, 128.53826,
+ 125.36151, 116.29949, 125.65676, 118.60371,
+ 117.60477, 128.28911, 121.93446, 127.63119,
+ 97.61806, 94.25784, 102.66568, 100.75126,
+ 96.08688, 89.15305, 100.29993, 89.76010,
+ 103.79008, 96.71342, 95.31541, 107.68345,
+ 94.42277, 98.91443, 100.55720, 104.00674,
+ 91.39054, 94.11684, 102.08854, 97.04515),
+ campaign = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2))
+
+ seg1 <- data.frame(x = 0.85, xend = 1.15, y = 100.2, yend = 100.2)
+ seg2 <- data.frame(x = 1.85, xend = 2.15, y = 123.5, yend = 123.5)
+
+ gg <- ggplot() +
+ geom_point(data = df, aes(x = campaign, y = donation, colour = campaign)) +
+ geom_segment(data = seg1, aes(x, y, xend = xend, yend = yend)) +
+ geom_segment(data = seg2, aes(x, y, xend = xend, yend = yend))
+
+ fig <- save_outputs(gg, "segment-multiple-non-numeric")
+ # one trace is for the colorbar
+ expect_equivalent(length(fig$data), 4)
+ expect_equivalent(fig$data[[2]]$x[1], seg1$x)
+ expect_equivalent(fig$data[[2]]$x[2], seg1$xend)
+ expect_equivalent(fig$data[[3]]$x[1], seg2$x)
+ expect_equivalent(fig$data[[3]]$x[2], seg2$xend)
+})
diff --git a/tests/testthat/test-ggplot-sf.R b/tests/testthat/test-ggplot-sf.R
new file mode 100644
index 0000000..9d018ad
--- /dev/null
+++ b/tests/testthat/test-ggplot-sf.R
@@ -0,0 +1,56 @@
+context("geom_sf")
+
+nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
+
+
+test_that("geom_sf() basic polygons.", {
+ skip_if_not_installed("sf")
+
+ p <- ggplot(nc) + geom_sf()
+
+ l <- save_outputs(p, "sf")
+ # one trace is for the graticule
+ expect_length(l$data, 2)
+ # can translate degree symbol
+ expect_true(
+ all(grepl("\\°", l$layout$xaxis$ticktext))
+ )
+ expect_true(
+ all(grepl("\\°", l$layout$yaxis$ticktext))
+ )
+})
+
+
+
+test_that("geom_sf() polygons with fill/text.", {
+ skip_if_not_installed("sf")
+
+ p <- ggplot(nc) + geom_sf(aes(fill = AREA, text = NAME))
+
+ l <- save_outputs(p, "sf-fill-text")
+ # one trace for graticule, one for colorbar, and one for each row
+ expect_length(l$data, nrow(nc) + 2)
+ expect_true(
+ all(unlist(lapply(l$data, "[[", "hoverinfo")) %in% c("none", "text"))
+ )
+ unlist(lapply(l$data, "[[", "text"))
+})
+
+
+
+test_that("geom_sf() with basic polygons and points.", {
+ skip_if_not_installed("sf")
+
+ p <- ggplot(nc) +
+ geom_sf() +
+ annotate("point", x = -80, y = 35, colour = "red", size = 4) +
+ theme(panel.grid.major = element_line(colour = "red"))
+
+ l <- save_outputs(p, "sf-points")
+ # one trace for graticule, one for point, and one polygons
+ expect_length(l$data, 3)
+ # graticule should be red
+ expect_equivalent(l$data[[1]]$line$color, "rgba(255,0,0,1)")
+ expect_equivalent(l$data[[2]]$mode, "lines")
+ expect_equivalent(l$data[[3]]$mode, "markers")
+})
diff --git a/tests/testthat/test-ggplot-size.R b/tests/testthat/test-ggplot-size.R
new file mode 100644
index 0000000..ccf42f0
--- /dev/null
+++ b/tests/testthat/test-ggplot-size.R
@@ -0,0 +1,30 @@
+context("size")
+
+test_that("size is a vector if it is specified", {
+ iplot <- ggplot(iris) +
+ geom_point(aes(Petal.Width, Sepal.Width, size=Petal.Length))
+ L <- save_outputs(iplot, "size-is-a-vector")
+ m <- L$data[[1]]$marker
+ expect_that(m, is_a("list"))
+ expect_true(length(m$size) > 1)
+ expect_identical(L$data[[1]]$showlegend, FALSE)
+})
+
+countrypop <- data.frame(
+ country = c("Paraguay", "Peru", "Philippines"),
+ population = c(7, 31, 101),
+ edu = c(4.2, 1.75, 1.33),
+ illn = c(0.38, 1.67, 0.43)
+)
+
+gg <- ggplot(countrypop, aes(edu, illn, colour = country, size = population)) +
+ geom_point()
+
+test_that("global scaling works for sizes over different traces", {
+ L <- save_outputs(gg, "size-global-scaling")
+ expect_equivalent(length(L$data), 3) # 1 trace per country (3)
+ expect_true(as.numeric(L$data[[1]]$marker$size) <
+ as.numeric(L$data[[2]]$marker$size))
+ expect_true(as.numeric(L$data[[2]]$marker$size) <
+ as.numeric(L$data[[3]]$marker$size))
+})
diff --git a/tests/testthat/test-ggplot-smooth.R b/tests/testthat/test-ggplot-smooth.R
new file mode 100644
index 0000000..0e817bc
--- /dev/null
+++ b/tests/testthat/test-ggplot-smooth.R
@@ -0,0 +1,65 @@
+context("smooth")
+
+expect_traces <- function(gg, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("smooth-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+p <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + geom_smooth()
+
+test_that("geom_point() + geom_smooth() produces 3 traces", {
+ expect_traces(p, 3, "basic")
+})
+
+p2 <- ggplot(mtcars, aes(mpg, wt)) + geom_point() +
+ geom_smooth(se = FALSE)
+
+test_that("geom_point() + geom_smooth(se = FALSE) produces 2 traces", {
+ expect_traces(p2, 2, "se-false")
+})
+
+d <- diamonds[sample(nrow(diamonds), 1000), ]
+p3 <- qplot(carat, price, group = cut, data = d) + geom_smooth()
+
+test_that("geom_smooth() respects group aesthetic", {
+ info <- expect_traces(p3, 3, "group")
+})
+
+p4 <- qplot(carat, price, colour = cut, data = d) + geom_smooth()
+p5 <- qplot(carat, price, data = d) + geom_smooth(aes(colour = cut))
+
+test_that("geom_smooth() respects colour aesthetic", {
+ info <- expect_traces(p4, 15, "colour")
+
+ # 5 traces of points
+ expect_equivalent(
+ sum(vapply(info$data, function(x) x$mode == "markers", logical(1))), 5
+ )
+ # at least 5 paths
+ expect_true(
+ sum(vapply(info$data, function(x) x$mode == "lines", logical(1))) > 5
+ )
+})
+
+p7 <- qplot(carat, price, data = d) + geom_smooth(aes(fill = cut))
+
+test_that("geom_smooth() respects fill aesthetic", {
+ info <- expect_traces(p7, 11, "fill2")
+})
+
+# ensure legend is drawn when needed
+p8 <- qplot(carat, price, data = d) + facet_wrap(~cut) +
+ geom_smooth(aes(colour = cut, fill = cut))
+
+test_that("geom_smooth() works with facets", {
+ # 3 traces for each panel
+ info <- expect_traces(p8, 15, "facet")
+})
+
diff --git a/tests/testthat/test-ggplot-spoke.R b/tests/testthat/test-ggplot-spoke.R
new file mode 100644
index 0000000..389f77e
--- /dev/null
+++ b/tests/testthat/test-ggplot-spoke.R
@@ -0,0 +1,44 @@
+context("spoke")
+
+df <- expand.grid(x = 1:10, y = 1:10)
+df$angle <- runif(100, 0, 2*pi)
+df$speed <- runif(100, 0, sqrt(0.1 * df$x))
+
+p <- ggplot(df, aes(x, y)) +
+ geom_spoke(aes(angle = angle), radius = 0.5)
+
+test_that("Basic geom_spoke()", {
+
+ l <- plotly_build(p)$x
+ expect_length(l$data, 1)
+ expect_equivalent(l$data[[1]]$type, "scatter")
+ expect_equivalent(l$data[[1]]$mode, "lines")
+
+ txt <- strsplit(l$data[[1]]$text, br())
+ angle <- unlist(lapply(txt, function(x) x[grepl("angle", x, fixed = T)]))
+ radius <- unlist(lapply(txt, function(x) x[grepl("radius", x, fixed = T)]))
+ expect_equivalent(
+ angle, rep(paste0("angle: ", format(df$angle)), each = 2)
+ )
+ expect_equivalent(
+ unique(radius), "radius: 0.5"
+ )
+
+})
+
+df$color <- rep(1:2, each = 50)
+p <- ggplot(df, aes(x, y, color = color)) +
+ geom_spoke(aes(angle = angle), radius = 0.5)
+
+test_that("Basic geom_spoke() with color", {
+
+ l <- plotly_build(p)$x
+ # has to 3 traces since plotly.js doesn't (currently) support
+ # more than one `line.color`
+ expect_length(l$data, 3)
+ for (i in seq_along(l$data)) {
+ expect_equivalent(l$data[[i]]$type, "scatter")
+ expect_match(l$data[[i]]$mode, "markers|lines")
+ }
+
+})
diff --git a/tests/testthat/test-ggplot-step.R b/tests/testthat/test-ggplot-step.R
new file mode 100644
index 0000000..7e25b9a
--- /dev/null
+++ b/tests/testthat/test-ggplot-step.R
@@ -0,0 +1,36 @@
+context("Step")
+
+# Dataset for test
+oranges <- subset(Orange, Tree %in% c(1, 2))
+# Line shapes available in plotly: "linear", "spline", "hv", "vh", "hvh", "vhv"
+
+gg <- ggplot(oranges, aes(x=age, y=circumference,
+ group=Tree, colour=factor(Tree)))
+
+test_that("direction hv is translated to shape=hv", {
+ gg.hv <- gg + geom_step()
+ L <- save_outputs(gg.hv, "step-gg.hv")
+ expect_equivalent(length(L$data), 2)
+ expect_identical(L$data[[1]]$line$shape, "hv")
+})
+
+test_that("direction vh is translated to shape=vh", {
+ gg.vh <- gg + geom_step(direction = "vh")
+ L <- save_outputs(gg.vh, "step-gg.vh")
+ expect_equivalent(length(L$data), 2)
+ expect_identical(L$data[[1]]$line$shape, "vh")
+})
+
+test_that("direction hvh is translated to shape=hvh", {
+ gg.hvh <- gg + geom_step(direction="hvh")
+ L <- save_outputs(gg.hvh, "step-gg.hvh")
+ expect_equivalent(length(L$data), 2)
+ expect_identical(L$data[[1]]$line$shape, "hvh")
+})
+
+test_that("direction vhv is translated to shape=vhv", {
+ gg.vhv <- gg + geom_step(direction="vhv")
+ L <- save_outputs(gg.vhv, "step-gg.vhv")
+ expect_equivalent(length(L$data), 2)
+ expect_identical(L$data[[1]]$line$shape, "vhv")
+})
diff --git a/tests/testthat/test-ggplot-text.R b/tests/testthat/test-ggplot-text.R
new file mode 100644
index 0000000..268924f
--- /dev/null
+++ b/tests/testthat/test-ggplot-text.R
@@ -0,0 +1,44 @@
+context("Text")
+
+gg <- ggplot(mtcars, aes(x = wt, y = mpg, label = rownames(mtcars))) +
+ geom_text(size = 18)
+info <- save_outputs(gg, "text")
+
+test_that("label is translated correctly", {
+ greps <- Map(function(x, y) grepl(x, y), rownames(mtcars), info$data[[1]]$text)
+ expect_true(all(unlist(greps)))
+})
+
+test_that("position is translated correctly", {
+ expect_equivalent(info$data[[1]]$x, mtcars$wt)
+ expect_equivalent(info$data[[1]]$y, mtcars$mpg)
+})
+
+test_that("geom_text splits along colour", {
+ mds <- data.frame(
+ State = c("Alabama", "Alabama", "Alabama", "Alabama",
+ "Arizona", "Arizona"),
+ City = c("HUNTSVILLE", "MOBILE", "BIRMINGHAM", "MONTGOMERY",
+ "TUCSON", "PEORIA"),
+ coord.1 = c(1.561284, 6.088862, 9.978292, 15.454877,
+ 23.225289, -7.283954),
+ coord.2 = c(0.2228790, 0.8343259, -3.6507234, -4.8520206,
+ -0.4438650, 9.1252792),
+ Division = c("East South Central", "East South Central",
+ "East South Central", "East South Central",
+ "Mountain", "Mountain")
+ )
+ gg <- ggplot(mds) +
+ geom_text(aes(x = coord.1, y = coord.2, label = City, colour = Division))
+
+ L <- save_outputs(gg, "text-colour")
+
+ expect_equivalent(length(L$data), 2) # 2 traces
+ # Proper type and mode conversion
+ expect_identical(L$data[[1]]$type, "scatter")
+ expect_identical(L$data[[1]]$mode, "text")
+ expect_identical(L$data[[2]]$type, "scatter")
+ expect_identical(L$data[[2]]$mode, "text")
+ # Right colour for each trace
+ expect_true(L$data[[1]]$textfont$color != L$data[[2]]$textfont$color)
+})
diff --git a/tests/testthat/test-ggplot-theme.R b/tests/testthat/test-ggplot-theme.R
new file mode 100644
index 0000000..aa48fac
--- /dev/null
+++ b/tests/testthat/test-ggplot-theme.R
@@ -0,0 +1,76 @@
+context("ggplot themes")
+
+iris.base <- ggplot(iris) +
+ geom_point(aes(Petal.Width, Sepal.Width)) +
+ theme_grey()
+
+test_that("background translated correctly",{
+ ggiris <- iris.base +
+ theme(panel.background = element_rect(fill = "blue"),
+ plot.background = element_rect(fill = "green"))
+ info <- save_outputs(ggiris, "theme-background")
+ L <- info$layout
+ expect_true(L$plot_bgcolor == toRGB("blue"))
+ expect_true(L$paper_bgcolor == toRGB("green"))
+})
+
+test_that("grid/ticks translated correctly",{
+ ggiris <- iris.base +
+ theme(axis.ticks = element_line(colour = "red"),
+ panel.grid.major = element_line(colour = "violet"))
+ info <- save_outputs(ggiris, "theme-ticks-and-grids")
+ for (xy in c("x", "y")) {
+ ax.list <- info$layout[[paste0(xy, "axis")]]
+ expect_true(ax.list$tickcolor == toRGB("red"))
+ expect_true(ax.list$gridcolor == toRGB("violet"))
+ }
+})
+
+test_that("show ticks as 'outside' by default", {
+ ggiris <- iris.base
+ info <- save_outputs(ggiris, "theme-ticks-default")
+ for (xy in c("x", "y")) {
+ ax.list <- info$layout[[paste0(xy, "axis")]]
+ expect_identical(ax.list$ticks, "outside")
+ }
+})
+
+test_that("do not show zeroline by default", {
+ ggiris <- iris.base
+ info <- save_outputs(ggiris, "theme-zeroline-default")
+ for (xy in c("x", "y")) {
+ ax.list <- info$layout[[paste0(xy, "axis")]]
+ expect_identical(ax.list$zeroline, FALSE)
+ }
+})
+
+countrypop <- data.frame(
+ country = c("Paraguay", "Peru", "Philippines"),
+ population = c(7, 31, 101),
+ edu = c(4.2, 1.75, 1.33),
+ illn = c(0.38, 1.67, 0.43)
+)
+
+gg <- ggplot(countrypop) +
+ geom_point(aes(edu, illn, colour = country, size = population))
+
+test_that("marker default shape is a circle", {
+ info <- save_outputs(gg, "theme-marker-default")
+ for (i in c(1:3)) {
+ expect_equivalent(info$data[[i]]$marker$symbol, "circle")
+ expect_true(info$data[[i]]$showlegend)
+ }
+})
+
+test_that("plot panel border is translated correctly", {
+ ggiris <- iris.base + theme_grey() # has no panel.border
+ info <- save_outputs(ggiris, "theme-panel-border-1")
+
+ red <- ggplot(iris) +
+ theme_grey() +
+ geom_point(aes(Petal.Width, Sepal.Width)) +
+ theme(panel.border = element_rect(colour = "red", fill = NA))
+
+ info <- save_outputs(red, "theme-panel-border-2")
+ expect_true(info$layout$shapes[[1]]$line$color == toRGB("red"))
+})
diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R
new file mode 100644
index 0000000..876f3da
--- /dev/null
+++ b/tests/testthat/test-ggplot-ticks.R
@@ -0,0 +1,191 @@
+context("ggplot ticks")
+
+PlantGrowth$type <-
+ ifelse(PlantGrowth$group == "ctrl", "control", "treatment")
+boxes <- ggplot(PlantGrowth, aes(x = group, y = weight)) + geom_boxplot()
+
+expect_traces <- function(gg, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("ticks-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+
+test_that("boxes without coord_flip()", {
+ info <- expect_traces(boxes, 1, "boxes")
+})
+
+test_that("boxes with facet_grid", {
+ facets <- boxes + facet_grid(. ~ type)
+ info <- expect_traces(facets, 2, "boxes-facet-grid")
+})
+
+test_that('boxes with facet_grid(scales="free")', {
+ facets.scales <- boxes + facet_grid(. ~ type, scales = "free")
+ info <- expect_traces(facets.scales, 2, "boxes-scales-free")
+})
+
+test_that('boxes with facet_grid(scales="free", space="free")', {
+ ## TODO: implement space free!
+ facets.space <- boxes + facet_grid(. ~ type, scales = "free", space = "free")
+ info <- expect_traces(facets.space, 2, "boxes-space-free")
+})
+
+flipped <- boxes + coord_flip()
+
+test_that("boxes with coord_flip()", {
+ info <- expect_traces(flipped, 1, "flip")
+})
+
+test_that("boxes with coord_flip()+facet_grid()", {
+ flip.facet <- flipped + facet_grid(type ~ .)
+ info <- expect_traces(flip.facet, 2, "flip-grid")
+})
+
+test_that('boxes with coord_flip()+facet_grid(scales="free")', {
+ # bug in ggplot2?
+ flip.facet.scales <- flipped + facet_grid(type ~ ., scales = "free")
+ info <- expect_traces(flip.facet.scales, 2, "flip-grid-free")
+})
+
+test_that("limits can hide data", {
+ boxes.limits <- boxes + scale_x_discrete(limits = c("trt1", "ctrl"))
+ info <- expect_traces(boxes.limits, 1, "limits-hide")
+ expect_equivalent(info$layout$xaxis$ticktext, c("trt1", "ctrl"))
+})
+
+test_that("limits can create a gap", {
+ boxes.limits <- boxes + scale_x_discrete(limits = c("trt1", "trt2", "GAP", "ctrl"))
+ info <- expect_traces(boxes.limits, 1, "limits-gap")
+ expect_equivalent(info$layout$xaxis$ticktext, c("trt1", "trt2", "GAP", "ctrl"))
+})
+
+boxes.breaks <- boxes +
+ scale_x_discrete(breaks = c("trt1", "ctrl", "trt2"))
+
+test_that("setting breaks does not change order", {
+ info <- expect_traces(boxes.breaks, 1, "breaks-nochange")
+ expect_identical(
+ info$layout$xaxis$ticktext[info$layout$xaxis$tickvals],
+ c("ctrl", "trt1", "trt2")
+ )
+})
+
+boxes.more <- boxes +
+ scale_x_discrete(breaks = c("trt1", "ctrl", "trt2", "FOO"))
+
+test_that("more breaks is fine", {
+ info <- expect_traces(boxes.more, 1, "breaks-more")
+ expect_identical(
+ info$layout$xaxis$ticktext[info$layout$xaxis$tickvals],
+ c("ctrl", "trt1", "trt2")
+ )
+})
+
+boxes.less <- boxes +
+ scale_x_discrete(breaks=c("trt1", "ctrl"))
+
+test_that("less breaks is fine", {
+ info <- expect_traces(boxes.less, 1, "breaks-less")
+ expect_equivalent(info$layout$xaxis$ticktext, c("trt1", "ctrl"))
+})
+
+boxes.labels <- boxes +
+ scale_x_discrete(breaks=c("trt1", "ctrl", "trt2"),
+ labels=c("Treatment 1", "Control", "Treatment 2"))
+
+test_that("scale(labels) changes trace names", {
+ info <- expect_traces(boxes.labels, 1, "scale-labels")
+ expect_equivalent(
+ info$layout$xaxis$ticktext,
+ c("Treatment 1", "Control", "Treatment 2")
+ )
+})
+
+no.breaks <- boxes + scale_x_discrete(breaks = NULL)
+
+test_that("hide x ticks, lines, and labels", {
+ info <- expect_traces(no.breaks, 1, "hide-ticks-lines-labels")
+ expect_true(
+ is.na(info$layout$xaxis$ticktext) || length(info$layout$xaxis$ticktext) == 0
+ )
+ expect_true(
+ is.na(info$layout$xaxis$tickvals) || length(info$layout$xaxis$tickvals) == 0
+ )
+})
+
+test_that("Hide X ticks and labels, but keep the gridlines", {
+ boxes.grid <- boxes +
+ theme(axis.ticks = element_blank(), axis.text.x = element_blank())
+ info <- expect_traces(boxes.grid, 1, "hide-ticks-labels")
+ x <- info$layout$xaxis
+ expect_false(x$showticklabels)
+ expect_true(x$showgrid)
+ expect_true(length(x$ticktext) == 3)
+})
+
+test_that("scale_y_continuous(limits) means yaxis$ranges", {
+ boxes.range <- boxes + scale_y_continuous(limits = c(0,8))
+ info <- expect_traces(boxes.range, 1, "ycontinuous-ranges")
+ y.axis <- info$layout$yaxis
+ expect_equivalent(range(y.axis$tickvals), c(0, 8))
+})
+
+test_that("ylim() means yaxis$ranges", {
+ boxes.range <- boxes + ylim(0, 8)
+ info <- expect_traces(boxes.range, 1, "ylim-ranges")
+ y.axis <- info$layout$yaxis
+ expect_equivalent(range(y.axis$tickvals), c(0, 8))
+})
+
+test_that("scale_y_reverse() -> yaxis$ranges reversed", {
+ boxes.reverse <- boxes + scale_y_reverse()
+ info <- expect_traces(boxes.reverse, 1, "yreverse-ranges")
+})
+
+test_that("scale_y_reverse(limits) -> yaxis$ranges reversed", {
+ boxes.reverse <- boxes + scale_y_reverse(limits = c(10, -2))
+ info <- expect_traces(boxes.reverse, 1, "yreverse-limits-ranges")
+})
+
+test_that("ylim(reversed) -> yaxis$ranges reversed", {
+ boxes.reverse <- boxes + ylim(7.5, -1)
+ info <- expect_traces(boxes.reverse, 1, "ylim-reversed-ranges")
+})
+
+test_that("Set the X tick mark locations", {
+ ## This will show tick marks on every 0.25 from 1 to 10. The scale will
+ ## show only the ones that are within range (3.50-6.25 in this case)
+ boxes.ticks <- boxes + scale_y_continuous(breaks = seq(4, 5, length.out = 12))
+ info <- expect_traces(boxes.ticks, 1, "evenly-spaced-ticks")
+ y.axis <- info$layout$yaxis
+ expect_equivalent(length(y.axis$ticktext), 12)
+})
+
+test_that("The breaks can be spaced unevenly", {
+ boxes.uneven <- boxes +
+ scale_y_continuous(breaks = c(4, 4.25, 4.5, 5, 6, 8))
+ info <- expect_traces(no.breaks, 1, "uneven")
+})
+
+test_that("R line breaks are translated to HTML line breaks", {
+ df_x <- data.frame(
+ x = "this is very loooooooooooong text to illustrate",
+ y = 100
+ )
+ p <- ggplot(aes(x = x, y = y), data = df_x) +
+ geom_bar(stat = "identity") +
+ scale_x_discrete(labels = function(x) stringr::str_wrap(x, width = 10))
+ info <- expect_traces(p, 1, "line-breaks")
+ expect_length(
+ strsplit(info$layout$xaxis$ticktext, "<br />", fixed = T)[[1]], 5
+ )
+})
+
+
diff --git a/tests/testthat/test-ggplot-tooltip.R b/tests/testthat/test-ggplot-tooltip.R
new file mode 100644
index 0000000..e69df1e
--- /dev/null
+++ b/tests/testthat/test-ggplot-tooltip.R
@@ -0,0 +1,119 @@
+context("tooltip")
+
+test <- data.frame(
+ time = strptime("2016-03-12 16:32:56", format = "%Y-%m-%d %X") + 60 * 1:100,
+ x = cos(1:100)
+)
+p <- ggplot(test, aes(time, x)) + geom_point()
+
+test_that("datetimes are displayed in tooltip properly", {
+ l <- save_outputs(p, "tooltip-datetime")
+ txt <- strsplit(l$data[[1]]$text, br())
+ expect_identical(
+ paste0("time: ", test$time), sapply(txt, "[[", 1)
+ )
+})
+
+test <- data.frame(
+ time = strptime("2016-03-12", format = "%Y-%m-%d") + 1:100,
+ x = sin(1:100)
+)
+p <- ggplot(test, aes(time, x)) + geom_point()
+
+test_that("dates are displayed in tooltip properly", {
+ l <- save_outputs(p, "tooltip-date")
+ txt <- strsplit(l$data[[1]]$text, br())
+ expect_identical(
+ paste0("time: ", test$time), sapply(txt, "[[", 1)
+ )
+})
+
+test_that("tooltip argument respects ordering", {
+ p <- qplot(mpg, fill = factor(cyl), data = mtcars, geom = "density")
+ p <- ggplotly(p, tooltip = c("density", "x"))
+ info <- plotly_build(p)$x
+ txt <- strsplit(info$data[[1]]$text, br())
+ expect_true(all(grepl("^density", sapply(txt, "[[", 1))))
+ expect_true(all(grepl("^mpg", sapply(txt, "[[", 2))))
+})
+
+test_that("can hide x values in tooltip", {
+ gg2 <- ggplot(mtcars, aes(factor(cyl), mpg, fill = factor(cyl))) + geom_violin()
+ p <- ggplotly(gg2, tooltip = "y")
+ l <- plotly_build(p)$x
+ expect_equivalent(sum(grepl("cyl", l$data[[1]]$text)), 0)
+})
+
+cars <- ggplot(mtcars, aes(mpg, factor(cyl)))
+p <- cars + stat_bin2d(aes(fill = ..density..), binwidth = c(3,1))
+
+test_that("geom_tile() displays correct info in tooltip with discrete y", {
+ L <- save_outputs(p, "heatmap-discrete-tooltip")
+ expect_equivalent(length(L$data), 2)
+ expect_equivalent(L$data[[1]]$type, "heatmap")
+ txt <- c(L$data[[1]]$text)
+ txt <- txt[!is.na(txt)]
+ # tooltip should show y-values on the _data_ scale
+ expect_true(all(grepl("factor\\(cyl\\): [4,6,8]", txt)))
+})
+
+p <- ggplot(txhousing, aes(x = date, y = median, group = city)) +
+ geom_line(alpha = 0.3)
+
+test_that("group domain is included in hovertext", {
+ L <- save_outputs(p, "group-lines-hovertext")
+ expect_equivalent(length(L$data), 1)
+ txt <- L$data[[1]]$text
+ txt <- txt[!is.na(txt)]
+ pattern <- paste(unique(txhousing$city), collapse = "|")
+ expect_true(all(grepl(pattern, txt)))
+})
+
+test_that("tooltip elements are not crossed", {
+ # Tooltips with y == 10 should belong to Sample2 in this example
+ mydata <- data.frame(id = paste0("Sample", rep(1:2, times = 4)),
+ x = rep(1:4, each = 2),
+ y = rep(c(1, 10), times = 4),
+ stringsAsFactors = FALSE)
+ # id x y
+ # 1 Sample1 1 1
+ # 2 Sample2 1 10
+ # 3 Sample1 2 1
+ # 4 Sample2 2 10
+ # 5 Sample1 3 1
+ # 6 Sample2 3 10
+ # 7 Sample1 4 1
+ # 8 Sample2 4 10
+ gplt <- ggplot(mydata) + geom_line(aes(x=x, y = y, group = id))
+ pltly <- plotly::ggplotly(gplt)
+ y_equal_ten <- grepl("y: 10", pltly$x$data[[1]]$text)
+ sample_2 <- grepl("id: Sample2", pltly$x$data[[1]]$text)
+ expect_equivalent(y_equal_ten, sample_2)
+})
+
+labelDF <- data.frame(
+ label = paste0(("label"), c(1:10)),
+ x = runif(10, 1, 10),
+ y = runif(10, 1, 10)
+)
+# Create data frame for 10 edges
+edgeDF <- data.frame(
+ x = runif(10, 1, 10),
+ y = runif(10, 1, 10),
+ xend = runif(10, 1, 10),
+ yend = runif(10, 1, 10)
+)
+
+myPlot <- ggplot(data = labelDF, aes(x = x, y = y)) +
+ geom_segment(data = edgeDF, aes(x = x, y = y, xend = xend, yend = yend),
+ colour = "pink") +
+ geom_text(data = labelDF, aes(x = x, y = y, label = label), size = 10)
+
+test_that("Hoverinfo is only displayed if no tooltip variables are present", {
+ L <- save_outputs(p, "hovertext-display")
+ L <- plotly_build(ggplotly(myPlot, tooltip = "label"))[["x"]]
+ expect_equivalent(length(L$data), 2)
+ expect_equivalent(sum(nchar(L$data[[1]]$text)), 0)
+ expect_true(all(grepl("^label", L$data[[2]]$text)))
+})
+
diff --git a/tests/testthat/test-ggplot-violin.R b/tests/testthat/test-ggplot-violin.R
new file mode 100644
index 0000000..ce11ec7
--- /dev/null
+++ b/tests/testthat/test-ggplot-violin.R
@@ -0,0 +1,26 @@
+context("violin")
+
+gg <- ggplot(mtcars, aes(factor(cyl), mpg)) + geom_violin()
+
+test_that("basic geom_violin works", {
+ L <- save_outputs(gg, "violin")
+ expect_equivalent(length(L$data), 1)
+ tr <- L$data[[1]]
+ expect_identical(tr$type, "scatter")
+ expect_true(tr$fill == "toself")
+ expect_false(tr$showlegend)
+ expect_true(all(grepl("density", tr$text[!is.na(tr$text)])))
+ expect_true(tr$hoverinfo == "text")
+})
+
+
+gg2 <- ggplot(mtcars, aes(factor(cyl), mpg, fill = factor(cyl))) + geom_violin()
+
+test_that("geom_violin with fill aes works", {
+ L <- save_outputs(gg2, "violin-aes")
+ expect_equivalent(length(L$data), 3)
+ expect_true(L$layout$showlegend)
+ expect_equivalent(sum(unlist(lapply(L$data, "[[", "showlegend"))), 3)
+})
+
+
diff --git a/tests/testthat/test-ggplot-vline.R b/tests/testthat/test-ggplot-vline.R
new file mode 100644
index 0000000..7644cc4
--- /dev/null
+++ b/tests/testthat/test-ggplot-vline.R
@@ -0,0 +1,35 @@
+context("Vline")
+
+x <- seq(0, 3.5, by = 0.5)
+y <- x * 0.95
+df <- data.frame(x, y)
+gg <- ggplot(df, aes(x, y)) + geom_point()
+
+test_that("second trace be the vline", {
+ p <- gg + geom_vline(xintercept = 1.1, colour = "green", size = 3)
+
+ L <- save_outputs(p, "vline")
+ l <- L$data[[2]]
+
+ expect_equivalent(length(L$data), 2)
+ expect_equivalent(l$x[1], 1.1)
+ expect_true(l$y[1] <= 0)
+ expect_true(l$y[2] >= 3.325)
+ expect_true(l$mode == "lines")
+ expect_true(l$line$color == "rgba(0,255,0,1)")
+})
+
+test_that("vector xintercept results in multiple vertical lines", {
+ p <- gg + geom_vline(xintercept = 1:2, colour = "blue", size = 3)
+
+ L <- save_outputs(p, "vline-multiple")
+ expect_equivalent(length(L$data), 2)
+ l <- L$data[[2]]
+ xs <- unique(l$x)
+ ys <- unique(l$y)
+ expect_identical(xs, c(1, NA, 2))
+ expect_true(min(ys, na.rm = TRUE) <= min(y))
+ expect_true(max(ys, na.rm = TRUE) >= max(y))
+ expect_true(l$mode == "lines")
+ expect_true(l$line$color == "rgba(0,0,255,1)")
+})
diff --git a/tests/testthat/test-ggplot-ylim.R b/tests/testthat/test-ggplot-ylim.R
new file mode 100644
index 0000000..648cc5f
--- /dev/null
+++ b/tests/testthat/test-ggplot-ylim.R
@@ -0,0 +1,33 @@
+context("ggplot ylim")
+
+# http://www.cookbook-r.com/Graphs/Bar_and_line_graphs_%28ggplot2%29/
+
+df <- data.frame(
+ time = factor(c("Lunch","Dinner"), levels = c("Lunch","Dinner")),
+ total_bill = c(14.89, 17.23)
+)
+
+gg.ylim <-
+ ggplot(data = df, aes(x = time, y = total_bill, group = 1)) +
+ geom_line() +
+ ylim(0, max(df$total_bill)) +
+ xlab("Time of day") + ylab("Total bill") +
+ ggtitle("Average bill for 2 people")
+
+expect_traces <- function(gg, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(gg, paste0("ylim-", name))
+ all.traces <- L$data
+ no.data <- sapply(all.traces, function(tr) {
+ is.null(tr[["x"]]) && is.null(tr[["y"]])
+ })
+ has.data <- all.traces[!no.data]
+ expect_equivalent(length(has.data), n.traces)
+ list(data = has.data, layout = L$layout)
+}
+
+test_that("ylim is respected for 1 trace", {
+ info <- expect_traces(gg.ylim, 1, "one-trace")
+ expect_equivalent(min(info$layout$yaxis$tickvals), 0)
+ expect_identical(info$data[[1]]$showlegend, FALSE)
+})
diff --git a/tests/testthat/test-group2NA.R b/tests/testthat/test-group2NA.R
new file mode 100644
index 0000000..e6b0db3
--- /dev/null
+++ b/tests/testthat/test-group2NA.R
@@ -0,0 +1,76 @@
+context("group2NA")
+
+test_that("group2NA() returns the data untouched if vars don't exist", {
+
+ data(mtcars)
+ expect_equivalent(group2NA(mtcars), mtcars)
+
+})
+
+test_that("group2NA() handles simple group var", {
+
+ # TODO: are there cases where we wouldn't want to sort by group?
+ data(mtcars)
+ m <- group2NA(mtcars, "vs")
+ expect_true(nrow(m) == 33)
+ expect_equivalent(
+ as.numeric(m[19, ]),
+ c(NA, NA, NA, NA, NA, NA, NA, 0, NA, NA, NA)
+ )
+
+})
+
+test_that("group2NA() yields the correct result", {
+
+ # Yes, this is a saved result
+ # dput(group2NA(mtcars, "vs", "cyl", "id"))
+ res <- structure(list(
+ mpg = c(26, NA, 22.8, 24.4, 22.8, 32.4, 30.4,
+ 33.9, 21.5, 27.3, 30.4, 21.4, 21, 21, 19.7, NA, 21.4, 18.1, 19.2,
+ 17.8, 18.7, 14.3, 16.4, 17.3, 15.2, 10.4, 10.4, 14.7, 15.5, 15.2,
+ 13.3, 19.2, 15.8, 15),
+ cyl = c(4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8),
+ disp = c(120.3, NA, 108, 146.7, 140.8, 78.7, 75.7,
+ 71.1, 120.1, 79, 95.1, 121, 160, 160, 145, NA, 258, 225, 167.6,
+ 167.6, 360, 360, 275.8, 275.8, 275.8, 472, 460, 440, 318, 304,
+ 350, 400, 351, 301),
+ hp = c(91, NA, 93, 62, 95, 66, 52, 65, 97,
+ 66, 113, 109, 110, 110, 175, NA, 110, 105, 123, 123, 175, 245,
+ 180, 180, 180, 205, 215, 230, 150, 150, 245, 175, 264, 335),
+ drat = c(4.43, NA, 3.85, 3.69, 3.92, 4.08, 4.93, 4.22, 3.7,
+ 4.08, 3.77, 4.11, 3.9, 3.9, 3.62, NA, 3.08, 2.76, 3.92, 3.92,
+ 3.15, 3.21, 3.07, 3.07, 3.07, 2.93, 3, 3.23, 2.76, 3.15,
+ 3.73, 3.08, 4.22, 3.54),
+ wt = c(2.14, NA, 2.32, 3.19, 3.15,
+ 2.2, 1.615, 1.835, 2.465, 1.935, 1.513, 2.78, 2.62, 2.875,
+ 2.77, NA, 3.215, 3.46, 3.44, 3.44, 3.44, 3.57, 4.07, 3.73,
+ 3.78, 5.25, 5.424, 5.345, 3.52, 3.435, 3.84, 3.845, 3.17,
+ 3.57),
+ qsec = c(16.7, NA, 18.61, 20, 22.9, 19.47, 18.52,
+ 19.9, 20.01, 18.9, 16.9, 18.6, 16.46, 17.02, 15.5, NA, 19.44,
+ 20.22, 18.3, 18.9, 17.02, 15.84, 17.4, 17.6, 18, 17.98, 17.82,
+ 17.42, 16.87, 17.3, 15.41, 17.05, 14.5, 14.6),
+ vs = c(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ am = c(1, NA, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, NA, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1),
+ gear = c(5, NA, 4, 4, 4, 4, 4, 4, 3, 4, 5, 4, 4, 4, 5, NA, 3, 3, 4, 4, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5),
+ carb = c(2, NA, 1, 2, 2, 1, 2, 1, 1, 1, 2, 2, 4, 4, 6, NA, 1, 1, 4, 4, 2, 4,
+ 3, 3, 3, 4, 4, 4, 2, 2, 4, 2, 4, 8),
+ id = c(27L, NA, 3L, 8L, 9L, 18L, 19L, 20L, 21L, 26L, 28L, 32L, 1L, 2L, 30L,
+ NA, 4L, 6L, 10L, 11L, 5L, 7L, 12L, 13L, 14L, 15L, 16L, 17L, 22L,
+ 23L, 24L, 25L, 29L, 31L)),
+ .Names = c("mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb", "id"),
+ class = "data.frame",
+ row.names = c(NA, 34L))
+
+ data(mtcars)
+ m <- transform(mtcars, id = seq_len(nrow(mtcars)))
+ expect_equivalent(
+ group2NA(m, "vs", "cyl", "id"), res
+ )
+
+})
diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R
new file mode 100644
index 0000000..bf63f57
--- /dev/null
+++ b/tests/testthat/test-mean-error-bars.R
@@ -0,0 +1,56 @@
+context("means and error bars")
+
+one.line.df <- data.frame(
+ x = c(1, 2, 3, 4),
+ y = c(2, 1, 3, 4),
+ array = c(0.1, 0.2, 0.1, 0.1),
+ arrayminus = c(0.2, 0.4, 1, 0.2)
+)
+
+test_that("only asymmetric error bars", {
+ error.gg <- ggplot(one.line.df, aes(x, y)) +
+ geom_errorbar(aes(ymin = y - arrayminus, ymax = y + array))
+ L <- save_outputs(error.gg, "error-simple")
+})
+
+test_that("asymmetric error bars, geom_errorbar last", {
+ one.line.gg <- ggplot(one.line.df, aes(x, y)) +
+ geom_line() +
+ geom_point() +
+ geom_errorbar(aes(ymin = y - arrayminus, ymax = y + array))
+ L <- save_outputs(one.line.gg, "error-simple-line")
+})
+
+test_that("asymmetric error bars, geom_errorbar first", {
+ one.line.gg <- ggplot(one.line.df, aes(x, y)) +
+ geom_errorbar(aes(ymin = y - arrayminus, ymax = y + array)) +
+ geom_line() +
+ geom_point()
+ L <- save_outputs(one.line.gg, "error-simple-line-point")
+})
+
+test_that("different colors for error bars, points, and lines", {
+ one.line.gg <- ggplot(one.line.df, aes(x, y)) +
+ geom_errorbar(aes(ymin = y - arrayminus, ymax = y + array), color = "red") +
+ geom_line(color = "violet") +
+ geom_point(color = "blue", size = 14)
+ L <- save_outputs(one.line.gg, "error-simple-line-point-crazy")
+})
+
+# example from https://github.com/ropensci/plotly/issues/513
+
+d <- data.frame(
+ x = 1:5,
+ y = 1:5,
+ label = letters[1:5],
+ group = factor(c('one', 'one', 'two', 'two', 'one'))
+)
+fig1 <- ggplot(d, aes(x = x, y = y, text = label, colour = group)) +
+ geom_rect(fill = 'lightgrey', colour = 'lightgrey',
+ xmin = 3, xmax = 4, ymin = -4, ymax = 7) +
+ geom_point() + geom_errorbarh(aes(xmin = x - 1, xmax = x + 2), alpha = 0.5) +
+ theme_bw()
+
+test_that("error bars respect trace ordering", {
+ L <- save_outputs(fig1, "error-rect-alpha")
+})
diff --git a/tests/testthat/test-plotly-color.R b/tests/testthat/test-plotly-color.R
new file mode 100644
index 0000000..a35ce11
--- /dev/null
+++ b/tests/testthat/test-plotly-color.R
@@ -0,0 +1,113 @@
+context("plotly-color")
+
+expect_traces <- function(p, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(p, paste0("plotly-color-", name))
+ expect_equivalent(length(L$data), n.traces)
+ L
+}
+
+test_that("plot_ly() handles a simple scatterplot", {
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, color = ~Petal.Width)
+})
+
+test_that("Mapping a factor variable to color works", {
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, color = ~Species)
+ l <- expect_traces(p, 3, "scatterplot-color-factor")
+ markers <- lapply(l$data, "[[", "marker")
+ cols <- unlist(lapply(markers, "[[", "color"))
+ expect_equivalent(length(cols), 3)
+})
+
+test_that("Custom RColorBrewer pallette works for factor variable", {
+ cols <- RColorBrewer::brewer.pal(9, "Set1")
+ # convert hex to rgba spec for comparison's sake
+ colsToCompare <- toRGB(cols)
+ # specifying a pallette set should "span the gamut"
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, color = ~Species,
+ colors = "Set1")
+ l <- expect_traces(p, 3, "scatterplot-color-factor-custom")
+ markers <- lapply(l$data, "[[", "marker")
+ colz <- unlist(lapply(markers, "[[", "color"))
+ expect_identical(sort(colsToCompare[c(1, 5, 9)]), sort(colz))
+ # providing vector of RGB codes should also work
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, color = ~Species,
+ colors = cols[1:3])
+ l <- expect_traces(p, 3, "scatterplot-color-factor-custom2")
+ markers <- lapply(l$data, "[[", "marker")
+ colz <- unlist(lapply(markers, "[[", "color"))
+ expect_identical(sort(colsToCompare[1:3]), sort(colz))
+})
+
+test_that("Passing hex codes to colors argument works", {
+ colz <- c('#FE8268', '#81E8FE', '#FED681', '#81FED6', '#FE81A9')
+ d <- data.frame(Category = LETTERS[1:5], Value = 1:5, stringsAsFactors = FALSE)
+ p <- plot_ly(d, x = ~Category, y = ~Value, type = "bar",
+ color = ~Category, colors = colz)
+ l <- expect_traces(p, 5, "bar-color-factor-custom")
+ colz2 <- sapply(l$data, function(x) x[["marker"]][["color"]])
+ expect_identical(sort(toRGB(colz)), sort(colz2))
+})
+
+test_that("Mapping a numeric variable to color works", {
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, color = ~Petal.Width)
+ # one trace is for the colorbar
+ l <- expect_traces(p, 2, "scatterplot-color-numeric")
+ idx <- vapply(l$data, is.colorbar, logical(1))
+ markerScale <- l$data[[which(idx)]]$marker
+ markerDat <- l$data[[which(!idx)]]$marker
+ expect_equivalent(markerDat$color, iris$Petal.Width)
+ expect_equivalent(markerScale$colorbar$title, "Petal.Width")
+ expect_equivalent(min(iris$Petal.Width), markerScale$cmin)
+ expect_equivalent(max(iris$Petal.Width), markerScale$cmax)
+ expect_true(all(0 <= markerScale$colorscale[,1] & markerScale$colorscale[,1] <= 1))
+})
+
+test_that("Custom RColorBrewer pallette works for numeric variable", {
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length,
+ color = ~Petal.Width, colors = "Greens")
+ # one trace is for the colorbar
+ l <- expect_traces(p, 2, "scatterplot-color-numeric-custom")
+})
+
+test_that("axis titles get attached to scene object for 3D plots", {
+ p <- plot_ly(iris, x = ~Petal.Length, y = ~Petal.Width, z = ~Sepal.Width)
+ l <- expect_traces(p, 1, "scatterplot-scatter3d-axes")
+ expect_identical(l$data[[1]]$type, "scatter3d")
+ scene <- l$layout$scene
+ expect_identical(scene$xaxis$title, "Petal.Length")
+ expect_identical(scene$yaxis$title, "Petal.Width")
+ expect_identical(scene$zaxis$title, "Sepal.Width")
+})
+
+test_that("Can specify a scale manually", {
+ pal <- c("1" = "red", "0" = "blue")
+ p <- plot_ly(mtcars, x = ~mpg, y = ~disp, color = ~factor(vs), colors = pal)
+ l <- expect_traces(p, 2, "color-manual")
+ markers <- lapply(l$data, "[[", "marker")
+ expected <- setNames(pal[sapply(l$data, "[[", "name")], NULL)
+ expect_equivalent(toRGB(expected), sapply(markers, "[[", "color"))
+})
+
+test_that("attributes are boxed-up correctly", {
+ df <- data.frame(
+ a = rnorm(5, 50, 1),
+ b = letters[1:5],
+ stringsAsFactors = FALSE
+ )
+
+ p <- plot_ly(df, x = ~a, y = ~b, color = ~b) %>%
+ add_bars(text = ~paste("Value: ", round(a, 1)), hoverinfo = "text")
+
+ l <- plotly_build(p)$x
+
+ expect_length(l$data, 5)
+
+ for (i in seq_along(l$data)) {
+ expect_is(l$data[[i]]$x, "AsIs")
+ expect_is(l$data[[i]]$y, "AsIs")
+ expect_length(l$data[[i]]$text, 1)
+ }
+
+})
+
diff --git a/tests/testthat/test-plotly-colorbar.R b/tests/testthat/test-plotly-colorbar.R
new file mode 100644
index 0000000..604e4b6
--- /dev/null
+++ b/tests/testthat/test-plotly-colorbar.R
@@ -0,0 +1,56 @@
+context("colorbar")
+
+expect_traces <- function(p, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(p, paste0("plotly-", name))
+ expect_equivalent(length(L$data), n.traces)
+ L
+}
+
+
+test_that("Can set colorbar attributes", {
+ p <- plot_ly(mtcars, x = ~wt, y = ~cyl, color = ~cyl)
+ p <- colorbar(p, len = 0.5)
+ l <- expect_traces(p, 2, "colorbar")
+ expect_equivalent(l$data[[2]]$marker$colorbar$len, 0.5)
+})
+
+
+test_that("Can expand limits", {
+ p <- plot_ly(mtcars, x = ~wt, y = ~cyl, color = ~cyl)
+ p <- colorbar(p, limits = c(0, 20))
+ l <- expect_traces(p, 2, "colorbar-expand")
+ expect_equivalent(l$data[[1]]$marker$cmin, 0)
+ expect_equivalent(l$data[[2]]$marker$cmin, 0)
+ expect_equivalent(l$data[[1]]$marker$cmax, 20)
+ expect_equivalent(l$data[[2]]$marker$cmax, 20)
+})
+
+test_that("Can restrict limits", {
+ p <- plot_ly(mtcars, x = ~wt, y = ~cyl, color = ~cyl)
+ p <- colorbar(p, limits = c(5, 7))
+ l <- expect_traces(p, 2, "colorbar-restrict")
+ expect_equivalent(unique(l$data[[1]]$marker$color), c(6, NA))
+ expect_equivalent(l$data[[2]]$marker$cmin, 5)
+ expect_equivalent(l$data[[2]]$marker$cmax, 7)
+})
+
+test_that("Can expand z limits", {
+ p <- plot_ly(z = ~volcano)
+ p <- colorbar(p, limits = c(0, 300))
+ l <- expect_traces(p, 1, "colorbar-z-expand")
+ expect_equivalent(l$data[[1]]$zmin, 0)
+ expect_equivalent(l$data[[1]]$zmax, 300)
+})
+
+test_that("Can restrict z limits", {
+ p <- plot_ly(z = ~volcano)
+ p <- colorbar(p, limits = c(140, 160))
+ l <- expect_traces(p, 1, "colorbar-z-restrict")
+ expect_equivalent(l$data[[1]]$zmin, 140)
+ expect_equivalent(l$data[[1]]$zmax, 160)
+ v <- c(volcano)
+ v[v < 140 | 160 < v] <- NA
+ dim(v) <- dim(volcano)
+ expect_equivalent(l$data[[1]][["z"]], v)
+})
diff --git a/tests/testthat/test-plotly-data.R b/tests/testthat/test-plotly-data.R
new file mode 100644
index 0000000..b9b7739
--- /dev/null
+++ b/tests/testthat/test-plotly-data.R
@@ -0,0 +1,46 @@
+context("plotly data")
+
+test_that("uniq works as expected", {
+ expect_equivalent(uniq(c("red", "red", NA)), "red")
+})
+
+test_that("plotly_data returns empty data frame when none is specified", {
+ d <- plotly_data(plot_ly())
+ expect_true(is.data.frame(d) && NROW(d) == 0)
+})
+
+test_that("plotly_data returns data frame", {
+ d <- plotly_data(plot_ly(economics))
+ expect_identical(economics, d)
+})
+
+test_that("plotly_data preserves groups in data", {
+ d <- plotly_data(group_by_(plot_ly(mtcars), c("vs", "am")))
+ expect_true(dplyr::groups(d)[[1]] == "vs")
+})
+
+test_that("add_fun can apply two different chunks of data to a plot", {
+ p <- plot_ly(mtcars, x = ~wt, y = ~mpg) %>%
+ add_markers() %>%
+ add_fun(function(p) {
+ p %>% slice(which.max(mpg)) %>% add_annotations("Good mileage")
+ }) %>%
+ add_fun(function(p) {
+ p %>% slice(which.min(mpg)) %>% add_annotations(text = "Bad mileage")
+ })
+ l <- plotly_build(p)[["x"]]
+ expect_equivalent(length(l$layout$annotations), 2)
+ expect_equivalent(
+ sort(sapply(l$layout$annotations, "[[", "text")),
+ c("Bad mileage", "Good mileage")
+ )
+ expect_equivalent(
+ sort(sapply(l$layout$annotations, "[[", "x")),
+ c(1.835, 5.250)
+ )
+ expect_equivalent(
+ sort(sapply(l$layout$annotations, "[[", "y")),
+ c(10.4, 33.9)
+ )
+})
+
diff --git a/tests/testthat/test-plotly-filename.R b/tests/testthat/test-plotly-filename.R
new file mode 100644
index 0000000..9dd73fb
--- /dev/null
+++ b/tests/testthat/test-plotly-filename.R
@@ -0,0 +1,17 @@
+context("Filename")
+
+test_that("filename supports names with paths included ", {
+ skip_on_cran()
+ skip_if_not_master()
+ p <- plot_ly(mtcars, x = ~wt, y = ~vs)
+ filename <- "directory/awesome"
+ # trash the file if it already exists
+ file <- api_lookup_file(filename)
+ if (is.file(file)) {
+ endpt <- sprintf("files/%s/trash", file$fid)
+ res <- api(endpt, "POST")
+ }
+ f <- plotly_POST(p, filename = filename)
+ expect_match(f$filename, "awesome")
+ expect_true(f$parented)
+})
diff --git a/tests/testthat/test-plotly-getfigure.R b/tests/testthat/test-plotly-getfigure.R
new file mode 100644
index 0000000..4a58417
--- /dev/null
+++ b/tests/testthat/test-plotly-getfigure.R
@@ -0,0 +1,52 @@
+context("get_figure")
+
+test_that("requests made by a user who doesn't exist error a 404", {
+ skip_on_cran()
+ skip_if_not_master()
+ expect_error({
+ get_figure("klmadslfjdfljdsf", 0)
+ }, ".*404.*")
+})
+
+test_that("requests made to retrieve a figure that doesn't exist returns a 404", {
+ skip_on_cran()
+ skip_if_not_master()
+ expect_error({
+ get_figure("get_test_user", 18324823)
+ }, ".*404.*")
+})
+
+test_that("requests made to retrieve some elses private file errors", {
+ skip_on_cran()
+ skip_if_not_master()
+ expect_error(get_figure("get_test_user", 1))
+})
+
+test_that("retrieving a public figure ... works.", {
+ skip_on_cran()
+ skip_if_not_master()
+ fig <- get_figure("get_test_user", 0)
+ # get the data behind the hash
+ p <- plotly_build(fig)$x
+ expect_equivalent(p$data[[1]]$x, c("1", "2", "3"))
+})
+
+test_that("can add traces to a subplot figure", {
+ skip_on_cran()
+ skip_if_not_master()
+ fig <- get_figure('chelsea_lyn', 6366)
+ p <- add_lines(fig, x = c(1, 2, 3), y = c(4, 2, 4))
+ expect_equivalent(
+ length(plotly_build(fig)$x$data) + 1,
+ length(plotly_build(p)$x$data)
+ )
+})
+
+test_that("posting a hidden plot returns a secret key", {
+ skip_on_cran()
+ skip_if_not_master()
+ res <- plotly_POST(plot_ly(), sharing = "secret")
+ expect_true(res$share_key_enabled)
+ expect_true(nchar(res$share_key) > 1)
+})
+
diff --git a/tests/testthat/test-plotly-group.R b/tests/testthat/test-plotly-group.R
new file mode 100644
index 0000000..87858a1
--- /dev/null
+++ b/tests/testthat/test-plotly-group.R
@@ -0,0 +1,67 @@
+context("plotly-group")
+
+expect_traces <- function(p, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(p, paste0("plotly-", name))
+ expect_equivalent(length(L$data), n.traces)
+ L
+}
+
+# oh no, June is missing!
+e <- economics
+isMissing <- format(economics$date, "%m") == "06"
+e[isMissing, ] <- NA
+
+p <- plot_ly(e, x = ~date, y = ~pce)
+
+test_that("Missing values are preserved for lines", {
+ l <- expect_traces(add_lines(p), 1, "NAs")
+ expect_equivalent(sum(is.na(l$data[[1]]$x)), sum(isMissing))
+})
+
+test_that("Can connectgaps without groups", {
+ l <- expect_traces(add_lines(p, connectgaps = TRUE), 1, "NAs-connect")
+ expect_true(l$data[[1]]$connectgaps)
+ expect_equivalent(sum(is.na(l$data[[1]]$x)), sum(isMissing))
+})
+
+test_that("Warning is thrown when missing values are ignored", {
+ expect_warning(
+ plotly_build(add_markers(p)),
+ paste("Ignoring", sum(isMissing))
+ )
+})
+
+e2 <- tidyr::gather(e, variable, value, -date)
+
+p2 <- e2 %>%
+ group_by(variable) %>%
+ plot_ly(x = ~date, y = ~value)
+
+test_that("Missing values are preserved for lines within a group variable", {
+ l <- expect_traces(add_lines(p2), 1, "NAs-within-group")
+ expect_true(sum(is.na(l$data[[1]]$x)) >= sum(is.na(e2$date)))
+ p2b <- add_lines(p2, connectgaps = TRUE)
+ expect_error(plotly_build(p2b), "connectgaps")
+})
+
+p3 <- plot_ly(e2, x = ~date, y = ~value, color = ~variable)
+
+test_that("Missing values are preserved for lines within a color variable", {
+ l <- expect_traces(add_lines(p3), 5, "NAs-within-color")
+ # connectgaps makes sense in this case
+ l <- expect_traces(add_lines(p3, connectgaps = TRUE), 5, "NAs-within-color2")
+})
+
+m <- mtcars
+m$rowname <- rownames(mtcars)
+p <- m %>%
+ dplyr::group_by_("rowname") %>%
+ plot_ly(x = ~wt, y = ~mpg) %>%
+ add_markers()
+
+test_that("Groups are ignored if grouping is irrelevant for the geom", {
+ l <- expect_traces(p, 1, "no-NAs-for-irrelevant-group")
+ expect_length(l$data[[1]][["x"]], 32)
+ expect_length(l$data[[1]][["y"]], 32)
+})
diff --git a/tests/testthat/test-plotly-internals.R b/tests/testthat/test-plotly-internals.R
new file mode 100644
index 0000000..4ddc83e
--- /dev/null
+++ b/tests/testthat/test-plotly-internals.R
@@ -0,0 +1,10 @@
+context("internals")
+
+# test some assumptions we make about internal functions
+
+test_that("remove_class() can remove 'AsIs' class", {
+ x <- 1:10
+ class(x) <- c("x", "y")
+ x <- remove_class(I(x), "AsIs")
+ expect_equivalent(class(x), c("x", "y"))
+})
diff --git a/tests/testthat/test-plotly-linetype.R b/tests/testthat/test-plotly-linetype.R
new file mode 100644
index 0000000..4005157
--- /dev/null
+++ b/tests/testthat/test-plotly-linetype.R
@@ -0,0 +1,64 @@
+context("plotly-linetype")
+
+expect_traces <- function(p, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(p, paste0("plotly-linetype-", name))
+ expect_equivalent(length(L$data), n.traces)
+ L
+}
+
+# subset to five cities
+cities <- unique(txhousing$city)[1:5]
+tx <- txhousing[txhousing$city %in% cities, ]
+tx$city_ <- match(tx$city, cities)
+
+test_that("Mapping a variable to linetype works", {
+ p <- plot_ly(tx, x = ~date, y = ~median, linetype = ~city)
+ l <- expect_traces(p, 5, "linetype")
+ lines <- lapply(l$data, "[[", "line")
+ dashes <- unlist(lapply(lines, "[[", "dash"))
+ expect_equivalent(length(dashes), 5)
+})
+
+test_that("Can set the linetype range.", {
+ p <- plot_ly(tx, x = ~date, y = ~median, linetype = ~city, linetypes = 5:1)
+ l <- expect_traces(p, 5, "linetype2")
+ lines <- lapply(l$data, "[[", "line")
+ dashes <- unlist(lapply(lines, "[[", "dash"))
+ expect_equivalent(dashes, plotly:::lty2dash(5:1))
+})
+
+test_that("Can avoid scaling", {
+ p <- plot_ly(tx, x = ~date, y = ~median, linetype = I(3))
+ l <- expect_traces(p, 1, "linetype3")
+ lines <- lapply(l$data, "[[", "line")
+ dashes <- unlist(lapply(lines, "[[", "dash"))
+ expect_equivalent(dashes, plotly:::lty2dash(3))
+})
+
+test_that("Warn about invalid linetypes", {
+ p <- plot_ly(x = 1:2, y = 1:2, linetype = I("DNE"))
+ expect_warning(plotly_build(p), "DNE")
+})
+
+test_that("Can specify a scale manually", {
+ pal <- c("1" = "dot", "0" = "dash")
+ p <- plot_ly(mtcars, x = ~mpg, y = ~disp, linetype = ~factor(vs), linetypes = pal)
+ l <- expect_traces(p, 2, "manual")
+ dashes <- lapply(l$data, "[[", "line")
+ expected <- setNames(pal[sapply(l$data, "[[", "name")], NULL)
+ expect_equivalent(expected, sapply(dashes, "[[", "dash"))
+})
+
+test_that("Trace ordering matches factor levels", {
+ p <- plot_ly(mtcars, x = ~mpg, y = ~disp, linetype = ~factor(vs, levels = c(1, 0))) %>% add_lines()
+ l <- expect_traces(p, 2, "ordering")
+ expect_equivalent(sapply(l$data, "[[", "name"), c("1", "0"))
+})
+
+test_that("Trace ordering is alphabetical", {
+ lvls <- sort(unique(mpg$class))
+ p <- plot_ly(mpg, x = ~cty, y = ~hwy, linetype = ~class) %>% add_lines()
+ l <- expect_traces(p, length(lvls), "alphabetical")
+ expect_equivalent(sapply(l$data, "[[", "name"), lvls)
+})
diff --git a/tests/testthat/test-plotly-pie.R b/tests/testthat/test-plotly-pie.R
new file mode 100644
index 0000000..fd8bc04
--- /dev/null
+++ b/tests/testthat/test-plotly-pie.R
@@ -0,0 +1,21 @@
+context("pie")
+
+ds <- data.frame(
+ labels = c("A", "B", "C"),
+ values = c(10, 40, 60)
+)
+
+plot_ly(ds, labels = ~labels, values = ~values) %>%
+ add_pie()
+
+test_that("No cartesian axes are supplied to a pie chart", {
+ p <- plot_ly(ds, labels = ~labels, values = ~values) %>%
+ add_pie()
+
+ l <- plotly_build(p)$x
+
+ expect_true(all(l$data[[1]]$labels == ds$labels))
+ expect_true(all(l$data[[1]]$values == ds$values))
+ expect_null(l$layout$xaxis)
+ expect_null(l$layout$yaxis)
+})
diff --git a/tests/testthat/test-plotly-subplot.R b/tests/testthat/test-plotly-subplot.R
new file mode 100644
index 0000000..fb07db2
--- /dev/null
+++ b/tests/testthat/test-plotly-subplot.R
@@ -0,0 +1,223 @@
+context("subplot")
+
+expect_traces <- function(p, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(p, paste0("plotly-subplot-", name))
+ expect_equivalent(length(L$data), n.traces)
+ L
+}
+
+test_that("simple subplot works", {
+ p1 <- plot_ly(x = c(1, 2))
+ p2 <- plot_ly(x = c(1, 2))
+ s <- expect_traces(subplot(p1, p2), 2, "simple")
+ expect_identical(s$data[[2]]$xaxis, s$layout[["yaxis2"]][["anchor"]])
+ expect_identical(s$data[[2]]$yaxis, s$layout[["xaxis2"]][["anchor"]])
+ doms <- lapply(s$layout[grepl("^xaxis", names(s$layout))], "[[", "domain")
+ expect_true(doms$xaxis[2] <= doms$xaxis2[1])
+})
+
+test_that("nrows argument works", {
+ p1 <- plot_ly(x = c(1, 2))
+ p2 <- plot_ly(x = c(1, 2))
+ s <- expect_traces(subplot(p1, p2, nrows = 2), 2, "simple2")
+ expect_identical(s$data[[2]]$xaxis, s$layout[["yaxis2"]][["anchor"]])
+ expect_identical(s$data[[2]]$yaxis, s$layout[["xaxis2"]][["anchor"]])
+ doms <- lapply(s$layout[grepl("^[x-y]axis", names(s$layout))], "[[", "domain")
+ expect_true(doms$yaxis[2] > doms$yaxis[1])
+ expect_true(doms$yaxis[1] > doms$yaxis2[2])
+ expect_true(doms$yaxis2[2] > doms$yaxis2[1])
+})
+
+test_that("group + [x/y]axis works", {
+ iris$id <- as.integer(iris$Species)
+ p <- plot_ly(iris, x = ~Petal.Length, y = ~Petal.Width, color = ~Species,
+ xaxis = ~paste0("x", id), mode = "markers")
+ s <- expect_traces(subplot(p, margin = 0.05), 3, "group")
+ ax <- s$layout[grepl("^[x-y]axis", names(s$layout))]
+ doms <- lapply(ax, "[[", "domain")
+ # make sure y domain is [0, 1] on every axis
+ ydom <- doms[grepl("^y", names(doms))]
+ expect_equivalent(sort(unique(unlist(ydom))), c(0, 1))
+ xdom <- doms[grepl("^x", names(doms))]
+ expect_true(all(1/3 > xdom[[1]] & xdom[[1]] >= 0))
+ expect_true(all(2/3 > xdom[[2]] & xdom[[2]] > 1/3))
+ expect_true(all(1 >= xdom[[3]] & xdom[[3]] > 2/3))
+})
+
+test_that("shareX produces one x-axis and a legend", {
+ s <- subplot(plot_ly(x = 1), plot_ly(x = 1), nrows = 2, shareX = TRUE)
+ l <- expect_traces(s, 2, "shareX")
+ expect_true(sum(grepl("^xaxis", names(l$layout))) == 1)
+ expect_true(l$data[[1]]$showlegend %||% TRUE)
+ expect_true(l$data[[2]]$showlegend %||% TRUE)
+ expect_true(l$layout$showlegend %||% TRUE)
+})
+
+test_that("shareY produces one y-axis", {
+ s <- subplot(plot_ly(x = 1), plot_ly(x = 1), shareY = TRUE)
+ l <- expect_traces(s, 2, "shareY")
+ expect_true(sum(grepl("^yaxis", names(l$layout))) == 1)
+})
+
+test_that("share both axes", {
+ s <- subplot(
+ plot_ly(x = 1), plot_ly(x = 1), plot_ly(x = 1), plot_ly(x = 1),
+ nrows = 2, shareX = TRUE, shareY = TRUE
+ )
+ l <- expect_traces(s, 4, "shareBoth")
+ expect_true(sum(grepl("^yaxis", names(l$layout))) == 2)
+ expect_true(sum(grepl("^xaxis", names(l$layout))) == 2)
+})
+
+# https://github.com/ropensci/plotly/issues/376
+d <- data.frame(
+ x = rnorm(100),
+ y = rnorm(100)
+)
+hist_top <- ggplot(d) + geom_histogram(aes(x = x))
+empty <- ggplot() + geom_blank()
+scatter <- ggplot(d) + geom_point(aes(x = x, y = y))
+hist_right <- ggplot(d) + geom_histogram(aes(x = y)) + coord_flip()
+s <- subplot(
+ hist_top, empty, scatter, hist_right,
+ nrows = 2, widths = c(0.8, 0.2), heights = c(0.2, 0.8),
+ margin = 0.005, shareX = TRUE, shareY = TRUE
+)
+
+test_that("Row/column height/width", {
+ l <- expect_traces(s, 3, "width-height")
+ expect_equivalent(diff(l$layout$xaxis$domain), 0.8 - 0.005)
+ expect_equivalent(diff(l$layout$xaxis2$domain), 0.2 - 0.005)
+ expect_equivalent(diff(l$layout$yaxis$domain), 0.2 - 0.005)
+ expect_equivalent(diff(l$layout$yaxis2$domain), 0.8 - 0.005)
+})
+
+test_that("recursive subplots work", {
+ p1 <- plot_ly(economics, x = ~date, y = ~unemploy)
+ p2 <- plot_ly(economics, x = ~date, y = ~uempmed)
+ s1 <- subplot(p1, p1, shareY = TRUE)
+ s2 <- subplot(p2, p2, shareY = TRUE)
+ s <- subplot(s1, s2, nrows = 2, shareX = TRUE)
+ l <- expect_traces(s, 4, "recursive")
+ xaxes <- l$layout[grepl("^xaxis", names(l$layout))]
+ yaxes <- l$layout[grepl("^yaxis", names(l$layout))]
+ expect_true(length(xaxes) == 2)
+ expect_true(length(yaxes) == 2)
+ # both x-axes are anchored on the same y-axis
+ yanchor <- unique(unlist(lapply(xaxes, "[[", "anchor")))
+ expect_true(length(yanchor) == 1)
+ # both y-axes are anchored on the same x-axis
+ xanchor <- unique(unlist(lapply(yaxes, "[[", "anchor")))
+ expect_true(length(xanchor) == 1)
+ # x/y are anchored on the bottom/left
+ expect_true(l$layout[[sub("x", "xaxis", xanchor)]]$domain[1] == 0)
+ expect_true(l$layout[[sub("y", "yaxis", yanchor)]]$domain[1] == 0)
+ # every trace is anchored on a different x/y axis pair
+ xTraceAnchors <- sapply(l$data, "[[", "xaxis")
+ yTraceAnchors <- sapply(l$data, "[[", "yaxis")
+ expect_true(length(unique(paste(xTraceAnchors, yTraceAnchors))) == 4)
+})
+
+test_that("subplot accepts a list of plots", {
+ vars <- setdiff(names(economics), "date")
+ plots <- lapply(vars, function(var) {
+ plot_ly(x = economics$date, y = economics[[var]], name = var)
+ })
+ s <- subplot(plots, nrows = length(plots), shareX = TRUE, titleX = FALSE)
+ l <- expect_traces(s, 5, "plot-list")
+ xaxes <- l$layout[grepl("^xaxis", names(l$layout))]
+ yaxes <- l$layout[grepl("^yaxis", names(l$layout))]
+ expect_true(length(xaxes) == 1)
+ expect_true(length(yaxes) == 5)
+ # x-axis is anchored at the bottom
+ expect_true(l$layout[[sub("y", "yaxis", xaxes[[1]]$anchor)]]$domain[1] == 0)
+})
+
+
+test_that("ggplotly understands ggmatrix", {
+ L <- save_outputs(GGally::ggpairs(iris), "plotly-subplot-ggmatrix")
+})
+
+test_that("geo+cartesian behaves", {
+ # specify some map projection/options
+ g <- list(
+ scope = 'usa',
+ projection = list(type = 'albers usa'),
+ lakecolor = toRGB('white')
+ )
+ # create a map of population density
+ density <- state.x77[, "Population"] / state.x77[, "Area"]
+ map <- plot_geo(
+ z = ~density, text = state.name,
+ locations = state.abb, locationmode = 'USA-states'
+ ) %>% layout(geo = g)
+ # create a bunch of horizontal bar charts
+ vars <- colnames(state.x77)
+ barcharts <- lapply(vars, function(var) {
+ plot_ly(x = state.x77[, var], y = state.name, type = "bar",
+ orientation = "h", name = var) %>%
+ layout(showlegend = FALSE, hovermode = "y",
+ yaxis = list(showticklabels = FALSE))
+ })
+ s <- subplot(
+ subplot(barcharts, margin = 0.01), map,
+ nrows = 2, heights = c(0.3, 0.7)
+ )
+ l <- expect_traces(s, 9, "geo-cartesian")
+ geoDom <- l$layout[[grep("^geo", names(l$layout))]]$domain
+ expect_equivalent(geoDom$x, c(0, 1))
+ expect_equivalent(geoDom$y, c(0, 0.68))
+})
+
+
+
+test_that("May specify legendgroup with through a vector of values", {
+
+ # example adapted from https://github.com/ropensci/plotly/issues/817
+ df <- dplyr::bind_rows(
+ data.frame(x = rnorm(100,2), Name = "x1"),
+ data.frame(x = rnorm(100,6), Name = "x2"),
+ data.frame(x = rnorm(100,4), Name = "x3")
+ )
+ df$y <- rnorm(300)
+
+ # marker definition...
+ m <- list(
+ size = 10,
+ line = list(
+ width = 1,
+ color = "black"
+ )
+ )
+
+ base <- plot_ly(
+ df,
+ marker = m,
+ color = ~factor(Name),
+ legendgroup = ~factor(Name)
+ )
+
+ s <- subplot(
+ add_histogram(base, x = ~x, showlegend = FALSE),
+ plotly_empty(),
+ add_markers(base, x = ~x, y = ~y),
+ add_histogram(base, y = ~y, showlegend = FALSE),
+ nrows = 2, heights = c(0.2, 0.8), widths = c(0.8, 0.2),
+ shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE
+ ) %>% layout(barmode = "stack")
+
+ # one trace for the empty plot
+ l <- expect_traces(s, 10, "subplot-legendgroup")
+
+ # really this means show three legend items (one is blank)
+ expect_equivalent(
+ sum(sapply(l$data, function(tr) tr$showlegend %||% TRUE)), 4
+ )
+
+ expect_length(
+ unlist(lapply(l$data, "[[", "legendgroup")), 9
+ )
+
+})
+
diff --git a/tests/testthat/test-plotly-symbol.R b/tests/testthat/test-plotly-symbol.R
new file mode 100644
index 0000000..fbf6128
--- /dev/null
+++ b/tests/testthat/test-plotly-symbol.R
@@ -0,0 +1,70 @@
+context("plotly-symbol")
+
+expect_traces <- function(p, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(p, paste0("plotly-symbol-", name))
+ expect_equivalent(length(L$data), n.traces)
+ L
+}
+
+test_that("Mapping a variable to symbol works", {
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, symbol = ~Species)
+ l <- expect_traces(p, 3, "scatterplot-symbol")
+ markers <- lapply(l$data, "[[", "marker")
+ syms <- unlist(lapply(markers, "[[", "symbol"))
+ expect_identical(syms, c("circle", "triangle-up", "square"))
+})
+
+test_that("Can set the symbol range.", {
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, symbol = ~Species,
+ symbols = 1:3)
+ l <- expect_traces(p, 3, "scatterplot-symbol2")
+ markers <- lapply(l$data, "[[", "marker")
+ syms <- unlist(lapply(markers, "[[", "symbol"))
+ expect_identical(syms, plotly:::pch2symbol(1:3))
+})
+
+
+test_that("Setting a constant symbol works", {
+ p <- plot_ly(iris, x = 1:25, y = 1:25, symbol = I(0:24))
+ l <- expect_traces(p, 1, "pch")
+ markers <- lapply(l$data, "[[", "marker")
+ syms <- unlist(lapply(markers, "[[", "symbol"))
+ expect_identical(syms, plotly:::pch2symbol(0:24))
+})
+
+test_that("Warn about invalid symbol codes", {
+ p <- plot_ly(iris, x = ~Sepal.Length, y = ~Petal.Length, symbol = I("DNE"))
+ expect_warning(plotly_build(p), "DNE")
+})
+
+test_that("Formula resulting in logical vector works", {
+ s <- c("triangle-up", "circle-open")
+ p <- plot_ly(x = 1:10, y = 1:10, symbol = ~1:10 > 5, symbols = s)
+ l <- expect_traces(p, 2, "logical")
+ markers <- lapply(l$data, "[[", "marker")
+ syms <- unlist(lapply(markers, "[[", "symbol"))
+ expect_identical(syms, s)
+})
+
+test_that("Can specify a scale manually", {
+ pal <- c("1" = "cross", "0" = "diamond")
+ p <- plot_ly(mtcars, x = ~mpg, y = ~disp, symbol = ~factor(vs), symbols = pal)
+ l <- expect_traces(p, 2, "symbol-manual")
+ markers <- lapply(l$data, "[[", "marker")
+ expected <- setNames(pal[sapply(l$data, "[[", "name")], NULL)
+ expect_equivalent(expected, sapply(markers, "[[", "symbol"))
+})
+
+test_that("Trace ordering matches factor levels", {
+ p <- plot_ly(mtcars, x = ~mpg, y = ~disp, symbol = ~factor(vs, levels = c(1, 0)))
+ l <- expect_traces(p, 2, "ordering")
+ expect_equivalent(sapply(l$data, "[[", "name"), c("1", "0"))
+})
+
+test_that("Trace ordering is alphabetical", {
+ lvls <- sort(unique(mpg$class))
+ p <- plot_ly(mpg, x = ~cty, y = ~hwy, symbol = ~class)
+ l <- expect_traces(p, length(lvls), "alphabetical")
+ expect_equivalent(sapply(l$data, "[[", "name"), lvls)
+})
diff --git a/tests/testthat/test-plotly.R b/tests/testthat/test-plotly.R
new file mode 100644
index 0000000..5b1b6ad
--- /dev/null
+++ b/tests/testthat/test-plotly.R
@@ -0,0 +1,191 @@
+context("plotly")
+
+expect_traces <- function(p, n.traces, name){
+ stopifnot(is.numeric(n.traces))
+ L <- save_outputs(p, paste0("plotly-", name))
+ expect_equivalent(length(L$data), n.traces)
+ L
+}
+
+# expect 2 plotly graphs to have the same traces
+expect_same_data <- function(p1, p2) {
+ if (!is.plotly(p1) || !is.plotly(p2)) {
+ stop("Both arguments must be plotly objects", call. = FALSE)
+ }
+ d1 <- plotly_build(p1)$x$data
+ d2 <- plotly_build(p2)$x$data
+ if (length(d1) != length(d2)) {
+ stop("Number of traces is different.", call. = FALSE)
+ }
+ # for each trace, align the names (since ordering doesn't matter)
+ d1 <- Map(function(x, y) structure(x[names(y)], class = oldClass(x)), d1, d2)
+ expect_identical(d1, d2)
+}
+
+test_that("vector values with repeated values are returned verbatim", {
+ p <- plot_ly(x = c(1, 2), y = c(1, 1))
+ l <- plotly_build(p)$x
+ expect_equivalent(l$data[[1]]$x, c(1, 2))
+ expect_equivalent(l$data[[1]]$y, c(1, 1))
+})
+
+test_that("plot_ly defaults to scatterplot", {
+ p1 <- plot_ly(mtcars, x = ~wt, y = ~mpg)
+ p2 <- plot_ly(mtcars, x = ~wt, y = ~mpg) %>% add_markers()
+ expect_same_data(p1, p2)
+})
+
+test_that("Variable mappings return same result regardless of where they appear", {
+ p1 <- plot_ly(mtcars, x = ~wt, y = ~mpg, size = ~disp)
+ p2 <- plot_ly(mtcars, x = ~wt, y = ~mpg) %>% add_markers(size = ~disp)
+ expect_same_data(p1, p2)
+ p1 <- plot_ly(mtcars, x = ~wt, y = ~mpg, color = ~disp)
+ p2 <- plot_ly(mtcars, x = ~wt, y = ~mpg) %>% add_markers(color = ~disp)
+ expect_same_data(p1, p2)
+ p1 <- plot_ly(mtcars, x = ~wt, y = ~mpg, symbol = ~factor(am))
+ p2 <- plot_ly(mtcars, x = ~wt, y = ~mpg) %>% add_markers(symbol = ~factor(am))
+ expect_same_data(p1, p2)
+ p1 <- plot_ly(mtcars, x = ~wt, y = ~mpg, linetype = ~factor(am))
+ p2 <- plot_ly(mtcars, x = ~wt, y = ~mpg) %>% add_markers(linetype = ~factor(am))
+ expect_message(plotly_build(p1), "Adding lines to mode")
+ expect_message(plotly_build(p2), "Adding lines to mode")
+ expect_same_data(p1, p2)
+})
+
+
+
+test_that("plot_ly() handles a simple scatterplot", {
+ p <- plot_ly(data = iris, x = ~Sepal.Length, y = ~Petal.Length, mode = "markers")
+ l <- expect_traces(p, 1, "scatterplot")
+ expect_equivalent(l$data[[1]]$mode, "markers")
+ expect_equivalent(l$data[[1]]$x, iris$Sepal.Length)
+ expect_equivalent(l$data[[1]]$y, iris$Petal.Length)
+ expect_equivalent(l$layout$xaxis$title, "Sepal.Length")
+ expect_equivalent(l$layout$yaxis$title, "Petal.Length")
+})
+
+test_that("type inference + add_data + layering works as expected", {
+p <- plot_ly(iris, x = ~Species) %>%
+ add_trace(opacity = 0.3) %>%
+ add_data(iris[sample(nrow(iris), 10), ]) %>%
+ add_trace() %>%
+ layout(barmode = "overlay")
+ l <- expect_traces(p, 2, "bar-inference")
+ types <- unique(unlist(lapply(l$data, "[[", "type")))
+ expect_equivalent(types, "histogram")
+ expect_equivalent(l$data[[1]]$opacity, 0.3)
+ expect_equivalent(l$layout$barmode, "overlay")
+ expect_true(length(l$data[[1]]$x) > length(l$data[[2]]$x))
+})
+
+test_that("x/y/z properties have a class of AsIs", {
+ p <- plot_ly(x = 1, y = 1, z = 1, type = "scatter3d")
+ l <- expect_traces(p, 1, "box-data-array")
+ tr <- l$data[[1]]
+ expect_true(inherits(tr$x, "AsIs"))
+ expect_true(inherits(tr$y, "AsIs"))
+ expect_true(inherits(tr$z, "AsIs"))
+})
+
+test_that("grouping within multiples traces works", {
+ g <- expand.grid(visit = 1:2, id = 1:3, cohort = c("A", "B"))
+ g$response <- rnorm(nrow(g))
+ d <- group_by(g, id)
+ p <- plot_ly(d, x = ~visit, y = ~response, color = ~cohort, colors = c("red", "blue"))
+ l <- expect_traces(add_lines(p), 2, "group-within-trace")
+ expect_equivalent(l$data[[1]]$x, c(1, 2, NA, 1, 2, NA, 1, 2))
+ expect_equivalent(l$data[[2]]$x, c(1, 2, NA, 1, 2, NA, 1, 2))
+ expect_true(l$data[[1]]$line$color == toRGB("red"))
+ expect_true(l$data[[2]]$line$color == toRGB("blue"))
+})
+
+test_that("Alpha can be applied to both constant and scaled colors", {
+ p <- plot_ly(x = rnorm(100), y = rnorm(100), color = ~rnorm(100))
+ p <- add_markers(p, alpha = 0.05)
+ p <- add_lines(p, x = -1:1, y = -1:1, color = I("red"), alpha = 0.4)
+ # one trace for the colorbar
+ l <- expect_traces(p, 3, "alpha-blending")
+ # verify the correct alpha for the points
+ rgbs <- l$data[[1]]$marker$colorscale[, 2]
+ alphas <- unique(sub("\\)", "", sapply(strsplit(rgbs, ","), "[[", 4)))
+ expect_equivalent("0.05", alphas)
+ # verify the correct alpha for the lines
+ rgb <- l$data[[2]]$line$color
+ alpha <- sub("\\)", "", sapply(strsplit(rgb, ","), "[[", 4))
+ expect_equivalent("0.4", alpha)
+})
+
+
+test_that("Alpha still applies when no color is applied", {
+ p <- plot_ly(mtcars, x = ~mpg, y = ~disp, alpha = 0.5)
+ l <- expect_traces(p, 1, "alpha-no-color")
+ # verify the correct alpha for the points
+ expect_true(l$data[[1]]$marker$color == "rgba(31,119,180,0.5)")
+})
+
+
+test_that("Factors correctly mapped to a positional axis", {
+ x <- factor(c(1, 2, 4, 8, 16, 32))
+ p <- plot_ly(x = x, y = c(1, 2, 3, 4, 5, 6)) %>% add_markers()
+ l <- expect_traces(p, 1, "factor-axis")
+ expect_equivalent(l$layout$xaxis$type, "category")
+ expect_equivalent(l$layout$xaxis$categoryorder, "array")
+ expect_equivalent(l$layout$xaxis$categoryarray, levels(x))
+})
+
+test_that("Character strings correctly mapped to a positional axis", {
+ # scramble alphabet order
+ letters <- LETTERS[as.numeric(sort(as.character(1:26)))]
+ p <- plot_ly(x = letters, y = seq_along(letters)) %>%
+ add_bars(color = rep(c("a1", "a2"), length.out = 26))
+ l <- expect_traces(p, 2, "character-axis")
+ expect_equivalent(l$layout$xaxis$type, "category")
+ expect_equivalent(l$layout$xaxis$categoryorder, "array")
+ expect_equivalent(l$layout$xaxis$categoryarray, LETTERS)
+})
+
+test_that("Histogram", {
+ p <- plot_ly(x = rnorm(100))
+ l <- expect_traces(p, 1, "histogram")
+ o <- unlist(lapply(l$data, "[[", "orientation"))
+ types <- unlist(lapply(l$data, "[[", "type"))
+ expect_null(o)
+ expect_equivalent(unique(types), "histogram")
+})
+
+test_that("Discrete variable mapped to x creates horizontal bar chart", {
+ p <- plot_ly(y = rnorm(100))
+ l <- expect_traces(p, 1, "histogram-vert")
+ o <- unlist(lapply(l$data, "[[", "orientation"))
+ types <- unlist(lapply(l$data, "[[", "type"))
+ expect_equivalent(unique(o), "h")
+ expect_equivalent(unique(types), "histogram")
+})
+
+test_that("Can avoid inheriting attributes", {
+ p <- plot_ly(mtcars, x = ~wt, y = ~mpg, color = I("red")) %>%
+ add_histogram(x = ~factor(vs), inherit = FALSE)
+ l <- expect_traces(p, 1, "inherit-FALSE")
+ expect_equivalent(l$data[[1]][["type"]], "histogram")
+ expect_equivalent(l$data[[1]][["x"]], factor(mtcars[["vs"]]))
+ expect_null(l$data[[1]][["y"]])
+ expect_true(l$data[[1]][["marker"]][["color"]] != toRGB("red"))
+})
+
+test_that("Complex example works", {
+ # note how median (the variable) doesn't exist in the second layer
+ p <- txhousing %>%
+ plot_ly(x = ~date, y = ~median) %>%
+ group_by(city) %>%
+ add_lines(alpha = 0.2, name = "Texan Cities", hoverinfo = "none") %>%
+ group_by(date) %>%
+ summarise(
+ q1 = quantile(median, 0.25, na.rm = TRUE),
+ m = median(median, na.rm = TRUE),
+ q3 = quantile(median, 0.75, na.rm = TRUE)
+ ) %>%
+ add_lines(y = ~m, color = I("red"), name = "median") %>%
+ add_ribbons(ymin = ~q1, ymax = ~q3, color = I("red"), name = "IQR")
+
+ l <- expect_traces(p, 3, "time-series-summary")
+})
diff --git a/tests/testthat/test-rotated-ticks.R b/tests/testthat/test-rotated-ticks.R
new file mode 100644
index 0000000..f529c66
--- /dev/null
+++ b/tests/testthat/test-rotated-ticks.R
@@ -0,0 +1,69 @@
+context("rotated ticks")
+
+ss <- data.frame(State=paste("some long text", c("CA", "NY", "TX")),
+ Prop.Inv=c(0, 1, 0.7),
+ Year=c(1984, 2015, 1999))
+
+fg <- ggplot() +
+ geom_point(aes(x=State, y=Prop.Inv), data=ss) +
+ xlab("STATE SOME REALLY REALLY LONG TEXT THAT MAY OVERLAP TICKS")
+
+## TODO: change the details of getTicks and expect_rotate_anchor to
+## test plotly web pages.
+getTicks <- function(html, p.name){
+ xp <- sprintf('//svg[@id="%s"]//g[@id="xaxis"]//text', p.name)
+ nodes <- getNodeSet(html, xp)
+ stopifnot(length(nodes) > 0)
+ sapply(nodes, xmlAttrs)
+}
+expect_rotate_anchor <- function(info, rotate, anchor){
+ return()#TODO:remove.
+ not <- getTicks(info$html, 'not')
+ expect_match(not["style", ], "text-anchor: middle", fixed=TRUE)
+ expect_match(not["transform", ], "rotate(0", fixed=TRUE)
+ rotated <- getTicks(info$html, 'rotated')
+ expect_match(rotated["style", ], paste("text-anchor:", anchor), fixed=TRUE)
+ expect_match(rotated["transform", ], paste0("rotate(", rotate), fixed=TRUE)
+
+ e.axis <- remDr$findElement(using="css selector", "g#xaxis")
+ e.text <- e.axis$findChildElement("css selector", "text")
+ tick.loc <- e.text$getElementLocation()
+ tick.size <- e.text$getElementSize()
+ ## Subtract a magic number that lets the test pass for un-rotated
+ ## labels in firefox.
+ tick.bottom.y <- tick.loc$y + tick.size$height - 6
+ e.title <- remDr$findElement("css selector", "text#xtitle")
+ title.loc <- e.title$getElementLocation()
+ expect_true(tick.bottom.y < title.loc$y)
+}
+
+## TODO: implement renderHTML which should upload and plot the data,
+## then download the rendered HTML using RSelenium to control a
+## headless browser.
+renderHTML <- function(gg){
+ gg2list(gg)
+}
+
+test_that('no axis rotation is fine', {
+ info <- renderHTML(fg)
+ expect_rotate_anchor(info, "0", "middle")
+})
+
+test_that('axis.text.x=element_text(angle=90)"', {
+ rotated <- fg+theme(axis.text.x=element_text(angle=90))
+ info <- renderHTML(rotated)
+ expect_rotate_anchor(info, "-90", "end")
+})
+
+test_that('axis.text.x=element_text(angle=70) means transform="rotate(-70)"', {
+ rotated <- fg+theme(axis.text.x=element_text(angle=70))
+ info <- renderHTML(rotated)
+ expect_rotate_anchor(info, "-70", "end")
+})
+
+## test_that('hjust=0.75 is an error', {
+## problem <- fg+theme(axis.text.x=element_text(hjust=0.75)
+## expect_error({
+## info <- renderHTML(problem)
+## }, "ggplotly only supports hjust values 0, 0.5, 1")
+## })
diff --git a/tests/testthat/test-toRGB.R b/tests/testthat/test-toRGB.R
new file mode 100644
index 0000000..298269b
--- /dev/null
+++ b/tests/testthat/test-toRGB.R
@@ -0,0 +1,10 @@
+context("toRGB")
+
+test_that("toRGB(NULL) is NULL", {
+ expect_identical(toRGB(NULL), NULL)
+})
+
+test_that("Can apply alpha recursively with toRGB()", {
+ col <- toRGB(toRGB("black", 0.9), 0.9)
+ expect_match(col, "rgba\\(0,0,0,0\\.80")
+})
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/r-cran-plotly.git
More information about the debian-science-commits
mailing list