简体   繁体   中英

positioning labels on geom_bar

I am trying to create a horizontal bar chart with category labels using ggplot.

I have been able to create the plot without hassles, and can put labels on, however I suffer issues with the formatting. Ultimately I would like to have my label within the bar if it fits, otherwise just outside the bar without truncating the label.

The following are what I have tried so far.

Data

dt1 <- data.table(x=c("a","b","c","d","e"), y=c(43,52,296,102,157), y2=c(50,10,100,45,80))

Chart 1

ggplot() + geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") + coord_flip() +
  geom_text(data=dt1, aes(x=x, y=y, label=paste0("$",y," from ",y2," records")),hjust=0)

As you can see below the labels get truncated. 图1

Chart 2

I then came across this question which was helpful and made me realise that I was setting the label position based on my y variable so I have hardcoded it now and use hjust to pad it from the axis.

ggplot() + geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") + coord_flip() +
  geom_text(data=dt1, aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust=-0.1)

But you can see below that only 2 of the labels fit within the bar, so I would prefer the others to be placed at the end, on the outside of the bar like in chart 1. 图2

Is there a programatic way I can get the best of both worlds from chart 1 and chart 2?

Here is one way. It is a bit lengthy approach, but you can subset your data for geom_text . In this way, you can manually assign the position you want for each bar.

ggplot() + 
geom_bar(data = dt1, aes(x=x, y=y), stat="identity",fill="red") +
coord_flip() +
geom_text(data = filter(dt1, x == "e" | x == "c"),
     aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = -0.1) +
geom_text(data = filter(dt1, x == "d"),
     aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = - 1.1) +
geom_text(data = filter(dt1, x == "b"),
     aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = - 0.6) +
geom_text(data = filter(dt1, x == "a"),
     aes(x=x, y=0, label=paste0("$",y," from ",y2," records")),hjust = - 0.5) 

在此输入图像描述

Move the hjust into the aes so we may vary off the value, then move it if the bar is a certain way past the max. It's a bit hacky still, since it makes assumptions about the scaling, but looks pretty good. Divisor may need tweaking:

library(tidyverse)

dt1 <- data.frame(x=c("a","b","c","d","e"), y=c(43,52,296,102,157), y2=c(50,10,100,45,80))

ggplot() +
  geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") +
  coord_flip() +
  geom_text(
    data=dt1,
    aes(
      x=x, y=y,
      label=paste0("$",y," from ",y2," records"),
      hjust=ifelse(y < max(dt1$y) / 1.5, -0.1, 1.1), # <- Here lies the magic
    ),
  )

Results in this plot:

由上面的代码生成的图

I'm going to misread programmatic as 'pragmatic'. Adding "+ scale_y_continuous(limits=c(0,max(dt1$y)+100))" created sufficient room for the labels. I lack the reputation to upload the plot.

ggplot() + geom_bar(data=dt1, aes(x=x, y=y), stat="identity",fill="red") + coord_flip() + geom_text(data=dt1, aes(x=x, y=y, label=paste0("$",y," from ",y2," records")),hjust=0) + scale_y_continuous(limits=c(0,max(dt1$y)+100))

Edit 2; I altered the code to retrieve the maximum value and add 100 to it. It's still not fitting the plot to include the text specifically but it'll work with fixed labels.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM