aphalo / ggpmisc

R package ggpmisc is an extension to ggplot2 and the Grammar of Graphics
https://docs.r4photobiology.info/ggpmisc
94 stars 6 forks source link

Conditional use of "geom" in stat_correlation #28

Closed erikagucciardo closed 1 year ago

erikagucciardo commented 1 year ago

Hi,

Thank you for a nice package! Is there a way to determine the use of geom (either "text" or "label") based on whether the P value is significant or not? I tried to do so but it didn't work.

x <- (1:100) / 10
y <- x + rnorm(length(x))
my.data <- data.frame(x = x,
                      y = y,
                      y.desc = - y,
                      group = c("A", "B"))

ggplot(my.data, aes(x, y)) +
  geom_point() +
  stat_correlation(geom = ifelse(after_stat(p.value) < 0.05, "text", "label"))

Thanks!

aphalo commented 1 year ago

Hi, No, it is not possible in 'ggplot', as far as I know as geom is not an aesthetic. I can think of two approaches. 1) You could use label in all cases but make fill and the border of the label visible of not. 1) Add stat_correlation() twice. Once using each of the two geoms and setting alpha (or colour and fill) so that the labels or text would be visible or not depending on the p.value. I would suggest using 1) but the border of the label needs to remain always invisible because label.size is not an aesthetic. Of course a third alternative would be simpply using label and setting the fill to different colours depending on p.value.

library(ggpmisc)
#> Loading required package: ggpp
#> Loading required package: ggplot2
#> 
#> Attaching package: 'ggpp'
#> The following object is masked from 'package:ggplot2':
#> 
#>     annotate

x <- (1:100) / 10
y <- x + rnorm(length(x))
my.data <- data.frame(x = x,
                      y = y,
                      y.desc = - y,
                      group = c("A", "B"))

ggplot(my.data, aes(x, y)) +
  geom_point() +
  stat_correlation(geom = "label", label.size = 0,
                   fill = "lightblue",
                   aes(alpha = stage(after_stat = ifelse(p.value < 0.05, 1, 0)),
                       label = after_stat(p.value.label)))


ggplot(my.data, aes(x, y)) +
  geom_point() +
  stat_correlation(geom = "label", label.size = 0,
                   aes(fill = stage(after_stat = ifelse(p.value < 0.05, "lightblue", NA_character_)),
                       label = after_stat(p.value.label)))


ggplot(my.data, aes(x, y)) +
  geom_point() +
  stat_correlation(geom = "label",
                   aes(fill = stage(after_stat = ifelse(p.value < 0.05, "white", rgb(0,0,0,0))),
                       colour = stage(after_stat = ifelse(p.value < 0.05, "black", rgb(0,0,0,0))),
                       label = after_stat(p.value.label))) +
  stat_correlation(geom = "text",
                   aes(colour = stage(after_stat = ifelse(p.value > 0.05, "black", rgb(0,0,0,0))),
                       label = after_stat(p.value.label))) +
  scale_colour_identity(aesthetics = c("colour", "fill"))
#> Warning: Duplicated aesthetics after name standardisation: NA

Created on 2022-09-10 with reprex v2.0.2

aphalo commented 1 year ago

A useful example to add to the vignette.

erikagucciardo commented 1 year ago

Thank you! So is there any possibility to have the text black regardless of the p.value? The "colour" parameter changes both the text colour and the label border colour, why is that so?

erikagucciardo commented 1 year ago

It could work as shown here below, by having always the colour of the label set to red in the first stat_correlation and the colour of the text set to black regardless of the p.value in the second stat_correlation, and counting on the text overlap, but the texts do not overlap. Is there a workaround for this missed overlap?

ggplot(my.data, aes(x, y)) +
  geom_point() +
  stat_correlation(geom = "label",
                   aes(fill = stage(after_stat = ifelse(p.value < 0.05, "white", rgb(0,0,0,0))),
                       colour = stage(after_stat = ifelse(p.value < 0.05, "red", rgb(0,0,0,0))),
                       label = after_stat(p.value.label))) +
  stat_correlation(geom = "text",
                   aes(colour = stage(after_stat = ifelse(p.value > 0.05, "black", "black)),
                       label = after_stat(p.value.label))) +
  scale_colour_identity(aesthetics = c("colour", "fill"))

Thanks!

smouksassi commented 1 year ago

the color affects both this is how geom_label works https://github.com/tidyverse/ggplot2/blob/main/R/geom-label.R#L96

library(ggplot2)
library(ggpmisc)
#> Loading required package: ggpp
#> 
#> Attaching package: 'ggpp'
#> The following object is masked from 'package:ggplot2':
#> 
#>     annotate
ggplot(mtcars, aes(mpg, disp)) +
  geom_point() +
  stat_correlation(geom = "label",
                   aes(fill   = stage(after_stat = ifelse(p.value < 0.05, "white", "transparent")),
                       colour = stage(after_stat = ifelse(p.value < 0.05, "red", "transparent")),
                       label  = after_stat(p.value.label))) +
  stat_correlation(geom = "text",
                   aes(colour = stage(after_stat = ifelse(p.value > 0.05, "black", "transparent")),
                       label = after_stat(p.value.label))) +
  scale_colour_identity(aesthetics = c("colour", "fill"))+
  facet_grid(~cyl)
#> Warning: Duplicated aesthetics after name standardisation: NA

Created on 2022-09-13 with reprex v2.0.2

aphalo commented 1 year ago

@smouksassi That text and border colour are the same is not a restriction in the 'grid' package. This could be very easily implemented but may be thought as using the same aesthetic for two different purposes, which goes against the phylosophy of the grammar of graphics. On the other hand, I think, an option to apply 'colour' to only text, only border or both, would make sense, and a parameter to set the "base.colour" would be very useful. Do you agree?

smouksassi commented 1 year ago

Hi @aphalo totally agree with you that this is a limitation of geom_label and go back to its initial implementation each now and then people get caught by something working versus others not working e.g for the label outline size we have label.size while the text inside it controlled by size ( not sure how the new linewidth renaming of size affect this or not.

yes we need better fine tuning to be able to control color and transparency independently for text, border and fill

bests, SM

aphalo commented 1 year ago

@erikagucciardo @smouksassi After last night's edits, this is possible with the current code here in GitHib. (It will still take some months before I submit a new version to CRAN, and the design may in the meantime change a bit.)

library(ggpmisc)
#> Loading required package: ggpp
#> Loading required package: ggplot2
#> 
#> Attaching package: 'ggpp'
#> The following object is masked from 'package:ggplot2':
#> 
#>     annotate

ggplot(mtcars, aes(mpg, disp)) +
  geom_point() +
  stat_correlation(geom = "label_s",
                   colour.target = "box",
                   label.size = 1,
                   fill   = "transparent",
                   aes(colour = stage(after_stat = ifelse(p.value < 0.05, "red", "transparent")),
                       label  = after_stat(p.value.label))) +
  scale_colour_identity(aesthetics = c("colour", "fill")) +
  facet_grid(~cyl)

Created on 2022-10-08 with reprex v2.0.2

aphalo commented 1 year ago

Available in the still unreleased main branch.

smouksassi commented 1 year ago

exciting to see geom_label_s in action !