eliocamp / ggnewscale

Multiple Fill, Color and Other Scales in `ggplot2`
https://eliocamp.github.io/ggnewscale/
GNU General Public License v3.0
406 stars 18 forks source link

Multiple new_scale_fill doesn't work. #45

Closed zemega closed 4 months ago

zemega commented 2 years ago

Hello. I ran into a problem with using multiple new_scale_fill. It doesn't seems to work for me when I used more than two. Please see the example belows.

plot_data <-
  data.table(X = c(rnorm(300, 5, 2), rnorm(300,  5, 2),rnorm(300,  5, 2),rnorm(300,  5, 2)),
             Y = c(rnorm(300, 5, 1), rnorm(300, 10, 1),rnorm(300, 15, 1),rnorm(300, 20, 1)),
             Label = c(rep('A', 300), rep('B', 300), rep('C', 300), rep('D', 300)))

Using fill under ggplot. There's 4 groups.

ggplot(data=plot_data, aes(x=X, y=Y,fill=Label)) + 
  geom_bin2d() 

image

Using a single new_scale_fill

ggplot() +
  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="A"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "red", high = "white")

image

Using two new_scale_fill

ggplot() +
  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="A"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "red", high = "white")+

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="B"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "green", high = "white") 

image

Using three new_scale_fill

ggplot() +
  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="A"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "red", high = "white")+

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="B"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "green", high = "white") +

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="C"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "blue", high = "white") 

image Using four new_scale_fill

ggplot() +
  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="A"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "red", high = "white")+

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="B"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "green", high = "white") +

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="C"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "blue", high = "white") +

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="D"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "purple", high = "white")

image

I'm taking this approach because I want to use new_scale_fill in a for loop situation. Such as below.

Main=ggplot()

for(count in 1:4){
  Main =Main + new_scale_fill() +
  geom_bin2d(data=plot_data[Label==Group_Information[count]],aes(x=X, y=Y))+
  scale_fill_continuous(low = Colours[count], high = "white")
}
Main
#The output is the same as the figure above.

As you can see, when using multiple new_scale_fill, the output was not what I was expecting, something similar to the first figure. I tried going through stackoverflow.com, but most example are only using two new_scale_fill.

sessionInfo()
R version 4.0.2 (2020-06-22)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19043)

Matrix products: default

locale:
[1] LC_COLLATE=English_Malaysia.1252  LC_CTYPE=English_Malaysia.1252    LC_MONETARY=English_Malaysia.1252
[4] LC_NUMERIC=C                      LC_TIME=English_Malaysia.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggnewscale_0.4.7  ggplot2_3.3.5     data.table_1.14.0

loaded via a namespace (and not attached):
 [1] knitr_1.37       magrittr_2.0.1   tidyselect_1.1.1 munsell_0.5.0    colorspace_2.0-1 R6_2.5.0        
 [7] rlang_0.4.11     fansi_0.5.0      dplyr_1.0.6      tools_4.0.2      grid_4.0.2       gtable_0.3.0    
[13] xfun_0.29        utf8_1.2.1       DBI_1.1.1        withr_2.4.2      ellipsis_0.3.2   digest_0.6.27   
[19] assertthat_0.2.1 tibble_3.1.2     lifecycle_1.0.0  crayon_1.4.1     farver_2.1.0     purrr_0.3.4     
[25] vctrs_0.3.8      glue_1.4.2       labeling_0.4.2   compiler_4.0.2   pillar_1.6.1     generics_0.1.0  
[31] scales_1.1.1     pkgconfig_2.0.3 
eliocamp commented 2 years ago

Thanks for the report. It should work with as many as you'd like. This seems like a regression. Running your examples in previous versions, the last version without this bug is 0.4.3. So as a workaround you can install that version with:

devtools::install_github("eliocamp/ggnewscale@v0.4.3")

Output:

library(data.table)
library(ggplot2)
library(ggnewscale)
packageVersion("ggnewscale")
#> [1] '0.4.3'

plot_data <-
  data.table(X = c(rnorm(300, 5, 2), rnorm(300,  5, 2),rnorm(300,  5, 2),rnorm(300,  5, 2)),
             Y = c(rnorm(300, 5, 1), rnorm(300, 10, 1),rnorm(300, 15, 1),rnorm(300, 20, 1)),
             Label = c(rep('A', 300), rep('B', 300), rep('C', 300), rep('D', 300)))

ggplot() +
  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="A"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "red", high = "white")+

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="B"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "green", high = "white") +

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="C"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "blue", high = "white") +

  new_scale_fill() +
  geom_bin2d(data=plot_data[Label=="D"],aes(x=X, y=Y))+
  scale_fill_continuous(low = "purple", high = "white")

Created on 2022-04-04 by the reprex package (v2.0.1)

zemega commented 2 years ago

Just installed version 0.4.3, and it runs as intended. Thank you for your help.

eliocamp commented 1 year ago

Update to this bug: It seems to be caused by default mapping. Summary:

library(data.table)
library(ggplot2)
library(ggnewscale)
packageVersion("ggnewscale")
#> [1] '0.4.8.9000'
set.seed(42)

plot_data <-
  data.table(X = c(rnorm(300, 5, 2), rnorm(300,  5, 2),rnorm(300,  5, 2),rnorm(300,  5, 2)),
             Y = c(rnorm(300, 5, 1), rnorm(300, 10, 1),rnorm(300, 15, 1),rnorm(300, 20, 1)),
             Label = c(rep('A', 300), rep('B', 300), rep('C', 300), rep('D', 300)))

Adding one extra scale works correctly

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"]) +
  scale_fill_continuous("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="B"]) +
  scale_fill_continuous("2", low = "green", high = "white")

But adding an extra one breaks everything

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"]) +
  scale_fill_continuous("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="B"]) +
  scale_fill_continuous("2", low = "green", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="C"]) +
  scale_fill_continuous("3", low = "blue", high = "white") 

This still works with explicit mapping

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"], aes(fill = after_stat(count))) +
  scale_fill_continuous("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="B"], aes(fill = after_stat(count))) +
  scale_fill_continuous("2", low = "green", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="C"], aes(fill = after_stat(count))) +
  scale_fill_continuous("3", low = "blue", high = "white") 

Created on 2023-05-08 with reprex v2.0.2

eliocamp commented 1 year ago

Well, this took forever to fix, but it now is. The new version is on its way to CRAN.

jan-glx commented 1 year ago

Not fully, I am afraid... There still is an issue e.g. when the third scale is discrete while 1 & 2 are continuous:

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"], aes(fill = after_stat(count))) +
  scale_fill_gradient("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="B"], aes(fill = after_stat(count))) +
  scale_fill_discrete() +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="C"], aes(fill = after_stat(as.character(round(count))))) +
  scale_fill_discrete()

fails with Error: Continuous value supplied to discrete scale. (It does work as expected when commenting out the middle part). This is on latest master (‘0.4.9’).

eliocamp commented 12 months ago

@jan-glx Your example fails in part because the second layer indeed uses a discrete scale for a continuous value. However, fixing that error shows that there's still an issue in ggnewscale.

library(data.table)
library(ggplot2)
library(ggnewscale)

plot_data <-
  data.table(X = c(rnorm(300, 5, 2), rnorm(300,  5, 2),rnorm(300,  5, 2),rnorm(300,  5, 2)),
             Y = c(rnorm(300, 5, 1), rnorm(300, 10, 1),rnorm(300, 15, 1),rnorm(300, 20, 1)),
             Label = c(rep('A', 300), rep('B', 300), rep('C', 300), rep('D', 300)))

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"], aes(fill = after_stat(count))) +
  scale_fill_gradient("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="B"], aes(fill = after_stat(as.character(count)))) +
  scale_fill_discrete() +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="C"], aes(fill = after_stat(as.character(round(count))))) +
  scale_fill_discrete()
#> Error in `scale_fill_discrete()`:
#> ! Continuous values supplied to discrete scale
#> ℹ Example values: 1, 1, 1, 1, and 2
#> Backtrace:
#>      ▆
#>   1. ├─base::tryCatch(...)
#>   2. │ └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   3. │   ├─base (local) tryCatchOne(...)
#>   4. │   │ └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   5. │   └─base (local) tryCatchList(expr, names[-nh], parentenv, handlers[-nh])
#>   6. │     └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>   7. │       └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>   8. ├─base::withCallingHandlers(...)
#>   9. ├─base::saveRDS(...)
#>  10. ├─base::do.call(...)
#>  11. ├─base (local) `<fn>`(...)
#>  12. └─global `<fn>`(input = base::quote("flat-ram_reprex.R"))
#>  13.   └─rmarkdown::render(input, quiet = TRUE, envir = globalenv(), encoding = "UTF-8")
#>  14.     └─knitr::knit(knit_input, knit_output, envir = envir, quiet = quiet)
#>  15.       └─knitr:::process_file(text, output)
#>  16.         ├─base::withCallingHandlers(...)
#>  17.         ├─base::withCallingHandlers(...)
#>  18.         ├─knitr:::process_group(group)
#>  19.         └─knitr:::process_group.block(group)
#>  20.           └─knitr:::call_block(x)
#>  21.             └─knitr:::block_exec(params)
#>  22.               └─knitr:::eng_r(options)
#>  23.                 ├─knitr:::in_input_dir(...)
#>  24.                 │ └─knitr:::in_dir(input_dir(), expr)
#>  25.                 └─knitr (local) evaluate(...)
#>  26.                   └─evaluate::evaluate(...)
#>  27.                     └─evaluate:::evaluate_call(...)
#>  28.                       ├─evaluate (local) handle(...)
#>  29.                       │ └─base::try(f, silent = TRUE)
#>  30.                       │   └─base::tryCatch(...)
#>  31.                       │     └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>  32.                       │       └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  33.                       │         └─base (local) doTryCatch(return(expr), name, parentenv, handler)
#>  34.                       ├─base::withCallingHandlers(...)
#>  35.                       ├─base::withVisible(value_fun(ev$value, ev$visible))
#>  36.                       └─knitr (local) value_fun(ev$value, ev$visible)
#>  37.                         └─knitr (local) fun(x, options = options)
#>  38.                           ├─base::withVisible(knit_print(x, ...))
#>  39.                           ├─knitr::knit_print(x, ...)
#>  40.                           └─knitr:::knit_print.default(x, ...)
#>  41.                             └─evaluate (local) normal_print(x)
#>  42.                               ├─base::print(x)
#>  43.                               └─ggplot2:::print.ggplot(x)
#>  44.                                 ├─ggplot2::ggplot_build(x)
#>  45.                                 └─ggplot2:::ggplot_build.ggplot(x)
#>  46.                                   └─base::lapply(data, npscales$train_df)
#>  47.                                     └─ggplot2 (local) FUN(X[[i]], ...)
#>  48.                                       └─ggplot2 (local) train_df(..., self = self)
#>  49.                                         └─base::lapply(self$scales, function(scale) scale$train_df(df = df))
#>  50.                                           └─ggplot2 (local) FUN(X[[i]], ...)
#>  51.                                             └─scale$train_df(df = df)
#>  52.                                               └─ggplot2 (local) train_df(..., self = self)
#>  53.                                                 └─self$train(df[[aesthetic]])
#>  54.                                                   └─ggplot2 (local) train(..., self = self)
#>  55.                                                     └─cli::cli_abort(...)
#>  56.                                                       └─rlang::abort(...)

The issue will be resolved in a new version which refactors the code and avoids a lot of the annoying logic that created this issue in the first place.

jan-glx commented 12 months ago

Ooops, based on the text I wrote, I guess I meant the first scale_fill_discrete to be a continuous scale in which case it also fails with the same error message. Great to hear that you are working on it!

HRodenhizer commented 6 months ago

I'm having a similar issue which may already be solved by the planned changes, but the error message is different and I just wanted to make sure that it is considered. I run into this issue when the third new scale is provided as a string within the aesthetics (to provide the legend text for a single fill color, but I'm not sure what to call this).

library(data.table)
library(ggplot2)
library(ggnewscale)

plot_data <-
  data.table(X = c(rnorm(300, 5, 2), rnorm(300,  5, 2),rnorm(300,  5, 2),rnorm(300,  5, 2)),
             Y = c(rnorm(300, 5, 1), rnorm(300, 10, 1),rnorm(300, 15, 1),rnorm(300, 20, 1)),
             Label = c(rep('A', 300), rep('B', 300), rep('C', 300), rep('D', 300)))

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"], aes(fill = after_stat(count))) +
  scale_fill_gradient("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="B"], aes(fill = after_stat(as.character(count)))) +
  scale_fill_discrete() +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="C"], aes(fill = 'C')) +
  scale_fill_manual(values = c('magenta'))
Error in scale$guide == "none" :
comparison (==) is possible only for atomic and list types

For comparison, the following works as expected:

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"], aes(fill = after_stat(count))) +
  scale_fill_gradient("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="C"], aes(fill = 'C')) +
  scale_fill_manual(values = c('magenta'))
Pacomito commented 6 months ago

Hello, I am having the same error as above, using ggnewscale as part of the FlowSOM package, which I think might be related to the same issue :

PlotStars(flowsom_model, backgroundValues = flowsom_model$metaclustering)

Erreur dans scale$guide == "none" :
comparison (==) is possible only for atomic and list types

12. isTRUE(scale$guide == "none")
11. FUN(X[[i]], ...)
10. lapply(scales, bump_aes_scale, new_aes = new_aes)
9. bump_aes_scales(plot$scales$scales, new_aes = object)
8. ggplot_add.new_aes(object, p, objectname)
7. ggplot_add(object, p, objectname)
6. add_ggplot(e1, e2, e2name)
5. `+.gg`(p, ggnewscale::new_scale(type))
4. AddScale(p = p, values = arcs$Markers, colors = colorPalette,
showLegend = showLegend)
3. AddStarsPies(p, starValues, colorPalette, showLegend = FALSE)
2. AddStars(p = p, fsom = fsom, markers = channels, colorPalette = colorPalette)
1. plotStars.(flowsom_model, backgroundValues = flowsom_model$metaclustering)
eliocamp commented 4 months ago

Hi! Thanks for the reports.

@HRodenhizer example works correctly in the current develompent version of ggnewscale. Note that ggplot2 changed their internals significantly so now ggnewscale requires ggplot2 3.5.0.

library(data.table)
#> Warning: package 'data.table' was built under R version 4.4.1
library(ggplot2)
library(ggnewscale)

plot_data <-
  data.table(X = c(rnorm(300, 5, 2), rnorm(300,  5, 2),rnorm(300,  5, 2),rnorm(300,  5, 2)),
             Y = c(rnorm(300, 5, 1), rnorm(300, 10, 1),rnorm(300, 15, 1),rnorm(300, 20, 1)),
             Label = c(rep('A', 300), rep('B', 300), rep('C', 300), rep('D', 300)))

ggplot(mapping = aes(x = X, y = Y)) +
  geom_bin2d(data = plot_data[Label=="A"], aes(fill = after_stat(count))) +
  scale_fill_gradient("1", low = "red", high = "white") +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="B"], aes(fill = after_stat(as.character(count)))) +
  scale_fill_discrete() +

  new_scale_fill() +
  geom_bin2d(data = plot_data[Label=="C"], aes(fill = 'C')) +
  scale_fill_manual(values = c('magenta'))

Created on 2024-07-18 with reprex v2.1.0