1

I want to set positon with some conditions, but an error occured when I use if_else().

A reproducible example is below.

My question is :

How can I use conditions in position in ggplot2.

library(dplyr)

pd_ture = position_dodge(.4)
pd_false = position_dodge(.6)

mtcars_true = mtcars %>% mutate(test = TRUE)
mtcars_false =mtcars %>% mutate(test = FALSE)


ggplot(mtcars_true, aes(factor(cyl), fill = factor(vs))) +
  geom_bar(position = if_else(unique(mtcars_true$test), pd_ture, pd_false))

# Error in true[rep(NA_integer_, length(condition))] : 
#  object of type 'environment' is not subsettable

ggplot(mtcars_false, aes(factor(cyl), fill = factor(vs))) +
  geom_bar(position = if_else(unique(mtcars_false$test), pd_false, pd_ture))

# Error in true[rep(NA_integer_, length(condition))] : 
#  object of type 'environment' is not subsettable
tjebo
  • 21,977
  • 7
  • 58
  • 94
zhiwei li
  • 1,635
  • 8
  • 26

2 Answers2

4

You can do this, but not with if_else or ifelse, since they will apply the subsetting operator to the output of position_dodge, which is not subsettable. Instead, you need a good old-fashioned if(unique(mtcars_true$test)) pd_ture else pd_false:

ggplot(mtcars_true, aes(factor(cyl), fill = factor(vs))) +
  geom_bar(position = if(unique(mtcars_true$test)) pd_ture else pd_false)

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • I was wondering why ifelse threw that error. I am slightly confused (as so often with ifelse vs if... else... ). Why does ifelse apply the subsetting operator to its TRUE and FALSE arguments? – tjebo May 30 '22 at 12:59
  • 1
    @tjebo look at the source code for `ifelse`. Effectively, in the last couple of lines, it is trying to call `rep(position_dodge(.4), length.out = 1)[1]`, which fails because of attempting to `rep` an environment. `if_else` fails because its second line is `out <- true[rep(NA_integer_, length(condition))]`, i.e. it sets up the output vector to be the same class as `true`, and uses subsetting to do it. – Allan Cameron May 30 '22 at 13:11
0

I don't think you can do that. What you want is a pattern like this:

ggplot(mtcars_true, aes(factor(cyl), fill = factor(vs))) +
  geom_bar(data = . %>% filter(test), position = pd_ture) +
  geom_bar(data = . %>% filter(!test), position = pd_false)

But the way you set it up, the mtcars_true/mt_cars_false is not really made for this. So to make the current setup work, you need:

ggplot(mtcars_true, aes(factor(cyl), fill = factor(vs))) +
  geom_bar(data = mtcars_true, position = pd_ture) +
  geom_bar(data = mtcars_false, position = pd_false)

But the pattern of data = . %>% [data transforming pipes] will allow you to subset which part of the data the current layer / geom_* is being fed.

Mossa
  • 1,656
  • 12
  • 16