-
Notifications
You must be signed in to change notification settings - Fork 633
Toby cookbook lines #196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Toby cookbook lines #196
Changes from all commits
e47ca93
3ef9be5
b60d935
1bea709
8e68d8a
305ad6c
7308760
6c72262
84ab366
6318787
4a2dc57
a4d0059
4a7f713
56ede93
3105143
bd8c3f6
5fa2434
96f0d40
f0eb05e
8f937e0
9ab52e1
f90928b
d142570
ab4ca1c
498455d
4cfedf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
Package: plotly | ||
Type: Package | ||
Title: Interactive, publication-quality graphs online. | ||
Version: 0.6 | ||
Version: 0.6.1 | ||
Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"), | ||
email = "[email protected]"), | ||
person("Scott", "Chamberlain", role = "aut", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,11 +62,12 @@ markLegends <- | |
errorbarh=c("colour", "linetype"), | ||
area=c("colour", "fill"), | ||
step=c("linetype", "size", "colour"), | ||
boxplot=c("x"), | ||
text=c("colour")) | ||
|
||
markUnique <- as.character(unique(unlist(markLegends))) | ||
|
||
markSplit <- c(markLegends,list(boxplot=c("x"))) | ||
|
||
#' Convert a ggplot to a list. | ||
#' @import ggplot2 | ||
#' @param p ggplot2 plot. | ||
|
@@ -97,9 +98,11 @@ gg2list <- function(p) { | |
# worry about combining global and layer-specific aes/data later. | ||
for(layer.i in seq_along(p$layers)) { | ||
layer.aes <- p$layers[[layer.i]]$mapping | ||
to.copy <- names(p$mapping)[!names(p$mapping) %in% names(layer.aes)] | ||
layer.aes[to.copy] <- p$mapping[to.copy] | ||
mark.names <- markUnique[markUnique %in% names(layer.aes)] | ||
if(p$layers[[layer.i]]$inherit.aes){ | ||
to.copy <- names(p$mapping)[!names(p$mapping) %in% names(layer.aes)] | ||
layer.aes[to.copy] <- p$mapping[to.copy] | ||
} | ||
mark.names <- names(layer.aes) # make aes.name for all aes. | ||
name.names <- sprintf("%s.name", mark.names) | ||
layer.aes[name.names] <- layer.aes[mark.names] | ||
p$layers[[layer.i]]$mapping <- layer.aes | ||
|
@@ -108,78 +111,94 @@ gg2list <- function(p) { | |
} | ||
} | ||
|
||
# Test fill and color to see if they encode a quantitative | ||
# variable. This may be useful for several reasons: (1) it is | ||
# sometimes possible to plot several different colors in the same | ||
# trace (e.g. points), and that is faster for large numbers of | ||
# data points and colors; (2) factors on x or y axes should be | ||
# sent to plotly as characters, not as numeric data (which is | ||
# what ggplot_build gives us). | ||
misc <- list() | ||
for(a in c("fill", "colour", "x", "y", "size")){ | ||
for(data.type in c("continuous", "date", "datetime", "discrete")){ | ||
fun.name <- sprintf("scale_%s_%s", a, data.type) | ||
misc.name <- paste0("is.", data.type) | ||
misc[[misc.name]][[a]] <- tryCatch({ | ||
fun <- get(fun.name) | ||
suppressMessages({ | ||
with.scale <- original.p + fun() | ||
}) | ||
ggplot_build(with.scale) | ||
TRUE | ||
}, error=function(e){ | ||
FALSE | ||
}) | ||
} | ||
} | ||
|
||
## scales are needed for legend ordering. | ||
misc$breaks <- list() | ||
for(sc in p$scales$scales){ | ||
a.vec <- sc$aesthetics | ||
default.breaks <- inherits(sc$breaks, "waiver") | ||
if (length(a.vec) == 1 && (!default.breaks) ) { | ||
## TODO: generalize for x/y scales too. | ||
br <- sc$breaks | ||
ranks <- seq_along(br) | ||
names(ranks) <- br | ||
misc$breaks[[a.vec]] <- ranks | ||
} | ||
## store if this is a reverse scale so we can undo that later. | ||
if(is.character(sc$trans$name)){ | ||
misc$trans[sc$aesthetics] <- sc$trans$name | ||
} | ||
} | ||
reverse.aes <- names(misc$trans)[misc$trans=="reverse"] | ||
|
||
# Extract data from built ggplots | ||
built <- ggplot_build2(p) | ||
# Get global x-range now because we need some of its info in layer2traces | ||
ggranges <- built$panel$ranges | ||
# Extract x.range | ||
xrange <- sapply(ggranges, `[[`, "x.range", simplify=FALSE, USE.NAMES=FALSE) | ||
ggxmin <- min(sapply(xrange, min)) | ||
ggxmax <- max(sapply(xrange, max)) | ||
# Extract y.range | ||
yrange <- sapply(ggranges, `[[`, "y.range", simplify=FALSE, USE.NAMES=FALSE) | ||
ggymin <- min(sapply(yrange, min)) | ||
ggymax <- max(sapply(yrange, max)) | ||
# Get global ranges now because we need some of its info in layer2traces | ||
ranges.list <- list() | ||
for(xy in c("x", "y")){ | ||
use.ranges <- | ||
misc$is.continuous[[xy]] || | ||
misc$is.date[[xy]] || | ||
misc$is.datetime[[xy]] | ||
range.values <- if(use.ranges){ | ||
range.name <- paste0(xy, ".range") | ||
sapply(built$panel$ranges, "[[", range.name) | ||
}else{ | ||
## for categorical variables on the axes, panel$ranges info is | ||
## meaningless. | ||
name.name <- paste0(xy, ".name") | ||
sapply(built$data, function(df){ | ||
if(name.name %in% names(df)){ | ||
## usually for discrete data there is a .name column. | ||
paste(df[[name.name]]) | ||
}else{ | ||
## for heatmaps there may not be. | ||
df[[xy]] | ||
} | ||
}) | ||
} | ||
ranges.list[[xy]] <- range(range.values) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getting global ranges is more complicated now that we respect inherit.aes |
||
|
||
# Get global size range because we need some of its info in layer2traces | ||
if ("size.name" %in% name.names) { | ||
sizerange <- sapply(built$prestats.data, `[[`, "size") | ||
ggsizemin <- min(unlist(sizerange)) | ||
ggsizemax <- max(unlist(sizerange)) | ||
} | ||
|
||
layer.legends <- list() | ||
for(i in seq_along(built$plot$layers)){ | ||
# This is the layer from the original ggplot object. | ||
L <- p$layers[[i]] | ||
|
||
# for each layer, there is a correpsonding data.frame which | ||
# evaluates the aesthetic mapping. | ||
df <- built$data[[i]] | ||
|
||
# Test fill and color to see if they encode a quantitative | ||
# variable. This may be useful for several reasons: (1) it is | ||
# sometimes possible to plot several different colors in the same | ||
# trace (e.g. points), and that is faster for large numbers of | ||
# data points and colors; (2) factors on x or y axes should be | ||
# sent to plotly as characters, not as numeric data (which is | ||
# what ggplot_build gives us). | ||
misc <- list() | ||
for(a in c("fill", "colour", "x", "y", "size")){ | ||
for(data.type in c("continuous", "date", "datetime", "discrete")){ | ||
fun.name <- sprintf("scale_%s_%s", a, data.type) | ||
misc.name <- paste0("is.", data.type) | ||
misc[[misc.name]][[a]] <- tryCatch({ | ||
fun <- get(fun.name) | ||
suppressMessages({ | ||
with.scale <- original.p + fun() | ||
}) | ||
ggplot_build(with.scale) | ||
TRUE | ||
}, error=function(e){ | ||
FALSE | ||
}) | ||
} | ||
} | ||
|
||
# scales are needed for legend ordering. | ||
misc$breaks <- list() | ||
for(sc in p$scales$scales){ | ||
a.vec <- sc$aesthetics | ||
default.breaks <- inherits(sc$breaks, "waiver") | ||
if (length(a.vec) == 1 && (!default.breaks) ) { | ||
# TODO: generalize for x/y scales too. | ||
br <- sc$breaks | ||
ranks <- seq_along(br) | ||
names(ranks) <- br | ||
misc$breaks[[a.vec]] <- ranks | ||
} | ||
## store if this is a reverse scale so we can undo that later. | ||
if(is.character(sc$trans$name)){ | ||
misc$trans[sc$aesthetics] <- sc$trans$name | ||
} | ||
} | ||
reverse.aes <- names(misc$trans)[misc$trans=="reverse"] | ||
df <- built$data[[i]] | ||
|
||
# get gglayout now because we need some of its info in layer2traces | ||
gglayout <- built$panel$layout | ||
|
@@ -203,21 +222,24 @@ gg2list <- function(p) { | |
for (a in replace.aes) { | ||
prestats[[a]] <- -1 * prestats[[a]] | ||
} | ||
misc$prestats.data <- | ||
L$prestats.data <- | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prestats.data is layer-specific (misc is plot-specific) |
||
merge(prestats, | ||
gglayout[, c("PANEL", "plotly.row", "COL")]) | ||
|
||
# Add global x-range info | ||
misc$prestats.data$globxmin <- ggxmin | ||
misc$prestats.data$globxmax <- ggxmax | ||
# Add global y-range info | ||
misc$prestats.data$globymin <- ggymin | ||
misc$prestats.data$globymax <- ggymax | ||
# Add global range info. | ||
for(xy in names(ranges.list)){ | ||
range.vec <- ranges.list[[xy]] | ||
names(range.vec) <- c("min", "max") | ||
for(range.name in names(range.vec)){ | ||
glob.name <- paste0("glob", xy, range.name) | ||
L$prestats.data[[glob.name]] <- range.vec[[range.name]] | ||
} | ||
} | ||
|
||
# Add global size info if relevant | ||
if ("size.name" %in% name.names) { | ||
misc$prestats.data$globsizemin <- ggsizemin | ||
misc$prestats.data$globsizemax <- ggsizemax | ||
L$prestats.data$globsizemin <- ggsizemin | ||
L$prestats.data$globsizemax <- ggsizemax | ||
} | ||
|
||
# This extracts essential info for this geom/layer. | ||
|
@@ -334,7 +356,7 @@ gg2list <- function(p) { | |
grid <- theme.pars$panel.grid | ||
grid.major <- theme.pars$panel.grid.major | ||
if ((!is.null(grid$linetype) || !is.null(grid.major$linetype)) && | ||
c(grid$linetype, grid.major$linetype) %in% c(2, 3, "dashed", "dotted")) { | ||
c(grid$linetype, grid.major$linetype) %in% c(2, 3, "dashed", "dotted")) { | ||
ax.list$gridcolor <- ifelse(is.null(grid.major$colour), | ||
toRGB(grid$colour, 0.1), | ||
toRGB(grid.major$colour, 0.1)) | ||
|
@@ -370,7 +392,7 @@ gg2list <- function(p) { | |
ax.list$tickangle <- -tick.text$angle | ||
} | ||
ax.list$tickfont <- theme2font(tick.text) | ||
|
||
## determine axis type first, since this information is used later | ||
## (trace.order.list is only used for type=category). | ||
title.text <- e(s("axis.title.%s")) | ||
|
@@ -415,7 +437,7 @@ gg2list <- function(p) { | |
sc$limits | ||
}else{ | ||
if(misc$is.continuous[[xy]]){ | ||
ggranges[[1]][[s("%s.range")]] #TODO: facets! | ||
built$panel$ranges[[1]][[s("%s.range")]] #TODO: facets! | ||
}else{ # for a discrete scale, range should be NULL. | ||
NULL | ||
} | ||
|
@@ -431,7 +453,7 @@ gg2list <- function(p) { | |
}else{ | ||
p$labels[[xy]] | ||
} | ||
|
||
ax.list$zeroline <- FALSE # ggplot2 plots do not show zero lines | ||
# Lines drawn around the plot border. | ||
ax.list$showline <- !is.blank("panel.border", TRUE) | ||
|
@@ -581,22 +603,19 @@ gg2list <- function(p) { | |
nann <- nann + 1 | ||
} | ||
} | ||
# axes titles | ||
annotations[[nann]] <- make.label(xaxis.title, | ||
0.5, | ||
-outer.margin, | ||
yanchor="top") | ||
nann <- nann + 1 | ||
annotations[[nann]] <- make.label(yaxis.title, | ||
-outer.margin, | ||
0.5, | ||
textangle=-90) | ||
# axes titles | ||
annotations[[nann]] <- make.label(xaxis.title, | ||
0.5, | ||
-outer.margin, | ||
yanchor="top") | ||
nann <- nann + 1 | ||
annotations[[nann]] <- make.label(yaxis.title, | ||
-outer.margin, | ||
0.5, | ||
textangle=-90) | ||
layout$annotations <- annotations | ||
} | ||
|
||
# Remove legend if theme has no legend position | ||
layout$showlegend <- !(theme.pars$legend.position=="none") | ||
|
||
# Main plot title. | ||
layout$title <- built$plot$labels$title | ||
|
||
|
@@ -612,11 +631,7 @@ gg2list <- function(p) { | |
layout$legend <- list(bordercolor="transparent", | ||
x=1.05, y=1/2, | ||
xanchor="center", yanchor="top") | ||
# Workaround for removing unnecessary legends. | ||
# [markUnique != "x"] is for boxplot's particular case. | ||
if (any(names(layer.aes) %in% markUnique[markUnique != "x"]) == FALSE) | ||
layout$showlegend <- FALSE | ||
|
||
|
||
## Legend hiding when guides(fill="none"). | ||
legends.present <- unique(unlist(layer.legends)) | ||
is.false <- function(x){ | ||
|
@@ -628,11 +643,16 @@ gg2list <- function(p) { | |
is.hidden <- function(x){ | ||
is.false(x) || is.none(x) | ||
} | ||
layout$showlegend <- if(length(legends.present) == 0) FALSE else TRUE | ||
for(a in legends.present){ | ||
if(is.hidden(p$guides[[a]])){ | ||
layout$showlegend <- FALSE | ||
} | ||
} | ||
# Legend hiding from theme. | ||
if(theme.pars$legend.position=="none"){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spaces at least around |
||
layout$showlegend <- FALSE | ||
} | ||
|
||
# Only show a legend title if there is at least 1 trace with | ||
# showlegend=TRUE. | ||
|
@@ -817,6 +837,7 @@ gg2list <- function(p) { | |
fill_set <- unlist(lapply(merged.traces, entries, "fillcolor")) | ||
line_set <- unlist(lapply(merged.traces, entries, c("line", "color"))) | ||
mark_set <- unlist(lapply(merged.traces, entries, c("marker", "color"))) | ||
mode_set <- lapply(merged.traces, "[[", "mode") | ||
legend_intersect <- function(x, y) { | ||
i <- intersect(x, y) | ||
# restrict intersection to valid legend entries | ||
|
@@ -825,7 +846,7 @@ gg2list <- function(p) { | |
# if there is a mark & line legend, get rid of line | ||
t1 <- line_set %in% legend_intersect(mark_set, line_set) | ||
# that is, unless the mode is 'lines+markers'... | ||
t1 <- t1 & !(unlist(lapply(merged.traces, "[[", "mode")) %in% "lines+markers") | ||
t1 <- t1 & !(mode_set %in% "lines+markers") | ||
# if there is a mark & fill legend, get rid of fill | ||
t2 <- fill_set %in% legend_intersect(mark_set, fill_set) | ||
# if there is a line & fill legend, get rid of fill | ||
|
@@ -887,7 +908,7 @@ gg2list <- function(p) { | |
} | ||
|
||
fig <- list(data=flipped.traces, layout=flipped.layout) | ||
|
||
fig | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,7 +82,7 @@ For more help, see https://plot.ly/R or contact <[email protected]>.") | |
|
||
# public attributes/methods that the user has access to | ||
pub <- list(username=username, key=key, filename="from api", fileopt=NULL, | ||
version="0.6") | ||
version="0.6.1") | ||
priv <- list() | ||
|
||
pub$makecall <- function(args, kwargs, origin) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this whole block which creates misc was inside the for(layer.i) block below, which is potentially confusing, since misc contains plot-specific, not layer-specific, information.