From baa31e893f3b5f226309f6d61433c2412fcd7827 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 27 Jan 2015 17:43:16 -0500 Subject: [PATCH 01/49] test for error bars first or last --- tests/testthat/test-mean-error-bars.R | 109 ++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tests/testthat/test-mean-error-bars.R diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R new file mode 100644 index 0000000000..420eaf7ef4 --- /dev/null +++ b/tests/testthat/test-mean-error-bars.R @@ -0,0 +1,109 @@ +context("means and error bars") + +library(ggplot2) + +one.line.df <- + data.frame( + x = c(1, 2, 3, 4), + y = c(2, 1, 3, 4), + array = c(0.1, 0.2, 0.1, 0.1), + arrayminus = c(0.2, 0.4, 1, 0.2)) +one.line.json <- list( + list( + x = c(1, 2, 3, 4), + y = c(2, 1, 3, 4), + error_y = list( + type = "data", + symmetric = FALSE, + array = c(0.1, 0.2, 0.1, 0.1), + arrayminus = c(0.2, 0.4, 1, 0.2) + ), + type = "scatter" + ) +) + +test_that("asymmetric error bars, geom_errorbar last", { + one.line.gg <- ggplot(one.line.df, aes(x, y))+ + geom_line()+ + geom_point()+ + geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array)) + generated.json <- gg2list(one.line.gg) + is.trace <- names(generated.json) == "" + traces <- generated.json[is.trace] + expect_identical(length(traces), 1L) +}) + +test_that("asymmetric error bars, geom_errorbar first", { + one.line.gg <- ggplot(one.line.df, aes(x, y))+ + geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array))+ + geom_line()+ + geom_point() + generated.json <- gg2list(one.line.gg) + is.trace <- names(generated.json) == "" + traces <- generated.json[is.trace] + expect_identical(length(traces), 1L) +}) + + +## from https://github.com/chriddyp/ggplot2-plotly-cookbook/blob/a45f2c70b7adf484e0b0eb8810a1e59e018adbb8/means_and_error_bars.R#L162-L191 +df <- ToothGrowth + +## Summarizes data. +## Gives count, mean, standard deviation, standard error of the mean, and confidence interval (default 95%). +## data: a data frame. +## measurevar: the name of a column that contains the variable to be summariezed +## groupvars: a vector containing names of columns that contain grouping variables +## na.rm: a boolean that indicates whether to ignore NA's +## conf.interval: the percent range of the confidence interval (default is 95%) +summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, + conf.interval=.95, .drop=TRUE) { + require(plyr) + length2 <- function (x, na.rm=FALSE) { + if (na.rm) sum(!is.na(x)) + else length(x) + } + datac <- ddply(data, groupvars, .drop=.drop, + .fun = function(xx, col) { + c(N = length2(xx[[col]], na.rm=na.rm), + mean = mean (xx[[col]], na.rm=na.rm), + sd = sd (xx[[col]], na.rm=na.rm) + ) + }, + measurevar + ) + datac <- rename(datac, c("mean" = measurevar)) + datac$se <- datac$sd / sqrt(datac$N) # Calculate standard error of the mean + ciMult <- qt(conf.interval/2 + .5, datac$N-1) + datac$ci <- datac$se * ciMult + return(datac) +} +dfc <- summarySE(df, measurevar="len", groupvars=c("supp","dose")) +bad <- ggplot(dfc, aes(x=dose, y=len, colour=supp)) + + geom_errorbar(aes(ymin=len-se, ymax=len+se), width=.1) + + geom_line() + + geom_point() + +good <- ggplot(dfc, aes(x=dose, y=len, colour=supp)) + + geom_line() + + geom_errorbar(aes(ymin=len-se, ymax=len+se), width=.1) + + geom_point() +good.json <- gg2list(good) +py$ggplotly(good, kwargs=list(fileopt='overwrite', filename='R-Cookbook/means-and-error-bars/basic-error-bars')) + +## The conversion from geom_errorbar to plotly error bars is not +## straightforward. + +## we need to make a plotly trace with list(error_y=list(array=c(1, 2, +## 3), symmetric=FALSE, arrayminus=c(4.45, 3.91, 2.65))) -- and maybe +## traceref=2 and tracerefminus=1, which are indices of other traces +## that contain the same error bar data in the master trace list. + +## https://plot.ly/~tdhock/184 has some plotly error bars. + +good.json[[1]]$error_y <- + list(array=c(1, 2, 3), arrayminus=c(4, 5, 6), + symmetric=FALSE, + traceref=1, tracerefminus=1, + visible=TRUE, + type="data") + From adcc2ba73791247a981440f55ac053cc23c2c724 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 27 Jan 2015 18:10:22 -0500 Subject: [PATCH 02/49] test different colors --- R/print.R | 4 +++ R/trace_generation.R | 1 + tests/testthat/test-mean-error-bars.R | 37 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 R/print.R diff --git a/R/print.R b/R/print.R new file mode 100644 index 0000000000..fefdb661ba --- /dev/null +++ b/R/print.R @@ -0,0 +1,4 @@ +print.trace <- function(x, ...){ + str(x) + invisible(x) +} diff --git a/R/trace_generation.R b/R/trace_generation.R index 5477972f5d..f4621dc73b 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -198,6 +198,7 @@ layer2traces <- function(l, d, misc) { data.params <- data.list[[data.i]] data.params$params$stat.type <- l$stat$objname tr <- do.call(getTrace, data.params) + class(tr) <- "trace" for (v.name in c("x", "y")) { vals <- tr[[v.name]] if (length(vals) > 0 && is.na(vals[length(vals)])) { diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 420eaf7ef4..da815ac668 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -8,6 +8,7 @@ one.line.df <- y = c(2, 1, 3, 4), array = c(0.1, 0.2, 0.1, 0.1), arrayminus = c(0.2, 0.4, 1, 0.2)) + one.line.json <- list( list( x = c(1, 2, 3, 4), @@ -28,9 +29,15 @@ test_that("asymmetric error bars, geom_errorbar last", { geom_point()+ geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array)) generated.json <- gg2list(one.line.gg) + ## BUG: lines do not appear in plotly. + generated.json[[2]]$mode <- "lines+markers" + ## when there is 1 trace with error bars, lines, and markers, plotly + ## shows error bars in the background, lines in the middle and + ## markers in front. is.trace <- names(generated.json) == "" traces <- generated.json[is.trace] expect_identical(length(traces), 1L) + ## TODO: check that data agrees in one.line.json and generated.json. }) test_that("asymmetric error bars, geom_errorbar first", { @@ -44,7 +51,36 @@ test_that("asymmetric error bars, geom_errorbar first", { expect_identical(length(traces), 1L) }) +colors.json <- list( + list( + x = c(1, 2, 3, 4), + y = c(2, 1, 3, 4), + error_y = list( + type = "data", + symmetric = FALSE, + array = c(0.1, 0.2, 0.1, 0.1), + arrayminus = c(0.2, 0.4, 1, 0.2), + color="red" + ), + type = "scatter", + marker=list(color="blue", size=14), + line=list(color="black") + ) +) +test_that("different colors for error bars, points, and lines", { + one.line.gg <- ggplot(one.line.df, aes(x, y))+ + geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array), color="red")+ + geom_line(color="black")+ + geom_point(color="blue", size=14) + generated.json <- gg2list(one.line.gg) + is.trace <- names(generated.json) == "" + traces <- generated.json[is.trace] + expect_identical(length(traces), 1L) + ## TODO: check that colors agree in colors.json and generated.json. +}) + +if(FALSE){ ## from https://github.com/chriddyp/ggplot2-plotly-cookbook/blob/a45f2c70b7adf484e0b0eb8810a1e59e018adbb8/means_and_error_bars.R#L162-L191 df <- ToothGrowth @@ -107,3 +143,4 @@ good.json[[1]]$error_y <- visible=TRUE, type="data") +} From 8169a3dc59aa37a73dbdf42dec4c2131d1fc301d Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 28 Jan 2015 17:37:56 -0500 Subject: [PATCH 03/49] test only error bars --- R/ggplotly.R | 17 +++++++------ tests/testthat/test-mean-error-bars.R | 35 ++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 49c4b4a872..e46e0dd3b9 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -612,13 +612,11 @@ gg2list <- function(p){ layout$legend$bgcolor <- toRGB(s(rect_fill)) } - trace.list$kwargs <- list(layout=layout) - - if (length(trace.list) < 2) { + if(length(trace.list) == 0) { stop("No exportable traces") } - if (length(trace.list) > 2) { + if(length(trace.list) > 1) { # Maybe some traces should be merged. nr <- length(trace.list) - 1 comp <- data.frame(matrix(ncol=2, nrow=nr)) @@ -633,6 +631,7 @@ gg2list <- function(p){ } # Compare the "name"s of the traces (so far naively inherited from layers) layernames <- unique(comp$name) + browser() if (length(layernames) < nr) { # Some traces (layers at this stage) have the same "name"s. for (j in 1:length(layernames)) { @@ -665,10 +664,12 @@ gg2list <- function(p){ # Update comparison table comp <- comp[-lind[2], ] } - } - } - } - } + }#all(lmod) + }#j in layernames + }#if layernames + }#if we have multiple traces2 + + trace.list$kwargs <- list(layout=layout) trace.list } diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index da815ac668..732cfead86 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -9,6 +9,38 @@ one.line.df <- array = c(0.1, 0.2, 0.1, 0.1), arrayminus = c(0.2, 0.4, 1, 0.2)) +none.json <- list( + list( + x = c(1, 2, 3, 4), + y = c(2, 1, 3, 4), + error_y = list( + type = "data", + symmetric = FALSE, + array = c(0.1, 0.2, 0.1, 0.1), + arrayminus = c(0.2, 0.4, 1, 0.2) + ), + type = "scatter", + mode = "none" + ) +) + +test_that("only asymmetric error bars", { + error.gg <- ggplot(one.line.df, aes(x, y))+ + geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array)) + generated.json <- gg2list(error.gg) + is.trace <- names(generated.json) == "" + traces <- generated.json[is.trace] + expect_identical(length(traces), 1L) + tr <- traces[[1]] + expect_identical(tr$mode, "none") + expect_identical(tr$type, "scatter") + ey <- tr$error_y + expect_identical(ey$type, "data") + expect_identical(ey$symmetric, FALSE) + expect_identical(ey$array, c(0.1, 0.2, 0.1, 0.1)) + expect_identical(ey$arrayminus, c(0.2, 0.4, 1, 0.2)) +}) + one.line.json <- list( list( x = c(1, 2, 3, 4), @@ -30,7 +62,8 @@ test_that("asymmetric error bars, geom_errorbar last", { geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array)) generated.json <- gg2list(one.line.gg) ## BUG: lines do not appear in plotly. - generated.json[[2]]$mode <- "lines+markers" + ##generated.json[[2]]$mode <- "lines+markers" + ## when there is 1 trace with error bars, lines, and markers, plotly ## shows error bars in the background, lines in the middle and ## markers in front. From 2a3722157e77ac9866d0a18a5468ba2cdd118d7a Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 28 Jan 2015 17:46:43 -0500 Subject: [PATCH 04/49] only error bars passes test --- R/ggplotly.R | 3 ++- R/trace_generation.R | 8 ++++++++ tests/testthat/test-mean-error-bars.R | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index e46e0dd3b9..5a590b8305 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -195,7 +195,8 @@ gg2list <- function(p){ traces <- layer2traces(L, df, misc) # Associate error bars with previous traces - if (grepl("errorbar", L$geom$objname)) { + ##if (grepl("errorbar", L$geom$objname)) { #TDH 28 Jan 2015. + if(FALSE){ for (j in 1:length(trace.list)) { temp <- list() ind <- traces[[1]]$x %in% trace.list[[j]]$x diff --git a/R/trace_generation.R b/R/trace_generation.R index f4621dc73b..272fb59dbb 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -539,15 +539,23 @@ geom2trace <- list( errorbar=function(data, params) { list(x=data$x, y=data$y, + type="scatter", + mode="none", error_y=list(arrayminus=data$y-data$ymin, array=data$ymax-data$y, + type="data", + symmetric=FALSE, color=toRGB(data$colour))) }, errorbarh=function(data, params) { list(x=data$x, y=data$y, + type="scatter", + mode="none", error_x=list(arrayminus=data$x-data$xmin, array=data$xmax-data$x, + type="data", + symmetric=FALSE, color=toRGB(data$colour))) }, area=function(data, params) { diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 732cfead86..3a8e2ab051 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -37,8 +37,8 @@ test_that("only asymmetric error bars", { ey <- tr$error_y expect_identical(ey$type, "data") expect_identical(ey$symmetric, FALSE) - expect_identical(ey$array, c(0.1, 0.2, 0.1, 0.1)) - expect_identical(ey$arrayminus, c(0.2, 0.4, 1, 0.2)) + expect_equal(ey$array, c(0.1, 0.2, 0.1, 0.1)) + expect_equal(ey$arrayminus, c(0.2, 0.4, 1, 0.2)) }) one.line.json <- list( From 12d8019154487aeba346209accb1a6b9f115d090 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 28 Jan 2015 18:36:01 -0500 Subject: [PATCH 05/49] error bars after passes test --- R/ggplotly.R | 93 ++++++++++++--------------- tests/testthat/test-mean-error-bars.R | 9 ++- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 5a590b8305..14025b0baa 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -616,61 +616,48 @@ gg2list <- function(p){ if(length(trace.list) == 0) { stop("No exportable traces") } - - if(length(trace.list) > 1) { - # Maybe some traces should be merged. - nr <- length(trace.list) - 1 - comp <- data.frame(matrix(ncol=2, nrow=nr)) - colnames(comp) <- c("name", "mode") - - for (j in 1:nr) { - # Use lapply to be elegant? - for (d in colnames(comp)) { - try(comp[[d]][j] <- trace.list[[j]][[d]], silent=TRUE) - # "names" might be NULL in trace.list + + mode.mat <- matrix(NA, 3, 3) + rownames(mode.mat) <- colnames(mode.mat) <- c("markers", "lines", "none") + mode.mat["markers", "lines"] <- + mode.mat["lines", "markers"] <- "lines+markers" + mode.mat["markers", "none"] <- mode.mat["none", "markers"] <- "markers" + mode.mat["lines", "none"] <- mode.mat["none", "lines"] <- "lines" + merged.traces <- list() + not.merged <- trace.list + while(length(not.merged)){ + tr <- not.merged[[1]] + not.merged <- not.merged[-1] + ## Are there any traces that have not yet been merged, and can be + ## merged with tr? + can.merge <- rep(FALSE, l=length(not.merged)) + for(other.i in seq_along(not.merged)){ + other <- not.merged[[other.i]] + y.same <- isTRUE(all.equal(other$y, tr$y)) + x.same <- isTRUE(all.equal(other$x, tr$x)) + if(x.same & y.same){ + can.merge[[other.i]] <- TRUE } } - # Compare the "name"s of the traces (so far naively inherited from layers) - layernames <- unique(comp$name) - browser() - if (length(layernames) < nr) { - # Some traces (layers at this stage) have the same "name"s. - for (j in 1:length(layernames)) { - lind <- which(layernames[j] == comp$name) - lmod <- c("lines", "markers") %in% comp$mode[lind] - # Is there one with "mode": "lines" and another with "mode": "markers"? - if (all(lmod)) { - # Data comparison - xcomp <- (trace.list[[lind[1]]]$x == trace.list[[lind[2]]]$x) - ycomp <- (trace.list[[lind[1]]]$y == trace.list[[lind[2]]]$y) - if (all(xcomp) && all(ycomp)) { - # Union of the two traces - keys <- unique(c(names(trace.list[[lind[1]]]), - names(trace.list[[lind[2]]]))) - temp <- setNames(mapply(c, trace.list[[lind[1]]][keys], - trace.list[[lind[2]]][keys]), keys) - # Info is duplicated in fields which are in common - temp <- lapply(temp, unique) - # But unique() is detrimental to line or marker sublist - temp$line <- trace.list[[lind[1]]]$line - temp$marker <- trace.list[[lind[2]]]$marker - # Overwrite x and y to be safe - temp$x <- trace.list[[lind[1]]]$x - temp$y <- trace.list[[lind[1]]]$y - # Specify new one mode - temp$mode <- "lines+markers" - # Keep one trace and remove the other one - trace.list[[lind[1]]] <- temp - trace.list <- trace.list[-lind[2]] - # Update comparison table - comp <- comp[-lind[2], ] - } - }#all(lmod) - }#j in layernames - }#if layernames - }#if we have multiple traces2 + to.merge <- not.merged[can.merge] + not.merged <- not.merged[!can.merge] + for(other in to.merge){ + new.mode <- tryCatch({ + mode.mat[tr$mode, other$mode] + }, error=function(e){ + NA + }) + if(is.character(new.mode) && !is.na(new.mode)){ + tr$mode <- new.mode + } + if(is.list(other$error_y)){ + tr$error_y <- other$error_y + } + } + merged.traces[[length(merged.traces)+1]] <- tr + } - trace.list$kwargs <- list(layout=layout) + merged.traces$kwargs <- list(layout=layout) - trace.list + merged.traces } diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 3a8e2ab051..20003ddb04 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -70,7 +70,6 @@ test_that("asymmetric error bars, geom_errorbar last", { is.trace <- names(generated.json) == "" traces <- generated.json[is.trace] expect_identical(length(traces), 1L) - ## TODO: check that data agrees in one.line.json and generated.json. }) test_that("asymmetric error bars, geom_errorbar first", { @@ -82,6 +81,14 @@ test_that("asymmetric error bars, geom_errorbar first", { is.trace <- names(generated.json) == "" traces <- generated.json[is.trace] expect_identical(length(traces), 1L) + tr <- traces[[1]] + expect_identical(tr$mode, "lines+markers") + expect_identical(tr$type, "scatter") + ey <- tr$error_y + expect_identical(ey$type, "data") + expect_identical(ey$symmetric, FALSE) + expect_equal(ey$array, c(0.1, 0.2, 0.1, 0.1)) + expect_equal(ey$arrayminus, c(0.2, 0.4, 1, 0.2)) }) colors.json <- list( From f27f77a48dfb96f260d6be001452251469926cb5 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 28 Jan 2015 18:37:07 -0500 Subject: [PATCH 06/49] error bars before passes test --- tests/testthat/test-mean-error-bars.R | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 20003ddb04..536aab50f5 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -61,15 +61,20 @@ test_that("asymmetric error bars, geom_errorbar last", { geom_point()+ geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array)) generated.json <- gg2list(one.line.gg) - ## BUG: lines do not appear in plotly. - ##generated.json[[2]]$mode <- "lines+markers" - ## when there is 1 trace with error bars, lines, and markers, plotly ## shows error bars in the background, lines in the middle and ## markers in front. is.trace <- names(generated.json) == "" traces <- generated.json[is.trace] expect_identical(length(traces), 1L) + tr <- traces[[1]] + expect_identical(tr$mode, "lines+markers") + expect_identical(tr$type, "scatter") + ey <- tr$error_y + expect_identical(ey$type, "data") + expect_identical(ey$symmetric, FALSE) + expect_equal(ey$array, c(0.1, 0.2, 0.1, 0.1)) + expect_equal(ey$arrayminus, c(0.2, 0.4, 1, 0.2)) }) test_that("asymmetric error bars, geom_errorbar first", { From d907817cc88589d0436dcf1aa0aa13e6f9941aaa Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 2 Feb 2015 17:28:15 -0500 Subject: [PATCH 07/49] colored error bars --- R/corresp_one_one.R | 88 --------------------- R/ggplotly.R | 106 ++++++++++++++++++++++++-- R/trace_generation.R | 43 ++++++----- tests/testthat/test-mean-error-bars.R | 17 ++++- 4 files changed, 140 insertions(+), 114 deletions(-) diff --git a/R/corresp_one_one.R b/R/corresp_one_one.R index 30499350d0..e69de29bb2 100644 --- a/R/corresp_one_one.R +++ b/R/corresp_one_one.R @@ -1,88 +0,0 @@ -# Convert R pch point codes to plotly "symbol" codes. -pch2symbol <- c("0"="square-open", - "1"="circle-open", - "2"="triangle-up-open", - "3"="cross-thin-open", - "4"="x-thin-open", - "5"="diamond-open", - "6"="triangle-down-open", - "7"="square-x-open", - "8"="asterisk-open", - "9"="diamond-x-open", - "10"="circle-cross-open", - "11"="hexagram-open", - "12"="square-cross-open", - "13"="circle-x-open", - "14"="square-open-dot", - "15"="square", - "16"="circle", - "17"="triangle-up", - "18"="diamond", - "19"="circle", - "20"="circle", - "21"="circle", - "22"="square", - "23"="diamond", - "24"="triangle-up", - "25"="triangle-down", - "32"="circle", - "35"="hash-open", - "42"="asterisk-open", - "43"="cross-thin-open", - "45"="line-ew-open", - "47"="line-ne-open", - "48"="circle-open", - "79"="circle-open", - "88"="x-thin-open", - "92"="line-nw-open", - "95"="line-ew-open", - "111"="circle-open", - "o"="circle-open", - "O"="circle-open", - "+"="cross-thin-open") - -# Convert ggplot2 aes to plotly "marker" codes. -aes2marker <- c(alpha="opacity", - colour="color", - size="size", - sizeref="sizeref", - sizemode="sizemode", - shape="symbol") - -# Convert numeric line type. -numeric.lty <- c("0"="none", - "1"="solid", - "2"="dash", - "3"="dot", - "4"="dashdot", - "5"="longdash", - "6"="longdashdot") - -# Convert named line type. -named.lty <- c("blank"="none", - "solid"="solid", - "dashed"="dash", - "dotted"="dot", - "dotdash"="dashdot", - "longdash"="longdash", - "twodash"="longdashdot") - -# Convert coded line type. -coded.lty <- c("22"="dash", - "42"="dot", - "44"="dashdot", - "13"="longdash", - "1343"="longdashdot", - "73"="dash", - "2262"="dotdash", - "12223242"="dotdash", - "F282"="dash", - "F4448444"="dash", - "224282F2"="dash", - "F1"="dash") - -# Convert ggplot2 aes to line parameters. -aes2line <- c(linetype="dash", - colour="color", - size="width", - direction="shape") diff --git a/R/ggplotly.R b/R/ggplotly.R index 14025b0baa..72d7c1d845 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -1,3 +1,92 @@ +# Convert R pch point codes to plotly "symbol" codes. +pch2symbol <- c("0"="square-open", + "1"="circle-open", + "2"="triangle-up-open", + "3"="cross-thin-open", + "4"="x-thin-open", + "5"="diamond-open", + "6"="triangle-down-open", + "7"="square-x-open", + "8"="asterisk-open", + "9"="diamond-x-open", + "10"="circle-cross-open", + "11"="hexagram-open", + "12"="square-cross-open", + "13"="circle-x-open", + "14"="square-open-dot", + "15"="square", + "16"="circle", + "17"="triangle-up", + "18"="diamond", + "19"="circle", + "20"="circle", + "21"="circle", + "22"="square", + "23"="diamond", + "24"="triangle-up", + "25"="triangle-down", + "32"="circle", + "35"="hash-open", + "42"="asterisk-open", + "43"="cross-thin-open", + "45"="line-ew-open", + "47"="line-ne-open", + "48"="circle-open", + "79"="circle-open", + "88"="x-thin-open", + "92"="line-nw-open", + "95"="line-ew-open", + "111"="circle-open", + "o"="circle-open", + "O"="circle-open", + "+"="cross-thin-open") + +# Convert ggplot2 aes to plotly "marker" codes. +aes2marker <- c(alpha="opacity", + colour="color", + size="size", + sizeref="sizeref", + sizemode="sizemode", + shape="symbol") + +# Convert numeric line type. +numeric.lty <- c("0"="none", + "1"="solid", + "2"="dash", + "3"="dot", + "4"="dashdot", + "5"="longdash", + "6"="longdashdot") + +# Convert named line type. +named.lty <- c("blank"="none", + "solid"="solid", + "dashed"="dash", + "dotted"="dot", + "dotdash"="dashdot", + "longdash"="longdash", + "twodash"="longdashdot") + +# Convert coded line type. +coded.lty <- c("22"="dash", + "42"="dot", + "44"="dashdot", + "13"="longdash", + "1343"="longdashdot", + "73"="dash", + "2262"="dotdash", + "12223242"="dotdash", + "F282"="dash", + "F4448444"="dash", + "224282F2"="dash", + "F1"="dash") + +# Convert ggplot2 aes to line parameters. +aes2line <- c(linetype="dash", + colour="color", + size="width", + direction="shape") + ## calc. the epoch now <- Sys.time() the.epoch <- now - as.numeric(now) @@ -633,9 +722,13 @@ gg2list <- function(p){ can.merge <- rep(FALSE, l=length(not.merged)) for(other.i in seq_along(not.merged)){ other <- not.merged[[other.i]] - y.same <- isTRUE(all.equal(other$y, tr$y)) - x.same <- isTRUE(all.equal(other$x, tr$x)) - if(x.same & y.same){ + criteria <- c() + for(must.be.equal in c("x", "y", "xaxis", "yaxis")){ + other.attr <- other[[must.be.equal]] + tr.attr <- tr[[must.be.equal]] + criteria[[must.be.equal]] <- isTRUE(all.equal(other.attr, tr.attr)) + } + if(all(criteria)){ can.merge[[other.i]] <- TRUE } } @@ -650,8 +743,11 @@ gg2list <- function(p){ if(is.character(new.mode) && !is.na(new.mode)){ tr$mode <- new.mode } - if(is.list(other$error_y)){ - tr$error_y <- other$error_y + attrs <- c("error_y", "marker", "line") + for(attr in attrs){ + if(!is.null(other[[attr]]) && is.null(tr[[attr]])){ + tr[[attr]] <- other[[attr]] + } } } merged.traces[[length(merged.traces)+1]] <- tr diff --git a/R/trace_generation.R b/R/trace_generation.R index 272fb59dbb..2e2cc1ff0e 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -379,6 +379,29 @@ group2NA <- function(g, geom) { g } +### Make a trace for geom_errorbar -> error_y or geom_errorbarh -> +### error_x. +make.errorbar <- function(data, params, xy){ + tr <- + list(x=data$x, + y=data$y, + type="scatter", + mode="none") + err.name <- paste0("error_", xy) + min.name <- paste0(xy, "min") + max.name <- paste0(xy, "max") + tr[[err.name]] <- + list(arrayminus=data[[xy]]-data[[min.name]], + array=data[[max.name]]-data[[xy]], + type="data", + symmetric=FALSE, + color=if(!is.null(params$colour)){ + toRGB(params$colour) + }else{ + toRGB(data$colour) + }) + tr +} # Convert basic geoms to traces. geom2trace <- list( @@ -537,26 +560,10 @@ geom2trace <- list( L }, errorbar=function(data, params) { - list(x=data$x, - y=data$y, - type="scatter", - mode="none", - error_y=list(arrayminus=data$y-data$ymin, - array=data$ymax-data$y, - type="data", - symmetric=FALSE, - color=toRGB(data$colour))) + make.errorbar(data, params, "y") }, errorbarh=function(data, params) { - list(x=data$x, - y=data$y, - type="scatter", - mode="none", - error_x=list(arrayminus=data$x-data$xmin, - array=data$xmax-data$x, - type="data", - symmetric=FALSE, - color=toRGB(data$colour))) + make.errorbar(data, params, "x") }, area=function(data, params) { list(x=c(data$x[1], data$x, tail(data$x, n=1)), diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 536aab50f5..5835c23f34 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -109,20 +109,31 @@ colors.json <- list( ), type = "scatter", marker=list(color="blue", size=14), - line=list(color="black") + line=list(color="violet") ) ) test_that("different colors for error bars, points, and lines", { one.line.gg <- ggplot(one.line.df, aes(x, y))+ geom_errorbar(aes(ymin=y-arrayminus, ymax=y+array), color="red")+ - geom_line(color="black")+ + geom_line(color="violet")+ geom_point(color="blue", size=14) generated.json <- gg2list(one.line.gg) is.trace <- names(generated.json) == "" traces <- generated.json[is.trace] expect_identical(length(traces), 1L) - ## TODO: check that colors agree in colors.json and generated.json. + tr <- traces[[1]] + expect_identical(tr$mode, "lines+markers") + expect_identical(tr$type, "scatter") + expect_identical(tr$marker$color, toRGB("blue")) + expect_identical(tr$marker$size, 14) + expect_identical(tr$line$color, toRGB("violet")) + ey <- tr$error_y + expect_identical(ey$type, "data") + expect_identical(ey$color, toRGB("red")) + expect_identical(ey$symmetric, FALSE) + expect_equal(ey$array, c(0.1, 0.2, 0.1, 0.1)) + expect_equal(ey$arrayminus, c(0.2, 0.4, 1, 0.2)) }) if(FALSE){ From 565102d23db9762ec46db018e9d3344b7ae4d794 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 2 Feb 2015 17:56:42 -0500 Subject: [PATCH 08/49] colored error bar test fails --- tests/testthat/test-mean-error-bars.R | 73 +++++++++++++++------------ 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 5835c23f34..1233ba64cd 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -136,10 +136,8 @@ test_that("different colors for error bars, points, and lines", { expect_equal(ey$arrayminus, c(0.2, 0.4, 1, 0.2)) }) -if(FALSE){ ## from https://github.com/chriddyp/ggplot2-plotly-cookbook/blob/a45f2c70b7adf484e0b0eb8810a1e59e018adbb8/means_and_error_bars.R#L162-L191 df <- ToothGrowth - ## Summarizes data. ## Gives count, mean, standard deviation, standard error of the mean, and confidence interval (default 95%). ## data: a data frame. @@ -169,34 +167,47 @@ summarySE <- function(data=NULL, measurevar, groupvars=NULL, na.rm=FALSE, datac$ci <- datac$se * ciMult return(datac) } + dfc <- summarySE(df, measurevar="len", groupvars=c("supp","dose")) -bad <- ggplot(dfc, aes(x=dose, y=len, colour=supp)) + - geom_errorbar(aes(ymin=len-se, ymax=len+se), width=.1) + - geom_line() + - geom_point() - -good <- ggplot(dfc, aes(x=dose, y=len, colour=supp)) + - geom_line() + - geom_errorbar(aes(ymin=len-se, ymax=len+se), width=.1) + - geom_point() -good.json <- gg2list(good) -py$ggplotly(good, kwargs=list(fileopt='overwrite', filename='R-Cookbook/means-and-error-bars/basic-error-bars')) - -## The conversion from geom_errorbar to plotly error bars is not -## straightforward. - -## we need to make a plotly trace with list(error_y=list(array=c(1, 2, -## 3), symmetric=FALSE, arrayminus=c(4.45, 3.91, 2.65))) -- and maybe -## traceref=2 and tracerefminus=1, which are indices of other traces -## that contain the same error bar data in the master trace list. - -## https://plot.ly/~tdhock/184 has some plotly error bars. - -good.json[[1]]$error_y <- - list(array=c(1, 2, 3), arrayminus=c(4, 5, 6), - symmetric=FALSE, - traceref=1, tracerefminus=1, - visible=TRUE, - type="data") +color.code <- c(OJ="orange", VC="violet") +supp.list <- split(dfc, dfc$supp) + +test_that("errorbar(aes(color)) + other geoms", { + bad <- + ggplot(dfc, aes(x=dose, y=len, colour=supp)) + + geom_errorbar(aes(ymin=len-se, ymax=len+se), width=.1) + + geom_line() + + geom_point() +}) + +test_that("other geoms + errorbar(aes(color))", { + after <- + ggplot(dfc, aes(x=dose, y=len, colour=supp)) + + geom_line() + + geom_errorbar(aes(ymin=len-se, ymax=len+se), width=.1) + + geom_point()+ + scale_color_manual(values=color.code) + + after.json <- gg2list(after) + is.trace <- names(after.json) == "" + traces <- after.json[is.trace] + + expect_identical(length(traces), 2L) + for(tr in traces[1:2]){ + expected.color <- toRGB(color.code[[tr$name]]) + expected.data <- supp.list[[tr$name]] + expect_identical(tr$mode, "lines+markers") + expect_identical(tr$type, "scatter") + expect_identical(tr$marker$color, expected.color) + expect_identical(tr$line$color, expected.color) + ey <- tr$error_y + expect_identical(ey$type, "data") + expect_identical(ey$color, expected.color) + expect_equal(ey$width, .1) + expect_identical(ey$symmetric, TRUE) + expect_equal(ey$array, expected.data$se) + } +}) + +Plotly$ggplotly(good) -} From ea78dc3fa08f93120afd3091b0d4fd933146da42 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 2 Feb 2015 18:13:03 -0500 Subject: [PATCH 09/49] symmetric and colored error bar test passes --- R/ggplotly.R | 2 ++ R/trace_generation.R | 14 ++++++++++---- tests/testthat/test-mean-error-bars.R | 25 +++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 72d7c1d845..7b78f757ca 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -141,6 +141,8 @@ markLegends <- path=c("linetype", "size", "colour", "shape"), polygon=c("colour", "fill", "linetype", "size", "group"), bar=c("colour", "fill"), + errorbar=c("colour", "linetype"), + errorbarh=c("colour", "linetype"), step=c("linetype", "size", "colour"), boxplot=c("x"), text=c("colour")) diff --git a/R/trace_generation.R b/R/trace_generation.R index 2e2cc1ff0e..3116fc8a47 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -390,16 +390,22 @@ make.errorbar <- function(data, params, xy){ err.name <- paste0("error_", xy) min.name <- paste0(xy, "min") max.name <- paste0(xy, "max") - tr[[err.name]] <- - list(arrayminus=data[[xy]]-data[[min.name]], - array=data[[max.name]]-data[[xy]], + e <- + list(array=data[[max.name]]-data[[xy]], type="data", - symmetric=FALSE, + width=params$width, + symmetric=TRUE, color=if(!is.null(params$colour)){ toRGB(params$colour) }else{ toRGB(data$colour) }) + arrayminus <- data[[xy]]-data[[min.name]] + if(!isTRUE(all.equal(e$array, arrayminus))){ + e$arrayminus <- arrayminus + e$symmetric <- FALSE + } + tr[[err.name]] <- e tr } diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 1233ba64cd..60d810d5fe 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -173,11 +173,32 @@ color.code <- c(OJ="orange", VC="violet") supp.list <- split(dfc, dfc$supp) test_that("errorbar(aes(color)) + other geoms", { - bad <- + before <- ggplot(dfc, aes(x=dose, y=len, colour=supp)) + geom_errorbar(aes(ymin=len-se, ymax=len+se), width=.1) + geom_line() + + scale_color_manual(values=color.code)+ geom_point() + + before.json <- gg2list(before) + is.trace <- names(before.json) == "" + traces <- before.json[is.trace] + + expect_identical(length(traces), 2L) + for(tr in traces){ + expected.color <- toRGB(color.code[[tr$name]]) + expected.data <- supp.list[[tr$name]] + expect_identical(tr$mode, "lines+markers") + expect_identical(tr$type, "scatter") + expect_identical(tr$marker$color, expected.color) + expect_identical(tr$line$color, expected.color) + ey <- tr$error_y + expect_identical(ey$type, "data") + expect_identical(ey$color, expected.color) + expect_equal(ey$width, .1) + expect_identical(ey$symmetric, TRUE) + expect_equal(ey$array, expected.data$se) + } }) test_that("other geoms + errorbar(aes(color))", { @@ -193,7 +214,7 @@ test_that("other geoms + errorbar(aes(color))", { traces <- after.json[is.trace] expect_identical(length(traces), 2L) - for(tr in traces[1:2]){ + for(tr in traces){ expected.color <- toRGB(color.code[[tr$name]]) expected.data <- supp.list[[tr$name]] expect_identical(tr$mode, "lines+markers") From c8e98eb738350c679970aac262215682d01c3851 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 2 Feb 2015 18:28:52 -0500 Subject: [PATCH 10/49] errorbarh merges, ggplot-errorbar tests pass --- R/ggplotly.R | 2 +- tests/testthat/test-ggplot-errorbar.R | 4 ++-- tests/testthat/test-mean-error-bars.R | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 7b78f757ca..a5fbd4c6aa 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -745,7 +745,7 @@ gg2list <- function(p){ if(is.character(new.mode) && !is.na(new.mode)){ tr$mode <- new.mode } - attrs <- c("error_y", "marker", "line") + attrs <- c("error_x", "error_y", "marker", "line") for(attr in attrs){ if(!is.null(other[[attr]]) && is.null(tr[[attr]])){ tr[[attr]] <- other[[attr]] diff --git a/tests/testthat/test-ggplot-errorbar.R b/tests/testthat/test-ggplot-errorbar.R index 323dab85d4..dceac9ffcc 100644 --- a/tests/testthat/test-ggplot-errorbar.R +++ b/tests/testthat/test-ggplot-errorbar.R @@ -39,8 +39,8 @@ test_that("geom_errorbarh gives horizontal errorbars", { expect_identical(L[[1]]$marker$color, L[[1]]$error_x$color) expect_identical(L[[2]]$marker$color, L[[2]]$error_x$color) # Expect given errorbar values - expect_equal(L[[1]]$error_x$arrayminus, c(0.1, 0.3)) - expect_equal(L[[1]]$error_x$array, L[[1]]$error_x$arrayminus) + expect_equal(L[[1]]$error_x$array, c(0.1, 0.3)) + expect_equal(L[[1]]$error_x$symmetric, TRUE) save_outputs(g, "errorbar-horizontal") }) diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 60d810d5fe..60e4db53dc 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -230,5 +230,4 @@ test_that("other geoms + errorbar(aes(color))", { } }) -Plotly$ggplotly(good) From dea0c1a4faf6337d8c5c6d09b28bc5891f116dc9 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 2 Feb 2015 18:45:46 -0500 Subject: [PATCH 11/49] fix tile and theme tests --- tests/testthat/test-ggplot-errorbar.R | 25 ------------------------- tests/testthat/test-ggplot-heatmap.R | 6 ++++-- tests/testthat/test-ggplot-theme.R | 7 ++++--- 3 files changed, 8 insertions(+), 30 deletions(-) diff --git a/tests/testthat/test-ggplot-errorbar.R b/tests/testthat/test-ggplot-errorbar.R index dceac9ffcc..62f9f61b26 100644 --- a/tests/testthat/test-ggplot-errorbar.R +++ b/tests/testthat/test-ggplot-errorbar.R @@ -19,28 +19,3 @@ test_that("geom_errorbar gives errorbars", { save_outputs(g, "errorbar") }) -test_that("geom_errorbarh gives horizontal errorbars", { - - df <- data.frame( - trt = factor(c(1, 1, 2, 2)), - resp = c(1, 5, 3, 4), - group = factor(c(1, 2, 1, 2)), - se = c(0.1, 0.3, 0.3, 0.4) - ) - g <- ggplot(df, aes(resp, trt, colour=group)) + geom_point() - # Define the limits of the horizontal errorbars - g <- g + geom_errorbarh(aes(xmax = resp + se, xmin = resp - se)) - - L <- gg2list(g) - - # Expect 2 traces - expect_equal(length(L), 3) - # Expect scatter plot and its error bars to have the same color - expect_identical(L[[1]]$marker$color, L[[1]]$error_x$color) - expect_identical(L[[2]]$marker$color, L[[2]]$error_x$color) - # Expect given errorbar values - expect_equal(L[[1]]$error_x$array, c(0.1, 0.3)) - expect_equal(L[[1]]$error_x$symmetric, TRUE) - - save_outputs(g, "errorbar-horizontal") -}) diff --git a/tests/testthat/test-ggplot-heatmap.R b/tests/testthat/test-ggplot-heatmap.R index 870d94bfdd..1dc941507c 100644 --- a/tests/testthat/test-ggplot-heatmap.R +++ b/tests/testthat/test-ggplot-heatmap.R @@ -4,10 +4,12 @@ wdays <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday") dtimes <- c("Morning", "Afternoon", "Evening") workweek <- matrix(c(1, 20, 30, 20, 1, 60, 30, 60, 1, 50, 80, -10, 1, 30, 20), nrow=5, ncol=3, byrow=TRUE, - dimnames=list(wdays, dtimes)) + dimnames=list(day=wdays, time=dtimes)) ww <- reshape2::melt(workweek) +ww$day <- factor(ww$day, wdays) +ww$time <- factor(ww$time, dtimes) # Plot a heatmap using geom_tile -hm <- ggplot(ww) + geom_tile(aes(x=Var1, y=Var2, fill=value)) +hm <- ggplot(ww) + geom_tile(aes(x=day, y=time, fill=value)) test_that("geom_tile is translated to type=heatmap", { L <- gg2list(hm) diff --git a/tests/testthat/test-ggplot-theme.R b/tests/testthat/test-ggplot-theme.R index 74a4720e4c..40ca1772e4 100644 --- a/tests/testthat/test-ggplot-theme.R +++ b/tests/testthat/test-ggplot-theme.R @@ -1,6 +1,8 @@ context("ggplot themes") -iris.base <- ggplot(iris) + geom_point(aes(Petal.Width, Sepal.Width)) +iris.base <- ggplot(iris) + + geom_point(aes(Petal.Width, Sepal.Width))+ + theme_grey() test_that("background translated correctly",{ ggiris <- iris.base + theme(panel.background=element_rect(fill="blue")) + @@ -54,10 +56,9 @@ test_that("dotted/dashed grid translated as line with alpha=0.1",{ for (xy in c("x", "y")) { ax.list <- info$kwargs$layout[[paste0(xy, "axis")]] expect_identical(ax.list$gridcolor, toRGB("white", 0.1)) - expect_identical(ax.list$gridcolor, "rgba(255,255,255,0.1)") } - save_outputs(ggiris, "theme-dashed-grid-lines") + save_outputs(ggiris, "theme-dashed-grid-lines") }) countrypop <- data.frame(country=c("Paraguay", "Peru", "Philippines"), From 1d032af5792bedcba0aabfd888e7cc9d78035db1 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 4 Feb 2015 09:02:35 -0500 Subject: [PATCH 12/49] move back constants --- R/corresp_one_one.R | 92 +++++++++++++++++++++++++++++++++++++++++++++ R/ggplotly.R | 92 --------------------------------------------- 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/R/corresp_one_one.R b/R/corresp_one_one.R index e69de29bb2..967ddbfcb9 100644 --- a/R/corresp_one_one.R +++ b/R/corresp_one_one.R @@ -0,0 +1,92 @@ +# Convert R pch point codes to plotly "symbol" codes. +pch2symbol <- c("0"="square-open", + "1"="circle-open", + "2"="triangle-up-open", + "3"="cross-thin-open", + "4"="x-thin-open", + "5"="diamond-open", + "6"="triangle-down-open", + "7"="square-x-open", + "8"="asterisk-open", + "9"="diamond-x-open", + "10"="circle-cross-open", + "11"="hexagram-open", + "12"="square-cross-open", + "13"="circle-x-open", + "14"="square-open-dot", + "15"="square", + "16"="circle", + "17"="triangle-up", + "18"="diamond", + "19"="circle", + "20"="circle", + "21"="circle", + "22"="square", + "23"="diamond", + "24"="triangle-up", + "25"="triangle-down", + "32"="circle", + "35"="hash-open", + "42"="asterisk-open", + "43"="cross-thin-open", + "45"="line-ew-open", + "47"="line-ne-open", + "48"="circle-open", + "79"="circle-open", + "88"="x-thin-open", + "92"="line-nw-open", + "95"="line-ew-open", + "111"="circle-open", + "o"="circle-open", + "O"="circle-open", + "+"="cross-thin-open") + +# Convert ggplot2 aes to plotly "marker" codes. +aes2marker <- c(alpha="opacity", + colour="color", + size="size", + sizeref="sizeref", + sizemode="sizemode", + shape="symbol") + +# Convert numeric line type. +numeric.lty <- c("0"="none", + "1"="solid", + "2"="dash", + "3"="dot", + "4"="dashdot", + "5"="longdash", + "6"="longdashdot") + +# Convert named line type. +named.lty <- c("blank"="none", + "solid"="solid", + "dashed"="dash", + "dotted"="dot", + "dotdash"="dashdot", + "longdash"="longdash", + "twodash"="longdashdot") + +# Convert coded line type. +coded.lty <- c("22"="dash", + "42"="dot", + "44"="dashdot", + "13"="longdash", + "1343"="longdashdot", + "73"="dash", + "2262"="dotdash", + "12223242"="dotdash", + "F282"="dash", + "F4448444"="dash", + "224282F2"="dash", + "F1"="dash") + +# Convert R lty line type codes to plotly "dash" codes. +lty2dash <- c(numeric.lty, named.lty, coded.lty) + +# Convert ggplot2 aes to line parameters. +aes2line <- c(linetype="dash", + colour="color", + size="width", + direction="shape") + diff --git a/R/ggplotly.R b/R/ggplotly.R index a5fbd4c6aa..43384646c5 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -1,92 +1,3 @@ -# Convert R pch point codes to plotly "symbol" codes. -pch2symbol <- c("0"="square-open", - "1"="circle-open", - "2"="triangle-up-open", - "3"="cross-thin-open", - "4"="x-thin-open", - "5"="diamond-open", - "6"="triangle-down-open", - "7"="square-x-open", - "8"="asterisk-open", - "9"="diamond-x-open", - "10"="circle-cross-open", - "11"="hexagram-open", - "12"="square-cross-open", - "13"="circle-x-open", - "14"="square-open-dot", - "15"="square", - "16"="circle", - "17"="triangle-up", - "18"="diamond", - "19"="circle", - "20"="circle", - "21"="circle", - "22"="square", - "23"="diamond", - "24"="triangle-up", - "25"="triangle-down", - "32"="circle", - "35"="hash-open", - "42"="asterisk-open", - "43"="cross-thin-open", - "45"="line-ew-open", - "47"="line-ne-open", - "48"="circle-open", - "79"="circle-open", - "88"="x-thin-open", - "92"="line-nw-open", - "95"="line-ew-open", - "111"="circle-open", - "o"="circle-open", - "O"="circle-open", - "+"="cross-thin-open") - -# Convert ggplot2 aes to plotly "marker" codes. -aes2marker <- c(alpha="opacity", - colour="color", - size="size", - sizeref="sizeref", - sizemode="sizemode", - shape="symbol") - -# Convert numeric line type. -numeric.lty <- c("0"="none", - "1"="solid", - "2"="dash", - "3"="dot", - "4"="dashdot", - "5"="longdash", - "6"="longdashdot") - -# Convert named line type. -named.lty <- c("blank"="none", - "solid"="solid", - "dashed"="dash", - "dotted"="dot", - "dotdash"="dashdot", - "longdash"="longdash", - "twodash"="longdashdot") - -# Convert coded line type. -coded.lty <- c("22"="dash", - "42"="dot", - "44"="dashdot", - "13"="longdash", - "1343"="longdashdot", - "73"="dash", - "2262"="dotdash", - "12223242"="dotdash", - "F282"="dash", - "F4448444"="dash", - "224282F2"="dash", - "F1"="dash") - -# Convert ggplot2 aes to line parameters. -aes2line <- c(linetype="dash", - colour="color", - size="width", - direction="shape") - ## calc. the epoch now <- Sys.time() the.epoch <- now - as.numeric(now) @@ -115,9 +26,6 @@ ribbon.line.defaults$colour <- NA polygon.line.defaults <- line.defaults polygon.line.defaults$colour <- NA -# Convert R lty line type codes to plotly "dash" codes. -lty2dash <- c(numeric.lty, named.lty, coded.lty) - aesConverters <- list(linetype=function(lty) { lty2dash[as.character(lty)] }, From c768c2f3a02d93ba32142103472f4c7ea40ac108 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 4 Feb 2015 19:10:14 -0500 Subject: [PATCH 13/49] tick tests fail --- .../test-ggplot-errorbar-horizontal.R | 27 ++++ tests/testthat/test-ggplot-ticks.R | 122 ++++++++++++++++++ tests/testthat/test-rotated-ticks.R | 72 +++++++++++ 3 files changed, 221 insertions(+) create mode 100644 tests/testthat/test-ggplot-errorbar-horizontal.R create mode 100644 tests/testthat/test-ggplot-ticks.R create mode 100644 tests/testthat/test-rotated-ticks.R diff --git a/tests/testthat/test-ggplot-errorbar-horizontal.R b/tests/testthat/test-ggplot-errorbar-horizontal.R new file mode 100644 index 0000000000..b9cccb9909 --- /dev/null +++ b/tests/testthat/test-ggplot-errorbar-horizontal.R @@ -0,0 +1,27 @@ +context("geom_errorbarh") + +test_that("geom_errorbarh gives horizontal errorbars", { + + df <- data.frame( + trt = factor(c(1, 1, 2, 2)), + resp = c(1, 5, 3, 4), + group = factor(c(1, 2, 1, 2)), + se = c(0.1, 0.3, 0.3, 0.4) + ) + g <- ggplot(df, aes(resp, trt, colour=group)) + geom_point() + # Define the limits of the horizontal errorbars + g <- g + geom_errorbarh(aes(xmax = resp + se, xmin = resp - se)) + + L <- gg2list(g) + + # Expect 2 traces + expect_equal(length(L), 3) + # Expect scatter plot and its error bars to have the same color + expect_identical(L[[1]]$marker$color, L[[1]]$error_x$color) + expect_identical(L[[2]]$marker$color, L[[2]]$error_x$color) + # Expect given errorbar values + expect_equal(L[[1]]$error_x$array, c(0.1, 0.3)) + expect_equal(L[[1]]$error_x$symmetric, TRUE) + + save_outputs(g, "errorbar-horizontal") +}) diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R new file mode 100644 index 0000000000..583ee9fdb8 --- /dev/null +++ b/tests/testthat/test-ggplot-ticks.R @@ -0,0 +1,122 @@ +context("ggplot ticks") + +boxes <- ggplot(PlantGrowth, aes(x=group, y=weight)) + geom_boxplot() + +expect_traces <- function(gg, n.traces){ + stopifnot(is.ggplot(gg)) + stopifnot(is.numeric(n.traces)) + L <- gg2list(gg) + is.trace <- names(L) == "" + traces <- L[is.trace] + expect_equal(length(traces), n.traces) + list(traces=traces, kwargs=L$kwargs) +} + +plant.list <- split(PlantGrowth, PlantGrowth$group) + +test_that("boxes without coord_flip()", { + info <- expect_traces(boxes, 3) + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } +}) + +test_that("boxes with coord_flip()", { + flipped <- boxes + coord_flip() + info <- expect_traces(flipped, 3) + for(tr in info$traces){ + expect_true(is.null(tr[["y"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["x"]] + expect_equal(computed, expected) + } +}) + + +test_that("Manually set the order of a discrete-valued axis", { + expected.order <- c("trt1", "ctrl", "trt2") + boxes.limits <- boxes + scale_x_discrete(limits=expected.order) + info <- expect_traces(boxes.limits, 3) + computed.order <- sapply(info$traces, "[[", "name") + expect_identical(as.character(computed.order), expected.order) +}) + +expected.labels <- c("Control", "Treat 1", "Treat 2") +boxes.labels <- boxes + + scale_x_discrete(breaks=c("ctrl", "trt1", "trt2"), + labels=expected.labels) + +test_that("Manually set the order of a discrete-valued axis", { + info <- expect_traces(boxes.labels, 3) + computed.labels <- sapply(info$traces, "[[", "name") + expect_identical(as.character(computed.labels), expected.labels) +}) + +no.breaks <- boxes + scale_x_discrete(breaks=NULL) + +test_that("hide x ticks, lines, and labels", { + info <- expect_traces(no.breaks, 3) + x <- info$kwargs$layout$xaxis + expect_identical(x[["showticklabels"]], FALSE) + ##expect_identical(x[["showline"]], FALSE) #irrelevant. + expect_identical(x[["showgrid"]], FALSE) + + ## ticks ('' | 'inside' | 'outside') Sets the format of the ticks on + ## this axis. For hidden ticks, link 'ticks' to an empty string. + expect_identical(x[["ticks"]], "") + + ## xaxis has parameter autotick (a boolean: TRUE | FALSE) Toggle + ## whether or not the axis ticks parameters are picked automatically + ## by Plotly. Once 'autotick' is set to FALSE, the axis ticks + ## parameters can be declared with 'ticks', 'tick0', 'dtick0' and + ## other tick-related key in this axis object. + ##expect_identical(x[["autotick"]], FALSE) #not necessary +}) + +test_that("Hide X ticks and labels, but keep the gridlines" { + boxes.grid <- boxes + + theme(axis.ticks = element_blank(), axis.text.x = element_blank()) + info <- expect_traces(boxes.grid, 3) + x <- info$kwargs$layout$xaxis + expect_identical(x[["showticklabels"]], FALSE) + expect_identical(x[["showgrid"]], TRUE) + expect_identical(x[["ticks"]], "") +}) + +test_that("Set continuous Y axis range", { + boxes.range <- boxes + ylim(0,8) + boxes.range <- boxes + scale_y_continuous(limits=c(0,8)) + info <- expect_traces(boxes.range, 3) + ## TODO: can plotly zoom be specified? +}) + +test_that("Reverse order of a continuous-valued axis", { + boxes.reverse <- boxes + scale_y_reverse() + ##TODO +}) + +test_that("Set the X tick mark locations", { + ## This will show tick marks on every 0.25 from 1 to 10. The scale will + ## show only the ones that are within range (3.50-6.25 in this case) + boxes.ticks <- boxes + scale_y_continuous(breaks=seq(1,10,1/4)) + ##TODO +}) + +test_that("The breaks can be spaced unevenly", { + boxes.uneven <- boxes + scale_y_continuous(breaks=c(4, 4.25, 4.5, 5, 6,8)) + ##TODO +}) + +test_that("Suppress ticks and gridlines", { + ticks.nobreaks <- boxes + scale_y_continuous(breaks=NULL) + ##TODO +}) + +test_that("Hide tick marks and labels (on Y axis), but keep the gridlines", { + boxes.ygrid <- boxes + + theme(axis.ticks = element_blank(), axis.text.y = element_blank()) + ##TODO +}) diff --git a/tests/testthat/test-rotated-ticks.R b/tests/testthat/test-rotated-ticks.R new file mode 100644 index 0000000000..92c3b8bfce --- /dev/null +++ b/tests/testthat/test-rotated-ticks.R @@ -0,0 +1,72 @@ +context("rotated ticks") + +ss <- data.frame(State=paste("some long text", c("CA", "NY", "TX")), + Prop.Inv=c(0, 1, 0.7), + Year=c(1984, 2015, 1999)) + +fg <- ggplot() + + geom_point(aes(x=State, y=Prop.Inv), data=ss) + + xlab("STATE SOME REALLY REALLY LONG TEXT THAT MAY OVERLAP TICKS") + +## TODO: change the details of getTicks and expect_rotate_anchor to +## test plotly web pages. +getTicks <- function(html, p.name){ + xp <- sprintf('//svg[@id="%s"]//g[@id="xaxis"]//text', p.name) + nodes <- getNodeSet(html, xp) + stopifnot(length(nodes) > 0) + sapply(nodes, xmlAttrs) +} +expect_rotate_anchor <- function(info, rotate, anchor){ + return()#TODO:remove. + not <- getTicks(info$html, 'not') + expect_match(not["style", ], "text-anchor: middle", fixed=TRUE) + expect_match(not["transform", ], "rotate(0", fixed=TRUE) + rotated <- getTicks(info$html, 'rotated') + expect_match(rotated["style", ], paste("text-anchor:", anchor), fixed=TRUE) + expect_match(rotated["transform", ], paste0("rotate(", rotate), fixed=TRUE) + + e.axis <- remDr$findElement(using="css selector", "g#xaxis") + e.text <- e.axis$findChildElement("css selector", "text") + tick.loc <- e.text$getElementLocation() + tick.size <- e.text$getElementSize() + ## Subtract a magic number that lets the test pass for un-rotated + ## labels in firefox. + tick.bottom.y <- tick.loc$y + tick.size$height - 6 + e.title <- remDr$findElement("css selector", "text#xtitle") + title.loc <- e.title$getElementLocation() + expect_true(tick.bottom.y < title.loc$y) +} + +## TODO: implement renderHTML which should upload and plot the data, +## then download the rendered HTML using RSelenium to control a +## headless browser. +renderHTML <- function(gg){ + gg2list(gg) +} + +test_that('no axis rotation is fine', { + info <- renderHTML(fg) + expect_rotate_anchor(info, "0", "middle") +}) + +test_that('axis.text.x=element_text(angle=90)"', { + rotated <- fg+theme(axis.text.x=element_text(angle=90)) + info <- renderHTML(rotated) + expect_rotate_anchor(info, "-90", "end") +}) + +test_that('axis.text.x=element_text(angle=70) means transform="rotate(-70)"', { + rotated <- fg+theme(axis.text.x=element_text(angle=70)) + info <- renderHTML(rotated) + expect_rotate_anchor(info, "-70", "end") +}) + +## test_that('hjust=0.75 is an error', { +## problem <- fg+theme(axis.text.x=element_text(hjust=0.75) +## expect_error({ +## info <- renderHTML(problem) +## }, "ggplotly only supports hjust values 0, 0.5, 1") +## }) + + + From f2d7cf3f93a1b9f0182a41cbe7a2d0dbd36bcbc2 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 9 Feb 2015 15:34:28 -0500 Subject: [PATCH 14/49] for discussion --- NEWS | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/NEWS b/NEWS index 3ec054e9c8..d25ed30795 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,14 @@ +TO DISCUSS + +Visual testing table instead of GitHub diffs? +http://sugiyama-www.cs.titech.ac.jp/~toby/flags/ +https://sjp.co.nz/projects/grimport2/ +https://dl.dropboxusercontent.com/u/54315147/import/state-table.html + +Can zoom = ggplot()+ylim(0,8) be specified in plotly? + +Use RSelenium for headless browser testing? + 0.5.19 -- 23 January 2015. Support class conversion such as as.Date() within ggplot code. From eab03484f9b1a16e2766a7e4fddd18d11c31ef56 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 9 Feb 2015 18:07:48 -0500 Subject: [PATCH 15/49] test y axis range --- NEWS | 4 ++-- tests/testthat/test-ggplot-ticks.R | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index d25ed30795..fbca6c3b81 100644 --- a/NEWS +++ b/NEWS @@ -1,12 +1,12 @@ TO DISCUSS +R-GSOC project? + Visual testing table instead of GitHub diffs? http://sugiyama-www.cs.titech.ac.jp/~toby/flags/ https://sjp.co.nz/projects/grimport2/ https://dl.dropboxusercontent.com/u/54315147/import/state-table.html -Can zoom = ggplot()+ylim(0,8) be specified in plotly? - Use RSelenium for headless browser testing? 0.5.19 -- 23 January 2015. diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 583ee9fdb8..3c9bd06877 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -90,7 +90,8 @@ test_that("Set continuous Y axis range", { boxes.range <- boxes + ylim(0,8) boxes.range <- boxes + scale_y_continuous(limits=c(0,8)) info <- expect_traces(boxes.range, 3) - ## TODO: can plotly zoom be specified? + y.axis <- info$kwargs$layout$yaxis + expect_equal(y.axis$range, c(0, 8)) }) test_that("Reverse order of a continuous-valued axis", { From 8062b9c77987f7b1496664c71b6b091fe46700da Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 9 Feb 2015 18:24:30 -0500 Subject: [PATCH 16/49] reverse y axis --- tests/testthat/test-ggplot-ticks.R | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 3c9bd06877..9468f5e2df 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -13,6 +13,7 @@ expect_traces <- function(gg, n.traces){ } plant.list <- split(PlantGrowth, PlantGrowth$group) +weight.range <- range(PlantGrowth$weight) test_that("boxes without coord_flip()", { info <- expect_traces(boxes, 3) @@ -76,7 +77,7 @@ test_that("hide x ticks, lines, and labels", { ##expect_identical(x[["autotick"]], FALSE) #not necessary }) -test_that("Hide X ticks and labels, but keep the gridlines" { +test_that("Hide X ticks and labels, but keep the gridlines", { boxes.grid <- boxes + theme(axis.ticks = element_blank(), axis.text.x = element_blank()) info <- expect_traces(boxes.grid, 3) @@ -86,17 +87,40 @@ test_that("Hide X ticks and labels, but keep the gridlines" { expect_identical(x[["ticks"]], "") }) -test_that("Set continuous Y axis range", { - boxes.range <- boxes + ylim(0,8) +test_that("scale_y_continuous(limits) means yaxis$ranges", { boxes.range <- boxes + scale_y_continuous(limits=c(0,8)) info <- expect_traces(boxes.range, 3) y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, c(0, 8)) }) -test_that("Reverse order of a continuous-valued axis", { +test_that("ylim() means yaxis$ranges", { + boxes.range <- boxes + ylim(0,8) + info <- expect_traces(boxes.range, 3) + y.axis <- info$kwargs$layout$yaxis + expect_equal(y.axis$range, c(0, 8)) +}) + +test_that("scale_y_reverse() -> yaxis$ranges reversed", { boxes.reverse <- boxes + scale_y_reverse() - ##TODO + info <- expect_traces(boxes.reverse, 3) + y.axis <- info$kwargs$layout$yaxis + expect_equal(y.axis$range, rev(weight.range)) +}) + +test_that("scale_y_reverse(limits) -> yaxis$ranges reversed", { + y.lim <- c(10, -2) + boxes.reverse <- boxes + scale_y_reverse(limits=y.lim) + info <- expect_traces(boxes.reverse, 3) + y.axis <- info$kwargs$layout$yaxis + expect_equal(y.axis$range, y.lim) +}) + +test_that("ylim(reversed) -> yaxis$ranges reversed", { + boxes.reverse <- boxes + ylim(7.5, -1) + info <- expect_traces(boxes.reverse, 3) + y.axis <- info$kwargs$layout$yaxis + expect_equal(y.axis$range, c(7.5, -1)) }) test_that("Set the X tick mark locations", { From 56e7515b5370167de7bc65860e914f89873ddc55 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 9 Feb 2015 18:52:52 -0500 Subject: [PATCH 17/49] tick tests complete --- NEWS | 3 +++ tests/testthat/test-ggplot-ticks.R | 29 ++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 45a9061303..3622065ba3 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ TO DISCUSS +unevenly spaced ticks? +scale_y_continuous(breaks=c(4, 4.25, 4.5, 5, 6,8)) + R-GSOC project? Visual testing table instead of GitHub diffs? diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 9468f5e2df..dd50b4de78 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -36,7 +36,6 @@ test_that("boxes with coord_flip()", { } }) - test_that("Manually set the order of a discrete-valued axis", { expected.order <- c("trt1", "ctrl", "trt2") boxes.limits <- boxes + scale_x_discrete(limits=expected.order) @@ -127,21 +126,33 @@ test_that("Set the X tick mark locations", { ## This will show tick marks on every 0.25 from 1 to 10. The scale will ## show only the ones that are within range (3.50-6.25 in this case) boxes.ticks <- boxes + scale_y_continuous(breaks=seq(1,10,1/4)) - ##TODO + y.axis <- info$kwargs$layout$yaxis + expect_equal(y.axis$dtick, 0.25) + expect_identical(y.axis$autotick, FALSE) }) test_that("The breaks can be spaced unevenly", { - boxes.uneven <- boxes + scale_y_continuous(breaks=c(4, 4.25, 4.5, 5, 6,8)) - ##TODO + boxes.uneven <- boxes + + scale_y_continuous(breaks=c(4, 4.25, 4.5, 5, 6,8)) + ##TODO: is this possible in plotly? + ## https://plot.ly/python/reference/#YAxis }) -test_that("Suppress ticks and gridlines", { - ticks.nobreaks <- boxes + scale_y_continuous(breaks=NULL) - ##TODO +test_that("hide y ticks, lines, and labels", { + no.breaks <- boxes + scale_y_continuous(breaks=NULL) + info <- expect_traces(no.breaks, 3) + y.axis <- info$kwargs$layout$yaxis + expect_identical(y.axis[["showgrid"]], FALSE) + expect_identical(y.axis[["ticks"]], "") + expect_identical(y.axis[["showticklabels"]], FALSE) }) -test_that("Hide tick marks and labels (on Y axis), but keep the gridlines", { +test_that("hide y ticks and labels, but keep the gridlines", { boxes.ygrid <- boxes + theme(axis.ticks = element_blank(), axis.text.y = element_blank()) - ##TODO + info <- expect_traces(boxes.ygrid, 3) + y.axis <- info$kwargs$layout$yaxis + expect_identical(y.axis[["showgrid"]], TRUE) + expect_identical(y.axis[["ticks"]], "") + expect_identical(y.axis[["showticklabels"]], FALSE) }) From ba1c2060da985b11536f5dc1a812193d30f7a7b3 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 9 Feb 2015 19:33:03 -0500 Subject: [PATCH 18/49] facet tests --- NEWS | 3 + R/ggplotly.R | 3 + tests/testthat/test-ggplot-ticks.R | 89 +++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 3622065ba3..d9caf423ba 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ TO DISCUSS +for plotlys with panels, does list(xaxis="x1") mean "xaxis" and +list(xaxis="x2") mean "xaxis2" ? + unevenly spaced ticks? scale_y_continuous(breaks=c(4, 4.25, 4.5, 5, 6,8)) diff --git a/R/ggplotly.R b/R/ggplotly.R index e1b2e3dd3e..e105942256 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -617,6 +617,9 @@ gg2list <- function(p){ stop("No exportable traces") } + ## TODO: If coord_flip is defined, then flip x/y in each trace, and + ## in each axis...? + mode.mat <- matrix(NA, 3, 3) rownames(mode.mat) <- colnames(mode.mat) <- c("markers", "lines", "none") mode.mat["markers", "lines"] <- diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index dd50b4de78..8768a884f4 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -1,5 +1,7 @@ context("ggplot ticks") +PlantGrowth$type <- + ifelse(PlantGrowth$group=="ctrl", "control", "treatment") boxes <- ggplot(PlantGrowth, aes(x=group, y=weight)) + geom_boxplot() expect_traces <- function(gg, n.traces){ @@ -7,9 +9,13 @@ expect_traces <- function(gg, n.traces){ stopifnot(is.numeric(n.traces)) L <- gg2list(gg) is.trace <- names(L) == "" - traces <- L[is.trace] - expect_equal(length(traces), n.traces) - list(traces=traces, kwargs=L$kwargs) + 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) + list(traces=has.data, kwargs=L$kwargs) } plant.list <- split(PlantGrowth, PlantGrowth$group) @@ -25,6 +31,48 @@ test_that("boxes without coord_flip()", { } }) +test_that("boxes with facet_grid", { + facets <- boxes + facet_grid(. ~ type) + info <- expect_traces(facets, 3) + ## TODO: expect boxes of equal size. + + ## TODO: expect empty space. + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } +}) + +test_that('boxes with facet_grid(scales="free")', { + facets.scales <- boxes + facet_grid(. ~ type, scales="free") + info <- expect_traces(facets.scales, 3) + ## TODO: expect boxes of unequal size. + + ## TODO: expect no empty space. + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } +}) + +test_that('boxes with facet_grid(scales="free", space="free")', { + facets.space <- boxes + facet_grid(. ~ type, scales="free", space="free") + info <- expect_traces(facets.space, 3) + ## TODO: expect boxes of equal size. + + ## TODO: expect no empty space. + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } +}) + test_that("boxes with coord_flip()", { flipped <- boxes + coord_flip() info <- expect_traces(flipped, 3) @@ -36,6 +84,41 @@ test_that("boxes with coord_flip()", { } }) +test_that("boxes with coord_flip()+facet_grid()", { + flip.facet <- flipped + facet_grid(type ~ .) + info <- expect_traces(flip.facet, 3) + for(tr in info$traces){ + expect_true(is.null(tr[["y"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["x"]] + expect_equal(computed, expected) + } +}) + +test_that('boxes with coord_flip()+facet_grid(scales="free")', { + flip.facet.scales <- flipped + facet_grid(type ~ ., scales="free") + info <- expect_traces(flip.facet.scales, 3) + for(tr in info$traces){ + expect_true(is.null(tr[["y"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["x"]] + expect_equal(computed, expected) + } +}) + +test_that('boxes+coord_flip()+facet_grid(scales="free", space="free")', { + flip.facet.space <- flipped + + facet_grid(type ~ ., scales="free", space="free") + ## BUG in ggplot2! +}) + +test_that('boxes+facet_grid(scales="free", space="free")+coord_flip()', { + flip.facet.space <- boxes + + facet_grid(type ~ ., scales="free", space="free")+ + coord_flip() + ## BUG in ggplot2! +}) + test_that("Manually set the order of a discrete-valued axis", { expected.order <- c("trt1", "ctrl", "trt2") boxes.limits <- boxes + scale_x_discrete(limits=expected.order) From 5cad2f406ef7688cbc828db33627a66800b7f2e6 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 17:47:08 -0500 Subject: [PATCH 19/49] disable coord_flip + facet tests --- tests/testthat/test-ggplot-ticks.R | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 8768a884f4..bad4074ab9 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -84,26 +84,29 @@ test_that("boxes with coord_flip()", { } }) +## coord_flip + facets are not really even supported in ggplot2, so +## these tests are disabled for now. + test_that("boxes with coord_flip()+facet_grid()", { flip.facet <- flipped + facet_grid(type ~ .) info <- expect_traces(flip.facet, 3) - for(tr in info$traces){ - expect_true(is.null(tr[["y"]])) - expected <- plant.list[[tr$name]]$weight - computed <- tr[["x"]] - expect_equal(computed, expected) - } + ## for(tr in info$traces){ + ## expect_true(is.null(tr[["y"]])) + ## expected <- plant.list[[tr$name]]$weight + ## computed <- tr[["x"]] + ## expect_equal(computed, expected) + ## } }) test_that('boxes with coord_flip()+facet_grid(scales="free")', { flip.facet.scales <- flipped + facet_grid(type ~ ., scales="free") info <- expect_traces(flip.facet.scales, 3) - for(tr in info$traces){ - expect_true(is.null(tr[["y"]])) - expected <- plant.list[[tr$name]]$weight - computed <- tr[["x"]] - expect_equal(computed, expected) - } + ## for(tr in info$traces){ + ## expect_true(is.null(tr[["y"]])) + ## expected <- plant.list[[tr$name]]$weight + ## computed <- tr[["x"]] + ## expect_equal(computed, expected) + ## } }) test_that('boxes+coord_flip()+facet_grid(scales="free", space="free")', { From 3358708cb6431cf19fb601de5af5af551639d7c3 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 18:00:35 -0500 Subject: [PATCH 20/49] order traces --- R/ggplotly.R | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index e105942256..93b5e3e2ea 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -269,6 +269,7 @@ gg2list <- function(p){ cls <- attr(e(el.name),"class") "element_blank" %in% cls || null.is.blank && is.null(cls) } + trace.order.list <- list() for(xy in c("x","y")){ ax.list <- list() s <- function(tmp)sprintf(tmp, xy) @@ -319,6 +320,7 @@ gg2list <- function(p){ scale.i <- which(p$scales$find(xy)) ax.list$title <- if(length(scale.i)){ sc <- p$scales$scales[[scale.i]] + trace.order.list[[xy]] <- sc$limits if(!is.null(sc$name)){ sc$name }else{ @@ -327,6 +329,7 @@ gg2list <- function(p){ }else{ p$labels[[xy]] } + title.text <- e(s("axis.title.%s")) ax.list$titlefont <- theme2font(title.text) ax.list$type <- if(misc$is.continuous[[xy]]){ @@ -667,7 +670,16 @@ gg2list <- function(p){ merged.traces[[length(merged.traces)+1]] <- tr } - merged.traces$kwargs <- list(layout=layout) + ## Put the traces in correct order, according to any manually + ## specified scales. + trace.order <- unlist(trace.order.list) + trace.order.score <- seq_along(trace.order) + names(trace.order.score) <- trace.order + trace.name <- sapply(merged.traces, "[[", "name") + trace.score <- trace.order.score[trace.name] + ordered.traces <- merged.traces[order(trace.score)] + + ordered.traces$kwargs <- list(layout=layout) - merged.traces + ordered.traces } From b2f3c11591bcf6c35aece14bca230ddf736a929f Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 18:07:51 -0500 Subject: [PATCH 21/49] do not order if there is no order --- R/ggplotly.R | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 93b5e3e2ea..09d58f3db3 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -673,11 +673,15 @@ gg2list <- function(p){ ## Put the traces in correct order, according to any manually ## specified scales. trace.order <- unlist(trace.order.list) - trace.order.score <- seq_along(trace.order) - names(trace.order.score) <- trace.order - trace.name <- sapply(merged.traces, "[[", "name") - trace.score <- trace.order.score[trace.name] - ordered.traces <- merged.traces[order(trace.score)] + ordered.traces <- if(length(trace.order)){ + trace.order.score <- seq_along(trace.order) + names(trace.order.score) <- trace.order + trace.name <- sapply(merged.traces, "[[", "name") + trace.score <- trace.order.score[trace.name] + merged.traces[order(trace.score)] + }else{ + merged.traces + } ordered.traces$kwargs <- list(layout=layout) From a04c70537485284a466d865c357856c5cb908fea Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 18:43:10 -0500 Subject: [PATCH 22/49] flip traces --- R/ggplotly.R | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 09d58f3db3..768356f5b1 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -620,9 +620,6 @@ gg2list <- function(p){ stop("No exportable traces") } - ## TODO: If coord_flip is defined, then flip x/y in each trace, and - ## in each axis...? - mode.mat <- matrix(NA, 3, 3) rownames(mode.mat) <- colnames(mode.mat) <- c("markers", "lines", "none") mode.mat["markers", "lines"] <- @@ -682,8 +679,30 @@ gg2list <- function(p){ }else{ merged.traces } + + ## If coord_flip is defined, then flip x/y in each trace, and in + ## each axis. + flipped.traces <- ordered.traces + flipped.layout <- layout + if("flip" %in% attr(built$plot$coordinates, "class")){ + if(!inherits(p$facet, "null")){ + stop("coord_flip + facet conversion not supported") + } + for(trace.i in seq_along(ordered.traces)){ + tr <- ordered.traces[[trace.i]] + x <- tr[["x"]] + y <- tr[["y"]] + tr[["y"]] <- x + tr[["x"]] <- y + flipped.traces[[trace.i]] <- tr + } + x <- layout[["xaxis"]] + y <- layout[["yaxis"]] + flipped.layout[["xaxis"]] <- y + flipped.layout[["yaxis"]] <- x + } - ordered.traces$kwargs <- list(layout=layout) + flipped.traces$kwargs <- list(layout=flipped.layout) - ordered.traces + flipped.traces } From a29a3c7ea1b15fe1d9ce0606b24b3b276509183a Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:05:33 -0500 Subject: [PATCH 23/49] more breaks, labels, limits tests --- R/ggplotly.R | 2 + tests/testthat/test-ggplot-ticks.R | 60 +++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 768356f5b1..2b2c23cb7e 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -270,6 +270,7 @@ gg2list <- function(p){ "element_blank" %in% cls || null.is.blank && is.null(cls) } trace.order.list <- list() + trace.name.map <- c() for(xy in c("x","y")){ ax.list <- list() s <- function(tmp)sprintf(tmp, xy) @@ -321,6 +322,7 @@ gg2list <- function(p){ ax.list$title <- if(length(scale.i)){ sc <- p$scales$scales[[scale.i]] trace.order.list[[xy]] <- sc$limits + trace.name.map[sc$breaks] <- sc$labels if(!is.null(sc$name)){ sc$name }else{ diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index bad4074ab9..7390b4ce82 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -73,8 +73,9 @@ test_that('boxes with facet_grid(scales="free", space="free")', { } }) +flipped <- boxes + coord_flip() + test_that("boxes with coord_flip()", { - flipped <- boxes + coord_flip() info <- expect_traces(flipped, 3) for(tr in info$traces){ expect_true(is.null(tr[["y"]])) @@ -89,7 +90,7 @@ test_that("boxes with coord_flip()", { test_that("boxes with coord_flip()+facet_grid()", { flip.facet <- flipped + facet_grid(type ~ .) - info <- expect_traces(flip.facet, 3) + ##info <- expect_traces(flip.facet, 3) ## for(tr in info$traces){ ## expect_true(is.null(tr[["y"]])) ## expected <- plant.list[[tr$name]]$weight @@ -100,7 +101,7 @@ test_that("boxes with coord_flip()+facet_grid()", { test_that('boxes with coord_flip()+facet_grid(scales="free")', { flip.facet.scales <- flipped + facet_grid(type ~ ., scales="free") - info <- expect_traces(flip.facet.scales, 3) + ##info <- expect_traces(flip.facet.scales, 3) ## for(tr in info$traces){ ## expect_true(is.null(tr[["y"]])) ## expected <- plant.list[[tr$name]]$weight @@ -130,12 +131,59 @@ test_that("Manually set the order of a discrete-valued axis", { expect_identical(as.character(computed.order), expected.order) }) -expected.labels <- c("Control", "Treat 1", "Treat 2") +test_that("limits can hide data", { + expected.order <- c("trt1", "ctrl") + boxes.limits <- boxes + scale_x_discrete(limits=expected.order) + info <- expect_traces(boxes.limits, 2) + computed.order <- sapply(info$traces, "[[", "name") + expect_identical(as.character(computed.order), expected.order) +}) + +test_that("limits can create a gap", { + expected.order <- c("trt1", "trt2", "GAP", "ctrl") + boxes.limits <- boxes + scale_x_discrete(limits=expected.order) + info <- expect_traces(boxes.limits, 3) + computed.order <- sapply(info$traces, "[[", "name") + ##expect_identical(as.character(computed.order), expected.order) + + ## TODO: can we make this in plotly? +}) + +boxes.breaks <- boxes + + scale_x_discrete(breaks=c("trt1", "ctrl", "trt2")) + +test_that("setting breaks does not change order", { + info <- expect_traces(boxes.breaks, 3) + computed.labels <- sapply(info$traces, "[[", "name") + expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) +}) + +boxes.more <- boxes + + scale_x_discrete(breaks=c("trt1", "ctrl", "trt2", "FOO")) + +test_that("more breaks is fine", { + info <- expect_traces(boxes.more, 3) + computed.labels <- sapply(info$traces, "[[", "name") + expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) +}) + +boxes.less <- boxes + + scale_x_discrete(breaks=c("trt1", "ctrl")) + +test_that("less breaks is fine", { + info <- expect_traces(boxes.less, 3) + computed.labels <- sapply(info$traces, "[[", "name") + ##expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) + + ## TODO: can we make this in plotly? +}) + +expected.labels <- c("Treat 1", "Control", "Treat 2") boxes.labels <- boxes + - scale_x_discrete(breaks=c("ctrl", "trt1", "trt2"), + scale_x_discrete(breaks=c("trt1", "ctrl", "trt2"), labels=expected.labels) -test_that("Manually set the order of a discrete-valued axis", { +test_that("scale(labels) changes trace names", { info <- expect_traces(boxes.labels, 3) computed.labels <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.labels), expected.labels) From 3047869e6b7215393ec19d6b7db98ac6d2596771 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:12:35 -0500 Subject: [PATCH 24/49] scale(labels) for trace names --- R/ggplotly.R | 12 +++++++++--- tests/testthat/test-ggplot-ticks.R | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 2b2c23cb7e..cb9a07a690 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -681,17 +681,23 @@ gg2list <- function(p){ }else{ merged.traces } + + named.traces <- ordered.traces + for(trace.i in seq_along(named.traces)){ + tr.name <- named.traces[[trace.i]][["name"]] + named.traces[[trace.i]][["name"]] <- trace.name.map[[tr.name]] + } ## If coord_flip is defined, then flip x/y in each trace, and in ## each axis. - flipped.traces <- ordered.traces + flipped.traces <- named.traces flipped.layout <- layout if("flip" %in% attr(built$plot$coordinates, "class")){ if(!inherits(p$facet, "null")){ stop("coord_flip + facet conversion not supported") } - for(trace.i in seq_along(ordered.traces)){ - tr <- ordered.traces[[trace.i]] + for(trace.i in seq_along(flipped.traces)){ + tr <- flipped.traces[[trace.i]] x <- tr[["x"]] y <- tr[["y"]] tr[["y"]] <- x diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 7390b4ce82..271d26a61f 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -178,15 +178,15 @@ test_that("less breaks is fine", { ## TODO: can we make this in plotly? }) -expected.labels <- c("Treat 1", "Control", "Treat 2") boxes.labels <- boxes + scale_x_discrete(breaks=c("trt1", "ctrl", "trt2"), - labels=expected.labels) + labels=c("Treatment 1", "Control", "Treatment 2")) test_that("scale(labels) changes trace names", { info <- expect_traces(boxes.labels, 3) computed.labels <- sapply(info$traces, "[[", "name") - expect_identical(as.character(computed.labels), expected.labels) + expect_identical(as.character(computed.labels), + c("Control", "Treatment 1", "Treatment 2")) }) no.breaks <- boxes + scale_x_discrete(breaks=NULL) From f791134fab48d23a6738cb57cf22510afbffcca7 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:17:16 -0500 Subject: [PATCH 25/49] works with no scale(labels) --- R/ggplotly.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index cb9a07a690..e58c6b8235 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -682,10 +682,14 @@ gg2list <- function(p){ merged.traces } + ## Translate scale(labels) to trace name. named.traces <- ordered.traces for(trace.i in seq_along(named.traces)){ tr.name <- named.traces[[trace.i]][["name"]] - named.traces[[trace.i]][["name"]] <- trace.name.map[[tr.name]] + new.name <- trace.name.map[[tr.name]] + if(!is.null(new.name)){ + named.traces[[trace.i]][["name"]] <- new.name + } } ## If coord_flip is defined, then flip x/y in each trace, and in From e2ea24ba3cda2bff110f36bc3754637e646298e6 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:21:12 -0500 Subject: [PATCH 26/49] breaks=NULL hides ticks, lines, labels --- R/ggplotly.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/R/ggplotly.R b/R/ggplotly.R index e58c6b8235..8956baac61 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -323,6 +323,11 @@ gg2list <- function(p){ sc <- p$scales$scales[[scale.i]] trace.order.list[[xy]] <- sc$limits trace.name.map[sc$breaks] <- sc$labels + if(is.null(sc$breaks)){ + ax.list$showticklabels <- FALSE + ax.list$showgrid <- FALSE + ax.list$ticks <- "" + } if(!is.null(sc$name)){ sc$name }else{ From 7926a30d217ce920ca68187e3c6ccfac792351d7 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:24:16 -0500 Subject: [PATCH 27/49] scale(limits) means axis$range --- R/ggplotly.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/R/ggplotly.R b/R/ggplotly.R index 8956baac61..29721f68f0 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -328,6 +328,9 @@ gg2list <- function(p){ ax.list$showgrid <- FALSE ax.list$ticks <- "" } + if(!is.null(sc$limits)){ + ax.list$range <- sc$limits + } if(!is.null(sc$name)){ sc$name }else{ From 93585b4889810a7ff1db17263c369e6568780c3e Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:33:14 -0500 Subject: [PATCH 28/49] get trace range from ggplot range --- R/ggplotly.R | 9 +++++++-- tests/testthat/test-ggplot-ticks.R | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 29721f68f0..1eaa24d3d1 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -328,8 +328,13 @@ gg2list <- function(p){ ax.list$showgrid <- FALSE ax.list$ticks <- "" } - if(!is.null(sc$limits)){ - ax.list$range <- sc$limits + ax.list$range <- if(!is.null(sc$limits)){ + sc$limits + }else{ + ggranges[[1]][[s("%s.range")]] #TODO: facets! + } + if(is.character(sc$trans$name) && sc$trans$name == "reverse"){ + ax.list$range <- rev(ax.list$range) } if(!is.null(sc$name)){ sc$name diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 271d26a61f..6ba581495b 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -238,7 +238,7 @@ test_that("scale_y_reverse() -> yaxis$ranges reversed", { boxes.reverse <- boxes + scale_y_reverse() info <- expect_traces(boxes.reverse, 3) y.axis <- info$kwargs$layout$yaxis - expect_equal(y.axis$range, rev(weight.range)) + expect_that(y.axis$range[2], is_less_than(y.axis$range[1])) }) test_that("scale_y_reverse(limits) -> yaxis$ranges reversed", { From 623512eb4050fd57e40b520d50e8d13b768c3d00 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:39:25 -0500 Subject: [PATCH 29/49] scale(breaks=numeric) means axis$dtick and autotick --- R/ggplotly.R | 8 ++++++++ tests/testthat/test-ggplot-ticks.R | 1 + 2 files changed, 9 insertions(+) diff --git a/R/ggplotly.R b/R/ggplotly.R index 1eaa24d3d1..1b411f8631 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -328,6 +328,14 @@ gg2list <- function(p){ ax.list$showgrid <- FALSE ax.list$ticks <- "" } + if(is.numeric(sc$breaks)){ + dticks <- diff(sc$breaks) + dt <- dticks[1] + if(all(dticks == dt)){ + ax.list$dtick <- dt + ax.list$autotick <- FALSE + } + } ax.list$range <- if(!is.null(sc$limits)){ sc$limits }else{ diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 6ba581495b..e2cfd3e61e 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -260,6 +260,7 @@ test_that("Set the X tick mark locations", { ## This will show tick marks on every 0.25 from 1 to 10. The scale will ## show only the ones that are within range (3.50-6.25 in this case) boxes.ticks <- boxes + scale_y_continuous(breaks=seq(1,10,1/4)) + info <- expect_traces(boxes.ticks, 3) y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$dtick, 0.25) expect_identical(y.axis$autotick, FALSE) From 8b0a39f5e885762b3265e5b99990c5e42e3501d3 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:52:48 -0500 Subject: [PATCH 30/49] negative reverse scale --- R/ggplotly.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 1b411f8631..fdec9c3f4f 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -342,7 +342,7 @@ gg2list <- function(p){ ggranges[[1]][[s("%s.range")]] #TODO: facets! } if(is.character(sc$trans$name) && sc$trans$name == "reverse"){ - ax.list$range <- rev(ax.list$range) + ax.list$range <- sort(-ax.list$range, decreasing = TRUE) } if(!is.null(sc$name)){ sc$name @@ -375,7 +375,6 @@ gg2list <- function(p){ !is.blank(s("axis.line.%s")) layout[[s("%saxis")]] <- ax.list } - ## copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each xaxis.title <- layout$xaxis$title yaxis.title <- layout$yaxis$title From 96f998cd726512834a48734bd5e444aeda8690c0 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 19:57:33 -0500 Subject: [PATCH 31/49] test for correct values with reverse scale --- tests/testthat/test-ggplot-ticks.R | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index e2cfd3e61e..f0bc9e0388 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -232,6 +232,13 @@ test_that("ylim() means yaxis$ranges", { info <- expect_traces(boxes.range, 3) y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, c(0, 8)) + ## ensure correct positive values without reverse scale. + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } }) test_that("scale_y_reverse() -> yaxis$ranges reversed", { @@ -239,6 +246,13 @@ test_that("scale_y_reverse() -> yaxis$ranges reversed", { info <- expect_traces(boxes.reverse, 3) y.axis <- info$kwargs$layout$yaxis expect_that(y.axis$range[2], is_less_than(y.axis$range[1])) + ## ensure correct positive values, despite the reverse scale. + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } }) test_that("scale_y_reverse(limits) -> yaxis$ranges reversed", { @@ -247,6 +261,13 @@ test_that("scale_y_reverse(limits) -> yaxis$ranges reversed", { info <- expect_traces(boxes.reverse, 3) y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, y.lim) + ## ensure correct positive values, despite the reverse scale. + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } }) test_that("ylim(reversed) -> yaxis$ranges reversed", { @@ -254,6 +275,13 @@ test_that("ylim(reversed) -> yaxis$ranges reversed", { info <- expect_traces(boxes.reverse, 3) y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, c(7.5, -1)) + ## ensure correct positive values, despite the reverse scale. + for(tr in info$traces){ + expect_true(is.null(tr[["x"]])) + expected <- plant.list[[tr$name]]$weight + computed <- tr[["y"]] + expect_equal(computed, expected) + } }) test_that("Set the X tick mark locations", { From 2581b0ecf93b9a8e86b3de526a06786f29187faf Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Wed, 11 Feb 2015 20:16:07 -0500 Subject: [PATCH 32/49] multiply by -1 for reversed aes --- R/ggplotly.R | 15 ++++++++++++--- R/trace_generation.R | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index fdec9c3f4f..d0bafa8d21 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -100,7 +100,6 @@ gg2list <- function(p){ ## 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 @@ -161,7 +160,9 @@ gg2list <- function(p){ names(ranks) <- br misc$breaks[[sc$aesthetics]] <- ranks } + misc$trans[sc$aesthetics] <- sc$trans$name } + reverse.aes <- names(misc$trans)[misc$trans=="reverse"] ## get gglayout now because we need some of its info in layer2traces gglayout <- built$panel$layout @@ -175,8 +176,16 @@ gg2list <- function(p){ df <- df[order(df$order),] df$order <- NULL - misc$prestats.data <- merge(built$prestats.data[[i]], - gglayout[, c("PANEL", "plotly.row", "COL")]) + prestats <- built$prestats.data[[i]] + ## scale_reverse multiples x/y data by -1, so here we undo that so + ## that the actual data can be uploaded to plotly. + replace.aes <- intersect(names(prestats), reverse.aes) + for(a in replace.aes){ + prestats[[a]] <- -1 * prestats[[a]] + } + misc$prestats.data <- + merge(prestats, + gglayout[, c("PANEL", "plotly.row", "COL")]) # Add global x-range info misc$prestats.data$globxmin <- ggxmin diff --git a/R/trace_generation.R b/R/trace_generation.R index 307d39a5c1..26ee2a4dde 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -41,8 +41,8 @@ layer2traces <- function(l, d, misc) { ## For non-numeric data on the axes, we should take the values from ## the original data. - for (axis.name in c("x", "y")) { - if (!misc$is.continuous[[axis.name]]) { + for (axis.name in c("x", "y")) { + if(!misc$is.continuous[[axis.name]]) { aes.names <- paste0(axis.name, c("", "end", "min", "max")) aes.used <- aes.names[aes.names %in% names(g$aes)] for(a in aes.used) { From 4766545186138517c0fe45a91f0f794d97505cf6 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 16 Feb 2015 18:52:59 -0500 Subject: [PATCH 33/49] exclude NA data from traces --- R/trace_generation.R | 11 ++++++++--- tests/testthat.R | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/R/trace_generation.R b/R/trace_generation.R index 26ee2a4dde..16ec5a257e 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -5,9 +5,14 @@ #' @return list representing a layer, with corresponding aesthetics, ranges, and groups. #' @export layer2traces <- function(l, d, misc) { + not.na <- function(df){ + na.mat <- sapply(df, is.na) + to.exclude <- apply(na.mat, 1, any) + df[!to.exclude, ] + } g <- list(geom=l$geom$objname, - data=d, - prestats.data=misc$prestats.data) + data=not.na(d), + prestats.data=not.na(misc$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) @@ -61,7 +66,7 @@ layer2traces <- function(l, d, misc) { } # For some plot types, we overwrite `data` with `prestats.data`. - pdata.vec <- misc$prestats.data[[a]] + pdata.vec <- g$prestats.data[[a]] if (inherits(data.vec, "POSIXt")) { ## Re-create dates from nb seconds data.vec <- try(strftime(as.POSIXlt(g$data[[a]], origin=the.epoch), diff --git a/tests/testthat.R b/tests/testthat.R index c5273050e9..270cd979e4 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -3,3 +3,4 @@ save_outputs <- function(gg, name, ignore_ggplot=FALSE) { print(paste("running", name)) } test_check("plotly") + From 11522c2c5b0bd909b24f72d6852657a92ac2be15 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 16 Feb 2015 19:01:53 -0500 Subject: [PATCH 34/49] just use is.na not apply --- R/trace_generation.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/trace_generation.R b/R/trace_generation.R index 16ec5a257e..abeb0c63ba 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -6,7 +6,7 @@ #' @export layer2traces <- function(l, d, misc) { not.na <- function(df){ - na.mat <- sapply(df, is.na) + na.mat <- is.na(df) to.exclude <- apply(na.mat, 1, any) df[!to.exclude, ] } From 5c98619896b226be4252bfa8757f2ddb5676f29f Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 16 Feb 2015 19:14:19 -0500 Subject: [PATCH 35/49] add save_ouputs --- tests/testthat/test-ggplot-ticks.R | 47 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index f0bc9e0388..09fb65136c 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -4,9 +4,10 @@ PlantGrowth$type <- ifelse(PlantGrowth$group=="ctrl", "control", "treatment") boxes <- ggplot(PlantGrowth, aes(x=group, y=weight)) + geom_boxplot() -expect_traces <- function(gg, n.traces){ +expect_traces <- function(gg, n.traces, name){ stopifnot(is.ggplot(gg)) stopifnot(is.numeric(n.traces)) + save_outputs(gg, paste0("ticks-", name)) L <- gg2list(gg) is.trace <- names(L) == "" all.traces <- L[is.trace] @@ -22,7 +23,7 @@ plant.list <- split(PlantGrowth, PlantGrowth$group) weight.range <- range(PlantGrowth$weight) test_that("boxes without coord_flip()", { - info <- expect_traces(boxes, 3) + info <- expect_traces(boxes, 3, "boxes") for(tr in info$traces){ expect_true(is.null(tr[["x"]])) expected <- plant.list[[tr$name]]$weight @@ -33,7 +34,7 @@ test_that("boxes without coord_flip()", { test_that("boxes with facet_grid", { facets <- boxes + facet_grid(. ~ type) - info <- expect_traces(facets, 3) + info <- expect_traces(facets, 3, "boxes-facet-grid") ## TODO: expect boxes of equal size. ## TODO: expect empty space. @@ -47,7 +48,7 @@ test_that("boxes with facet_grid", { test_that('boxes with facet_grid(scales="free")', { facets.scales <- boxes + facet_grid(. ~ type, scales="free") - info <- expect_traces(facets.scales, 3) + info <- expect_traces(facets.scales, 3, "boxes-scales-free") ## TODO: expect boxes of unequal size. ## TODO: expect no empty space. @@ -61,7 +62,7 @@ test_that('boxes with facet_grid(scales="free")', { test_that('boxes with facet_grid(scales="free", space="free")', { facets.space <- boxes + facet_grid(. ~ type, scales="free", space="free") - info <- expect_traces(facets.space, 3) + info <- expect_traces(facets.space, 3, "boxes-space-free") ## TODO: expect boxes of equal size. ## TODO: expect no empty space. @@ -76,7 +77,7 @@ test_that('boxes with facet_grid(scales="free", space="free")', { flipped <- boxes + coord_flip() test_that("boxes with coord_flip()", { - info <- expect_traces(flipped, 3) + info <- expect_traces(flipped, 3, "flip") for(tr in info$traces){ expect_true(is.null(tr[["y"]])) expected <- plant.list[[tr$name]]$weight @@ -126,7 +127,7 @@ test_that('boxes+facet_grid(scales="free", space="free")+coord_flip()', { test_that("Manually set the order of a discrete-valued axis", { expected.order <- c("trt1", "ctrl", "trt2") boxes.limits <- boxes + scale_x_discrete(limits=expected.order) - info <- expect_traces(boxes.limits, 3) + info <- expect_traces(boxes.limits, 3, "discrete-order") computed.order <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.order), expected.order) }) @@ -134,7 +135,7 @@ test_that("Manually set the order of a discrete-valued axis", { test_that("limits can hide data", { expected.order <- c("trt1", "ctrl") boxes.limits <- boxes + scale_x_discrete(limits=expected.order) - info <- expect_traces(boxes.limits, 2) + info <- expect_traces(boxes.limits, 2, "limits-hide") computed.order <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.order), expected.order) }) @@ -142,7 +143,7 @@ test_that("limits can hide data", { test_that("limits can create a gap", { expected.order <- c("trt1", "trt2", "GAP", "ctrl") boxes.limits <- boxes + scale_x_discrete(limits=expected.order) - info <- expect_traces(boxes.limits, 3) + info <- expect_traces(boxes.limits, 3, "limits-gap") computed.order <- sapply(info$traces, "[[", "name") ##expect_identical(as.character(computed.order), expected.order) @@ -153,7 +154,7 @@ boxes.breaks <- boxes + scale_x_discrete(breaks=c("trt1", "ctrl", "trt2")) test_that("setting breaks does not change order", { - info <- expect_traces(boxes.breaks, 3) + info <- expect_traces(boxes.breaks, 3, "breaks-nochange") computed.labels <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) }) @@ -162,7 +163,7 @@ boxes.more <- boxes + scale_x_discrete(breaks=c("trt1", "ctrl", "trt2", "FOO")) test_that("more breaks is fine", { - info <- expect_traces(boxes.more, 3) + info <- expect_traces(boxes.more, 3, "breaks-more") computed.labels <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) }) @@ -171,7 +172,7 @@ boxes.less <- boxes + scale_x_discrete(breaks=c("trt1", "ctrl")) test_that("less breaks is fine", { - info <- expect_traces(boxes.less, 3) + info <- expect_traces(boxes.less, 3, "breaks-less") computed.labels <- sapply(info$traces, "[[", "name") ##expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) @@ -183,7 +184,7 @@ boxes.labels <- boxes + labels=c("Treatment 1", "Control", "Treatment 2")) test_that("scale(labels) changes trace names", { - info <- expect_traces(boxes.labels, 3) + info <- expect_traces(boxes.labels, 3, "scale-labels") computed.labels <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.labels), c("Control", "Treatment 1", "Treatment 2")) @@ -192,7 +193,7 @@ test_that("scale(labels) changes trace names", { no.breaks <- boxes + scale_x_discrete(breaks=NULL) test_that("hide x ticks, lines, and labels", { - info <- expect_traces(no.breaks, 3) + info <- expect_traces(no.breaks, 3, "hide-ticks-lines-labels") x <- info$kwargs$layout$xaxis expect_identical(x[["showticklabels"]], FALSE) ##expect_identical(x[["showline"]], FALSE) #irrelevant. @@ -213,7 +214,7 @@ test_that("hide x ticks, lines, and labels", { test_that("Hide X ticks and labels, but keep the gridlines", { boxes.grid <- boxes + theme(axis.ticks = element_blank(), axis.text.x = element_blank()) - info <- expect_traces(boxes.grid, 3) + info <- expect_traces(boxes.grid, 3, "hide-ticks-labels") x <- info$kwargs$layout$xaxis expect_identical(x[["showticklabels"]], FALSE) expect_identical(x[["showgrid"]], TRUE) @@ -222,14 +223,14 @@ test_that("Hide X ticks and labels, but keep the gridlines", { test_that("scale_y_continuous(limits) means yaxis$ranges", { boxes.range <- boxes + scale_y_continuous(limits=c(0,8)) - info <- expect_traces(boxes.range, 3) + info <- expect_traces(boxes.range, 3, "ycontinuous-ranges") y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, c(0, 8)) }) test_that("ylim() means yaxis$ranges", { boxes.range <- boxes + ylim(0,8) - info <- expect_traces(boxes.range, 3) + info <- expect_traces(boxes.range, 3, "ylim-ranges") y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, c(0, 8)) ## ensure correct positive values without reverse scale. @@ -243,7 +244,7 @@ test_that("ylim() means yaxis$ranges", { test_that("scale_y_reverse() -> yaxis$ranges reversed", { boxes.reverse <- boxes + scale_y_reverse() - info <- expect_traces(boxes.reverse, 3) + info <- expect_traces(boxes.reverse, 3, "yreverse-ranges") y.axis <- info$kwargs$layout$yaxis expect_that(y.axis$range[2], is_less_than(y.axis$range[1])) ## ensure correct positive values, despite the reverse scale. @@ -258,7 +259,7 @@ test_that("scale_y_reverse() -> yaxis$ranges reversed", { test_that("scale_y_reverse(limits) -> yaxis$ranges reversed", { y.lim <- c(10, -2) boxes.reverse <- boxes + scale_y_reverse(limits=y.lim) - info <- expect_traces(boxes.reverse, 3) + info <- expect_traces(boxes.reverse, 3, "yreverse-limits-ranges") y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, y.lim) ## ensure correct positive values, despite the reverse scale. @@ -272,7 +273,7 @@ test_that("scale_y_reverse(limits) -> yaxis$ranges reversed", { test_that("ylim(reversed) -> yaxis$ranges reversed", { boxes.reverse <- boxes + ylim(7.5, -1) - info <- expect_traces(boxes.reverse, 3) + info <- expect_traces(boxes.reverse, 3, "ylim-reversed-ranges") y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$range, c(7.5, -1)) ## ensure correct positive values, despite the reverse scale. @@ -288,7 +289,7 @@ test_that("Set the X tick mark locations", { ## This will show tick marks on every 0.25 from 1 to 10. The scale will ## show only the ones that are within range (3.50-6.25 in this case) boxes.ticks <- boxes + scale_y_continuous(breaks=seq(1,10,1/4)) - info <- expect_traces(boxes.ticks, 3) + info <- expect_traces(boxes.ticks, 3, "evenly-spaced-ticks") y.axis <- info$kwargs$layout$yaxis expect_equal(y.axis$dtick, 0.25) expect_identical(y.axis$autotick, FALSE) @@ -303,7 +304,7 @@ test_that("The breaks can be spaced unevenly", { test_that("hide y ticks, lines, and labels", { no.breaks <- boxes + scale_y_continuous(breaks=NULL) - info <- expect_traces(no.breaks, 3) + info <- expect_traces(no.breaks, 3, "hide-y") y.axis <- info$kwargs$layout$yaxis expect_identical(y.axis[["showgrid"]], FALSE) expect_identical(y.axis[["ticks"]], "") @@ -313,7 +314,7 @@ test_that("hide y ticks, lines, and labels", { test_that("hide y ticks and labels, but keep the gridlines", { boxes.ygrid <- boxes + theme(axis.ticks = element_blank(), axis.text.y = element_blank()) - info <- expect_traces(boxes.ygrid, 3) + info <- expect_traces(boxes.ygrid, 3, "hide-y-keep-grid") y.axis <- info$kwargs$layout$yaxis expect_identical(y.axis[["showgrid"]], TRUE) expect_identical(y.axis[["ticks"]], "") From 314a6ec37d1d423cc90120903b0a1deb0b4eb717 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Tue, 17 Feb 2015 11:32:38 -0500 Subject: [PATCH 36/49] explicit theme_grey and fill=NA for readable ggplot --- tests/testthat/test-ggplot-area.R | 8 ++++++-- tests/testthat/test-ggplot-theme.R | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-ggplot-area.R b/tests/testthat/test-ggplot-area.R index 49ec966aec..7f020a1d33 100644 --- a/tests/testthat/test-ggplot-area.R +++ b/tests/testthat/test-ggplot-area.R @@ -3,7 +3,9 @@ context("Area") huron <- data.frame(year=1875:1972, level=as.vector(LakeHuron)) huron$decade <- plyr::round_any(huron$year, 10, floor) -ar <- ggplot(huron) + geom_area(aes(x=year, y=level)) +ar <- ggplot(huron) + + theme_grey()+ + geom_area(aes(x=year, y=level)) L <- gg2list(ar) test_that("sanity check for geom_area", { @@ -17,7 +19,9 @@ test_that("sanity check for geom_area", { save_outputs(ar, "area") # Test alpha transparency in fill color -gg <- ggplot(huron) + geom_area(aes(x=year, y=level), alpha=0.4) +gg <- ggplot(huron) + + theme_grey()+ + geom_area(aes(x=year, y=level), alpha=0.4) L <- gg2list(gg) test_that("transparency alpha in geom_area is converted", { diff --git a/tests/testthat/test-ggplot-theme.R b/tests/testthat/test-ggplot-theme.R index 40ca1772e4..dc334852b9 100644 --- a/tests/testthat/test-ggplot-theme.R +++ b/tests/testthat/test-ggplot-theme.R @@ -88,8 +88,9 @@ test_that("plot panel border is translated correctly", { save_outputs(ggiris, "theme-panel-border-1") red <- ggplot(iris) + + theme_grey()+ geom_point(aes(Petal.Width, Sepal.Width)) + - theme(panel.border=element_rect(colour="red")) + theme(panel.border=element_rect(colour="red", fill=NA)) info <- gg2list(red) for (xy in c("x", "y")) { ax.list <- info$kwargs$layout[[paste0(xy, "axis")]] From a5fa7b221bf540c025449ed93a08ae7f1dd224f9 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Fri, 20 Feb 2015 13:00:53 -0500 Subject: [PATCH 37/49] delete old trace merging code --- R/ggplotly.R | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index d0bafa8d21..ffc706d5e7 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -203,30 +203,10 @@ gg2list <- function(p){ ## This extracts essential info for this geom/layer. traces <- layer2traces(L, df, misc) - # Associate error bars with previous traces - ##if (grepl("errorbar", L$geom$objname)) { #TDH 28 Jan 2015. - if(FALSE){ - for (j in 1:length(trace.list)) { - temp <- list() - ind <- traces[[1]]$x %in% trace.list[[j]]$x - only_ind <- function(x) x[ind] - if ("errorbarh" %in% L$geom$objname) { - temp <- lapply(traces[[1]]$error_x, only_ind) - # Colour of error bar has to be one string - if (length(temp$color) > 1) temp$color <- temp$color[1] - trace.list[[j]]["error_x"] <- list(temp) - } else { - temp <- lapply(traces[[1]]$error_y, only_ind) - if (length(temp$color) > 1) temp$color <- temp$color[1] - trace.list[[j]]["error_y"] <- list(temp) - } - } - } else { - # Do we really need to coord_transform? - # g$data <- ggplot2:::coord_transform(built$plot$coord, g$data, - # built$panel$ranges[[1]]) - trace.list <- c(trace.list, traces) - } + ## Do we really need to coord_transform? + ## g$data <- ggplot2:::coord_transform(built$plot$coord, g$data, + ## built$panel$ranges[[1]]) + trace.list <- c(trace.list, traces) } ## for barcharts, verify that all traces have the same barmode; we don't From a23b3f276d7302b80c09ffbe388d19dbd6790c7b Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Fri, 20 Feb 2015 13:15:21 -0500 Subject: [PATCH 38/49] tests fail for disappearing boxes --- tests/testthat/test-ggplot-ticks.R | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-ggplot-ticks.R b/tests/testthat/test-ggplot-ticks.R index 09fb65136c..1383af37d8 100644 --- a/tests/testthat/test-ggplot-ticks.R +++ b/tests/testthat/test-ggplot-ticks.R @@ -157,6 +157,9 @@ test_that("setting breaks does not change order", { info <- expect_traces(boxes.breaks, 3, "breaks-nochange") computed.labels <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) + ## For some reason plotly does not render the third box if range is + ## not NULL. + expect_identical(info$kwargs$layout$xaxis$range, NULL) }) boxes.more <- boxes + @@ -166,17 +169,33 @@ test_that("more breaks is fine", { info <- expect_traces(boxes.more, 3, "breaks-more") computed.labels <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) + ## For some reason plotly does not render the third box if range is + ## not NULL. + expect_identical(info$kwargs$layout$xaxis$range, NULL) }) boxes.less <- boxes + scale_x_discrete(breaks=c("trt1", "ctrl")) test_that("less breaks is fine", { + ## L <- gg2list(boxes.less) + ## sendJSON(L) # 2 boxes + ## sendJSON(L[1:3]) # 3 boxes + ## no.xaxis <- L + ## no.xaxis$kwargs$layout$xaxis <- NULL + ## sendJSON(no.xaxis) # 3 boxes + ## no.xrange <- L + ## no.xrange$kwargs$layout$xaxis$range <- NULL + ## sendJSON(no.xrange) # 3 boxes info <- expect_traces(boxes.less, 3, "breaks-less") computed.labels <- sapply(info$traces, "[[", "name") - ##expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) + expect_identical(as.character(computed.labels), c("ctrl", "trt1", "trt2")) + ## For some reason plotly does not render the third box if range is + ## not NULL. + expect_identical(info$kwargs$layout$xaxis$range, NULL) - ## TODO: can we make this in plotly? + ## TODO: as of 20 Feb 2015 it is not possible to make this in + ## plotly. (no boxes but only 2 tick labels) }) boxes.labels <- boxes + @@ -188,6 +207,9 @@ test_that("scale(labels) changes trace names", { computed.labels <- sapply(info$traces, "[[", "name") expect_identical(as.character(computed.labels), c("Control", "Treatment 1", "Treatment 2")) + ## For some reason plotly does not render the third box if range is + ## not NULL. + expect_identical(info$kwargs$layout$xaxis$range, NULL) }) no.breaks <- boxes + scale_x_discrete(breaks=NULL) @@ -209,6 +231,10 @@ test_that("hide x ticks, lines, and labels", { ## parameters can be declared with 'ticks', 'tick0', 'dtick0' and ## other tick-related key in this axis object. ##expect_identical(x[["autotick"]], FALSE) #not necessary + + ## For some reason plotly does not render the third box if range is + ## not NULL. + expect_identical(info$kwargs$layout$xaxis$range, NULL) }) test_that("Hide X ticks and labels, but keep the gridlines", { From 2742345233bda8f171a2654b306f4ce2c9fc39a0 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Fri, 20 Feb 2015 13:25:16 -0500 Subject: [PATCH 39/49] test pass, 3 boxes render --- R/ggplotly.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index ffc706d5e7..250f9c08c8 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -328,7 +328,11 @@ gg2list <- function(p){ ax.list$range <- if(!is.null(sc$limits)){ sc$limits }else{ - ggranges[[1]][[s("%s.range")]] #TODO: facets! + if(misc$is.continuous[[xy]]){ + ggranges[[1]][[s("%s.range")]] #TODO: facets! + }else{ # for a discrete scale, range should be NULL. + NULL + } } if(is.character(sc$trans$name) && sc$trans$name == "reverse"){ ax.list$range <- sort(-ax.list$range, decreasing = TRUE) From 63b2b710237c3f7354b4e05ac6ab2613c9b907a8 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Fri, 20 Feb 2015 13:30:33 -0500 Subject: [PATCH 40/49] remove TO DISCUSS --- NEWS | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/NEWS b/NEWS index d9caf423ba..4a5c9c3b12 100644 --- a/NEWS +++ b/NEWS @@ -1,20 +1,3 @@ -TO DISCUSS - -for plotlys with panels, does list(xaxis="x1") mean "xaxis" and -list(xaxis="x2") mean "xaxis2" ? - -unevenly spaced ticks? -scale_y_continuous(breaks=c(4, 4.25, 4.5, 5, 6,8)) - -R-GSOC project? - -Visual testing table instead of GitHub diffs? -http://sugiyama-www.cs.titech.ac.jp/~toby/flags/ -https://sjp.co.nz/projects/grimport2/ -https://dl.dropboxusercontent.com/u/54315147/import/state-table.html - -Use RSelenium for headless browser testing? - 0.5.20 -- 9 February 2015. Add alpha transparency to fill conversion. From 7f8ad6ccdde11a7300ebf81c80172a6d54fee7e1 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Fri, 20 Feb 2015 13:32:42 -0500 Subject: [PATCH 41/49] replace #+ with # --- R/ggplotly.R | 162 +++++++++++++++++++++--------------------- R/marker_conversion.R | 14 ++-- R/plotly.R | 2 +- R/tools.R | 4 +- R/trace_generation.R | 94 ++++++++++++------------ 5 files changed, 138 insertions(+), 138 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index 250f9c08c8..fa223ca242 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -1,4 +1,4 @@ -## calc. the epoch +# calc. the epoch now <- Sys.time() the.epoch <- now - as.numeric(now) @@ -42,9 +42,9 @@ aesConverters <- list(linetype=function(lty) { direction=identity) markLegends <- - ## NOTE: Do we also want to split on size? - ## Legends based on sizes not implemented yet in Plotly - ## list(point=c("colour", "fill", "shape", "size"), + # NOTE: Do we also want to split on size? + # Legends based on sizes not implemented yet in Plotly + # list(point=c("colour", "fill", "shape", "size"), list(point=c("colour", "fill", "shape"), path=c("linetype", "size", "colour", "shape"), polygon=c("colour", "fill", "linetype", "size", "group"), @@ -67,10 +67,10 @@ gg2list <- function(p){ if(length(p$layers) == 0) { stop("No layers in plot") } - ## Always use identity size scale so that plot.ly gets the real - ## units for the size variables. + # Always use identity size scale so that plot.ly gets the real + # units for the size variables. p <- tryCatch({ - ## this will be an error for discrete variables. + # this will be an error for discrete variables. suppressMessages({ ggplot_build(p+scale_size_continuous()) p+scale_size_identity() @@ -81,10 +81,10 @@ gg2list <- function(p){ layout <- list() trace.list <- list() - ## Before building the ggplot, we would like to add aes(name) to - ## figure out what the object group is later. This also copies any - ## needed global aes/data values to each layer, so we do not have to - ## worry about combining global and layer-specific aes/data later. + # Before building the ggplot, we would like to add aes(name) to + # figure out what the object group is later. This also copies any + # needed global aes/data values to each layer, so we do not have to + # 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)] @@ -98,7 +98,7 @@ gg2list <- function(p){ } } - ## Extract data from built ggplots + # 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 @@ -119,20 +119,20 @@ gg2list <- function(p){ } for(i in seq_along(built$plot$layers)){ - ## This is the layer from the original ggplot object. + # 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. + # 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). + # 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")){ for(data.type in c("continuous", "date", "datetime", "discrete")){ @@ -151,7 +151,7 @@ gg2list <- function(p){ } } - ## scales are needed for legend ordering. + # scales are needed for legend ordering. for(sc in p$scales$scales){ a <- sc$aesthetics if(length(a) == 1){ @@ -164,21 +164,21 @@ gg2list <- function(p){ } reverse.aes <- names(misc$trans)[misc$trans=="reverse"] - ## get gglayout now because we need some of its info in layer2traces + # get gglayout now because we need some of its info in layer2traces gglayout <- built$panel$layout - ## invert rows so that plotly and ggplot2 show panels in the same order + # invert rows so that plotly and ggplot2 show panels in the same order gglayout$plotly.row <- max(gglayout$ROW) - gglayout$ROW + 1 - ## Add ROW and COL to df: needed to link axes to traces; keep df's - ## original ordering while merging. + # Add ROW and COL to df: needed to link axes to traces; keep df's + # original ordering while merging. df$order <- seq_len(nrow(df)) df <- merge(df, gglayout[, c("PANEL", "plotly.row", "COL")]) df <- df[order(df$order),] df$order <- NULL prestats <- built$prestats.data[[i]] - ## scale_reverse multiples x/y data by -1, so here we undo that so - ## that the actual data can be uploaded to plotly. + # scale_reverse multiples x/y data by -1, so here we undo that so + # that the actual data can be uploaded to plotly. replace.aes <- intersect(names(prestats), reverse.aes) for(a in replace.aes){ prestats[[a]] <- -1 * prestats[[a]] @@ -200,17 +200,17 @@ gg2list <- function(p){ misc$prestats.data$globsizemax <- ggsizemax } - ## This extracts essential info for this geom/layer. + # This extracts essential info for this geom/layer. traces <- layer2traces(L, df, misc) - ## Do we really need to coord_transform? - ## g$data <- ggplot2:::coord_transform(built$plot$coord, g$data, - ## built$panel$ranges[[1]]) + # Do we really need to coord_transform? + # g$data <- ggplot2:::coord_transform(built$plot$coord, g$data, + # built$panel$ranges[[1]]) trace.list <- c(trace.list, traces) } - ## for barcharts, verify that all traces have the same barmode; we don't - ## support different barmodes on the same plot yet. + # for barcharts, verify that all traces have the same barmode; we don't + # support different barmodes on the same plot yet. barmodes <- do.call(c, lapply(trace.list, function (x) x$barmode)) barmodes <- barmodes[!is.null(barmodes)] if (length(barmodes) > 0) { @@ -234,27 +234,27 @@ gg2list <- function(p){ } } - ## Export axis specification as a combination of breaks and labels, on - ## the relevant axis scale (i.e. so that it can be passed into d3 on the - ## x axis scale instead of on the grid 0-1 scale). This allows - ## transformations to be used out of the box, with no additional d3 - ## coding. + # Export axis specification as a combination of breaks and labels, on + # the relevant axis scale (i.e. so that it can be passed into d3 on the + # x axis scale instead of on the grid 0-1 scale). This allows + # transformations to be used out of the box, with no additional d3 + # coding. theme.pars <- ggplot2:::plot_theme(p) - ## Flip labels if coords are flipped - transform does not take care - ## of this. Do this BEFORE checking if it is blank or not, so that - ## individual axes can be hidden appropriately, e.g. #1. - ## ranges <- built$panel$ranges[[1]] - ## if("flip"%in%attr(built$plot$coordinates, "class")){ - ## temp <- built$plot$labels$x - ## built$plot$labels$x <- built$plot$labels$y - ## built$plot$labels$y <- temp - ## } + # Flip labels if coords are flipped - transform does not take care + # of this. Do this BEFORE checking if it is blank or not, so that + # individual axes can be hidden appropriately, e.g. #1. + # ranges <- built$panel$ranges[[1]] + # if("flip"%in%attr(built$plot$coordinates, "class")){ + # temp <- built$plot$labels$x + # built$plot$labels$x <- built$plot$labels$y + # built$plot$labels$y <- temp + # } e <- function(el.name){ ggplot2::calc_element(el.name, p$theme) } is.blank <- function(el.name, null.is.blank=FALSE) { - ## NULL shows ticks and hides borders + # NULL shows ticks and hides borders cls <- attr(e(el.name),"class") "element_blank" %in% cls || null.is.blank && is.null(cls) } @@ -265,7 +265,7 @@ gg2list <- function(p){ s <- function(tmp)sprintf(tmp, xy) ax.list$tickcolor <- toRGB(theme.pars$axis.ticks$colour) - ## When gridlines are dotted or dashed: + # When gridlines are dotted or dashed: grid <- theme.pars$panel.grid grid.major <- theme.pars$panel.grid.major if ((!is.null(grid$linetype) || !is.null(grid.major$linetype)) && @@ -278,9 +278,9 @@ gg2list <- function(p){ } ax.list$showgrid <- !is.blank(s("panel.grid.major.%s")) - ## These numeric length variables are not easily convertible. - ##ax.list$gridwidth <- as.numeric(theme.pars$panel.grid.major$size) - ##ax.list$ticklen <- as.numeric(theme.pars$axis.ticks.length) + # These numeric length variables are not easily convertible. + #ax.list$gridwidth <- as.numeric(theme.pars$panel.grid.major$size) + #ax.list$ticklen <- as.numeric(theme.pars$axis.ticks.length) theme2font <- function(text){ if(!is.null(text)){ @@ -306,7 +306,7 @@ gg2list <- function(p){ } ax.list$tickfont <- theme2font(tick.text) - ## Translate axes labels. + # Translate axes labels. scale.i <- which(p$scales$find(xy)) ax.list$title <- if(length(scale.i)){ sc <- p$scales$scales[[scale.i]] @@ -363,16 +363,16 @@ gg2list <- function(p){ ax.list$showline <- !is.blank("panel.border", TRUE) ax.list$linecolor <- toRGB(theme.pars$panel.border$colour) ax.list$linewidth <- theme.pars$panel.border$size - ## Some other params that we used in animint but we don't yet - ## translate to plotly: + # Some other params that we used in animint but we don't yet + # translate to plotly: !is.blank(s("axis.line.%s")) layout[[s("%saxis")]] <- ax.list } - ## copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each + # copy [x/y]axis to [x/y]axisN and set domain, range, etc. for each xaxis.title <- layout$xaxis$title yaxis.title <- layout$yaxis$title - inner.margin <- 0.01 ## between facets - outer.margin <- 0.05 ## to put titles outside of the plots + inner.margin <- 0.01 # between facets + outer.margin <- 0.05 # to put titles outside of the plots orig.xaxis <- layout$xaxis orig.yaxis <- layout$yaxis if (nrow(gglayout) > 1) @@ -416,7 +416,7 @@ gg2list <- function(p){ } } - ## add panel titles as annotations + # add panel titles as annotations annotations <- list() nann <- 1 make.label <- function(text, x, y, xanchor="auto", yanchor="auto", textangle=0) @@ -460,8 +460,8 @@ gg2list <- function(p){ } } - ## add empty traces everywhere so that the background shows even if there - ## is no data for a facet + # add empty traces everywhere so that the background shows even if there + # is no data for a facet for (r in seq_len(max(gglayout$ROW))) for (c in seq_len(max(gglayout$COL))) trace.list <- c(trace.list, list(list(xaxis=paste0("x", c), yaxis=paste0("y", r), showlegend=FALSE))) @@ -486,7 +486,7 @@ gg2list <- function(p){ } } - ## axes titles + # axes titles annotations[[nann]] <- make.label(xaxis.title, 0.5, -outer.margin, @@ -500,17 +500,17 @@ gg2list <- function(p){ layout$annotations <- annotations } - ## Remove legend if theme has no legend position + # Remove legend if theme has no legend position layout$showlegend <- !(theme.pars$legend.position=="none") - ## Main plot title. + # Main plot title. layout$title <- built$plot$labels$title - ## Background color. + # Background color. layout$plot_bgcolor <- toRGB(theme.pars$panel.background$fill) layout$paper_bgcolor <- toRGB(theme.pars$plot.background$fill) - ## Legend. + # Legend. layout$margin$r <- 10 if (exists("increase_margin_r")) { layout$margin$r <- 60 @@ -551,23 +551,23 @@ gg2list <- function(p){ layout$annotations <- annotations } - ## Family font for text + # Family font for text if (!is.null(theme.pars$text$family)) { layout$titlefont$family <- theme.pars$text$family layout$legend$font$family <- theme.pars$text$family } - ## Family font for title + # Family font for title if (!is.null(theme.pars$plot.title$family)) { layout$titlefont$family <- theme.pars$plot.title$family } - ## Family font for legend + # Family font for legend if (!is.null(theme.pars$legend.text$family)) { layout$legend$font$family <- theme.pars$legend.text$family } - ## Bold, italic and bold.italic face for text + # Bold, italic and bold.italic face for text text_face <- theme.pars$text$face if (!is.null(text_face)) { if (text_face=="bold") { @@ -585,7 +585,7 @@ gg2list <- function(p){ } } - ## Bold, italic and bold.italic face for title + # Bold, italic and bold.italic face for title title_face <- theme.pars$plot.title$face if (!is.null(title_face)) { if (title_face=="bold") { @@ -597,7 +597,7 @@ gg2list <- function(p){ } } - ## Bold, italic, and bold.italic face for axis title + # Bold, italic, and bold.italic face for axis title title_face <- list(theme.pars$axis.title.y$face, theme.pars$axis.title.x$face) sub_elem <- c("yaxis", "xaxis") @@ -646,8 +646,8 @@ gg2list <- function(p){ while(length(not.merged)){ tr <- not.merged[[1]] not.merged <- not.merged[-1] - ## Are there any traces that have not yet been merged, and can be - ## merged with tr? + # Are there any traces that have not yet been merged, and can be + # merged with tr? can.merge <- rep(FALSE, l=length(not.merged)) for(other.i in seq_along(not.merged)){ other <- not.merged[[other.i]] @@ -682,8 +682,8 @@ gg2list <- function(p){ merged.traces[[length(merged.traces)+1]] <- tr } - ## Put the traces in correct order, according to any manually - ## specified scales. + # Put the traces in correct order, according to any manually + # specified scales. trace.order <- unlist(trace.order.list) ordered.traces <- if(length(trace.order)){ trace.order.score <- seq_along(trace.order) @@ -695,7 +695,7 @@ gg2list <- function(p){ merged.traces } - ## Translate scale(labels) to trace name. + # Translate scale(labels) to trace name. named.traces <- ordered.traces for(trace.i in seq_along(named.traces)){ tr.name <- named.traces[[trace.i]][["name"]] @@ -705,8 +705,8 @@ gg2list <- function(p){ } } - ## If coord_flip is defined, then flip x/y in each trace, and in - ## each axis. + # If coord_flip is defined, then flip x/y in each trace, and in + # each axis. flipped.traces <- named.traces flipped.layout <- layout if("flip" %in% attr(built$plot$coordinates, "class")){ diff --git a/R/marker_conversion.R b/R/marker_conversion.R index 9a50be51be..bc5af2f073 100644 --- a/R/marker_conversion.R +++ b/R/marker_conversion.R @@ -1,10 +1,10 @@ -##' Convert ggplot params to plotly. -##' @param params named list ggplot names -> values. -##' @param aesVec vector mapping ggplot names to plotly names. -##' @param defaults named list ggplot names -> values. -##' @export -##' @return named list. -##' @author Toby Dylan Hocking +#' Convert ggplot params to plotly. +#' @param params named list ggplot names -> values. +#' @param aesVec vector mapping ggplot names to plotly names. +#' @param defaults named list ggplot names -> values. +#' @export +#' @return named list. +#' @author Toby Dylan Hocking paramORdefault <- function(params, aesVec, defaults) { marker <- list() for (ggplot.name in names(aesVec)) { diff --git a/R/plotly.R b/R/plotly.R index 7e86ed3db3..50c73631b8 100644 --- a/R/plotly.R +++ b/R/plotly.R @@ -230,7 +230,7 @@ For more help, see https://plot.ly/R or contact .") cat(kwargs) return(pub$makecall(args = args, kwargs = kwargs, origin = "style")) } - ## wrap up the object + # wrap up the object pub <- list2env(pub) class(pub) <- "PlotlyClass" return(pub) diff --git a/R/tools.R b/R/tools.R index 5a97fe2564..6330659b98 100644 --- a/R/tools.R +++ b/R/tools.R @@ -20,7 +20,7 @@ ensure_file_exist <- function(abspath) { } -### Credentials Tools ### +# Credentials Tools ### #' Read Plotly credentials file (which is a JSON) #' @param args Character vector of keys you are looking up @@ -88,7 +88,7 @@ set_credentials_file <- function(username="", api_key="", } -### Config Tools ### +# Config Tools ### #' Read Plotly config file (which is a JSON) and create one if nonexistent #' @param args Character vector of keys you are looking up diff --git a/R/trace_generation.R b/R/trace_generation.R index abeb0c63ba..f3f6d1f418 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(misc$prestats.data)) - ## needed for when group, etc. is an expression. + # 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) if (g$geom == "violin") { @@ -22,7 +22,7 @@ layer2traces <- function(l, d, misc) { probability density estimation is not supported in Plotly yet.") } - ## Barmode and bargap + # Barmode and bargap barmode <- "group" if (g$geom == "bar" || g$geom == "histogram") { if (l$stat$objname == "bin") { @@ -44,8 +44,8 @@ layer2traces <- function(l, d, misc) { bargap <- 0 } - ## For non-numeric data on the axes, we should take the values from - ## the original data. + # For non-numeric data on the axes, we should take the values from + # the original data. for (axis.name in c("x", "y")) { if(!misc$is.continuous[[axis.name]]) { aes.names <- paste0(axis.name, c("", "end", "min", "max")) @@ -68,20 +68,20 @@ layer2traces <- function(l, d, misc) { # For some plot types, we overwrite `data` with `prestats.data`. pdata.vec <- g$prestats.data[[a]] if (inherits(data.vec, "POSIXt")) { - ## Re-create dates from nb seconds + # Re-create dates from nb seconds data.vec <- try(strftime(as.POSIXlt(g$data[[a]], origin=the.epoch), "%Y-%m-%d %H:%M:%S"), silent=TRUE) pdata.vec <- strftime(as.POSIXlt(g$prestats.data[[a]], origin=the.epoch), "%Y-%m-%d %H:%M:%S") } else if (inherits(data.vec, "Date")) { - ## Re-create dates from nb days + # Re-create dates from nb days data.vec <- try(strftime(as.Date(g$data[[a]], origin=the.epoch), "%Y-%m-%d %H:%M:%S"), silent=TRUE) 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. + # 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]]), ] @@ -97,60 +97,60 @@ layer2traces <- function(l, d, misc) { } } } - ## use un-named parameters so that they will not be exported - ## to JSON as a named object, since that causes problems with - ## e.g. colour. + # use un-named parameters so that they will not be exported + # to JSON as a named object, since that causes problems with + # e.g. colour. g$params <- c(l$geom_params, l$stat_params) - ## non-ggplot2 params like name are useful for plot.ly and ggplot2 - ## places them into stat_params. + # non-ggplot2 params like name are useful for plot.ly and ggplot2 + # places them into stat_params. for(p.name in names(g$params)){ - ## c("foo") is translated to "foo" in JSON, so instead we use - ## list("foo") which becomes ["foo"]. However we need to make sure - ## that the list does not have names since list(bar="foo") becomes - ## {"bar":"foo"} + # c("foo") is translated to "foo" in JSON, so instead we use + # list("foo") which becomes ["foo"]. However we need to make sure + # that the list does not have names since list(bar="foo") becomes + # {"bar":"foo"} names(g$params[[p.name]]) <- NULL } - ## Convert complex ggplot2 geoms so that they are treated as special - ## cases of basic geoms. In ggplot2, this processing is done in the - ## draw method of the geoms. + # Convert complex ggplot2 geoms so that they are treated as special + # cases of basic geoms. In ggplot2, this processing is done in the + # draw method of the geoms. - ## Every plotly trace has one of these types - ## type=scatter,bar,box,histogramx,histogram2d,heatmap + # Every plotly trace has one of these types + # type=scatter,bar,box,histogramx,histogram2d,heatmap - ## for type=scatter, you can define - ## mode=none,markers,lines,lines+markers where "lines" is the - ## default for 20 or more points, "lines+markers" is the default for - ## <20 points. "none" is useful mainly if fill is used to make area - ## plots with no lines. + # for type=scatter, you can define + # mode=none,markers,lines,lines+markers where "lines" is the + # default for 20 or more points, "lines+markers" is the default for + # <20 points. "none" is useful mainly if fill is used to make area + # plots with no lines. - ## marker=list(size,line,color="rgb(54,144,192)",opacity,symbol) + # marker=list(size,line,color="rgb(54,144,192)",opacity,symbol) - ## symbol=circle,square,diamond,cross,x, - ## triangle-up,triangle-down,triangle-left,triangle-right + # 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. + # First convert to a "basic" geom, e.g. segments become lines. convert <- toBasic[[g$geom]] basic <- if(is.null(convert)){ g }else{ convert(g) } - ## Then split on visual characteristics that will get different - ## legend entries. + # Then split on visual characteristics that will get different + # legend entries. data.list <- if (basic$geom %in% names(markLegends)) { mark.names <- markLegends[[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. + # However, continuously colored points are an exception: they do + # not need a legend entry, and they can be efficiently rendered + # using just 1 trace. - ## Maybe it is nice to show a legend for continuous points? - ## if(basic$geom == "point"){ - ## to.erase <- names(misc$is.continuous)[misc$is.continuous] - ## mark.names <- mark.names[!mark.names %in% to.erase] - ## } + # Maybe it is nice to show a legend for continuous points? + # if(basic$geom == "point"){ + # to.erase <- names(misc$is.continuous)[misc$is.continuous] + # mark.names <- mark.names[!mark.names %in% to.erase] + # } name.names <- sprintf("%s.name", mark.names) - ## split on 'PANEL' to support facets + # split on 'PANEL' to support facets is.split <- names(basic$data) %in% c(name.names, "PANEL") if(any(is.split)){ data.i <- which(is.split) @@ -182,7 +182,7 @@ layer2traces <- function(l, d, misc) { }) } - ## case of no legend, if either of the two ifs above failed. + # case of no legend, if either of the two ifs above failed. if(is.null(data.list)){ data.list <- structure(list(list(data=basic$data, params=basic$params)), names=basic$params$name) @@ -271,8 +271,8 @@ layer2traces <- function(l, d, misc) { # Preprocess data and params. toBasic <- list( segment=function(g){ - ## Every row is one segment, we convert to a line with several - ## groups which can be efficiently drawn by adding NA rows. + # Every row is one segment, we convert to a line with several + # groups which can be efficiently drawn by adding NA rows. g$data$group <- 1:nrow(g$data) used <- c("x", "y", "xend", "yend") others <- g$data[!names(g$data) %in% used] @@ -384,8 +384,8 @@ group2NA <- function(g, geom) { g } -### Make a trace for geom_errorbar -> error_y or geom_errorbarh -> -### error_x. +# Make a trace for geom_errorbar -> error_y or geom_errorbarh -> +# error_x. make.errorbar <- function(data, params, xy){ tr <- list(x=data$x, @@ -447,7 +447,7 @@ geom2trace <- list( if("size" %in% names(data)){ L$text <- paste("size:", data$size) L$marker$sizeref <- default.marker.sizeref - ## Make sure sizes are passed as a list even when there is only one element. + # Make sure sizes are passed as a list even when there is only one element. s <- data$size marker.size <- 5 * (s - params$sizemin)/(params$sizemax - params$sizemin) + 0.25 marker.size <- marker.size * marker.size.mult From 5cf9a3c46b49dbd28e0f081794159d932733bbec Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Fri, 20 Feb 2015 13:36:37 -0500 Subject: [PATCH 42/49] delete print.trace and trace class --- R/ggplotly.R | 8 ++++---- R/print.R | 4 ---- R/trace_generation.R | 1 - 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/R/ggplotly.R b/R/ggplotly.R index fa223ca242..3b2cf9e378 100644 --- a/R/ggplotly.R +++ b/R/ggplotly.R @@ -180,7 +180,7 @@ gg2list <- function(p){ # scale_reverse multiples x/y data by -1, so here we undo that so # that the actual data can be uploaded to plotly. replace.aes <- intersect(names(prestats), reverse.aes) - for(a in replace.aes){ + for (a in replace.aes) { prestats[[a]] <- -1 * prestats[[a]] } misc$prestats.data <- @@ -312,12 +312,12 @@ gg2list <- function(p){ sc <- p$scales$scales[[scale.i]] trace.order.list[[xy]] <- sc$limits trace.name.map[sc$breaks] <- sc$labels - if(is.null(sc$breaks)){ + if (is.null(sc$breaks)) { ax.list$showticklabels <- FALSE ax.list$showgrid <- FALSE ax.list$ticks <- "" } - if(is.numeric(sc$breaks)){ + if (is.numeric(sc$breaks)) { dticks <- diff(sc$breaks) dt <- dticks[1] if(all(dticks == dt)){ @@ -631,7 +631,7 @@ gg2list <- function(p){ layout$legend$bgcolor <- toRGB(s(rect_fill)) } - if(length(trace.list) == 0) { + if (length(trace.list) == 0) { stop("No exportable traces") } diff --git a/R/print.R b/R/print.R index fefdb661ba..e69de29bb2 100644 --- a/R/print.R +++ b/R/print.R @@ -1,4 +0,0 @@ -print.trace <- function(x, ...){ - str(x) - invisible(x) -} diff --git a/R/trace_generation.R b/R/trace_generation.R index f3f6d1f418..f18826059a 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -203,7 +203,6 @@ layer2traces <- function(l, d, misc) { data.params <- data.list[[data.i]] data.params$params$stat.type <- l$stat$objname tr <- do.call(getTrace, data.params) - class(tr) <- "trace" for (v.name in c("x", "y")) { vals <- tr[[v.name]] if (length(vals) > 0 && is.na(vals[length(vals)])) { From c47b8026a456a0ae382011db8ac4cce074fe5b1d Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Fri, 20 Feb 2015 13:41:04 -0500 Subject: [PATCH 43/49] fix marianne minor comments --- R/trace_generation.R | 2 +- tests/testthat/test-ggplot-area.R | 1 - tests/testthat/test-ggplot-errorbar-horizontal.R | 2 +- tests/testthat/test-mean-error-bars.R | 2 -- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/R/trace_generation.R b/R/trace_generation.R index f18826059a..dc8a62d741 100644 --- a/R/trace_generation.R +++ b/R/trace_generation.R @@ -47,7 +47,7 @@ layer2traces <- function(l, d, misc) { # For non-numeric data on the axes, we should take the values from # the original data. for (axis.name in c("x", "y")) { - if(!misc$is.continuous[[axis.name]]) { + if (!misc$is.continuous[[axis.name]]) { aes.names <- paste0(axis.name, c("", "end", "min", "max")) aes.used <- aes.names[aes.names %in% names(g$aes)] for(a in aes.used) { diff --git a/tests/testthat/test-ggplot-area.R b/tests/testthat/test-ggplot-area.R index 7f020a1d33..5564075030 100644 --- a/tests/testthat/test-ggplot-area.R +++ b/tests/testthat/test-ggplot-area.R @@ -4,7 +4,6 @@ huron <- data.frame(year=1875:1972, level=as.vector(LakeHuron)) huron$decade <- plyr::round_any(huron$year, 10, floor) ar <- ggplot(huron) + - theme_grey()+ geom_area(aes(x=year, y=level)) L <- gg2list(ar) diff --git a/tests/testthat/test-ggplot-errorbar-horizontal.R b/tests/testthat/test-ggplot-errorbar-horizontal.R index b9cccb9909..917fc2d38b 100644 --- a/tests/testthat/test-ggplot-errorbar-horizontal.R +++ b/tests/testthat/test-ggplot-errorbar-horizontal.R @@ -21,7 +21,7 @@ test_that("geom_errorbarh gives horizontal errorbars", { expect_identical(L[[2]]$marker$color, L[[2]]$error_x$color) # Expect given errorbar values expect_equal(L[[1]]$error_x$array, c(0.1, 0.3)) - expect_equal(L[[1]]$error_x$symmetric, TRUE) + expect_true(L[[1]]$error_x$symmetric) save_outputs(g, "errorbar-horizontal") }) diff --git a/tests/testthat/test-mean-error-bars.R b/tests/testthat/test-mean-error-bars.R index 60e4db53dc..45dff776ae 100644 --- a/tests/testthat/test-mean-error-bars.R +++ b/tests/testthat/test-mean-error-bars.R @@ -1,7 +1,5 @@ context("means and error bars") -library(ggplot2) - one.line.df <- data.frame( x = c(1, 2, 3, 4), From 781f6bf59372c31e934915bb10138e53a3ff5b86 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 23 Feb 2015 17:22:47 -0500 Subject: [PATCH 44/49] add space --- tests/testthat/test-ggplot-theme.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-ggplot-theme.R b/tests/testthat/test-ggplot-theme.R index dc334852b9..53968b1d41 100644 --- a/tests/testthat/test-ggplot-theme.R +++ b/tests/testthat/test-ggplot-theme.R @@ -1,7 +1,7 @@ context("ggplot themes") iris.base <- ggplot(iris) + - geom_point(aes(Petal.Width, Sepal.Width))+ + geom_point(aes(Petal.Width, Sepal.Width)) + theme_grey() test_that("background translated correctly",{ From 80536ed4e8d1e5ae7325f8d462fdbd1aa204dc6f Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 23 Feb 2015 17:50:36 -0500 Subject: [PATCH 45/49] delete theme_grey --- tests/testthat/test-ggplot-area.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-ggplot-area.R b/tests/testthat/test-ggplot-area.R index 5564075030..f5e00f5fe7 100644 --- a/tests/testthat/test-ggplot-area.R +++ b/tests/testthat/test-ggplot-area.R @@ -19,7 +19,6 @@ save_outputs(ar, "area") # Test alpha transparency in fill color gg <- ggplot(huron) + - theme_grey()+ geom_area(aes(x=year, y=level), alpha=0.4) L <- gg2list(gg) From 4af13aec9326600e8ab3330d3cee2ea3880f7786 Mon Sep 17 00:00:00 2001 From: Toby Dylan Hocking Date: Mon, 23 Feb 2015 17:56:01 -0500 Subject: [PATCH 46/49] update NEWS/DESCRIPTION --- DESCRIPTION | 2 +- NEWS | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index b1f9c1db81..c6ec3ebd62 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: plotly Type: Package Title: Interactive, publication-quality graphs online. -Version: 0.5.20 +Version: 0.5.21 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 4a5c9c3b12..c5d1e6d708 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +0.5.21 -- 23 February 2015. + +Fixes for error bars and tick marks. + 0.5.20 -- 9 February 2015. Add alpha transparency to fill conversion. From cf9d5a4de60ebd766f0ba660f7e4a0a510e5bd8d Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Mon, 23 Feb 2015 18:35:42 -0500 Subject: [PATCH 47/49] Remove funny file --- R/print.R | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 R/print.R diff --git a/R/print.R b/R/print.R deleted file mode 100644 index e69de29bb2..0000000000 From 91a5a45841cb0c6da3d7312cad8a79dbadf9f8e1 Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Mon, 23 Feb 2015 18:39:28 -0500 Subject: [PATCH 48/49] Revert testthat.R file to latest commit on master --- tests/testthat.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat.R b/tests/testthat.R index 270cd979e4..c5273050e9 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -3,4 +3,3 @@ save_outputs <- function(gg, name, ignore_ggplot=FALSE) { print(paste("running", name)) } test_check("plotly") - From 3337d85de993983b0aba159af11f2f3e9e3a252e Mon Sep 17 00:00:00 2001 From: Marianne Corvellec Date: Mon, 23 Feb 2015 18:40:40 -0500 Subject: [PATCH 49/49] Revert unrelated test file to latest commit on master --- tests/testthat/test-ggplot-area.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-ggplot-area.R b/tests/testthat/test-ggplot-area.R index f5e00f5fe7..49ec966aec 100644 --- a/tests/testthat/test-ggplot-area.R +++ b/tests/testthat/test-ggplot-area.R @@ -3,8 +3,7 @@ context("Area") huron <- data.frame(year=1875:1972, level=as.vector(LakeHuron)) huron$decade <- plyr::round_any(huron$year, 10, floor) -ar <- ggplot(huron) + - geom_area(aes(x=year, y=level)) +ar <- ggplot(huron) + geom_area(aes(x=year, y=level)) L <- gg2list(ar) test_that("sanity check for geom_area", { @@ -18,8 +17,7 @@ test_that("sanity check for geom_area", { save_outputs(ar, "area") # Test alpha transparency in fill color -gg <- ggplot(huron) + - geom_area(aes(x=year, y=level), alpha=0.4) +gg <- ggplot(huron) + geom_area(aes(x=year, y=level), alpha=0.4) L <- gg2list(gg) test_that("transparency alpha in geom_area is converted", {