ggplot2: geom_text resize with the plot and force/fit text within geom_bar
If horizontal bar charts are OK, then the issue is not the size of the labels but the placement. My solution would be
created by this code:
library(ggplot2)
data_gd <- data.frame(x = letters[1:26],
y = runif(26, 100, 99999))
ymid <- mean(range(data_gd$y))
ggplot(data = data_gd,
mapping = aes(x = x, y = y, fill = x)) +
geom_bar(stat = "identity") +
geom_text(mapping = aes(label = y, y = y,
hjust = ifelse(y < ymid, -0.1, 1.1)), size = 3) +
coord_flip()
The trick is done in three steps:
coord_flip
makes a horizontal bar chart.- The mapping in
geom_text
uses alsohjust
depending on the value of y. If the bar is shorter than half of the range of y, the text is printed outside of the bar (right to the y value). If the bar is longer than half of the range of y, the text is printed inside the bar (left to the y value). This makes sure that the text is always printed inside the plot area (if not too long at all). - I have added some additional space between the bar and the text. If you want the text to start or end directly at the y-value you can use
hjust = ifelse(y < ymid, 0, 1))
.
one option might be to write a geom that uses a textGrob with a custom drawDetails method to fit within the allocated space, set by the bar width.
library(grid)
library(ggplot2)
fitGrob <- function(label, x=0.5, y=0.5, width=1){
grob(x=x, y=y, width=width, label=label, cl = "fit")
}
drawDetails.fit <- function(x, recording=FALSE){
tw <- sapply(x$label, function(l) convertWidth(grobWidth(textGrob(l)), "native", valueOnly = TRUE))
cex <- x$width / tw
grid.text(x$label, x$x, x$y, gp=gpar(cex=cex), default.units = "native")
}
`%||%` <- ggplot2:::`%||%`
GeomFit <- ggproto("GeomFit", GeomRect,
required_aes = c("x", "label"),
setup_data = function(data, params) {
data$width <- data$width %||%
params$width %||% (resolution(data$x, FALSE) * 0.9)
transform(data,
ymin = pmin(y, 0), ymax = pmax(y, 0),
xmin = x - width / 2, xmax = x + width / 2, width = NULL
)
},
draw_panel = function(self, data, panel_scales, coord, width = NULL) {
bars <- ggproto_parent(GeomRect, self)$draw_panel(data, panel_scales, coord)
coords <- coord$transform(data, panel_scales)
width <- abs(coords$xmax - coords$xmin)
tg <- fitGrob(label=coords$label, y = coords$y/2, x = coords$x, width = width)
grobTree(bars, tg)
}
)
geom_fit <- function(mapping = NULL, data = NULL,
stat = "count", position = "stack",
...,
width = NULL,
binwidth = NULL,
na.rm = FALSE,
show.legend = NA,
inherit.aes = TRUE) {
layer(
data = data,
mapping = mapping,
stat = stat,
geom = GeomFit,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
params = list(
width = width,
na.rm = na.rm,
...
)
)
}
set.seed(1234567)
data_gd <- data.frame(x = letters[1:5],
y = runif(5, 100, 99999))
ggplot(data = data_gd,
mapping = aes(x = x, y = y, fill = x, label=round(y))) +
geom_fit(stat = "identity") +
theme()