Skip to content

Commit 53bcd01

Browse files
committed
Get dplyr integration working again and introduce last_plot()
1 parent 66397c4 commit 53bcd01

15 files changed

+122
-32
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export(ggplotly)
1212
export(group2NA)
1313
export(knit_print.offline)
1414
export(knit_print.plotly)
15+
export(last_plot)
1516
export(layer2traces)
1617
export(layout)
1718
export(offline)

R/offline.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#' }
4040
#'
4141

42-
offline <- function(p = get_plot(), height = 400, width = "100%",
42+
offline <- function(p = last_plot(), height = 400, width = "100%",
4343
out_dir = NULL, open_browser = interactive()) {
4444
haz <- has_offline()
4545
if (!haz) offline_stop()

R/plotly.R

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ plot_ly <- function(data = data.frame(), ..., type = "scatter",
9696
#' @author Carson Sievert
9797
#' @export
9898
#'
99-
add_trace <- function(p = get_plot(), ...,
99+
add_trace <- function(p = last_plot(), ...,
100100
group, color, colors, symbol, symbols, size,
101101
data = NULL, evaluate = FALSE) {
102102
# "native" plotly arguments
@@ -108,13 +108,13 @@ add_trace <- function(p = get_plot(), ...,
108108
if (!missing(symbol)) argz$symbol <- substitute(symbol)
109109
if (!missing(symbols)) argz$symbols <- substitute(symbols)
110110
if (!missing(size)) argz$size <- substitute(size)
111-
p <- get_plot(p)
111+
data <- data %||% if (is.data.frame(p)) p else list()
112112
tr <- list(
113113
args = argz,
114-
# if data is missing, adopt the most recent data environment
115-
env = if (is.null(data)) p$data[[length(p$data)]]$env else list2env(data),
114+
env = list2env(data),
116115
enclos = parent.frame()
117116
)
117+
p <- last_plot(p)
118118
p$data <- c(p$data, list(tr))
119119
if (evaluate) p <- plotly_build(p)
120120
hash_plot(data, p)
@@ -131,15 +131,15 @@ add_trace <- function(p = get_plot(), ...,
131131
#' @author Carson Sievert
132132
#' @export
133133
#'
134-
layout <- function(p = get_plot(), ...,
134+
layout <- function(p = last_plot(), ...,
135135
data = NULL, evaluate = FALSE) {
136-
p <- get_plot(p)
136+
data <- data %||% if (is.data.frame(p)) p else list()
137137
layout <- list(
138138
args = substitute(list(...)),
139-
# if data is missing, adopt the most recent data environment
140-
env = if (is.null(data)) p$data[[length(p$data)]]$env else list2env(data),
139+
env = list2env(data),
141140
enclos = parent.frame()
142141
)
142+
p <- last_plot(p)
143143
p$layout <- c(p$layout, list(layout))
144144
if (evaluate) p <- plotly_build(p)
145145
hash_plot(data, p)
@@ -158,8 +158,7 @@ layout <- function(p = get_plot(), ...,
158158
#' @author Carson Sievert
159159
#' @export
160160
#'
161-
style <- function(p = get_plot(strict = FALSE), ..., traces = 1, evaluate = FALSE) {
162-
p <- get_plot(p)
161+
style <- function(p = last_plot(), ..., traces = 1, evaluate = FALSE) {
163162
idx <- traces >= length(p$data)
164163
if (any(idx)) warning("You've referenced non-existent traces", call. = FALSE)
165164
style <- list(
@@ -184,14 +183,14 @@ style <- function(p = get_plot(strict = FALSE), ..., traces = 1, evaluate = FALS
184183
#'
185184
#' @param l a ggplot object, or a plotly object, or a list.
186185
#' @export
187-
plotly_build <- function(l = get_plot()) {
186+
plotly_build <- function(l = last_plot()) {
188187
# ggplot objects don't need any special type of handling
189-
if (is.ggplot(l)) return(gg2list(l))
188+
if (is.ggplot(l)) return(ggplotly(l))
190189
l <- get_plot(l)
191190
# plots without NSE don't need it either
192191
nms <- lapply(l$data, names)
193192
idx <- unique(unlist(lapply(l$data, names))) %in% c("args", "env")
194-
if (sum(idx) != 2) return(struct(l, "plotly"))
193+
if (sum(idx) != 2) return(structure(l, class = unique("plotly", class(l))))
195194
nms <- names(l)
196195
# assume unnamed list elements are data/traces
197196
idx <- nms %in% ""
@@ -307,7 +306,7 @@ plotly_build <- function(l = get_plot()) {
307306
# traces shouldn't have any names
308307
x$data <- setNames(x$data, NULL)
309308
# add plotly class mainly for printing method
310-
struct(x, "plotly")
309+
structure(x, class = unique("plotly", class(x)))
311310
}
312311

313312
# returns a _list of traces_.

R/plotly_POST.R

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
plotly_POST <- function(x) {
3030
x <- plotly_build(x)
31-
3231
# empty keyword arguments can cause problems
3332
kwargs <- x[get_kwargs()]
3433
kwargs <- kwargs[sapply(kwargs, length) > 0]

R/stream.R

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#' Stream objects
2+
#'
3+
#' @param p a plotly or figure object.
4+
#' @examples
5+
#'
6+
#' # generate 100 random observations
7+
#' n <- 100
8+
#' x <- rnorm(n)
9+
#'
10+
#' # start a plotly stream
11+
#' s <- stream()
12+
#' # write to the stream
13+
#' for (i in seq_len(50)) {
14+
#' s$write(x = x)
15+
#' x <- x[-n]
16+
#' x[1] <- rnorm(1)
17+
#' }
18+
#' # close the stream
19+
#' s$close()
20+
#'
21+
22+
# Implementation of https://plot.ly/streaming/
23+
stream <- function(x) {
24+
haz <- has_stream()
25+
if (!haz) {
26+
stop("To use plotly streams, you need to create streaming token(s). \n",
27+
"To see how to create token(s), please visit: \n",
28+
"https://plot.ly/python/streaming-tutorial/#Get-your-stream-tokens \n",
29+
"Once created, save these token(s) as an environment variable: \n",
30+
" Sys.setenv('plotly_streamtoken' = 'token1;token2;token3')")
31+
}
32+
# obtain streaming tokens
33+
toks <- strsplit(names(haz), ";")[[1]]
34+
if (length(toks)) {
35+
warning("Multiple token handling coming soon!")
36+
toks <- toks[1]
37+
}
38+
# request headers
39+
headerz <- httr::add_headers(.headers = c(
40+
"Transfer-Encoding" = "chunked",
41+
"plotly-streamtoken" = toks
42+
))
43+
# request body
44+
st <- list(stream = list(token = toks, maxpoints = 500))
45+
resp <- httr::POST(get_domain("stream"), headerz)
46+
httr::stop_for_status(resp)
47+
48+
49+
list(
50+
write = function() {},
51+
close = function() {}
52+
)
53+
}
54+
55+
has_stream <- function() {
56+
token <- Sys.getenv("plotly_streamtoken")
57+
setNames(token != "", token)
58+
}

R/subplots.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323

2424
## TODO: throw warning if geo and non-geo coordinates are used!!!
25-
subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.1 /nrows) {
25+
subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.02) {
2626
# note that dots is a _list of plotlys_
2727
dots <- lapply(list(...), plotly_build)
2828
# put existing plot anchors and domain information into a tidy format
@@ -125,11 +125,11 @@ subplot <- function(..., nrows = 1, which_layout = "merge", margin = 0.1 /nrows)
125125
# (but overwrite domain/anchor info)
126126
l <- dots[[info$plot]]$layout
127127
p$layout[[xaxis]] <- modifyList(
128-
l[names(l) %in% "xaxis"],
128+
if (any(idx <- names(l) %in% "xaxis")) l[idx][[1]] else list(),
129129
list(domain = xdom, anchor = info$yaxis)
130130
)
131131
p$layout[[yaxis]] <- modifyList(
132-
l[names(l) %in% "yaxis"],
132+
if (any(idx <- names(l) %in% "yaxis")) l[idx][[1]] else list(),
133133
list(domain = ydom, anchor = info$xaxis)
134134
)
135135
p$data[[i]]$xaxis <- info$xaxis

R/utils.R

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ plotlyEnv <- new.env(parent = emptyenv())
3030
hash_plot <- function(df, p) {
3131
if (missing(df) || is.null(df)) df <- data.frame()
3232
hash <- digest::digest(p)
33+
# terrible hack to ensure we can always find the most recent hash
34+
hash <- paste(hash, length(ls(plotlyEnv)), sep = "#")
3335
assign(hash, p, envir = plotlyEnv)
3436
attr(df, "plotly_hash") <- hash
3537
# add plotly class mainly for printing method
@@ -45,19 +47,33 @@ hash_plot <- function(df, p) {
4547
#' the last plotly object created in this R session is returned (if it exists).
4648
#'
4749
#' @param data a data frame with a class of plotly (and a plotly_hash attribute).
48-
get_plot <- function(data = NULL, strict = TRUE) {
50+
#' @param last if no plotly attribute is found, return the last plot or NULL?
51+
get_plot <- function(data = NULL, last = FALSE) {
4952
hash <- attr(data, "plotly_hash")
5053
if (!is.null(hash)) {
5154
get(hash, envir = plotlyEnv)
52-
} else if (is.data.frame(data)) {
53-
# safe to just grab the most recent environment?
54-
hash <- rev(ls(plotlyEnv))[1]
55-
plotlyEnv[[hash]]
55+
} else if (last) {
56+
envs <- strsplit(ls(plotlyEnv), "#")
57+
last_env <- ls(plotlyEnv)[which.max(sapply(envs, "[[", 2))]
58+
get(last_env, envir = plotlyEnv)
5659
} else {
57-
data
60+
data %||% list()
5861
}
5962
}
6063

64+
#' Retrive last plotly to be modified or created
65+
#'
66+
#' @seealso \link{plotly_build}
67+
#' @export
68+
#'
69+
last_plot <- function(...) {
70+
p <- get_plot(..., last = TRUE)
71+
structure(
72+
p,
73+
class = unique(c("plotly", class(p)))
74+
)
75+
}
76+
6177
# Check for credentials/configuration and throw warnings where appropriate
6278
verify <- function(what = "username") {
6379
val <- grab(what)

man/add_trace.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
\alias{add_trace}
55
\title{Add a trace to a plotly visualization}
66
\usage{
7-
add_trace(p = get_plot(), ..., group, color, colors, symbol, symbols, size,
7+
add_trace(p = last_plot(), ..., group, color, colors, symbol, symbols, size,
88
data = NULL, evaluate = FALSE)
99
}
1010
\arguments{

man/get_plot.Rd

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
\alias{get_plot}
55
\title{Obtain underlying data of plotly object}
66
\usage{
7-
get_plot(data = NULL, strict = TRUE)
7+
get_plot(data = NULL, last = FALSE)
88
}
99
\arguments{
1010
\item{data}{a data frame with a class of plotly (and a plotly_hash attribute).}
11+
12+
\item{last}{if no plotly attribute is found, return the last plot or NULL?}
1113
}
1214
\description{
1315
Given a data frame with a class of plotly, this function returns the arguments

man/last_plot.Rd

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
% Generated by roxygen2 (4.1.1): do not edit by hand
2+
% Please edit documentation in R/utils.R
3+
\name{last_plot}
4+
\alias{last_plot}
5+
\title{Retrive last plotly to be modified or created}
6+
\usage{
7+
last_plot(...)
8+
}
9+
\description{
10+
Retrive last plotly to be modified or created
11+
}
12+
\seealso{
13+
\link{plotly_build}
14+
}
15+

man/layout.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
\alias{layout}
55
\title{Add and/or modify layout of a plotly}
66
\usage{
7-
layout(p = get_plot(), ..., data = NULL, evaluate = FALSE)
7+
layout(p = last_plot(), ..., data = NULL, evaluate = FALSE)
88
}
99
\arguments{
1010
\item{p}{A plotly object.}

man/offline.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
\alias{offline}
55
\title{Plotly Offline}
66
\usage{
7-
offline(p = get_plot(), height = 400, width = "100\%", out_dir = NULL,
7+
offline(p = last_plot(), height = 400, width = "100\%", out_dir = NULL,
88
open_browser = interactive())
99
}
1010
\arguments{

man/plotly_build.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
\alias{plotly_build}
55
\title{Build a plotly object before viewing it}
66
\usage{
7-
plotly_build(l = get_plot())
7+
plotly_build(l = last_plot())
88
}
99
\arguments{
1010
\item{l}{a ggplot object, or a plotly object, or a list.}

man/style.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
\alias{style}
55
\title{Modify trace(s)}
66
\usage{
7-
style(p = get_plot(strict = FALSE), ..., traces = 1, evaluate = FALSE)
7+
style(p = last_plot(), ..., traces = 1, evaluate = FALSE)
88
}
99
\arguments{
1010
\item{p}{A plotly visualization.}

man/subplot.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
\alias{subplot}
55
\title{View multiple plots in a single view}
66
\usage{
7-
subplot(..., nrows = 1, which_layout = "merge", margin = 0)
7+
subplot(..., nrows = 1, which_layout = "merge", margin = 0.02)
88
}
99
\arguments{
1010
\item{...}{any number of plotly objects}

0 commit comments

Comments
 (0)