Reputation: 31619
Say I have this simple dotplot:
ggplot(mtcars, aes(hp)) +
geom_dotplot(binwidth = 10, stackdir = 'center')
And I want to label (some of) the points. This doesn't work:
ggplot(mtcars, aes(hp)) +
geom_dotplot(binwidth = 10, stackdir = 'center') +
geom_text(aes(label = rownames(mtcars)))
# Error: geom_text requires the following missing aesthetics: y
So how do I get access to the y
values computed for the geom_dotplot
, in so that I can place the labels at the correct location?
If I set y = 0
and use geom_text_repel
I get:
ggplot(mtcars, aes(hp)) +
geom_dotplot(binwidth = 10, stackdir = 'center') +
geom_text_repel(aes(label = rownames(mtcars)), box.padding = unit(2, 'lines'), y = 0)
This is close to what I want, except all of the line segments are pointing to y = 0
.
I got this to work using a modification of the accepted answer which tries to infer the y-scaling amount from the device dimensions:
library(ggplot2)
library(ggrepel)
bw <- 10
p <- ggplot(mtcars, aes(hp)) +
geom_dotplot(binwidth = bw, stackdir = 'center')
built <- ggplot_build(p)
point.pos <- built$data[[1]]
# Order rows of mtcars by hp
idx <- order(mtcars$hp)
mtcars2 <- mtcars[idx,]
# Get the dimensions of the target device in pixels
size <- dev.size(units = 'px')
# Get the range of x and y domain values
extent <- with(built$layout$panel_params[[1]], abs(c(diff(x.range), diff(y.range))))
mtcars2$ytext <- (size[1] / size[2]) * (extent[2] / extent[1]) * point.pos$stackpos * bw
mtcars2$xtext <- point.pos$x
ggplot(mtcars2, aes(hp)) +
geom_dotplot(binwidth = bw, stackdir = 'center') +
geom_text_repel(
aes(xtext, ytext, label = rownames(mtcars2)),
box.padding = unit(.5 * size[1] * bw / extent[1], 'points'),
color = 'red'
)
Which produces
It's not perfect--the segments don't point to the exact centers of the dots because the aspect ratio of the entire image is not the same as the aspect ratio of just the panel, but it's pretty close.
Upvotes: 5
Views: 5214
Reputation: 24262
The code proposed below is not elegant.
It works after fine tuning of the scaling factor scale.factor
and of the plot dimensions.
I hope that some ideas contained in my answer can be useful for the solution of your problem.
library(ggplot2)
p <- ggplot(mtcars, aes(hp)) +
geom_dotplot(binwidth = 10, stackdir = 'center')
# Get y-positions of points plotted by geom_dotplot
# Warning: these positions are not given
point.pos <- ggplot_build(p)$data[[1]]
# Order rows of mtcars by hp
idx <- order(mtcars$hp)
mtcars2 <- mtcars[idx,]
# scale.fact needs fine tuning
# It is strictly connected to the dimensions of the plot
scale.fact <- 0.105
mtcars2$ytext <- point.pos$stackpos*scale.fact
mtcars2$xtext <- point.pos$x
lbls <- gsub(" ","\n",rownames(mtcars2))
png(file="myplot.png", width=4000, height=1400, res=300)
ggplot(mtcars2, aes(hp)) +
geom_dotplot(binwidth = 10, stackdir = 'center', fill="#AAAAAA55") +
geom_text(aes(label=lbls, x=xtext, y=ytext), size=2)
dev.off()
Upvotes: 2