2

I would like to apply a position_nudge to an object, but it should always be a certain distance (e.g. in "cm") rather than relative to the scale of the measured variable.

data <- data.frame(
  name=c("de","gb","cn","ir","ru") ,
  value=c(3,12,5,18,45)*1
)

ggplot(data, 
       aes(x=name, y=value)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = 0,
                label = paste0(name,value)),
            position = position_nudge(y = -12)) +
  coord_cartesian(ylim = c(0, 50), # This focuses the x-axis on the range of interest
                  clip = 'off') +   # This keeps the labels from disappearing
  theme(plot.margin = unit(c(1,1,1,1), "lines"))

enter image description here

When changing the scale of the variable, that adjustment should not need to be made in the position_nudge argument, e.g.

factor = 100
data <- data.frame(
  name=c("de","gb","cn","ir","ru") ,
  value=c(3,12,5,18,45)*factor
)

ggplot(data, 
       aes(x=name, y=value)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = 0,
                label = paste0(name,value)),
            position = position_nudge(y = -12)) +
  coord_cartesian(ylim = c(0, 50*factor), # This focuses the x-axis on the range of interest
                  clip = 'off') +   # This keeps the labels from disappearing
  theme(plot.margin = unit(c(1,1,1,1), "lines"))

Currently, this does not work, so that I need to manually change -12 to -1200 to achieve this:

enter image description here

This is of course only a short reproducible example, the actual use-case is placing country flags as x-axis labels below the plot.

The final product will look somewhat like this, but currently requires updating the nudges each time the y-values change:

enter image description here

Thank you very much!

  • 1
    have you considered just multiplying nudge with the same factor? Using fixed distances is a bit tricky in ggplot and I am not so sure if this is really what you want – tjebo Jun 04 '20 at 22:56
  • Have also been thinking about that. Maybe multiply nudge based on the maximum bar/ylim. Because the nudge will have to change proportionally... – Marcel Schliebs Jun 04 '20 at 23:00

2 Answers2

2

The easiest "hack" is to make this two plots and bind them with patchwork or cowplot. If you try it differently, you'd soon get into deep grid ... trouble.

Related

baptiste on github

baptiste on stackoverflow

Sandy Muspratt's answer

The easy way:

library(ggplot2)
library(patchwork)

foo <- data.frame(
  name=c("de","gb","cn","ir","ru") ,
  value=c(3,12,5,18,45)*1
)
foo_label = paste(foo$name, foo$value)

p <- ggplot(foo, aes(x=name, y=value)) +
  geom_blank() # essential, so that both plots have same scaling

p_1 <- 
  p + geom_col() +
  coord_cartesian(ylim = c(0, 50),clip = 'off') +  
  theme(plot.margin = margin()) 

p_text <- 
  p + annotate("text", label = foo_label, x = 1:5, y = 0, col="red") +
  theme_void() + 
  coord_cartesian(clip = "off") +
  theme(plot.margin = margin(1,0,1,0, unit = "lines")) 

p_1/p_text + plot_layout(heights = c(1,0)) #this is a workaround to make the height of the text plot minimal!

You can then of course annotate with anything.

tjebo
  • 21,977
  • 7
  • 58
  • 94
2

For your stated goal, the ggtext library may be more appropriate, as it allows you to embed images directly into the x axis labels. See also here for another example.

library(ggplot2)
library(ggtext)

labels <- c(
  setosa = "<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/8/86/Iris_setosa.JPG/180px-Iris_setosa.JPG'
    width='100' /><br>*I. setosa*",
  virginica = "<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Iris_virginica_-_NRCS.jpg/320px-Iris_virginica_-_NRCS.jpg'
    width='100' /><br>*I. virginica*",
  versicolor = "<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/20140427Iris_versicolor1.jpg/320px-20140427Iris_versicolor1.jpg'
    width='100' /><br>*I. versicolor*"
)

ggplot(iris, aes(Species, Sepal.Width)) +
  geom_boxplot() +
  scale_x_discrete(
    name = NULL,
    labels = labels
  ) +
  theme(
    axis.text.x = element_markdown(color = "black", size = 11)
  )

enter image description here

Claus Wilke
  • 16,992
  • 7
  • 53
  • 104