I am attempting to graph data with peaks in R. The graphing itself has progressed well, but I've run into issues with labelling the relevant peaks. My current labelling system, detailed below, shifts the peak labels oddly to the side and results in lines crossing each other. Is there a way to align labels with the peaks themselves, or otherwise organize them aesthetically?
The following code reproduces my problem, using this data.
library(ggplot2)
library(ggpmisc)
library(ggrepel)
x=read.csv("data.csv")
colnames(x)=c("wv", "abs")
ggplot(x, aes(x=wv, y=abs)) + geom_line() + xlab(bquote('Wavenumbers ('~cm^-1*')')) + ylab("Absorbance (A.U.)") + scale_x_reverse(limits=c(2275,1975), expand=c(0,0)) + ylim(-0.01,0.29) + stat_peaks(colour = "black", span = 11, geom ="text_repel", direction = "y", angle = 90, ignore_threshold = 0.09, size = 3, x.label.fmt = "%.2f", vjust = 1, hjust = 0, segment.color = "red") + ggtitle("FTIR - Carbon Monoxide, Fundamentals")

hjust = 0.5 should work better. Using hjust = 0 aligns your labels a little to the right, with the top edge of the text aligned with the middle of each peak.
Here's a reproducible example that doesn't rely on external data that might not remain available at that link. (See bottom for application to the OP dataset.)
library(ggpmisc)
library(ggrepel)
library(ggplot2)
x <- data.frame(wv = 2300:2000)
x$abs = abs(cos(x$wv/50) * sin(x$wv/2))
ggplot(x, aes(wv, abs)) +
geom_line() +
stat_peaks(colour = "black", span = 11,
geom ="text_repel", direction = "y",
angle = 90, ignore_threshold = 0.09,
size = 3, x.label.fmt = "%.2f",
vjust = 1, hjust = 0.5, segment.color = "red") +
scale_x_reverse(limits = c(2300,2000))

Here's loading the original data:
library(readr)
x <- read_csv("~/Downloads/CO-FTIR Spectrum-1800 mTorr-2021.csv")
colnames(x)=c("wv", "abs")
Here with these parameters added:
box.padding = 0.0, nudge_y = 0.02,

Here's an approach using ggplot2::stage and the built-in segment and text geometries:
library(ggplot2)
library(ggpmisc)
id <- "1S345TaPqANriDPLN6H_PyunuM1QHi485"
x <- read.csv(sprintf("https://docs.google.com/uc?id=%s&export=download", id))
colnames(x)=c("wv", "abs")
ggplot(x, aes(x=wv, y=abs)) + geom_line() +
xlab(bquote('Wavenumbers ('~cm^-1*')')) + ylab("Absorbance (A.U.)") + ggtitle("FTIR - Carbon Monoxide, Fundamentals") + ylim(-0.01,0.29) +
stat_peaks(mapping = aes(x = stage(wv, after_scale = x + 0.25)),
geom ="text", colour = "black", span = 11, angle = 90,
ignore_threshold = 0.09, size = 2.5, x.label.fmt = "%.2f",
vjust = 0.5, hjust = -1) +
stat_peaks(mapping = aes(xend = stage(wv, after_scale = x + 0.25),
y = stage(abs, after_stat = y + 0.005),
yend = after_stat(y + 0.02)),
geom ="segment", lwd = 0.5, colour = "red",
span = 11, ignore_threshold = 0.09) +
scale_x_reverse(expand=c(0,0), limits = c(2275,1975))

From help(stat_peaks) we can see that x and y stats are calculated for each peak. Typically we can access these stats with after_stat, but because you've also transformed the x-axis, you need to actually access that stat after it's been scaled.
In the first layer of stat_peaks we use stage(wv, after_scale = x + 0.25) to add a bit of nudge to the x coordinate. In the second call of stat_peaks we set the geometry to segment. Segment requires an xend and yend. We can use stage and after_stat to again add a nudge and line length.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With