diff --git a/DESCRIPTION b/DESCRIPTION index 5edbe1d..91cfa54 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -5,7 +5,7 @@ Version: 0.5.1 Date: 2017-01-23 Authors@R: c( person("Francois", "Guillem", email = "francois.guillem@rte-france.com", role = c("aut", "cre")), - person("RTE", role = "cph"), + person("RTE", role = c("cph", "fnd")), person("JJ", "Allaire", role = "ctb") ) Description: Like package 'manipulate' does for static graphics, this package diff --git a/README.Rmd b/README.Rmd index 6651bac..0b5cae4 100644 --- a/README.Rmd +++ b/README.Rmd @@ -1,6 +1,7 @@ --- title: "Add more interactivity to interactive charts" output: github_document +always_allow_html: yes --- ```{r setup, include=FALSE} diff --git a/README.md b/README.md index e5383a8..7e2edc6 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,10 @@ plotClusters <- function(xvar, yvar, nclusters) { plotClusters("Sepal.Width", "Sepal.Length", 3) ``` -![](README_files/figure-markdown_github/kmeans-1.png) + + + Once this code has been written, it is very easy to produce a UI that lets the user change the values of the three parameters of the function `plotClusters`: ``` r diff --git a/_pkgdown.yml b/_pkgdown.yml new file mode 100644 index 0000000..7018a73 --- /dev/null +++ b/_pkgdown.yml @@ -0,0 +1,49 @@ +templates: + params: + bootswatch: flatly + +authors: + RTE: + href: http://www.rte-france.com + html: + +reference: +- title: ManipulateGadget + desc: > + Main description and help content of this package. + What is a manipultateWidget and how to create it ? + contents: + - manipulateWidget-package + - manipulateWidget + +- title: combining widget + desc: > + These function are the one for combining widget alltogether + contents: + - combineWidgets-shiny + - combineWidgets + +- title: Add interactive component a manipulateGagdet + desc: > + These fonction are the way to add interactive content to + complement your initial htmlwidget + contents: + - starts_with("mw") + +navbar: + title: ~ + type: default + left: + - text: Home + href: index.html + - text: Reference + href: reference/index.html + - text: Getting Started + href: articles/manipulateWidgets.html + - text: News + menu: + - text: "Change Log" + href: news/index.html + right: + - icon: fa-github + href: https://github.com/rte-antares-rpackage/manipulateWidget diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 0000000..a7d6e13 --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1 @@ +COPYRIGHT HOLDER: RTE Réseau de transport d’électricité diff --git a/docs/articles/comparison.gif b/docs/articles/comparison.gif new file mode 100644 index 0000000..2f6c701 Binary files /dev/null and b/docs/articles/comparison.gif differ diff --git a/docs/articles/conditional-inputs.gif b/docs/articles/conditional-inputs.gif new file mode 100644 index 0000000..458f205 Binary files /dev/null and b/docs/articles/conditional-inputs.gif differ diff --git a/docs/articles/dynamic_inputs.gif b/docs/articles/dynamic_inputs.gif new file mode 100644 index 0000000..1c111cd Binary files /dev/null and b/docs/articles/dynamic_inputs.gif differ diff --git a/docs/articles/example-kmeans.gif b/docs/articles/example-kmeans.gif new file mode 100644 index 0000000..76a4341 Binary files /dev/null and b/docs/articles/example-kmeans.gif differ diff --git a/docs/articles/fancy-example.gif b/docs/articles/fancy-example.gif new file mode 100644 index 0000000..c07259b Binary files /dev/null and b/docs/articles/fancy-example.gif differ diff --git a/docs/articles/groups-inputs.gif b/docs/articles/groups-inputs.gif new file mode 100644 index 0000000..099d8bc Binary files /dev/null and b/docs/articles/groups-inputs.gif differ diff --git a/docs/articles/index.html b/docs/articles/index.html new file mode 100644 index 0000000..677645a --- /dev/null +++ b/docs/articles/index.html @@ -0,0 +1,119 @@ + + + +
+ + + + +The manipulateWidget
package is largely inspired by the manipulate
package from Rstudio. It provides the function `manipulateWidget
that can be used to create in a very easy and quick way a graphical interface that lets the user modify the data or the parameters of an interactive chart. Technically, the function generates a Shiny gadget, but the user does not even have to know what is Shiny.
The package also provides the combineWidgets
function to easily combine multiple interactive charts in a single view. Of course both functions can be used together: here is an example that uses packages dygraphs
and plot_ly
(code at the end of the document).
The main function of the package is manipulateWidget
. It takes as argument an expression that generates an interactive chart (and more precisely an htmlwidget object. See http://www.htmlwidgets.org/ if you have never heard about it) and a set of input controls created with functions mwSlider, mwCheckbox… which are used to dynamically change values within the expression. Each time the user modifies the value of a control, the expression is evaluated again and the chart is updated. Consider the following code:
manipulateWidget(
+ myPlotFun(country),
+ country = mwSelect(c("BE", "DE", "ES", "FR"))
+)
It generates a graphical interface with a select input on its left with options “BE”, “DE”, “ES”, “FR”. The value of this input is mapped to the variable country
in the expression. By default, at the beginning the value of country
will be equal to the first choice of the input. So the function will first execute myPlotFun("BE")
and the result will be displayed in the main panel of the interface. If the user changes the value to “FR”, then the expression myPlotFun("FR")
is evaluated and the new result is displayed.
The interface also contains a button “Done”. When the user clicks on it, the last chart is returned. It can be stored in a variable, be modified by the user, saved as a html file with saveWidget from package htmlwidgets or converted to a static image file with package webshot
.
Of course, one can create as many controls as needed. The interface of the animated example in the introduction was generated with the following code:
+manipulateWidget(
+ myPlotFun(distribution, range, title),
+ distribution = mwSelect(choices = c("gaussian", "uniform")),
+ range = mwSlider(2000, 2100, value = c(2000, 2100), label = "period"),
+ title = mwText()
+)
To see all available controls that can be added to the UI, take a look at the list of the functions of the package:
+help(package = "manipulateWidget")
The combineWidgets
function gives an easy way to combine interactive charts (like par(mfrow = c(...))
or layout
for static plots). To do it, one has simply to pass to the function the widgets to combine. In the next example, we visualize two random time series with dygraphs and combine them.
library(dygraphs)
+
+plotRandomTS <- function(id) {
+ dygraph(data.frame(x = 1:10, y = rnorm(10)), main = paste("Random plot", id))
+}
+
+combineWidgets(plotRandomTS(1), plotRandomTS(2))
The functions tries to find the best number of columns and rows. But one can control them with parameters nrow
and ncol
. It is also possible to control their relative size with parameters rowsize
and colsize
. To achieve complex layouts, it is possible to use nested combined widgets. Here is an example of a complex layout.
combineWidgets(
+ ncol = 2, colsize = c(2, 1),
+ plotRandomTS(1),
+ combineWidgets(
+ ncol = 1,
+ plotRandomTS(2),
+ plotRandomTS(3),
+ plotRandomTS(4)
+ )
+)
Even if the main use of combineWidgets
is to combine htmlwidgets
, it can also display text or html tags. It can be useful to include comments in a chart. Moreover it has arguments to add a title and to add some html content in the sides of the chart.
combineWidgets(
+ plotRandomTS(1),
+ plotRandomTS(2),
+ plotRandomTS(3),
+ plotRandomTS(4),
+ title = "Four random plots",
+ header = "Here goes the header content. <span style='color:red'>It can include html code</span>.",
+ footer = "Here goes the footer content.",
+ leftCol = "<div style='margin-top:150px;'>left column</div>",
+ rightCol = "<div style='margin-top:150px;'>right column</div>"
+)
If you have a large number of inputs, you can easily group them. To do so, simply pass to the function manipulateWidget
a list of inputs instead of passing directly the inputs. Here is a toy example. Groups are by default collapsed and user can click on their title to display/collapse then.
mydata <- data.frame(x = 1:100, y = rnorm(100))
+manipulateWidget(
+ dygraph(mydata[range[1]:range[2], ],
+ main = title, xlab = xlab, ylab = ylab),
+ range = mwSlider(1, 100, c(1, 100)),
+ "Graphical parameters" = list(
+ title = mwText("Fictive time series"),
+ xlab = mwText("X axis label"),
+ ylab = mwText("Y axis label")
+ )
+)
Sometimes some inputs are relevant only if other inputs have some value. manipulateWidget
provides a way to show/hide inputs conditionally to the value of the other inputs thanks to parameter .display
. This parameter expects a named list of expressions. The names are the ones of the inputs to show/hide and the expressions can include any input and have to evaluate to TRUE/FALSE
. Here is a toy example, using package plot_ly
. User can choose points or lines to represent some data. If he chooses lines, then an input appears to let him choose the width of the lines.
mydata <- data.frame(x = 1:100, y = rnorm(100))
+
+myPlot <- function(type, lwd) {
+ if (type == "points") {
+ plot_ly(mydata, x= ~x, y = ~y, type = "scatter", mode = "markers")
+ } else {
+ plot_ly(mydata, x= ~x, y = ~y, type = "scatter", mode = "lines",
+ line = list(width = lwd))
+ }
+}
+
+manipulateWidget(
+ myPlot(type, lwd),
+ type = mwSelect(c("points", "lines"), "points"),
+ lwd = mwSlider(1, 10, 1),
+ .display = list(lwd = type == "lines")
+)
manipulateWidget
provides a mecanism to dynamically update inputs through the parameter .updateInputs
. For instance, one can update the choices of an input control conditionally to the value of other inputs.
This mechanism is similar to the used for conditionally displaying inputs: .updateInputs
expects a named list where names are the names of the inputs to update. Values are themselves named list where names are arguments of the input generator function and values are expressions that give the new values of the arguments.
Here is an example that uses plotly to represent with a barchart a car from the mtcars
dataset. User chooses the number of cylinders and then a car among the ones with this number of cylinders
colMax <- apply(mtcars, 2, max)
+
+plotCar <- function(carName) {
+ carValues <- unlist(mtcars[carName, ])
+ carValuesRel <- carValues / colMax
+ plot_ly() %>%
+ add_bars(x = names(mtcars), y = carValuesRel, text = carValues,
+ hoverinfo = c("x+text"))
+}
+
+carChoices <- split(row.names(mtcars), mtcars$cyl)
+
+str(carChoices)
+## $ 4: chr [1:11] "Datsun 710" "Merc 240D" "Merc 230" "Fiat 128" ...
+## $ 6: chr [1:7] "Mazda RX4" "Mazda RX4 Wag" "Hornet 4 Drive" "Valiant" ...
+## $ 8: chr [1:14] "Hornet Sportabout" "Duster 360" "Merc 450SE" "Merc 450SL" ...
+
+manipulateWidget(
+ plotCar(car),
+ cylinders = mwSelect(c("4", "6", "8")),
+ car = mwSelect(),
+ .updateInputs = list(
+ car = list(choices = carChoices[[cylinders]])
+ )
+)
The “normal” use of manipulateWidget
is to provide an expression that always return an htmlwidget
. In such case, every time the user changes the value of an input, the current widget is destroyed and a new one is created and rendered. This behavior is not optimal and sometimes it can be painful for the user: consider for instance an interactive map. Each time user changes an input, the map is destroyed and created again, then zoom and location on the map are lost every time.
Some packages provide functions to update a widget that has already been rendered. This is the case for instance for package leaflet
with the function leafletProxy
. To use such functions, manipulateWidget
evaluates the parameter .expr
with two extra variables:
.initial
: TRUE
if the expression is evaluated for the first time and then the widget has not been rendered yet, FALSE
if the widget has already been rendered.
.session
: A shiny session object.
Moreover the ID of the rendered widget is always “output”. Then it is quite easy to write an expression that initializes a widget when it is evaluated the first time and then that updates this widget. Here is an example using package leaflet
.
lon <- rnorm(10, sd = 20)
+lat <- rnorm(10, sd = 20)
+
+myMapFun <- function(radius, color, initial, session) {
+ if (initial) {
+ # Widget has not been rendered
+ map <- leaflet() %>% addTiles()
+ } else {
+ # widget has already been rendered
+ map <- leafletProxy("output", session) %>% clearMarkers()
+ }
+
+ map %>% addCircleMarkers(lon, lat, radius = radius, color = color)
+}
+
+manipulateWidget(myMapFun(radius, color, .initial, .session),
+ radius = mwSlider(5, 30, 10),
+ color = mwSelect(c("red", "blue", "green")))
Sometimes one wants to compare two similar charts to visualize the impact of some parameter or to compare different data sets. manipulateWidget
has an argument to perform such comparison without writing much code: .compare
. One just has to write the code to generate one chart and use this argument to specify which parameters should vary between the two charts. Here is a toy example that uses dygraphs
.
mydata <- data.frame(
+ timeId = 1:100,
+ series1 = rnorm(100),
+ series2 = rnorm(100),
+ series3 = rnorm(100)
+)
+manipulateWidget(
+ dygraph(mydata[range[1]:range[2], c("timeId", series)], main = title),
+ range = mwSlider(1, 100, c(1, 100)),
+ series = mwSelect(c("series1", "series2", "series3")),
+ title = mwText(),
+ .compare = list(
+ title = list("First chart", "Second chart"),
+ series = NULL
+ )
+)
manipulateWidget
in a documentmanipulateWidget
uses Shiny, so it does not work in a “normal” Rmarkdown document. If one uses the function in a code chunck, the htmlwidget will be outputed with the default values of the parameters and there will be no interface to modify the parameters.
Nevertheless, it is possible to include a shiny application in a document with the runtime: shiny (see http://rmarkdown.rstudio.com/authoring_shiny.html). In such setting manipulateWidget
works normally and the document can be published on a shiny server to let final users play with the parameters of the document.