byt*_*101 41 visualization r data-visualization ggplot2
有没有办法在 ggplot2 中沿着密度线放置文本,或者就此而言,沿着任何路径放置文本?我的意思是一次作为标签,采用这种 xkcd 风格:1835、1950(中间面板)、1392或2234(中间面板)。或者,有没有办法让该行重复文本,例如xkcd #930?我对所有 xkcd 表示歉意,我不确定这些样式叫什么,这是我能想到的唯一一个我以前见过的以这种方式区分区域的地方。
注意:我不是在谈论手绘的 xkcd 风格,也不是在顶部放置平面标签
我知道我可以放置一段笔直/平坦的文本,例如 viaannotate或geom_text,但我很好奇弯曲此类文本,使其看起来沿着数据的曲线。
我也很好奇这种文本风格有名字吗?
ggplot2 图表示例使用annotate(...):
上面的示例图在 Inkscape 中用弯曲文本修改:
编辑:根据要求,这是 3 月和 4 月前两次试运行的数据:
df <- data.frame(
monthly_run = c('March', 'March', 'March', 'March', 'March', 'March', 'March',
'March', 'March', 'March', 'March', 'March', 'March', 'March',
'April', 'April', 'April', 'April', 'April', 'April', 'April',
'April', 'April', 'April', 'April', 'April', 'April', 'April'),
duration = c(36, 44, 45, 48, 50, 50, 51, 54, 55, 57, 60, 60, 60, 60, 30,
40, 44, 47, 47, 47, 53, 53, 54, 55, 56, 57, 69, 77)
)
ggplot(df, aes(x = duration, group = monthly_run, color = monthly_run)) +
geom_density() +
theme_minimal()`
Run Code Online (Sandbox Code Playgroud)
All*_*ron 62
很好的问题。我经常思考这个问题。我不知道有什么软件包本身允许它,但自己做并不是非常困难,因为它geom_text被接受angle为一种美学映射。
假设我们有以下情节:
library(ggplot2)
df <- data.frame(y = sin(seq(0, pi, length.out = 100)),
x = seq(0, pi, length.out = 100))
p <- ggplot(df, aes(x, y)) +
geom_line() +
coord_equal() +
theme_bw()
p
Run Code Online (Sandbox Code Playgroud)

我们想要沿着它运行以下标签:
label <- "PIRATES VS NINJAS"
Run Code Online (Sandbox Code Playgroud)
我们可以将标签拆分为字符:
label <- strsplit(label, "")[[1]]
Run Code Online (Sandbox Code Playgroud)
现在是棘手的部分。我们需要沿着路径均匀地间隔字母,这需要计算出实现此目的的 x 坐标。我们这里需要几个辅助函数:
label <- strsplit(label, "")[[1]]
Run Code Online (Sandbox Code Playgroud)
这些使我们能够创建一个由字母、坐标和角度组成的小数据框来绘制字母:
next_x_along_sine <- function(x, d)
{
y <- sin(x)
uniroot(f = \(b) b^2 + (sin(x + b) - y)^2 - d^2, c(0, 2*pi))$root + x
}
x_along_sine <- function(x1, d, n)
{
while(length(x1) < n) x1 <- c(x1, next_x_along_sine(x1[length(x1)], d))
x1
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以用普通的 old 进行绘图geom_text:
p + geom_text(aes(y = y + 0.1, label = label, angle = angle), data = df2,
vjust = 1, size = 4, fontface = "bold")
Run Code Online (Sandbox Code Playgroud)
或者,如果我们想用文本替换部分行:
df2 <- as.data.frame(approx(df$x, df$y, x_along_sine(1, 1/13, length(label))))
df2$label <- label
df2$angle <- atan(cos(df2$x)) * 180/pi
Run Code Online (Sandbox Code Playgroud)
或者,进行一些主题调整:
附录
实际上,我可能不会抽出时间来编写一个geom_textpath包,但我认为展示可能适用于按照OP示例标记密度曲线的方法会很有用。它需要以下一组功能:
p + geom_text(aes(y = y + 0.1, label = label, angle = angle), data = df2,
vjust = 1, size = 4, fontface = "bold")
Run Code Online (Sandbox Code Playgroud)
定义了这些函数后,我们现在可以执行以下操作:
df$col <- cut(df$x, c(-1, 0.95, 2.24, 5), c("black", "white", "#000000"))
ggplot(df, aes(x, y)) +
geom_line(aes(color = col, group = col)) +
geom_text(aes(label = label, angle = angle), data = df2,
size = 4, fontface = "bold") +
scale_color_identity() +
coord_equal() +
theme_bw()
Run Code Online (Sandbox Code Playgroud)
All*_*ron 19
最后,这个问题促使我和 Teun van den Brand (@teunbrand) 开发了这个geomtextpath软件包,该软件包现已发布在 CRAN 上。
所以现在这个问题可以更直接、更简单地回答:
library(geomtextpath)
ggplot(df, aes(x = duration, color = monthly_run)) +
geom_textdensity(aes(label = monthly_run, hjust = monthly_run,
vjust = monthly_run), size = 6) +
scale_hjust_manual(values = c(0.4, 0.55)) +
scale_vjust_manual(values = c(1.1, -0.2)) +
scale_y_continuous(limits = c(0, 0.06)) +
theme_minimal() +
theme(legend.position = "none")
Run Code Online (Sandbox Code Playgroud)