From e47ca93c8b7c069003adb4075ccad2564dad1f0b Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 31 Mar 2015 18:31:02 -0400 Subject: [PATCH 01/22] expect_shape for hline --- tests/testthat/test-cookbook-lines.R | 188 +++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 tests/testthat/test-cookbook-lines.R diff --git a/tests/testthat/test-cookbook-lines.R b/tests/testthat/test-cookbook-lines.R new file mode 100644 index 0000000000..18d2189ac6 --- /dev/null +++ b/tests/testthat/test-cookbook-lines.R @@ -0,0 +1,188 @@ +context("cookbook lines") + +expect_traces_shapes <- function(gg, n.traces, n.shapes, name){ + stopifnot(is.ggplot(gg)) + stopifnot(is.numeric(n.traces)) + stopifnot(is.numeric(n.shapes)) + save_outputs(gg, paste0("cookbook-lines-", name)) + L <- gg2list(gg) + is.trace <- names(L) == "" + all.traces <- L[is.trace] + no.data <- sapply(all.traces, function(tr) { + is.null(tr[["x"]]) && is.null(tr[["y"]]) + }) + has.data <- all.traces[!no.data] + expect_equal(length(has.data), n.traces) + shapes <- L$kwargs$layout$shapes + expect_equal(length(shapes), n.shapes) + list(traces=has.data, + shapes=shapes, + kwargs=L$kwargs) +} + +expect_shape <- function(s, ...){ + expected.list <- list(...) + for(key in names(expected.list)){ + value <- expected.list[[key]] + expect_identical(s[[key]], value) + } +} + +# Some sample data +df <- read.table(header=T, text=" + cond result + control 10 +treatment 11.5 +") + +# Basic bar plot +bp <- ggplot(df, aes(x=cond, y=result)) + + geom_bar(position="dodge", stat="identity") + +## info <- gg2list(bp) +## info$kwargs$layout$shapes <- +## list(list(xref="paper", +## x0=0, +## x1=1, +## yref="y1", +## y0=10, +## y1=10)) +## sendJSON(info) + +test_that("geom_bar -> 1 trace", { + info <- expect_traces_shapes(bp, 1, 0, "basic-bar") +}) + +# Add a horizontal line +temp <- bp + geom_hline(aes(yintercept=12)) +test_that("bar + hline = 1 trace, 1 shape", { + info <- expect_traces_shapes(temp, 1, 1, "basic-horizontal-line") + expect_shape(info$shapes[[1]], + xref="paper", x0=0, x1=1, + yref="y1", y0=12, y1=12) +}) + +# Make the line red and dashed +temp <- bp + geom_hline(aes(yintercept=12), colour="#990000", linetype="dashed") +test_that("bar + red dashed hline", { + info <- expect_traces(temp, 2, "dashed-red-line") + info$traces[[2]] +}) + +# Draw separate hlines for each bar. First add another column to df +df$hline <- c(9,12) +# cond result hline +# control 10.0 9 +# treatment 11.5 12 + +# Need to re-specify bp, because the data has changed +bp <- ggplot(df, aes(x=cond, y=result)) + geom_bar(position=position_dodge()) + +# Draw with separate lines for each bar +bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") + +# Make the lines narrower +bp + geom_errorbar(width=0.5, aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") + + +# Can get the same result, even if we get the hline values from a second data frame +# Define data frame with hline +df.hlines <- data.frame(cond=c("control","treatment"), hline=c(9,12)) +# cond hline +# control 9 +# treatment 12 + +# The bar graph are from df, but the lines are from df.hlines +bp + geom_errorbar(data=df.hlines, aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") + +df <- read.table(header=T, text=" + cond group result hline + control A 10 9 +treatment A 11.5 12 + control B 12 9 +treatment B 14 12 +") + +# Define basic bar plot +bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + geom_bar(position=position_dodge()) +bp + +# The error bars get plotted over one another -- there are four but it looks like two +bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), linetype="dashed") + +df <- read.table(header=T, text=" + cond group result hline + control A 10 11 +treatment A 11.5 12 + control B 12 12.5 +treatment B 14 15 +") + +# Define basic bar plot +bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + geom_bar(position=position_dodge()) +bp + +bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), linetype="dashed", position=position_dodge()) + +df <- read.table(header=T, text=" + cond xval yval + control 11.5 10.8 + control 9.3 12.9 + control 8.0 9.9 + control 11.5 10.1 + control 8.6 8.3 + control 9.9 9.5 + control 8.8 8.7 + control 11.7 10.1 + control 9.7 9.3 + control 9.8 12.0 + treatment 10.4 10.6 + treatment 12.1 8.6 + treatment 11.2 11.0 + treatment 10.0 8.8 + treatment 12.9 9.5 + treatment 9.1 10.0 + treatment 13.4 9.6 + treatment 11.6 9.8 + treatment 11.5 9.8 + treatment 12.0 10.6 +") + +library(ggplot2) + +# The basic scatterplot +sp <- ggplot(df, aes(x=xval, y=yval, colour=cond)) + geom_point() + + +# Add a horizontal line +temp <- sp + geom_hline(aes(yintercept=10)) +save_outputs(temp, "lines/hline on scatter", file_prefix="") + +# Add a red dashed vertical line +temp <- sp + geom_hline(aes(yintercept=10)) + geom_vline(aes(xintercept=11.5), colour="#BB0000", linetype="dashed") +save_outputs(temp, "lines/hline n vline on scatter", file_prefix="") + +# Add colored lines for the mean xval of each group +temp <- sp + geom_hline(aes(yintercept=10)) + + geom_line(stat="vline", xintercept="mean") +save_outputs(temp, "lines/colored lines on scatter", file_prefix="") + +# Facet, based on cond +spf <- sp + facet_grid(. ~ cond) +spf + +# Draw a horizontal line in all of the facets at the same value +temp <- spf + geom_hline(aes(yintercept=10)) +save_outputs(temp, "lines/hline on facets", file_prefix="") + +df.vlines <- data.frame(cond=levels(df$cond), xval=c(10,11.5)) +# cond xval +# control 10.0 +# treatment 11.5 + +spf + geom_hline(aes(yintercept=10)) + + geom_vline(aes(xintercept=xval), data=df.vlines, + colour="#990000", linetype="dashed") + +spf + geom_hline(aes(yintercept=10)) + + geom_line(stat="vline", xintercept="mean") From 3ef9be59ac35b06c1461419c05dbcdf8113785dc Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 31 Mar 2015 19:38:12 -0400 Subject: [PATCH 02/22] test scatter facet lines --- tests/testthat/test-cookbook-lines.R | 169 +++++++++++++++++++-------- 1 file changed, 121 insertions(+), 48 deletions(-) diff --git a/tests/testthat/test-cookbook-lines.R b/tests/testthat/test-cookbook-lines.R index 18d2189ac6..3033cecd87 100644 --- a/tests/testthat/test-cookbook-lines.R +++ b/tests/testthat/test-cookbook-lines.R @@ -55,18 +55,20 @@ test_that("geom_bar -> 1 trace", { # Add a horizontal line temp <- bp + geom_hline(aes(yintercept=12)) -test_that("bar + hline = 1 trace, 1 shape", { - info <- expect_traces_shapes(temp, 1, 1, "basic-horizontal-line") - expect_shape(info$shapes[[1]], - xref="paper", x0=0, x1=1, - yref="y1", y0=12, y1=12) +test_that("bar + hline = 2 traces", { + info <- expect_traces_shapes(temp, 2, 0, "basic-horizontal-line") + ## expect_shape(info$shapes[[1]], + ## xref="paper", x0=0, x1=1, + ## yref="y1", y0=12, y1=12) }) # Make the line red and dashed temp <- bp + geom_hline(aes(yintercept=12), colour="#990000", linetype="dashed") test_that("bar + red dashed hline", { - info <- expect_traces(temp, 2, "dashed-red-line") - info$traces[[2]] + info <- expect_traces_shapes(temp, 2, 0, "dashed-red-line") + hline.info <- info$traces[[2]] + expect_identical(hline.info$line$color, toRGB("#990000")) + expect_identical(hline.info$line$dash, "dash") }) # Draw separate hlines for each bar. First add another column to df @@ -76,13 +78,21 @@ df$hline <- c(9,12) # treatment 11.5 12 # Need to re-specify bp, because the data has changed -bp <- ggplot(df, aes(x=cond, y=result)) + geom_bar(position=position_dodge()) +bp <- ggplot(df, aes(x=cond, y=result)) + + geom_bar(position=position_dodge(), stat="identity") -# Draw with separate lines for each bar -bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") +bp.err <- bp + + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") +test_that("Draw with separate lines for each bar", { + expect_traces_shapes(bp.err, 2, 0, "bar-error-wide") +}) -# Make the lines narrower -bp + geom_errorbar(width=0.5, aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") +bp.err.narrow <- bp + + geom_errorbar(width=0.5, aes(y=hline, ymax=hline, ymin=hline), + colour="#AA0000") +test_that("Make the lines narrower", { + info <- expect_traces_shapes(bp.err.narrow, 2, 0, "bar-error-narrow") +}) # Can get the same result, even if we get the hline values from a second data frame @@ -92,8 +102,12 @@ df.hlines <- data.frame(cond=c("control","treatment"), hline=c(9,12)) # control 9 # treatment 12 -# The bar graph are from df, but the lines are from df.hlines -bp + geom_errorbar(data=df.hlines, aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") +bp.err.diff <- bp + + geom_errorbar(data=df.hlines, aes(y=hline, ymax=hline, ymin=hline), + colour="#AA0000") +test_that("The bar graph are from df, but the lines are from df.hlines", { + info <- expect_traces_shapes(bp.err.diff, 2, 0, "bar-error-diff") +}) df <- read.table(header=T, text=" cond group result hline @@ -102,13 +116,20 @@ treatment A 11.5 12 control B 12 9 treatment B 14 12 ") - -# Define basic bar plot -bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + geom_bar(position=position_dodge()) -bp - -# The error bars get plotted over one another -- there are four but it looks like two -bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), linetype="dashed") +bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + + geom_bar(position=position_dodge(), stat="identity") +test_that("bar dodged colored -> 1 trace", { + info <- expect_traces_shapes(bp, 2, 0, "bar-dodge-color") +}) +bp.err <- + bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), linetype="dashed") +test_that("The error bars get plotted over one another", { + # there are four but it looks like two. + info <- expect_traces_shapes(bp.err, 3, 0, "bar-dodge-color-error") + err.y <- info$traces[[3]]$y + expect_equal(length(err.y), 4) + expect_equal(length(unique(err.y)), 2) +}) df <- read.table(header=T, text=" cond group result hline @@ -117,12 +138,17 @@ treatment A 11.5 12 control B 12 12.5 treatment B 14 15 ") - -# Define basic bar plot -bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + geom_bar(position=position_dodge()) -bp - -bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), linetype="dashed", position=position_dodge()) +bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + + geom_bar(position=position_dodge(), stat="identity") +bp.err4 <- bp + + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), + linetype="dashed", position=position_dodge()) +test_that("4 error bars", { + info <- expect_traces_shapes(bp.err4, 3, 0, "bar-dodge-color-err4") + err.y <- info$traces[[3]]$y + expect_equal(length(err.y), 4) + expect_equal(length(unique(err.y)), 4) +}) df <- read.table(header=T, text=" cond xval yval @@ -147,42 +173,89 @@ df <- read.table(header=T, text=" treatment 11.5 9.8 treatment 12.0 10.6 ") - -library(ggplot2) - -# The basic scatterplot sp <- ggplot(df, aes(x=xval, y=yval, colour=cond)) + geom_point() +test_that("basic scatterplot", { + info <- expect_traces_shapes(sp, 2, 0, "scatter-basic") +}) - -# Add a horizontal line temp <- sp + geom_hline(aes(yintercept=10)) -save_outputs(temp, "lines/hline on scatter", file_prefix="") +test_that("Add a horizontal line", { + info <- expect_traces_shapes(temp, 3, 0, "scatter-hline") +}) -# Add a red dashed vertical line -temp <- sp + geom_hline(aes(yintercept=10)) + geom_vline(aes(xintercept=11.5), colour="#BB0000", linetype="dashed") -save_outputs(temp, "lines/hline n vline on scatter", file_prefix="") +temp <- sp + + geom_hline(aes(yintercept=10)) + + geom_vline(aes(xintercept=11.5), + colour="#BB0000", linetype="dashed") +test_that("Add a red dashed vertical line", { + info <- expect_traces_shapes(temp, 4, 0, "scatter-hline-vline") + mode <- sapply(info$traces, "[[", "mode") + line.traces <- info$traces[mode == "lines"] + expect_equal(length(line.traces), 2) + dash <- sapply(line.traces, function(tr)tr$line$dash) + dash.traces <- line.traces[dash == "dash"] + expect_equal(length(dash.traces), 1) + dash.trace <- dash.traces[[1]] + expect_identical(dash.trace$line$color, toRGB("#BB0000")) +}) -# Add colored lines for the mean xval of each group temp <- sp + geom_hline(aes(yintercept=10)) + - geom_line(stat="vline", xintercept="mean") -save_outputs(temp, "lines/colored lines on scatter", file_prefix="") + geom_line(stat="vline", xintercept="mean") +test_that("Add colored lines for the mean xval of each group", { + info <- expect_traces_shapes(temp, 5, 0, "scatter-hline-vline-stat") + mode <- sapply(info$traces, "[[", "mode") + line.traces <- info$traces[mode == "lines"] + expect_equal(length(line.traces), 3) + lines.by.name <- list() + for(tr in line.traces){ + if(is.character(tr$name)){ + lines.by.name[[tr$name]] <- tr + } + } + marker.traces <- info$traces[mode == "markers"] + for(tr in marker.traces){ + line.trace <- lines.by.name[[tr$name]] + expect_equal(range(line.trace$y), range(tr$y)) + } +}) # Facet, based on cond spf <- sp + facet_grid(. ~ cond) -spf +test_that("scatter facet -> 2 traces", { + info <- expect_traces_shapes(spf, 2, 0, "scatter-facet") + expect_true(info$traces[[1]]$xaxis != info$traces[[2]]$xaxis) + expect_true(info$traces[[1]]$yaxis == info$traces[[2]]$yaxis) +}) -# Draw a horizontal line in all of the facets at the same value temp <- spf + geom_hline(aes(yintercept=10)) -save_outputs(temp, "lines/hline on facets", file_prefix="") +test_that("geom_hline -> 2 more traces", { + info <- expect_traces_shapes(temp, 4, 0, "scatter-facet-hline") + has.name <- sapply(info$traces, function(tr)is.character(tr$name)) + named.traces <- info$traces[has.name] + expect_equal(length(named.traces), 2) +}) df.vlines <- data.frame(cond=levels(df$cond), xval=c(10,11.5)) # cond xval # control 10.0 # treatment 11.5 -spf + geom_hline(aes(yintercept=10)) + - geom_vline(aes(xintercept=xval), data=df.vlines, - colour="#990000", linetype="dashed") +spf.vline <- + spf + + geom_hline(aes(yintercept=10)) + + geom_vline(aes(xintercept=xval), + data=df.vlines, + colour="#990000", linetype="dashed") +test_that("geom_vline -> 2 more traces", { + info <- expect_traces_shapes(spf.vline, 6, 0, "scatter-facet-hline-vline") +}) -spf + geom_hline(aes(yintercept=10)) + - geom_line(stat="vline", xintercept="mean") +spf.line.stat <- + spf + + geom_hline(aes(yintercept=10)) + + geom_line(stat="vline", xintercept="mean") +test_that("geom_line -> 2 more traces", { + info <- + expect_traces_shapes(spf.line.stat, 6, 0, + "scatter-facet-hline-line-stat") +}) From b60d935d1e9c51d2c7a05ea00ff673934a01bd31 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 31 Mar 2015 19:58:11 -0400 Subject: [PATCH 03/22] only copy global mapping to layer if inherit.aes --- R/ggplotly.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 08bf4af08f..98ef1997ae 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -97,8 +97,10 @@ 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] + 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 <- markUnique[markUnique %in% names(layer.aes)] name.names <- sprintf("%s.name", mark.names) layer.aes[name.names] <- layer.aes[mark.names] From 1bea7091b1e6eb44173b553ff13a5f4cbea98a3f Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 31 Mar 2015 20:50:04 -0400 Subject: [PATCH 04/22] compute ranges for factors --- R/ggplotly.R | 141 +++++++++++++++++++++++-------------------- R/trace_generation.R | 8 ++- 2 files changed, 81 insertions(+), 68 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 98ef1997ae..0dbc0e0564 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -109,19 +109,69 @@ gg2list <- function(p){ p$layers[[layer.i]]$data <- p$data } } + + ## 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")){ + range.values <- if(misc$is.continuous[[xy]]){ + 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){ + paste(df[[name.name]]) + }) + } + ranges.list[[xy]] <- range(range.values) + } # Get global size range because we need some of its info in layer2traces if ("size.name" %in% name.names) { @@ -137,51 +187,7 @@ gg2list <- function(p){ # 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 @@ -205,21 +211,24 @@ gg2list <- function(p){ for (a in replace.aes) { prestats[[a]] <- -1 * prestats[[a]] } - misc$prestats.data <- + L$prestats.data <- 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. @@ -382,7 +391,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 } diff --git a/R/trace_generation.R b/R/trace_generation.R index 2181e0e3c4..baba2637ef 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -1,7 +1,7 @@ #' Convert a layer to a list of traces. Called from gg2list() #' @param l one layer of the ggplot object #' @param d one layer of calculated data from ggplot2::ggplot_build(p) -#' @param misc named list. +#' @param misc named list of plot info, independent of layer. #' @return list representing a layer, with corresponding aesthetics, ranges, and groups. #' @export layer2traces <- function(l, d, misc) { @@ -12,7 +12,7 @@ layer2traces <- function(l, d, misc) { } g <- list(geom=l$geom$objname, data=not.na(d), - prestats.data=not.na(misc$prestats.data)) + prestats.data=not.na(l$prestats.data)) # needed for when group, etc. is an expression. g$aes <- sapply(l$mapping, function(k) as.character(as.expression(k))) @@ -379,11 +379,13 @@ toBasic <- list( g }, abline=function(g) { + ## TODO: why not treat abline as a basic path? g$params$xstart <- min(g$prestats.data$globxmin) g$params$xend <- max(g$prestats.data$globxmax) g }, hline=function(g) { + ## TODO: why not treat hline as a basic path? if (is.factor(g$data$x)) { g$params$xstart <- as.character(sort(g$data$x)[1]) g$params$xend <- as.character(sort(g$data$x)[length(g$data$x)]) @@ -394,11 +396,13 @@ toBasic <- list( g }, vline=function(g) { + ## TODO: why not treat vline as a basic path? g$params$ystart <- min(g$prestats.data$globymin) g$params$yend <- max(g$prestats.data$globymax) g }, point=function(g) { + ## TODO: why is this code here? point is a basic geom. if ("size" %in% names(g$data)) { g$params$sizemin <- min(g$prestats.data$globsizemin) g$params$sizemax <- max(g$prestats.data$globsizemax) From 8e68d8abfe1137ac265e8c80f92502b3fa3c7892 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 31 Mar 2015 21:00:21 -0400 Subject: [PATCH 05/22] also use ranges for date and datetime variables --- R/ggplotly.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 0dbc0e0564..ea3055a8fd 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -159,7 +159,11 @@ gg2list <- function(p){ # Get global ranges now because we need some of its info in layer2traces ranges.list <- list() for(xy in c("x", "y")){ - range.values <- if(misc$is.continuous[[xy]]){ + 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{ From 305ad6c98b65371cb51bc4a78ee8599552e74fb7 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 1 Apr 2015 17:51:32 -0400 Subject: [PATCH 06/22] tests fail --- tests/testthat/test-cookbook-lines.R | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-cookbook-lines.R b/tests/testthat/test-cookbook-lines.R index 3033cecd87..56cad2aec6 100644 --- a/tests/testthat/test-cookbook-lines.R +++ b/tests/testthat/test-cookbook-lines.R @@ -145,9 +145,11 @@ bp.err4 <- bp + linetype="dashed", position=position_dodge()) test_that("4 error bars", { info <- expect_traces_shapes(bp.err4, 3, 0, "bar-dodge-color-err4") - err.y <- info$traces[[3]]$y - expect_equal(length(err.y), 4) - expect_equal(length(unique(err.y)), 4) + tr <- info$traces[[3]] + expect_equal(length(tr$y), 4) + expect_equal(length(unique(tr$y)), 4) + expect_equal(length(tr$x), 4) + expect_equal(length(unique(tr$x)), 2) }) df <- read.table(header=T, text=" @@ -189,6 +191,7 @@ temp <- sp + colour="#BB0000", linetype="dashed") test_that("Add a red dashed vertical line", { info <- expect_traces_shapes(temp, 4, 0, "scatter-hline-vline") + expect_true(info$kwargs$layout$showlegend) mode <- sapply(info$traces, "[[", "mode") line.traces <- info$traces[mode == "lines"] expect_equal(length(line.traces), 2) @@ -203,17 +206,20 @@ temp <- sp + geom_hline(aes(yintercept=10)) + geom_line(stat="vline", xintercept="mean") test_that("Add colored lines for the mean xval of each group", { info <- expect_traces_shapes(temp, 5, 0, "scatter-hline-vline-stat") + expect_true(info$kwargs$layout$showlegend) mode <- sapply(info$traces, "[[", "mode") line.traces <- info$traces[mode == "lines"] expect_equal(length(line.traces), 3) lines.by.name <- list() for(tr in line.traces){ + expect_false(tr$showlegend) if(is.character(tr$name)){ lines.by.name[[tr$name]] <- tr } } marker.traces <- info$traces[mode == "markers"] for(tr in marker.traces){ + expect_true(tr$showlegend) line.trace <- lines.by.name[[tr$name]] expect_equal(range(line.trace$y), range(tr$y)) } @@ -230,6 +236,7 @@ test_that("scatter facet -> 2 traces", { temp <- spf + geom_hline(aes(yintercept=10)) test_that("geom_hline -> 2 more traces", { info <- expect_traces_shapes(temp, 4, 0, "scatter-facet-hline") + expect_true(info$kwargs$layout$showlegend) has.name <- sapply(info$traces, function(tr)is.character(tr$name)) named.traces <- info$traces[has.name] expect_equal(length(named.traces), 2) @@ -258,4 +265,8 @@ test_that("geom_line -> 2 more traces", { info <- expect_traces_shapes(spf.line.stat, 6, 0, "scatter-facet-hline-line-stat") + for(tr in info$traces){ + expected <- ifelse(tr$mode == "markers", TRUE, FALSE) + expect_identical(tr$showlegend, expected) + } }) From 7308760dc398437d0ba7aedcf126d22bed4af2d3 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 1 Apr 2015 18:27:25 -0400 Subject: [PATCH 07/22] simplify factor handling --- R/trace_generation.R | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/R/trace_generation.R b/R/trace_generation.R index baba2637ef..ca11e558ea 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -60,7 +60,7 @@ layer2traces <- function(l, d, misc) { if (g$geom == "density") { bargap <- 0 } - + # For non-numeric data on the axes, we should take the values from # the original data. for (axis.name in c("x", "y")) { @@ -98,16 +98,7 @@ layer2traces <- function(l, d, misc) { pdata.vec <- strftime(as.Date(g$prestats.data[[a]], origin=the.epoch), "%Y-%m-%d %H:%M:%S") } else if (inherits(data.vec, "factor")) { - # Re-order data so that Plotly gets it right from ggplot2. - g$data <- g$data[order(g$data[[a]]), ] - data.vec <- data.vec[match(g$data[[a]], as.numeric(data.vec))] - g$prestats.data <- g$prestats.data[order(g$prestats.data[[a]]), ] - pdata.vec <- pdata.vec[match(g$prestats.data[[a]], - as.numeric(pdata.vec))] - if (length(pdata.vec) == length(data.vec)) - pdata.vec <- data.vec - if (!is.factor(pdata.vec)) - pdata.vec <- g$prestats.data[[paste0(a, ".name")]] + pdata.vec <- data.vec <- g$prestats.data[[paste0(a, ".name")]] } g$data[[a]] <- data.vec g$prestats.data[[a]] <- pdata.vec @@ -145,7 +136,7 @@ layer2traces <- function(l, d, misc) { # symbol=circle,square,diamond,cross,x, # triangle-up,triangle-down,triangle-left,triangle-right - + # First convert to a "basic" geom, e.g. segments become lines. convert <- toBasic[[g$geom]] basic <- if(is.null(convert)){ @@ -185,7 +176,7 @@ layer2traces <- function(l, d, misc) { }) } } - + # Split hline and vline when multiple panels or intercepts: # Need multiple traces accordingly. if (g$geom == "hline" || g$geom == "vline") { @@ -352,8 +343,7 @@ toBasic <- list( g }, bar=function(g) { - if (any(is.na(g$prestats.data$x))) - g$prestats.data$x <- g$prestats.data$x.name + ## TODO: why is this here? bar is a basic geom. for(a in c("fill", "colour")){ g$prestats.data[[a]] <- g$data[[a]][match(g$prestats.data$group, g$data$group)] From 6c72262513e2b2e9e30c685a9fe601f00f94fd68 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 1 Apr 2015 18:40:59 -0400 Subject: [PATCH 08/22] handle dodged factors with aes.name --- R/trace_generation.R | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/R/trace_generation.R b/R/trace_generation.R index ca11e558ea..c137227b9d 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -98,7 +98,18 @@ layer2traces <- function(l, d, misc) { pdata.vec <- strftime(as.Date(g$prestats.data[[a]], origin=the.epoch), "%Y-%m-%d %H:%M:%S") } else if (inherits(data.vec, "factor")) { - pdata.vec <- data.vec <- g$prestats.data[[paste0(a, ".name")]] + # Re-order data so that Plotly gets it right from ggplot2. + a.name <- paste0(a, ".name") + g$data <- g$data[order(g$data[[a]]), ] + vec.i <- match(g$data[[a.name]], data.vec) + data.vec <- data.vec[vec.i] + g$prestats.data <- g$prestats.data[order(g$prestats.data[[a]]), ] + pvec.i <- match(g$prestats.data[[a]], as.numeric(pdata.vec)) + pdata.vec <- pdata.vec[pvec.i] + if (length(pdata.vec) == length(data.vec)) + pdata.vec <- data.vec + if (!is.factor(pdata.vec)) + pdata.vec <- g$prestats.data[[paste0(a, ".name")]] } g$data[[a]] <- data.vec g$prestats.data[[a]] <- pdata.vec From 84ab36624b058f6b27484a962d0c5416f8052521 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 1 Apr 2015 18:50:56 -0400 Subject: [PATCH 09/22] special cases --- R/ggplotly.R | 8 +++++++- R/trace_generation.R | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index ea3055a8fd..f6efa6b74f 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -171,7 +171,13 @@ gg2list <- function(p){ ## meaningless. name.name <- paste0(xy, ".name") sapply(built$data, function(df){ - paste(df[[name.name]]) + 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) diff --git a/R/trace_generation.R b/R/trace_generation.R index c137227b9d..7754d80e47 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -101,7 +101,10 @@ layer2traces <- function(l, d, misc) { # Re-order data so that Plotly gets it right from ggplot2. a.name <- paste0(a, ".name") g$data <- g$data[order(g$data[[a]]), ] - vec.i <- match(g$data[[a.name]], data.vec) + vec.i <- match(g$data[[a]], as.numeric(data.vec)) + if(anyNA(vec.i)){ + vec.i <- match(g$data[[a.name]], data.vec) + } data.vec <- data.vec[vec.i] g$prestats.data <- g$prestats.data[order(g$prestats.data[[a]]), ] pvec.i <- match(g$prestats.data[[a]], as.numeric(pdata.vec)) From 6318787e142773c9384e5c4481edb0ef5707a213 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 1 Apr 2015 19:32:59 -0400 Subject: [PATCH 10/22] markLegends + boxplot = markSplit --- R/ggplotly.R | 8 +++++--- R/trace_generation.R | 14 +++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index f6efa6b74f..66079455c0 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -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. @@ -101,7 +102,7 @@ gg2list <- function(p){ 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)] + 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 @@ -600,7 +601,8 @@ gg2list <- function(p){ 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) + ##obrowser() + if (!any(names(layer.aes) %in% markUnique)) layout$showlegend <- FALSE ## Legend hiding when guides(fill="none"). diff --git a/R/trace_generation.R b/R/trace_generation.R index 7754d80e47..27ebb3aaa6 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -68,13 +68,14 @@ layer2traces <- function(l, d, misc) { aes.names <- paste0(axis.name, c("", "end", "min", "max")) aes.used <- aes.names[aes.names %in% names(g$aes)] for(a in aes.used) { + a.name <- paste0(a, ".name") col.name <- g$aes[aes.used] dtemp <- l$data[[col.name]] if (is.null(dtemp)) { - if (!inherits(g$data[[paste0(a, ".name")]], "NULL")) { + if (!is.null(g$data[[a.name]])) { # Handle the case where as.Date() is passed in aes argument. - if (class(g$data[[a]]) != class(g$data[[paste0(a, ".name")]])) { - g$data[[a]] <- g$data[[paste0(a, ".name")]] + if (class(g$data[[a]]) != class(g$data[[a.name]])) { + g$data[[a]] <- g$data[[a.name]] data.vec <- g$data[[a]] } } @@ -99,7 +100,6 @@ layer2traces <- function(l, d, misc) { "%Y-%m-%d %H:%M:%S") } else if (inherits(data.vec, "factor")) { # Re-order data so that Plotly gets it right from ggplot2. - a.name <- paste0(a, ".name") g$data <- g$data[order(g$data[[a]]), ] vec.i <- match(g$data[[a]], as.numeric(data.vec)) if(anyNA(vec.i)){ @@ -112,7 +112,7 @@ layer2traces <- function(l, d, misc) { if (length(pdata.vec) == length(data.vec)) pdata.vec <- data.vec if (!is.factor(pdata.vec)) - pdata.vec <- g$prestats.data[[paste0(a, ".name")]] + pdata.vec <- g$prestats.data[[a.name]] } g$data[[a]] <- data.vec g$prestats.data[[a]] <- pdata.vec @@ -160,8 +160,8 @@ layer2traces <- function(l, d, misc) { } # Then split on visual characteristics that will get different # legend entries. - data.list <- if (basic$geom %in% names(markLegends)) { - mark.names <- markLegends[[basic$geom]] + data.list <- if (basic$geom %in% names(markSplit)) { + mark.names <- markSplit[[basic$geom]] # However, continuously colored points are an exception: they do # not need a legend entry, and they can be efficiently rendered # using just 1 trace. From 4a2dc570f929ee02041e30af6dcf28c6c1e29fd3 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 1 Apr 2015 19:38:16 -0400 Subject: [PATCH 11/22] no need for workaround --- R/ggplotly.R | 5 ----- 1 file changed, 5 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 66079455c0..4ca376758a 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -599,11 +599,6 @@ 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. - ##obrowser() - if (!any(names(layer.aes) %in% markUnique)) - layout$showlegend <- FALSE ## Legend hiding when guides(fill="none"). legends.present <- unique(unlist(layer.legends)) From a4d00593dea7fb2eba508a6b0f409517ad913511 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 1 Apr 2015 19:38:43 -0400 Subject: [PATCH 12/22] testing showlegend=FALSE for each trace is sufficient --- tests/testthat/test-ggplot-bar.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-ggplot-bar.R b/tests/testthat/test-ggplot-bar.R index eb8bd6176e..bbfcd4f006 100644 --- a/tests/testthat/test-ggplot-bar.R +++ b/tests/testthat/test-ggplot-bar.R @@ -91,9 +91,9 @@ test_that("Very basic bar graph", { expect_null(tr$marker$color) expect_null(tr$marker$line$color) expect_null(tr$marker$line$width) + expect_false(tr$showlegend) } expect_null(info$kwargs$layout$annotations) - expect_false(info$kwargs$layout$showlegend) }) test_that("Map the time of day to different fill colors", { From 4a7f713b53a86017c8491688b3f49eaffcc4c144 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Thu, 2 Apr 2015 17:47:39 -0400 Subject: [PATCH 13/22] set showlegend=FALSE for all but the first trace with a given name --- R/ggplotly.R | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/R/ggplotly.R b/R/ggplotly.R index 4ca376758a..4478b8c6fd 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -617,6 +617,21 @@ gg2list <- function(p){ } } + ## If there are several traces with the same name and + ## showlegend=TRUE, then turn off the legend for all but the first. + indices.by.name <- list() + for(trace.i in seq_along(trace.list)){ + tr <- trace.list[[trace.i]] + if(isTRUE(tr$showlegend)){ + indices.by.name[[tr$name]][[paste(trace.i)]] <- trace.i + } + } + for(trace.name in names(indices.by.name)){ + trace.indices <- indices.by.name[[trace.name]] + for(trace.i in trace.indices[-1]){ + trace.list[[trace.i]]$showlegend <- FALSE + } + } # Only show a legend title if there is at least 1 trace with # showlegend=TRUE. trace.showlegend <- sapply(trace.list, "[[", "showlegend") From 3105143f285dc2c5cb7194a3bab702539b28746f Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Thu, 2 Apr 2015 17:59:40 -0400 Subject: [PATCH 14/22] use more complicated legend merging from master --- R/ggplotly.R | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index acf980c18b..e01ce1e3d0 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -617,22 +617,6 @@ gg2list <- function(p){ } } - ## If there are several traces with the same name and - ## showlegend=TRUE, then turn off the legend for all but the first. - indices.by.name <- list() - for(trace.i in seq_along(trace.list)){ - tr <- trace.list[[trace.i]] - if(isTRUE(tr$showlegend)){ - indices.by.name[[tr$name]][[paste(trace.i)]] <- trace.i - } - } - for(trace.name in names(indices.by.name)){ - trace.indices <- indices.by.name[[trace.name]] - for(trace.i in trace.indices[-1]){ - trace.list[[trace.i]]$showlegend <- FALSE - } - } - # Only show a legend title if there is at least 1 trace with # showlegend=TRUE. trace.showlegend <- sapply(trace.list, "[[", "showlegend") From bd8c3f65dfc163e030c7eae044f2032d87de17fb Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 8 Apr 2015 17:57:21 -0400 Subject: [PATCH 15/22] test for no legends --- tests/testthat/test-cookbook-axes.R | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/testthat/test-cookbook-axes.R b/tests/testthat/test-cookbook-axes.R index c77928ad84..d1c360187e 100644 --- a/tests/testthat/test-cookbook-axes.R +++ b/tests/testthat/test-cookbook-axes.R @@ -18,6 +18,30 @@ expect_traces <- function(gg, n.traces, name){ list(traces=has.data, kwargs=L$kwargs) } +get_legend <- function(L){ + legend.on <- L$kwargs$layout$showlegend + legend.list <- list() + for(tr in L$traces){ + if(is.character(tr$name)){ + legend.list[[tr$name]] <- + data.frame(name=tr$name, showlegend=tr$showlegend) + } + } + legend.df <- do.call(rbind, legend.list) + subset(legend.df, showlegend) +} + +leg <- function(...){ + name <- c(...) + data.frame(name) +} + +expect_legend <- function(L, expected){ + stopifnot(is.data.frame(expected)) + shown <- get_legend(L) + expect_identical(shown$name, expected$name) +} + # Reverse the order of a discrete-valued axis # Get the levels of the factor flevels <- levels(PlantGrowth$group) @@ -32,6 +56,7 @@ test_that("factor levels determine tick order", { trace.names <- sapply(info$traces, "[[", "name") expect_identical(as.character(trace.names), c("trt2", "trt1", "ctrl")) + expect_legend(info, leg()) }) ## These two do the same thing; all data points outside the graphing @@ -39,17 +64,20 @@ test_that("factor levels determine tick order", { bp.ylim.hide <- bp + ylim(5, 7.5) test_that("ylim hides points", { info <- expect_traces(bp.ylim.hide, 3, "ylim.hide") + expect_legend(info, leg()) }) bp.scale.hide <- bp + scale_y_continuous(limits=c(5, 7.5)) test_that("scale_y(limits) hides points", { info <- expect_traces(bp.scale.hide, 3, "scale.hide") + expect_legend(info, leg()) expect_equal(info$kwargs$layout$yaxis$range, c(5, 7.5)) }) bp.coord <- bp + coord_cartesian(ylim=c(5, 7.5)) test_that("Using coord_cartesian zooms into the area", { info <- expect_traces(bp.coord, 3, "coord-ylim") + expect_legend(info, leg()) expect_equal(info$kwargs$layout$yaxis$range, c(5, 7.5)) }) @@ -64,6 +92,8 @@ sp <- ggplot(dat, aes(xval, yval)) + geom_point() test_that("A scatterplot with regular (linear) axis scaling", { info <- expect_traces(sp, 1, "linear-axes") + ## TODO: why does this test take so long? + expect_legend(info, leg()) }) library(scales) # Need the scales package @@ -71,12 +101,14 @@ sp.log2.scale <- sp + scale_y_continuous(trans=log2_trans()) test_that("log2 scaling of the y axis (with visually-equal spacing)", { info <- expect_traces(sp.log2.scale, 1, "log2-scale") + expect_legend(info, leg()) }) sp.log2.coord <- sp + coord_trans(ytrans="log2") test_that("log2 coordinate transformation with visually-diminishing spacing", { info <- expect_traces(sp.log2.coord, 1, "log2-coord") + expect_legend(info, leg()) }) sp.labels <- sp + @@ -86,12 +118,14 @@ sp.labels <- sp + test_that("log2 transform with labels", { info <- expect_traces(sp.labels, 1, "log2-labels") + expect_legend(info, leg()) }) sp.log10 <- sp + scale_y_log10() test_that("scale_y_log10", { info <- expect_traces(sp.log10, 1, "scale_y_log10") + expect_legend(info, leg()) }) sp.log10.labels <- sp + @@ -100,6 +134,7 @@ sp.log10.labels <- sp + test_that("log10 with exponents on tick labels", { info <- expect_traces(sp.log10.labels, 1, "scale_y_log10-labels") + expect_legend(info, leg()) }) # Data where x ranges from 0-10, y ranges from 0-30 @@ -111,12 +146,14 @@ sp.fixed <- sp + coord_fixed() test_that("Force equal scaling", { info <- expect_traces(sp.fixed, 1, "coord-fixed") + expect_legend(info, leg()) }) sp.ratio <- sp + coord_fixed(ratio=1/3) test_that("coord_fixed(ratio)", { info <- expect_traces(sp.ratio, 1, "coord-fixed-ratio") + expect_legend(info, leg()) }) no.x.title <- bp + @@ -125,6 +162,7 @@ no.x.title <- bp + test_that("coord_fixed(ratio)", { info <- expect_traces(no.x.title, 3, "no-x-title") + expect_legend(info, leg()) }) # Also possible to set the axis label with the scale @@ -135,6 +173,7 @@ bp.scale.name <- bp + scale_x_discrete(name="") + test_that("scale(name)", { info <- expect_traces(bp.scale.name, 3, "scale-name") + expect_legend(info, leg()) }) # Change font options: @@ -148,6 +187,7 @@ bp.fonts <- bp + test_that("element_text face, colour, size, angle, vjust, size", { info <- expect_traces(bp.fonts, 3, "fonts") + expect_legend(info, leg()) x <- info$kwargs$layout$xaxis xtitle <- x[["titlefont"]] xtick <- x[["tickfont"]] @@ -168,6 +208,7 @@ label.funs <- bp + test_that("In this particular case, x scale has no effect", { info <- expect_traces(label.funs, 3, "label-funs") + expect_legend(info, leg()) }) # Self-defined formatting function for times. @@ -184,6 +225,7 @@ custom.formatter <- bp + scale_y_continuous(label=timeHMS_formatter) test_that("custom HMS formatter function", { info <- expect_traces(custom.formatter, 3, "custom-formatter") + expect_legend(info, leg()) }) blank.minor.major <- bp + @@ -192,6 +234,7 @@ blank.minor.major <- bp + test_that("Hide all the gridlines", { info <- expect_traces(blank.minor.major, 3, "blank-minor-major") + expect_legend(info, leg()) }) blank.minor <- bp + @@ -199,6 +242,7 @@ blank.minor <- bp + test_that("Hide just the minor gridlines", { info <- expect_traces(blank.minor, 3, "blank-minor") + expect_legend(info, leg()) }) blank.x <- bp + @@ -207,6 +251,7 @@ blank.x <- bp + test_that("Hide all the horizontal gridlines", { info <- expect_traces(blank.x, 3, "blank-x") + expect_legend(info, leg()) }) blank.y <- bp + @@ -215,5 +260,6 @@ blank.y <- bp + test_that("Hide all the vertical gridlines", { info <- expect_traces(blank.y, 3, "blank-y") + expect_legend(info, leg()) }) From 5fa2434b54ccf3238850ba24ec8c60820b97851f Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 14 Apr 2015 17:32:37 -0400 Subject: [PATCH 16/22] hide boxplot legends --- R/ggplotly.R | 8 +++++--- tests/testthat/test-cookbook-axes.R | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index e01ce1e3d0..59dc84db3c 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -581,9 +581,6 @@ gg2list <- function(p){ 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 @@ -611,11 +608,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"){ + layout$showlegend <- FALSE + } # Only show a legend title if there is at least 1 trace with # showlegend=TRUE. diff --git a/tests/testthat/test-cookbook-axes.R b/tests/testthat/test-cookbook-axes.R index d1c360187e..0eb0b7c282 100644 --- a/tests/testthat/test-cookbook-axes.R +++ b/tests/testthat/test-cookbook-axes.R @@ -19,7 +19,9 @@ expect_traces <- function(gg, n.traces, name){ } get_legend <- function(L){ - legend.on <- L$kwargs$layout$showlegend + if(!isTRUE(L$kwargs$layout$showlegend)){ + return(data.frame()) + } legend.list <- list() for(tr in L$traces){ if(is.character(tr$name)){ From 96f0d4073aabf455731b0db3775c41d9830a24e6 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 14 Apr 2015 17:42:55 -0400 Subject: [PATCH 17/22] clean up comments --- R/ggplotly.R | 14 +++++++------- R/trace_generation.R | 5 ----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 59dc84db3c..57d4e81c9e 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -111,13 +111,13 @@ 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). + # 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")){ diff --git a/R/trace_generation.R b/R/trace_generation.R index e8529fc597..4789bcf6d5 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -363,7 +363,6 @@ toBasic <- list( g }, bar=function(g) { - ## TODO: why is this here? bar is a basic geom. for(a in c("fill", "colour")){ g$prestats.data[[a]] <- g$data[[a]][match(g$prestats.data$group, g$data$group)] @@ -389,13 +388,11 @@ toBasic <- list( g }, abline=function(g) { - ## TODO: why not treat abline as a basic path? g$params$xstart <- min(g$prestats.data$globxmin) g$params$xend <- max(g$prestats.data$globxmax) g }, hline=function(g) { - ## TODO: why not treat hline as a basic path? if (is.factor(g$data$x)) { g$params$xstart <- as.character(sort(g$data$x)[1]) g$params$xend <- as.character(sort(g$data$x)[length(g$data$x)]) @@ -406,13 +403,11 @@ toBasic <- list( g }, vline=function(g) { - ## TODO: why not treat vline as a basic path? g$params$ystart <- min(g$prestats.data$globymin) g$params$yend <- max(g$prestats.data$globymax) g }, point=function(g) { - ## TODO: why is this code here? point is a basic geom. if ("size" %in% names(g$data)) { g$params$sizemin <- min(g$prestats.data$globsizemin) g$params$sizemax <- max(g$prestats.data$globsizemax) From f0eb05e2e29951492632b5ed6ca724ad4e450782 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 15 Apr 2015 16:56:07 -0400 Subject: [PATCH 18/22] version update --- DESCRIPTION | 2 +- NEWS | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 02d7831a9a..4b1c431222 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: plotly Type: Package Title: Interactive, publication-quality graphs online. -Version: 0.5.27 +Version: 0.5.28 Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"), email = "chris@plot.ly"), person("Scott", "Chamberlain", role = "aut", diff --git a/NEWS b/NEWS index cefff8b5e7..d0cecb8016 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +0.5.28 -- 15 Apr 2015 + +Add test-cookbook-lines.R and fix bugs that showed up in those tests. + 0.5.27 -- 19 Mar 2015 Reimplement geom_ribbon as a basic polygon. Fix #191. Fix #192. From 8f937e0582c013ad986e6debffc71459491de9c3 Mon Sep 17 00:00:00 2001 From: cpsievert Date: Sat, 2 May 2015 13:53:42 -0500 Subject: [PATCH 19/22] Use ||, not |, for logical vector of length 1 --- R/ggplotly.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 57d4e81c9e..919c858f87 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -161,8 +161,8 @@ gg2list <- function(p){ ranges.list <- list() for(xy in c("x", "y")){ use.ranges <- - misc$is.continuous[[xy]] | - misc$is.date[[xy]] | + misc$is.continuous[[xy]] || + misc$is.date[[xy]] || misc$is.datetime[[xy]] range.values <- if(use.ranges){ range.name <- paste0(xy, ".range") From d142570fa055ba9ecc4fe8ddcbdeeeb0b9071bb6 Mon Sep 17 00:00:00 2001 From: cpsievert Date: Tue, 5 May 2015 11:39:54 -0500 Subject: [PATCH 20/22] test fixes for cookbook-lines (due to changes in gg2list()) --- R/ggplotly.R | 3 +- tests/testthat/test-cookbook-lines.R | 90 ++++++++++++---------------- 2 files changed, 40 insertions(+), 53 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 5cf5abefda..3d719c9faa 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -837,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 @@ -845,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 diff --git a/tests/testthat/test-cookbook-lines.R b/tests/testthat/test-cookbook-lines.R index 56cad2aec6..116b88be18 100644 --- a/tests/testthat/test-cookbook-lines.R +++ b/tests/testthat/test-cookbook-lines.R @@ -1,65 +1,49 @@ context("cookbook lines") -expect_traces_shapes <- function(gg, n.traces, n.shapes, name){ +expect_traces_shapes <- function(gg, n.traces, n.shapes, name) { stopifnot(is.ggplot(gg)) stopifnot(is.numeric(n.traces)) stopifnot(is.numeric(n.shapes)) save_outputs(gg, paste0("cookbook-lines-", name)) L <- gg2list(gg) - is.trace <- names(L) == "" - all.traces <- L[is.trace] + all.traces <- L$data no.data <- sapply(all.traces, function(tr) { is.null(tr[["x"]]) && is.null(tr[["y"]]) }) has.data <- all.traces[!no.data] expect_equal(length(has.data), n.traces) - shapes <- L$kwargs$layout$shapes + shapes <- L$layout$shapes expect_equal(length(shapes), n.shapes) - list(traces=has.data, - shapes=shapes, - kwargs=L$kwargs) + list(traces = has.data, shapes = shapes, layout = L$layout) } -expect_shape <- function(s, ...){ +expect_shape <- function(s, ...) { expected.list <- list(...) - for(key in names(expected.list)){ + for(key in names(expected.list)) { value <- expected.list[[key]] expect_identical(s[[key]], value) } } # Some sample data -df <- read.table(header=T, text=" +df <- read.table(header = T, text = " cond result control 10 treatment 11.5 ") # Basic bar plot -bp <- ggplot(df, aes(x=cond, y=result)) + - geom_bar(position="dodge", stat="identity") - -## info <- gg2list(bp) -## info$kwargs$layout$shapes <- -## list(list(xref="paper", -## x0=0, -## x1=1, -## yref="y1", -## y0=10, -## y1=10)) -## sendJSON(info) +bp <- ggplot(df, aes(x = cond, y = result)) + + geom_bar(position = "dodge", stat = "identity") test_that("geom_bar -> 1 trace", { info <- expect_traces_shapes(bp, 1, 0, "basic-bar") }) # Add a horizontal line -temp <- bp + geom_hline(aes(yintercept=12)) +temp <- bp + geom_hline(aes(yintercept = 12)) test_that("bar + hline = 2 traces", { info <- expect_traces_shapes(temp, 2, 0, "basic-horizontal-line") - ## expect_shape(info$shapes[[1]], - ## xref="paper", x0=0, x1=1, - ## yref="y1", y0=12, y1=12) }) # Make the line red and dashed @@ -82,14 +66,15 @@ bp <- ggplot(df, aes(x=cond, y=result)) + geom_bar(position=position_dodge(), stat="identity") bp.err <- bp + - geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), colour="#AA0000") + geom_errorbar(aes(y = hline, ymax = hline, ymin = hline), + colour = "#AA0000") test_that("Draw with separate lines for each bar", { expect_traces_shapes(bp.err, 2, 0, "bar-error-wide") }) bp.err.narrow <- bp + - geom_errorbar(width=0.5, aes(y=hline, ymax=hline, ymin=hline), - colour="#AA0000") + geom_errorbar(width = 0.5, aes(y = hline, ymax = hline, ymin = hline), + colour = "#AA0000") test_that("Make the lines narrower", { info <- expect_traces_shapes(bp.err.narrow, 2, 0, "bar-error-narrow") }) @@ -103,8 +88,8 @@ df.hlines <- data.frame(cond=c("control","treatment"), hline=c(9,12)) # treatment 12 bp.err.diff <- bp + - geom_errorbar(data=df.hlines, aes(y=hline, ymax=hline, ymin=hline), - colour="#AA0000") + geom_errorbar(data = df.hlines, aes(y = hline, ymax = hline, ymin = hline), + colour = "#AA0000") test_that("The bar graph are from df, but the lines are from df.hlines", { info <- expect_traces_shapes(bp.err.diff, 2, 0, "bar-error-diff") }) @@ -116,13 +101,14 @@ treatment A 11.5 12 control B 12 9 treatment B 14 12 ") -bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + - geom_bar(position=position_dodge(), stat="identity") +bp <- ggplot(df, aes(x = cond, y = result, fill = group)) + + geom_bar(position = position_dodge(), stat = "identity") test_that("bar dodged colored -> 1 trace", { info <- expect_traces_shapes(bp, 2, 0, "bar-dodge-color") }) bp.err <- - bp + geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), linetype="dashed") + bp + geom_errorbar(aes(y = hline, ymax = hline, ymin = hline), + linetype = "dashed") test_that("The error bars get plotted over one another", { # there are four but it looks like two. info <- expect_traces_shapes(bp.err, 3, 0, "bar-dodge-color-error") @@ -131,18 +117,18 @@ test_that("The error bars get plotted over one another", { expect_equal(length(unique(err.y)), 2) }) -df <- read.table(header=T, text=" +df <- read.table(header = TRUE, text = " cond group result hline control A 10 11 treatment A 11.5 12 control B 12 12.5 treatment B 14 15 ") -bp <- ggplot(df, aes(x=cond, y=result, fill=group)) + - geom_bar(position=position_dodge(), stat="identity") +bp <- ggplot(df, aes(x = cond, y = result, fill = group)) + + geom_bar(position = position_dodge(), stat = "identity") bp.err4 <- bp + - geom_errorbar(aes(y=hline, ymax=hline, ymin=hline), - linetype="dashed", position=position_dodge()) + geom_errorbar(aes(y = hline, ymax = hline, ymin = hline), + linetype = "dashed", position = position_dodge()) test_that("4 error bars", { info <- expect_traces_shapes(bp.err4, 3, 0, "bar-dodge-color-err4") tr <- info$traces[[3]] @@ -152,7 +138,7 @@ test_that("4 error bars", { expect_equal(length(unique(tr$x)), 2) }) -df <- read.table(header=T, text=" +df <- read.table(header = T, text = " cond xval yval control 11.5 10.8 control 9.3 12.9 @@ -175,7 +161,7 @@ df <- read.table(header=T, text=" treatment 11.5 9.8 treatment 12.0 10.6 ") -sp <- ggplot(df, aes(x=xval, y=yval, colour=cond)) + geom_point() +sp <- ggplot(df, aes(x = xval, y = yval, colour = cond)) + geom_point() test_that("basic scatterplot", { info <- expect_traces_shapes(sp, 2, 0, "scatter-basic") }) @@ -186,12 +172,12 @@ test_that("Add a horizontal line", { }) temp <- sp + - geom_hline(aes(yintercept=10)) + - geom_vline(aes(xintercept=11.5), - colour="#BB0000", linetype="dashed") + geom_hline(aes(yintercept = 10)) + + geom_vline(aes(xintercept = 11.5), + colour = "#BB0000", linetype = "dashed") test_that("Add a red dashed vertical line", { info <- expect_traces_shapes(temp, 4, 0, "scatter-hline-vline") - expect_true(info$kwargs$layout$showlegend) + expect_true(info$layout$showlegend) mode <- sapply(info$traces, "[[", "mode") line.traces <- info$traces[mode == "lines"] expect_equal(length(line.traces), 2) @@ -206,7 +192,7 @@ temp <- sp + geom_hline(aes(yintercept=10)) + geom_line(stat="vline", xintercept="mean") test_that("Add colored lines for the mean xval of each group", { info <- expect_traces_shapes(temp, 5, 0, "scatter-hline-vline-stat") - expect_true(info$kwargs$layout$showlegend) + expect_true(info$layout$showlegend) mode <- sapply(info$traces, "[[", "mode") line.traces <- info$traces[mode == "lines"] expect_equal(length(line.traces), 3) @@ -236,23 +222,23 @@ test_that("scatter facet -> 2 traces", { temp <- spf + geom_hline(aes(yintercept=10)) test_that("geom_hline -> 2 more traces", { info <- expect_traces_shapes(temp, 4, 0, "scatter-facet-hline") - expect_true(info$kwargs$layout$showlegend) + expect_true(info$layout$showlegend) has.name <- sapply(info$traces, function(tr)is.character(tr$name)) named.traces <- info$traces[has.name] expect_equal(length(named.traces), 2) }) -df.vlines <- data.frame(cond=levels(df$cond), xval=c(10,11.5)) +df.vlines <- data.frame(cond = levels(df$cond), xval = c(10,11.5)) # cond xval # control 10.0 # treatment 11.5 spf.vline <- spf + - geom_hline(aes(yintercept=10)) + - geom_vline(aes(xintercept=xval), - data=df.vlines, - colour="#990000", linetype="dashed") + geom_hline(aes(yintercept = 10)) + + geom_vline(aes(xintercept = xval), + data = df.vlines, + colour = "#990000", linetype = "dashed") test_that("geom_vline -> 2 more traces", { info <- expect_traces_shapes(spf.vline, 6, 0, "scatter-facet-hline-vline") }) From ab4ca1c0fad31d10a41711ea29afffad70cd5bf9 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Tue, 5 May 2015 17:39:07 -0400 Subject: [PATCH 21/22] Fix style (notably, RStudio indentation) --- R/ggplotly.R | 38 ++++++++++++++-------------- R/trace_generation.R | 10 ++++---- tests/testthat/test-cookbook-axes.R | 36 +++++++++++++------------- tests/testthat/test-cookbook-lines.R | 12 ++++----- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 3d719c9faa..13c80ddf30 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -110,7 +110,7 @@ gg2list <- function(p) { p$layers[[layer.i]]$data <- p$data } } - + # 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 @@ -190,7 +190,7 @@ gg2list <- function(p) { 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. @@ -225,7 +225,7 @@ gg2list <- function(p) { L$prestats.data <- merge(prestats, gglayout[, c("PANEL", "plotly.row", "COL")]) - + # Add global range info. for(xy in names(ranges.list)){ range.vec <- ranges.list[[xy]] @@ -356,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)) @@ -392,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")) @@ -453,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) @@ -603,16 +603,16 @@ 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 } @@ -631,7 +631,7 @@ gg2list <- function(p) { layout$legend <- list(bordercolor="transparent", x=1.05, y=1/2, xanchor="center", yanchor="top") - + ## Legend hiding when guides(fill="none"). legends.present <- unique(unlist(layer.legends)) is.false <- function(x){ @@ -653,7 +653,7 @@ gg2list <- function(p) { if(theme.pars$legend.position=="none"){ layout$showlegend <- FALSE } - + # Only show a legend title if there is at least 1 trace with # showlegend=TRUE. trace.showlegend <- sapply(trace.list, "[[", "showlegend") @@ -908,7 +908,7 @@ gg2list <- function(p) { } fig <- list(data=flipped.traces, layout=flipped.layout) - + fig } diff --git a/R/trace_generation.R b/R/trace_generation.R index ed66f09205..0c2598efe3 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -13,7 +13,7 @@ layer2traces <- function(l, d, misc) { g <- list(geom=l$geom$objname, data=not.na(d), prestats.data=not.na(l$prestats.data)) - + # needed for when group, etc. is an expression. g$aes <- sapply(l$mapping, function(k) as.character(as.expression(k))) # Partial conversion for geom_violin (Plotly does not offer KDE yet) @@ -46,7 +46,7 @@ layer2traces <- function(l, d, misc) { g$geom <- "bar" bargap <- 0 } - + # For non-numeric data on the axes, we should take the values from # the original data. for (axis.name in c("x", "y")) { @@ -136,7 +136,7 @@ layer2traces <- function(l, d, misc) { # symbol=circle,square,diamond,cross,x, # triangle-up,triangle-down,triangle-left,triangle-right - + # First convert to a "basic" geom, e.g. segments become lines. convert <- toBasic[[g$geom]] basic <- if(is.null(convert)){ @@ -247,7 +247,7 @@ layer2traces <- function(l, d, misc) { if (is.null(tr$name) || tr$name %in% names.in.legend) tr$showlegend <- FALSE names.in.legend <- c(names.in.legend, tr$name) - + # special handling for bars if (g$geom == "bar") { tr$bargap <- if (exists("bargap")) bargap else "default" @@ -270,7 +270,7 @@ layer2traces <- function(l, d, misc) { 0 } }) - + ord <- order(sort.val) no.sort <- traces[ord] for(tr.i in seq_along(no.sort)){ diff --git a/tests/testthat/test-cookbook-axes.R b/tests/testthat/test-cookbook-axes.R index d60998b6cf..44937fe422 100644 --- a/tests/testthat/test-cookbook-axes.R +++ b/tests/testthat/test-cookbook-axes.R @@ -3,7 +3,7 @@ context("cookbook axes") bp <- ggplot(PlantGrowth, aes(x=group, y=weight)) + geom_boxplot() -expect_traces <- function(gg, n.traces, name){ +expect_traces <- function(gg, n.traces, name) { stopifnot(is.ggplot(gg)) stopifnot(is.numeric(n.traces)) save_outputs(gg, paste0("cookbook-axes-", name)) @@ -17,13 +17,13 @@ expect_traces <- function(gg, n.traces, name){ list(traces=has.data, layout=L$layout) } -get_legend <- function(L){ - if(!isTRUE(L$kwargs$layout$showlegend)){ +get_legend <- function(L) { + if (!isTRUE(L$kwargs$layout$showlegend)) { return(data.frame()) } legend.list <- list() - for(tr in L$traces){ - if(is.character(tr$name)){ + for (tr in L$traces) { + if (is.character(tr$name)) { legend.list[[tr$name]] <- data.frame(name=tr$name, showlegend=tr$showlegend) } @@ -32,12 +32,12 @@ get_legend <- function(L){ subset(legend.df, showlegend) } -leg <- function(...){ +leg <- function(...) { name <- c(...) data.frame(name) } -expect_legend <- function(L, expected){ +expect_legend <- function(L, expected) { stopifnot(is.data.frame(expected)) shown <- get_legend(L) expect_identical(shown$name, expected$name) @@ -59,7 +59,7 @@ test_that("factor levels determine tick order", { c("trt2", "trt1", "ctrl")) expect_legend(info, leg()) }) - + ## These two do the same thing; all data points outside the graphing ## range are dropped, resulting in a misleading box plot. bp.ylim.hide <- bp + ylim(5, 7.5) @@ -74,7 +74,7 @@ test_that("scale_y(limits) hides points", { expect_legend(info, leg()) expect_equal(info$layout$yaxis$range, c(5, 7.5)) }) - + bp.coord <- bp + coord_cartesian(ylim=c(5, 7.5)) test_that("Using coord_cartesian zooms into the area", { info <- expect_traces(bp.coord, 3, "coord-ylim") @@ -93,11 +93,12 @@ sp <- ggplot(dat, aes(xval, yval)) + geom_point() test_that("A scatterplot with regular (linear) axis scaling", { info <- expect_traces(sp, 1, "linear-axes") - ## TODO: why does this test take so long? + # TODO: why does this test take so long? expect_legend(info, leg()) }) -library(scales) # Need the scales package +library(scales) +# TODO: Add package "scales" to the list of dependencies? sp.log2.scale <- sp + scale_y_continuous(trans=log2_trans()) test_that("log2 scaling of the y axis (with visually-equal spacing)", { @@ -214,12 +215,12 @@ test_that("In this particular case, x scale has no effect", { # Self-defined formatting function for times. timeHMS_formatter <- function(x) { - h <- floor(x/60) - m <- floor(x %% 60) - s <- round(60*(x %% 1)) # Round to nearest second - lab <- sprintf("%02d:%02d:%02d", h, m, s) # Format the strings as HH:MM:SS - lab <- gsub("^00:", "", lab) # Remove leading 00: if present - lab <- gsub("^0", "", lab) # Remove leading 0 if present + h <- floor(x/60) + m <- floor(x %% 60) + s <- round(60*(x %% 1)) # Round to nearest second + lab <- sprintf("%02d:%02d:%02d", h, m, s) # Format the strings as HH:MM:SS + lab <- gsub("^00:", "", lab) # Remove leading 00: if present + lab <- gsub("^0", "", lab) # Remove leading 0 if present } custom.formatter <- bp + scale_y_continuous(label=timeHMS_formatter) @@ -263,4 +264,3 @@ test_that("Hide all the vertical gridlines", { info <- expect_traces(blank.y, 3, "blank-y") expect_legend(info, leg()) }) - diff --git a/tests/testthat/test-cookbook-lines.R b/tests/testthat/test-cookbook-lines.R index 116b88be18..6fd5eb35d8 100644 --- a/tests/testthat/test-cookbook-lines.R +++ b/tests/testthat/test-cookbook-lines.R @@ -235,18 +235,18 @@ df.vlines <- data.frame(cond = levels(df$cond), xval = c(10,11.5)) spf.vline <- spf + - geom_hline(aes(yintercept = 10)) + - geom_vline(aes(xintercept = xval), - data = df.vlines, - colour = "#990000", linetype = "dashed") + geom_hline(aes(yintercept = 10)) + + geom_vline(aes(xintercept = xval), + data = df.vlines, + colour = "#990000", linetype = "dashed") test_that("geom_vline -> 2 more traces", { info <- expect_traces_shapes(spf.vline, 6, 0, "scatter-facet-hline-vline") }) spf.line.stat <- spf + - geom_hline(aes(yintercept=10)) + - geom_line(stat="vline", xintercept="mean") + geom_hline(aes(yintercept=10)) + + geom_line(stat="vline", xintercept="mean") test_that("geom_line -> 2 more traces", { info <- expect_traces_shapes(spf.line.stat, 6, 0, From 498455d49bde936befb58f57271d8957fe9d8312 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Tue, 5 May 2015 17:41:29 -0400 Subject: [PATCH 22/22] Update version and docs --- DESCRIPTION | 2 +- NEWS | 6 +++++- R/plotly-package.r | 2 +- R/plotly.R | 2 +- man/layer2traces.Rd | 2 +- man/plotly-package.Rd | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6f93779666..041cd58a3f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: plotly Type: Package Title: Interactive, publication-quality graphs online. -Version: 0.5.31 +Version: 0.6.1 Authors@R: c(person("Chris", "Parmer", role = c("aut", "cre"), email = "chris@plot.ly"), person("Scott", "Chamberlain", role = "aut", diff --git a/NEWS b/NEWS index 78c621e35a..a116b5d85f 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,11 @@ -0.5.31 -- 5 May 2015 +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). + 0.5.30 -- 4 May 2015 Let gg2list() return a figure object. diff --git a/R/plotly-package.r b/R/plotly-package.r index e4a4eabd88..bbd16326b3 100644 --- a/R/plotly-package.r +++ b/R/plotly-package.r @@ -7,7 +7,7 @@ #' \itemize{ #' \item Package: plotly #' \item Type: Package -#' \item Version: 0.5.30 +#' \item Version: 0.6.1 #' \item Date: 2014-03-07 #' \item License: MIT #' } diff --git a/R/plotly.R b/R/plotly.R index 8cf23792a7..5728d656e8 100644 --- a/R/plotly.R +++ b/R/plotly.R @@ -82,7 +82,7 @@ For more help, see https://plot.ly/R or contact .") # public attributes/methods that the user has access to pub <- list(username=username, key=key, filename="from api", fileopt=NULL, - version="0.5.30") + version="0.6.1") priv <- list() pub$makecall <- function(args, kwargs, origin) { diff --git a/man/layer2traces.Rd b/man/layer2traces.Rd index ce362768d5..e1e10cd29b 100644 --- a/man/layer2traces.Rd +++ b/man/layer2traces.Rd @@ -11,7 +11,7 @@ layer2traces(l, d, misc) \item{d}{one layer of calculated data from ggplot2::ggplot_build(p)} -\item{misc}{named list.} +\item{misc}{named list of plot info, independent of layer.} } \value{ list representing a layer, with corresponding aesthetics, ranges, and groups. diff --git a/man/plotly-package.Rd b/man/plotly-package.Rd index 8842b30d47..69f0c0cde6 100644 --- a/man/plotly-package.Rd +++ b/man/plotly-package.Rd @@ -15,7 +15,7 @@ An example of an interactive graph made from the R API: https://plot.ly/~chris/4 \itemize{ \item Package: plotly \item Type: Package - \item Version: 0.5.30 + \item Version: 0.6.1 \item Date: 2014-03-07 \item License: MIT }