Skip to content

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

Merged
merged 26 commits into from
May 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e47ca93
expect_shape for hline
tdhock Mar 31, 2015
3ef9be5
test scatter facet lines
tdhock Mar 31, 2015
b60d935
only copy global mapping to layer if inherit.aes
tdhock Mar 31, 2015
1bea709
compute ranges for factors
tdhock Apr 1, 2015
8e68d8a
also use ranges for date and datetime variables
tdhock Apr 1, 2015
305ad6c
tests fail
tdhock Apr 1, 2015
7308760
simplify factor handling
tdhock Apr 1, 2015
6c72262
handle dodged factors with aes.name
tdhock Apr 1, 2015
84ab366
special cases
tdhock Apr 1, 2015
6318787
markLegends + boxplot = markSplit
tdhock Apr 1, 2015
4a2dc57
no need for workaround
tdhock Apr 1, 2015
a4d0059
testing showlegend=FALSE for each trace is sufficient
tdhock Apr 1, 2015
4a7f713
set showlegend=FALSE for all but the first trace with a given name
tdhock Apr 2, 2015
56ede93
merge conflict
tdhock Apr 2, 2015
3105143
use more complicated legend merging from master
tdhock Apr 2, 2015
bd8c3f6
test for no legends
tdhock Apr 8, 2015
5fa2434
hide boxplot legends
tdhock Apr 14, 2015
96f0d40
clean up comments
tdhock Apr 14, 2015
f0eb05e
version update
tdhock Apr 15, 2015
8f937e0
Use ||, not |, for logical vector of length 1
cpsievert May 2, 2015
9ab52e1
Resolve merge conflicts with master
cpsievert May 2, 2015
f90928b
merge conflicts with master
cpsievert May 5, 2015
d142570
test fixes for cookbook-lines (due to changes in gg2list())
cpsievert May 5, 2015
ab4ca1c
Fix style (notably, RStudio indentation)
mkcor May 5, 2015
498455d
Update version and docs
mkcor May 5, 2015
4cfedf9
Fix merge conflicts with master
mkcor May 5, 2015
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 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",
Expand Down
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
0.6.1 -- 5 May 2015

Add test-cookbook-lines.R and fix bugs that showed up in those tests.

0.6 -- 4 May 2015

Let gg2list() return a figure object (backwards incompatible change).
Expand Down
207 changes: 114 additions & 93 deletions R/ggplotly.R
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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"]
Copy link
Contributor Author

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.


# 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)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Expand All @@ -203,21 +222,24 @@ gg2list <- function(p) {
for (a in replace.aes) {
prestats[[a]] <- -1 * prestats[[a]]
}
misc$prestats.data <-
L$prestats.data <-
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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"))
Expand Down Expand Up @@ -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
}
Expand All @@ -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)
Expand Down Expand Up @@ -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

Expand All @@ -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){
Expand All @@ -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"){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spaces at least around == and between ) {.

layout$showlegend <- FALSE
}

# Only show a legend title if there is at least 1 trace with
# showlegend=TRUE.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -887,7 +908,7 @@ gg2list <- function(p) {
}

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

fig

}
2 changes: 1 addition & 1 deletion R/plotly-package.r
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#' \itemize{
#' \item Package: plotly
#' \item Type: Package
#' \item Version: 0.6
#' \item Version: 0.6.1
#' \item Date: 2014-03-07
#' \item License: MIT
#' }
Expand Down
2 changes: 1 addition & 1 deletion R/plotly.R
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading