Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Send 'plotly_click' and 'plotly_selected' events to shiny when in shinyMode by cpsievert · Pull Request #416 · plotly/plotly.R · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Send 'plotly_click' and 'plotly_selected' events to shiny when in shinyMode by cpsievert · Pull Request #416 · plotly/plotly.R · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Send 'plotly_click' and 'plotly_selected' events to shiny when in shinyMode by cpsievert · Pull Request #416 · plotly/plotly.R · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Send 'plotly_click' and 'plotly_selected' events to shiny when in shinyMode by cpsievert · Pull Request #416 · plotly/plotly.R · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Send 'plotly_click' and 'plotly_selected' events to shiny when in shinyMode by cpsievert · Pull Request #416 · plotly/plotly.R · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Send 'plotly_click' and 'plotly_selected' events to shiny when in shinyMode by cpsievert · Pull Request #416 · plotly/plotly.R · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Send 'plotly_click' and 'plotly_selected' events to shiny when in shinyMode by cpsievert · Pull Request #416 · plotly/plotly.R · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
Package: plotly
Title: Create Interactive Web Graphics via Plotly's JavaScript Graphing Library
Version: 2.4.4
Version: 2.5.0
Authors@R: c(person("Carson", "Sievert", role = c("aut", "cre"),
email = "cpsievert1@gmail.com"),
person("Chris", "Parmer", role = c("aut", "cph"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ export(add_trace)
export(as.widget)
export(config)
export(embed_notebook)
export(event_data)
export(get_figure)
export(gg2list)
export(ggplot_build2)
Expand Down
16 changes: 16 additions & 0 deletions NEWS
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
2.5.0 -- 1 Mar 2015

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 2015

as.widget() now returns htmlwidget objects untouched. See #449.
Expand Down
31 changes: 13 additions & 18 deletions R/ggplotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,15 +4,9 @@
#' \url{https://plot.ly/ggplot2}
#'
#' @param p a ggplot object.
#' @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 world_readable logical. If \code{TRUE}, the graph is viewable
#' by anyone who has the link and in the owner's plotly account.
#' If \code{FALSE}, graph is only viewable in the owner's plotly account.
#' @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 source Only relevant for \link{event_data}.
#' @seealso \link{signup}, \link{plot_ly}
#' @import httr jsonlite
#' @export
Expand All@@ -32,13 +26,9 @@
#' ggplotly(viz)
#' }
#'
ggplotly <- function(p = ggplot2::last_plot(), filename, fileopt,
world_readable = TRUE) {
l <- gg2list(p)
# tack on special keyword arguments
if (!missing(filename)) l$filename <- filename
if (!missing(fileopt)) l$fileopt <- fileopt
l$world_readable <- world_readable
ggplotly <- function(p = ggplot2::last_plot(), width = NULL, height = NULL,
source = "A") {
l <- gg2list(p, width = width, height = height, source = source)
hash_plot(p$data, l)
}

Expand DownExpand Up@@ -116,9 +106,12 @@ guide_names <- function(p, aes = c("shape", "fill", "alpha", "area",
#' Convert a ggplot to a list.
#' @import ggplot2
#' @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 source Only relevant for \link{event_data}.
#' @return figure object (list with names "data" and "layout").
#' @export
gg2list <- function(p) {
gg2list <- function(p, width = NULL, height = NULL, source = "A") {
# ggplot now applies geom_blank() (instead of erroring) when no layers exist
if (length(p$layers) == 0) p <- p + geom_blank()
layout <- list()
Expand DownExpand Up@@ -960,6 +953,8 @@ gg2list <- function(p) {
}

l <- list(data = flipped.traces, layout = flipped.layout)

l$width <- width
l$height <- width
l$source <- source
structure(add_boxed(rm_asis(l)), class = "plotly")
}
6 changes: 4 additions & 2 deletions R/plotly.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,7 @@
#' @param height Height in pixels (optional, defaults to automatic sizing).
#' @param inherit logical. Should future traces inherit properties from this initial trace?
#' @param evaluate logical. Evaluate arguments when this function is called?
#' @param source Only relevant for \link{event_data}.
#' @seealso \code{\link{layout}()}, \code{\link{add_trace}()}, \code{\link{style}()}
#' @author Carson Sievert
#' @export
Expand DownExpand Up@@ -66,7 +67,7 @@
plot_ly <- function(data = data.frame(), ..., type = "scatter",
group, color, colors, symbol, symbols, size,
width = NULL, height = NULL, inherit = FALSE,
evaluate = FALSE) {
evaluate = FALSE, source = "A") {
# "native" plotly arguments
argz <- substitute(list(...))
# old arguments to this function that are no longer supported
Expand DownExpand Up@@ -97,7 +98,8 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
layout = NULL,
url = NULL,
width = width,
height = height
height = height,
source = source
)

if (evaluate) p <- plotly_build(p)
Expand Down
30 changes: 30 additions & 0 deletions R/shiny.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,3 +29,33 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) {
expr <- call("as.widget", 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', and 'plotly_selected' are supported.
#' @param source Which plot should the listener be tied to? This
#' (character string) should match the value of \code{source} in \link{plot_ly}.
#' @export
#' @author Carson Sievert
#' @examples \dontrun{
#' shiny::runApp(system.file("examples", "events", package = "plotly"))
#' }

event_data <- function(event = c("plotly_hover", "plotly_click", "plotly_selected"),
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.")
}
val <- session$input[[sprintf(".clientValue-%s-%s", event[1], source)]]
if (event[1] == "plotly_selected" && !is.null(val)) {
data.frame(lapply(val, as.numeric))
} else {
val
}
}
43 changes: 0 additions & 43 deletions inst/examples/brush/app.R

This file was deleted.

86 changes: 86 additions & 0 deletions inst/examples/lmGadget/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
# 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 character string specifying the x variable
#' @param y a character string specifying the y variable

lmGadget <- function(dat, x, y) {

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) {
selected <<- xor(selected, x)
selected
}
}
selection <- init()

server <- function(input, output) {

# obtain a subset of the data that is still under consideration
left <- reactive({
d <- event_data("plotly_click")
if (!is.null(d)) {
dat <- dat[!selection(row.names(dat) %in% d[["key"]]), ]
}
dat
})

# fit a model to subsetted data
refit <- reactive({
req(input$degree)
formula <- as.formula(
sprintf("%s ~ poly(%s, degree = %s)", y, x, input$degree)
)
lm(formula, left())
})

output$plot1 <- renderPlotly({
dat2 <- left()
dat2$yhat <- as.numeric(fitted(refit()))
# sort data by 'x' variable so we draw a line (not a path)
dat2 <- dat2[order(dat2[, x]), ]

plot_ly(x = dat[, x], y = dat[, y], key = row.names(dat), mode = "markers",
marker = list(color = toRGB("grey90"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2[, y], mode = "markers",
marker = list(color = toRGB("black"), size = 10)) %>%
add_trace(x = dat2[, x], y = dat2$yhat, mode = "lines",
marker = list(color = toRGB("black"))) %>%
layout(showlegend = FALSE, xaxis = list(title = x), yaxis = list(title = y))
})

# Return the most recent fitted model, when we press "done"
observeEvent(input$done, {
stopApp(refit())
})
}

runGadget(ui, server)
}

m <- lmGadget(mtcars, "wt", "mpg")
33 changes: 33 additions & 0 deletions inst/examples/map_click/app.R
Original file line numberDiff line numberDiff line change
@@ -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)
8 changes: 8 additions & 0 deletions inst/examples/plotlyEvents/DESCRIPTION
Original file line numberDiff line numberDiff line change
@@ -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

41 changes: 41 additions & 0 deletions inst/examples/plotlyEvents/app.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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) {

output$plot <- renderPlotly({
if (identical(input$plotType, "ggplotly")) {
p <- ggplot(mtcars, aes(x = mpg, y = wt)) + geom_point()
ggplotly(p) %>% layout(dragmode = "select")
} else {
plot_ly(mtcars, x = mpg, y = wt, mode = "markers") %>%
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"))
Loading