Comments_logticks() 是一个很棒的小函数,它向对数刻度轴添加小刻度线,如下所示。
默认情况下,该函数在绘图的面板区域内部绘制刻度。例如,如果在 y 轴上,刻度线出现在轴线的右侧;如果在 x 轴上,刻度线出现在轴线上方。
我发现对于大多数绘图情况来说,这种默认设置是不可取的。到目前为止,我还没有找到一种简单的方法来让这些小刻度显示在出现常规 ggplot2 刻度的面板区域的外部。例如,y 轴出现在线条的左侧而不是右侧。有没有一个简单的解决方案?
对于在主要刻度之间插入值很重要的绘图情况,将这些值显示在常规 ggplot2 标签和刻度通常出现的左侧将是一个非常好的选择。我还没有找到一个不涉及剪切或完全放弃annotation_logticks()的解决方案,而且我还没有成功地简单地使用刻度线长度的负值。
这是直接来自 ggplot2 网站示例的代码:
a <- ggplot(msleep, aes(bodywt, brainwt)) +
geom_point(na.rm = TRUE) +
scale_x_log10(
breaks = scales::trans_breaks("log10", function(x) 10^x),
labels = scales::trans_format("log10", scales::math_format(10^.x))
) +
scale_y_log10(
breaks = scales::trans_breaks("log10", function(x) 10^x),
labels = scales::trans_format("log10", scales::math_format(10^.x))
) +
theme_bw()
a + annotation_logticks()
Run Code Online (Sandbox Code Playgroud)
我正在寻找一个简单地将刻度翻转到轴线另一侧的结果。
我找到了一种简洁的方法来解决这个问题,尽管这个解决方案的刻度长度与annotation_logticks()
logticks <- function(datavar,type) {
minimum <- 1/10^abs(floor(log10(min(datavar, na.rm=TRUE))))
maximum <- 1*10^abs(floor(log10(max(datavar, na.rm=TRUE)))+1)
multiple <- floor(log10(maximum/minimum))
yourtickvector <- c()
if (type=="breaks") {
yourtickvector <- c(minimum)
for (x in seq(0,multiple)) {
andadd <- seq(minimum*10^x,minimum*10^(x+1),minimum*10^x)[-1]
yourtickvector <- c(yourtickvector,andadd)
}
} else if (type=="labels") {
for (x in seq(0,multiple)) {
andadd <- c(minimum*10^x,rep("",8))
yourtickvector <- c(yourtickvector,andadd)
}
yourtickvector <- c(yourtickvector,minimum*10^multiple)
}
return(yourtickvector)
}
# only changed the breaks / label fields below to call the above function
a <- ggplot(msleep, aes(bodywt, brainwt)) +
geom_point(na.rm = TRUE) +
scale_x_log10(
breaks = logticks(msleep$bodywt,"breaks"),
labels = logticks(msleep$bodywt,"labels")
) +
scale_y_log10(
breaks = logticks(msleep$brainwt,"breaks"),
labels = logticks(msleep$brainwt,"labels")
) +
theme_bw()
a
Run Code Online (Sandbox Code Playgroud)
据我所知,是的,但有点恶心。总而言之::
b <- a +
# reverse ticks
annotation_logticks(short=unit(-0.1, "cm"), mid=unit(-0.2, "cm"), long=unit(-0.3,"cm")) +
# remove clipping
coord_cartesian(clip="off") +
# add space between ticks and labels
theme(axis.text.x=element_text(margin = margin(t = 10)), axis.text.y=element_text(margin = margin(r = 10)))
# get the limits of the panel in data coordinates
bb <- ggplot_build(b)$layout$panel_params[[1]]
# draw a white rectangle to cover up the additional tick marks, gross
# the coordinates are actually in powers of 10 of the data
b +
annotation_custom(
grob=rectGrob(gp=gpar(col=NA)),
# the min values are -100 ie "something very large and negative"
# the max values are the bottom-left corner of the plot plus a tiny
# fudge factor to cover up the stubs of the ticks (gross)
xmin=-100, xmax=min(ab$x.range) + 0.01,
ymax=min(ab$y.range) + 0.01, ymin=-100
)
Run Code Online (Sandbox Code Playgroud)
产量
你必须捏造 中的 0.01s 和 -100s annotation_custom。
annotation_logticks如果你在控制台中查看,它有
layer(data = dumy_data(), # more stuff,
geo = GeomLogticks, # lots more stuff
Run Code Online (Sandbox Code Playgroud)
ggplot2:::GeomLogticks(它不是导出函数,因此:::需要检查它的三元组)显示具有各种函数的对象来绘制刻度线。一些试验和错误表明,ggplot2:::GeomLogticks$draw_panel该函数似乎可以完成所有工作。看起来这个函数创建了一个数据框,其中包含每个单独刻度的 xstart、xend、ystart、yend 坐标(!)。
例如该函数主体的片段
if (grepl("l", sides)) {
ticks$y_l <- with(data, segmentsGrob(y0 = unit(yticks$y,
"native"), y1 = unit(yticks$y, "native"), x0 = unit(yticks$start,
"cm"), x1 = unit(yticks$end, "cm"), gp = gpar(col = alpha(colour,
alpha), lty = linetype, lwd = size * .pt)))
}
Run Code Online (Sandbox Code Playgroud)
由此看来,我们可以只提供负长度annotation_logticks
a2 <- a + annotation_logticks(short=unit(-0.1, "cm"), mid=unit(-0.2, "cm"), long=unit(-0.3,"cm"))
a2
Run Code Online (Sandbox Code Playgroud)
这实际上不起作用,如果你用力眯着眼睛,你可以看到所有刻度线的残端。看起来刻度线绘制正确,但它们被面板区域剪切。(如果您暂时关闭面板边框,a2 + theme(panel.border=element_rect(fill=NA, color=NA))您可以看到这种情况)。
要关闭剪辑,你可以这样做+ coord_cartesian(clip='off')
a3 <- a2 + coord_cartesian(clip='off')
a3
Run Code Online (Sandbox Code Playgroud)
正如在参数中提到的?coord_cartesian,clip这可能会导致“意外的结果”——刻度被定义为比数据更大的区域(我认为它们是 10 的幂,所以如果最小的数据点是幂的一半) 10 时,它仍然会一直计算到下一个较低的幂),因此延伸到左下角之外。
解决此问题的一个粗略方法是在图表的左下部分(面板外部/边距中)绘制一个矩形以覆盖刻度线。annotation_custom这样做但需要数据坐标中的坐标。当使用像这样的对数刻度时,坐标是 10 的幂,因此例如“10^-4”是坐标 -4。
因此,为了绘制矩形来掩盖多余的标记,我们需要将左下角基本设置在负无穷大(图形的左下角),并将右上角设置在X/Y 轴上的最小值处。
在annotation_custom我们想要rectGrob画一个矩形。默认情况下它有白色背景,但我们禁用黑色边框gpar(col=NA)。xmin我们将和坐标设置ymin为 -100(本质上是非常大的负数;-Inf意味着“最低轴值”)。我们可以将xmax和ymax坐标设置为最低的轴值,这-Inf对我们来说就足够了。
a3 +
annotation_custom(
grob=rectGrob(gp=gpar(col=NA)),
xmin=-100, xmax=-Inf,
ymax=-Inf, ymin=-100
)
Run Code Online (Sandbox Code Playgroud)
对我来说,这并不能完全掩盖额外的刻度线,留下恼人的小点。
我真的希望xmax和ymax能够-Inf + a little bit覆盖整个刻度线。但要做到这一点,我们不能-Inf再用作“轴下限”的简写,我们必须使用显式访问它们
bb <- ggplot_build(b)$layout$panel_params[[1]]
# bb$x.range, bb$y.range
Run Code Online (Sandbox Code Playgroud)
然后我们修改上面的内容
a4 <- a3 +
annotation_custom(
grob=rectGrob(gp=gpar(col=NA)),
xmin=-100, xmax=min(ab$x.range) + 0.01,
ymax=min(ab$y.range) + 0.01, ymin=-100
)
a4
Run Code Online (Sandbox Code Playgroud)
(我通过反复试验发现了 0.01,这不太好)。
现在一切正常,只是 X 轴和 Y 轴标签碰到了刻度线。要添加标签和刻度线之间的间距,请使用
a5 <- a4 + theme(axis.text.x=element_text(margin = margin(t = 10)),
axis.text.y=element_text(margin = margin(r = 10)))
a5
Run Code Online (Sandbox Code Playgroud)
其中这些是以像素为单位的边距(您可以更改单位,请参阅?margin)
产生您首先看到的图片。也许您可以将其设为一个函数,允许用户修改各种模糊因子(-100、+ 0.01 和 10 像素的边距)。