Override horizontal positioning with ggrepel
TL;DR: probably a bug
Long answer:
I think it might be a bug in the code. I checked the gtable of the plot you made, wherein the hjust
was specified numerically and correctly:
# Assume 'g' is the plot saved under the variable 'g'
gt <- ggplotGrob(g)
# Your number at the end of the geom may vary
textgrob <- gt$grobs[[6]]$children$geom_text_repel.textrepeltree.1578
head(textgrob$data$hjust)
[1] 1 0 1 0 1 0
Which got me thinking that (1) the plot can't be fixed by messing around in the gtable and (2) the drawtime code for the textrepeltree
class of grobs may contain some errors. This makes sense, since the labels are repositioned when the plot device is resized. So when we look at the makeContent.textrepeltree()
code in the link you provided, we can see that the hjust
parameter is passed on to makeTextRepelGrobs()
. Let's have a look at the relevant formals:
makeTextRepelGrobs <- function(
...other_arguments...,
just = "center",
...other_arguments...,
hjust = 0.5,
vjust = 0.5
) { ...body...}
We can see that hjust
is a valid argument, but there also exists a just
argument, which is an argument that is not passed on from makeContent.textrepeltree()
.
When we look at the function body there are these two lines:
hj <- resolveHJust(just, NULL)
vj <- resolveVJust(just, NULL)
Where resolveH/VJust
are imported from the grid package. The resolveHJust()
essentially checks whether the second argument is NULL
and if that is true, default to the first argument, otherwise return the second argument. You can see that the hjust
that was passed on to makeTextRepelGrobs()
does not get passed to resolveHJust()
, and this seems to be the point where your hjust
parameter is dropped unexpectedly.
Further down the code is where the actual text grobs are made:
t <- textGrob(
...other_arguments...
just = c(hj, vj),
...other_arguments...
)
I imagine that the fix would be relatively straightforward: you would just have to supply hjust
as the second argument to resolveHJust()
. However, since that makeTextRepelGrobs()
is internal to ggrepel and does not get exported, you would have to copy a lot of extra code to get this to work. (Not sure if only copying the makeTextRepelGrob()
would be sufficient, haven't tested this)
All of this leaves me to conclude that the hjust
that you specified in geom_text_repel()
gets lost at the last moment of drawtime by the makeTextRepelGrobs()
internal function.
How to horizontally align repelled labels for overlapping points
For these data, geom_text_repel
might be overkill. You can achieve similar spacing with geom_label
. Only the "2005-2009" data point needs a different horizontal alignment from the rest, which can be be accomplished through the hjust
parameter:
ggplot(df, aes(x=I, y=G/100, label=interval)) + geom_point() +
geom_label(aes(label=interval, hjust = ifelse(interval == '2005-2009', 1, 0)), size=ts, label.padding = unit(0.3, "lines"), fill = NA, label.size = NA) +
scale_x_continuous(limits=c(-1,1), breaks=c(-1,0,1), labels=c("", 0, ""), expand=c(0,0)) +
scale_y_continuous(limits=c(0,1), breaks=c(0,.5,1), expand=c(0,0)) +
geom_hline(yintercept=0.5) + geom_vline(xintercept=0) +
annotate("text", size=ts, x=0.02, y=0.03, label="0", hjust=0) +
annotate("text", size=ts, x=0.97, y=0.03, label="I", hjust=1, fontface="italic") +
annotate("text", size=ts, x=0.02, y=0.47, label="0.5", hjust=0) +
annotate("text", size=ts, x=0.02, y=0.97, label="G", hjust=0, fontface="italic") + pt
Modify outwards label position in ggplot - avoid overlap between node and label
Solution:
Use ggrepel, change outward for inward
ggplot(ggnetwork(net1 ) )+
geom_edges(data=posData1, aes(x = xorig, y = yorig, xend = xtarg, yend = ytarg),
arrow = arrow(length = unit(10, "pt"), type = "closed")
) +
geom_nodes(aes(x=xpos, y=ypos ), size = 4) +
geom_nodelabel_repel(aes(x=xpos, y=ypos, label = vertex.names ),
hjust="inward",
vjust="inward"
,fontface = "italic", size=3
) + theme_blank()
Related:
https://github.com/slowkow/ggrepel/issues/191
ggrepel together with geom_smooth
One way you could do this is to get the last (or first etc. as desired) fitted value for each continent in a summary label data frame, then use that in ggrepel
:
library(tidyverse)
library(gapminder)
library(ggrepel)
library(broom)
label_df <- gapminder |>
nest(data = -continent) |>
mutate(model = map(data, ~loess(lifeExp ~ year, .x)),
augmented = map(model, augment),
fitted = map(augmented, ".fitted") |> map_dbl(last),
year = map(data, "year") |> map_int(last)) |>
select(continent, fitted, year)
ggplot(gapminder, aes(year, lifeExp, color = continent)) +
geom_line(size = .1, alpha = .2) +
guides(color = "none") +
theme_minimal() +
geom_smooth(aes(color = continent), se = F, method = "loess") +
geom_label_repel(aes(year, fitted, label = continent), data = label_df)
Created on 2022-07-03 by the reprex package (v2.0.1)
Justifying lines of text within individual ggrepel labels
This has now been addressed in the development version of ggrepel
(version 0.8.1.9000).
library(ggplot2)
devtools::install_github("slowkow/ggrepel")
p <- ggplot() +
coord_cartesian(xlim=c(0,1), ylim=c(0,1)) +
theme_void()
p
labelInfo <- data.frame(x=c(0.45,0.55), y=c(0.5,0.5),
g=c("I'd like very much to be\nright justified","And I'd like to be\nleft justified"))
p + geom_label_repel(data=labelInfo, aes(x,y,label=g), hjust=c(1,0),
box.padding = 0.5, point.padding = 0.75,
nudge_x = c(-0.05,0.05), nudge_y = 0, direction="x",
arrow=arrow(length=unit(2,"mm"), ends="last", type="closed"))
ggrepel: Repelling text in only one direction, and returning values of repelled text
ggrepel version 0.6.8 (Install from GitHub using devtools::github_install) now supports a "direction" argument, which enables repelling of labels only in "x" or "y" direction.
repelPlot2 <- ggplot(data) + geom_text_repel(aes(x, y, label = label), segment.size = 0, direction = "y") + theme_classic(base_size = 16)
Getting the y values is harder -- one approach can be to use the "repel_boxes" function from ggrepel first to get repelled values and then input those into ggplot with geom_text. For discussion and sample code of that approach, see https://github.com/slowkow/ggrepel/issues/24. Note that if using the latest version, the repel_boxes function now also has a "direction" argument, which takes in "both","x", or "y".
Use geom_label_repel only for certain observations?
You could do something like this to choose the labels you want:
library(tidyverse)
library(ggrepel)
df <- tribble(~team, ~aay, ~epa,
"LA", 8, 5,
"PIT", 6, -2,
"KC", 7, 5,
"DAL", 7, 5
)
# Select desired labels
labels <- df |> filter(team %in% c("KC", "DAL"))
df |>
ggplot(aes(aay, epa)) +
geom_point() +
geom_label_repel(aes(label = team), data = labels, force = 20) +
xlim(c(0, 10)) +
ylim(c(-8, 8))
Created on 2022-05-25 by the reprex package (v2.0.1)
Related Topics
Merge Plm Fitted Values to Dataset
Getsymbols and Using Lapply, Cl, and Merge to Extract Close Prices
Rselenium, Chrome, How to Set Download Directory, File Download Error
Combine/Merge Columns While Avoiding Na
How to Retrieve the Client's Current Time and Time Zone When Using Shiny
Add a Dynamic Value into Rmysql Getquery
Rhtml: Warning: Conversion Failure on '<Var>' in 'Mbcstosbcs': Dot Substituted for <Var>
Unexpected Symbol Error in Parse(Text = Str) with Hyphen After a Digit
Scraping Leaderboard Table on Golf Website in R
Knitr Compile Problems with Rstudio (Windows)
In R, Check If String Appears in Row of Dataframe (In Any Column)
Splitting String Based on Letters Case
Manipulating Files with Non-English Names in R