Skip to content

Commit 8895f08

Browse files
committed
attach group index to trace early-on to avoid reaching back into original data
1 parent 76f73ee commit 8895f08

File tree

1 file changed

+29
-27
lines changed

1 file changed

+29
-27
lines changed

R/plotly_build.R

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ plotly_build.plotly <- function(p) {
8080
rapply(x, eval_attr, data = dat, how = "list"),
8181
class = oldClass(x)
8282
)
83+
# if appropriate, tack on a group index
84+
grps <- tryCatch(
85+
as.character(dplyr::groups(dat)),
86+
error = function(e) character(0)
87+
)
88+
if (length(grps) && any(lengths(trace) == NROW(dat))) {
89+
trace[[".plotlyGroupIndex"]] <- interaction(dat[, grps, drop = F])
90+
}
8391

8492
# determine trace type (if not specified, can depend on the # of data points)
8593
# note that this should also determine a sensible mode, if appropriate
@@ -118,13 +126,16 @@ plotly_build.plotly <- function(p) {
118126
})
119127
# I don't think we ever want mesh3d's data attrs
120128
dataArrayAttrs <- if (identical(trace[["type"]], "mesh3d")) NULL else names(Attrs)[as.logical(isArray)]
121-
# for some reason, text isn't listed as a data array attributein some traces
122-
# I'm looking at you scattergeo...
123-
tr <- trace[names(trace) %in% c(npscales(), special_attrs(trace), dataArrayAttrs, "text")]
129+
allAttrs <- c(
130+
dataArrayAttrs, special_attrs(trace), npscales(), ".plotlyGroupIndex",
131+
# for some reason, text isn't listed as a data array in some traces
132+
# I'm looking at you scattergeo...
133+
"text"
134+
)
135+
tr <- trace[names(trace) %in% allAttrs]
124136
# TODO: does it make sense to "train" matrices/2D-tables (e.g. z)?
125137
tr <- tr[vapply(tr, function(x) is.null(dim(x)) && is.atomic(x), logical(1))]
126138
builtData <- tibble::as_tibble(tr)
127-
128139
# avoid clobbering I() (i.e., variables that shouldn't be scaled)
129140
for (i in seq_along(tr)) {
130141
if (inherits(tr[[i]], "AsIs")) builtData[[i]] <- I(builtData[[i]])
@@ -139,7 +150,7 @@ plotly_build.plotly <- function(p) {
139150
!isAsIs & isDiscrete & names(builtData) %in% c("symbol", "color")
140151
if (any(isSplit)) {
141152
paste2 <- function(x, y) paste(x, y, sep = "<br>")
142-
builtData$.plotlyTraceIndex <- Reduce(paste2, builtData[isSplit])
153+
builtData[[".plotlyTraceIndex"]] <- Reduce(paste2, builtData[isSplit])
143154
}
144155
# Build the index used to determine grouping (later on, NAs are inserted
145156
# via group2NA() to create the groups). This is done in 3 parts:
@@ -158,30 +169,23 @@ plotly_build.plotly <- function(p) {
158169
if (any(!isComplete) && !hasGrp) {
159170
warning("Ignoring ", sum(!isComplete), " observations", call. = FALSE)
160171
}
161-
builtData$.plotlyGroupIndex <- cumsum(!isComplete)
172+
builtData[[".plotlyMissingIndex"]] <- cumsum(!isComplete)
162173
builtData <- builtData[isComplete, ]
163-
grps <- tryCatch(
164-
as.character(dplyr::groups(dat)),
165-
error = function(e) character(0)
166-
)
167-
if (length(grps) && hasGrp) {
168-
if (isTRUE(trace[["connectgaps"]])) {
169-
stop(
170-
"Can't use connectgaps=TRUE when data has group(s).",
171-
call. = FALSE
172-
)
173-
}
174-
builtData$.plotlyGroupIndex <- interaction(
175-
interaction(dat[isComplete, grps, drop = FALSE]),
176-
builtData$.plotlyGroupIndex %||% ""
174+
if (length(grps) && hasGrp && isTRUE(trace[["connectgaps"]])) {
175+
stop(
176+
"Can't use connectgaps=TRUE when data has group(s).", call. = FALSE
177177
)
178178
}
179-
179+
builtData[[".plotlyGroupIndex"]] <- interaction(
180+
builtData[[".plotlyGroupIndex"]] %||% "",
181+
builtData[[".plotlyMissingIndex"]]
182+
)
180183
builtData <- arrange_safe(builtData,
181-
c(".plotlyTraceIndex", ".plotlyGroupIndex", if (inherits(trace, "plotly_line")) "x")
184+
c(".plotlyTraceIndex", ".plotlyGroupIndex",
185+
if (inherits(trace, "plotly_line")) "x")
182186
)
183187
builtData <- train_data(builtData, trace)
184-
trace$.plotlyVariableMapping <- names(builtData)
188+
trace[[".plotlyVariableMapping"]] <- names(builtData)
185189

186190
# copy over to the trace data
187191
for (i in names(builtData)) {
@@ -191,7 +195,6 @@ plotly_build.plotly <- function(p) {
191195

192196
# TODO: provide a better way to clean up "high-level" attrs
193197
trace[c("ymin", "ymax", "yend", "xend")] <- NULL
194-
195198
trace[lengths(trace) > 0]
196199

197200
}, p$x$attrs, names2(p$x$attrs))
@@ -235,7 +238,8 @@ plotly_build.plotly <- function(p) {
235238
for (i in seq_along(traces)) {
236239
mappingAttrs <- c(
237240
"alpha", npscales(), paste0(npscales(), "s"),
238-
".plotlyGroupIndex", ".plotlyTraceIndex", ".plotlyVariableMapping"
241+
".plotlyGroupIndex", ".plotlyMissingIndex",
242+
".plotlyTraceIndex", ".plotlyVariableMapping"
239243
)
240244
for (j in mappingAttrs) {
241245
traces[[i]][[j]] <- NULL
@@ -246,8 +250,6 @@ plotly_build.plotly <- function(p) {
246250
# (like figures pulled from a plotly server)
247251
p$x$data <- setNames(c(p$x$data, traces), NULL)
248252

249-
250-
251253
# get rid of data -> vis mapping stuff
252254
p$x[c("visdat", "cur_data", "attrs")] <- NULL
253255

0 commit comments

Comments
 (0)