ggplot2:多面图上的着色轴文本
ggplot2: Coloring axis text on a faceted plot
当 scales
参数设置为 "free"
时,我似乎无法正确地为多面图上的轴文本着色。考虑以下数据集:
library( ggplot2 )
X <- data.frame( V1 = LETTERS, V2 = runif( 26 ),
V3 = rep( c("F1", "F2"), each = 13 ) )
我们可以在单个面上绘制数据,突出显示字母 D、O、T,如下所示:
v <- ifelse( X$V1 %in% c( "D", "O", "T" ), "red", "black" )
g <- ggplot( X, aes( x = V1, y = V2 ) ) + geom_point() +
theme( axis.text.x = element_text( color = v ) )
使用默认 scales = "fixed"
使绘图多面化,在两个面上正确突出显示 D、O、T。
g + facet_wrap( ~V3 )
但是,将 scales
参数切换为 "free"
会导致意外行为,其中仅突出显示 D 和 Q。
g + facet_wrap( ~V3, scales = "free" )
我的问题:这是一个错误还是我需要以某种方式修改我对 v
的定义以考虑自由比例。如果这是一个错误,有人知道在每个(自由缩放的)方面突出显示特定轴文本的解决方法吗?
编辑:根据 Henrik 的建议,将自己的答案移至答案。
我不认为这是一个错误。问题是这里的 v
基本上是一串字符,长度为 26,它定义了 x 轴上前 26 个分隔符的颜色。当 x 轴恰好有 26 个断点时,well & good;当它小于那个值时(设置 scales="free"
时就是这种情况),它只是在每个轴的开头重新启动。 Q 在这里是红色的,因为它在第二个图中排在第四位,尽管在第一个图中 v[4]
的红色是 D 的意思。
根据我在 SO 上尝试和阅读的内容,无法将美学映射到 theme()
,后者控制 ggplot 中轴文本的外观。
可以通过隐藏轴并使用 geom_text()
来模拟轴来破解解决方案,因为后者确实接受从数据映射的美学。不过,它可能不是很优雅:
g2 <- ggplot(cbind(X, v), #add v to X
aes(x = V1, y = V2)) +
geom_point() +
# make space to accommodate the fake axis
expand_limits(y = -0.05) +
# create a strip of white background under the fake axis
geom_rect(ymin = -5, ymax = 0, xmin = 0, xmax = nrow(X) + 1, fill = "white") +
# fake axis layer, aligned below y = 0
geom_text(aes(colour = v, label = V1), y = 0, vjust = 1.1) +
# specify the font colours for fake axis
scale_colour_manual(values = c("black", "red"), guide = F) +
# hide the actual x-axis text / ticks
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank())
g2 + facet_wrap( ~V3, scales = "free" )
在深入研究了与情节相关的图形 objects(grobs)之后,我发现了一个潜在的破解方法来解决这个问题。虽然不如 优雅,但我想出于教育目的分享它。
我们首先使用
检索 grob
gt <- ggplotGrob( g + facet_wrap( ~V3, scales = "free" ) )
## TableGrob (11 x 11) "layout": 20 grobs
## z cells name grob
## 1 0 ( 1-11, 1-11) background rect[plot.background..rect.105]
## 2 1 ( 7- 7, 4- 4) panel-1-1 gTree[panel-1.gTree.17]
## 3 1 ( 7- 7, 8- 8) panel-2-1 gTree[panel-2.gTree.30]
## 4 3 ( 5- 5, 4- 4) axis-t-1-1 zeroGrob[NULL]
## 5 3 ( 5- 5, 8- 8) axis-t-2-1 zeroGrob[NULL]
## 6 3 ( 8- 8, 4- 4) axis-b-1-1 absoluteGrob[GRID.absoluteGrob.43]
## 7 3 ( 8- 8, 8- 8) axis-b-2-1 absoluteGrob[GRID.absoluteGrob.50]
## 8 3 ( 7- 7, 7- 7) axis-l-1-2 absoluteGrob[GRID.absoluteGrob.64]
## 9 3 ( 7- 7, 3- 3) axis-l-1-1 absoluteGrob[GRID.absoluteGrob.57]
## 10 3 ( 7- 7, 9- 9) axis-r-1-2 zeroGrob[NULL]
## 11 3 ( 7- 7, 5- 5) axis-r-1-1 zeroGrob[NULL]
## 12 2 ( 6- 6, 4- 4) strip-t-1-1 gtable[strip]
## 13 2 ( 6- 6, 8- 8) strip-t-2-1 gtable[strip]
## 14 4 ( 4- 4, 4- 8) xlab-t zeroGrob[NULL]
## 15 5 ( 9- 9, 4- 8) xlab-b titleGrob[axis.title.x..titleGrob.33]
## 16 6 ( 7- 7, 2- 2) ylab-l titleGrob[axis.title.y..titleGrob.36]
## 17 7 ( 7- 7,10-10) ylab-r zeroGrob[NULL]
## 18 8 ( 3- 3, 4- 8) subtitle zeroGrob[plot.subtitle..zeroGrob.102]
## 19 9 ( 2- 2, 4- 8) title zeroGrob[plot.title..zeroGrob.101]
## 20 10 (10-10, 4- 8) caption zeroGrob[plot.caption..zeroGrob.103]
Grob 是分层的 objects,遍历这些结构的一般规则分为两类:
- 如果 grob 的类型为
gtable
(如上面的 gt
),则可以通过 $grobs
访问进入 table 的单个 grob。
- 如果 grob 不是
gtable
类型,它的 children grob 可以通过 $children
. 访问
查看上面的 gtable
,我们观察到 grobs 6 和 7 分别对应于面 1 和 2 的底轴。这些轴 grob 中的每一个都是 absoluteGrob
类型,因此使用上面的两个规则,我们可以像这样检查它们是由什么组成的:
gt$grobs[[6]]$children
## (zeroGrob[axis.line.x..zeroGrob.40], gtable[axis])
## and likewise for gt$grobs[[7]]$children
注意到第二个 child 是一个 gtable
,我们可以继续降低 grobs 的层次结构,直到我们到达 gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]
,这是 grob 层次结构的叶子(它的$children
是 NULL
) 并且对应轴文本。让我们检查一下它的图形参数,可以通过 $gp
:
访问
## Double-check that we have the correct text object
gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$label
## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
## Display the summary of graphical parameters
str( gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$gp )
## List of 5
## $ fontsize : num 8.8
## $ col : chr [1:26] "black" "black" "black" "red" ...
## $ fontfamily: chr ""
## $ lineheight: num 0.9
## $ font : Named int 1
## ..- attr(*, "names")= chr "plain"
## - attr(*, "class")= chr "gpar"
请注意,col
属性的长度为 26,并且与问题中的 v
变量完全对应。如果我们查看第二个面 (gt$grobs[[7]]$...
) 的底轴,我们会看到那里也使用了相同的 col
值,导致两个面中的轴文本着色相同(如 Z.Lin的解决方案)。
因此,将这些颜色设置只设置为v
"by hand"对应的部分,可以让我们修改原图,达到想要的效果。
gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$gp$col <- v[1:13]
gt$grobs[[7]]$children[[2]]$grobs[[2]]$children[[1]]$gp$col <- v[14:26]
grid::grid.draw( gt )
当 scales
参数设置为 "free"
时,我似乎无法正确地为多面图上的轴文本着色。考虑以下数据集:
library( ggplot2 )
X <- data.frame( V1 = LETTERS, V2 = runif( 26 ),
V3 = rep( c("F1", "F2"), each = 13 ) )
我们可以在单个面上绘制数据,突出显示字母 D、O、T,如下所示:
v <- ifelse( X$V1 %in% c( "D", "O", "T" ), "red", "black" )
g <- ggplot( X, aes( x = V1, y = V2 ) ) + geom_point() +
theme( axis.text.x = element_text( color = v ) )
使用默认 scales = "fixed"
使绘图多面化,在两个面上正确突出显示 D、O、T。
g + facet_wrap( ~V3 )
但是,将 scales
参数切换为 "free"
会导致意外行为,其中仅突出显示 D 和 Q。
g + facet_wrap( ~V3, scales = "free" )
我的问题:这是一个错误还是我需要以某种方式修改我对 v
的定义以考虑自由比例。如果这是一个错误,有人知道在每个(自由缩放的)方面突出显示特定轴文本的解决方法吗?
编辑:根据 Henrik 的建议,将自己的答案移至答案。
我不认为这是一个错误。问题是这里的 v
基本上是一串字符,长度为 26,它定义了 x 轴上前 26 个分隔符的颜色。当 x 轴恰好有 26 个断点时,well & good;当它小于那个值时(设置 scales="free"
时就是这种情况),它只是在每个轴的开头重新启动。 Q 在这里是红色的,因为它在第二个图中排在第四位,尽管在第一个图中 v[4]
的红色是 D 的意思。
根据我在 SO 上尝试和阅读的内容,无法将美学映射到 theme()
,后者控制 ggplot 中轴文本的外观。
可以通过隐藏轴并使用 geom_text()
来模拟轴来破解解决方案,因为后者确实接受从数据映射的美学。不过,它可能不是很优雅:
g2 <- ggplot(cbind(X, v), #add v to X
aes(x = V1, y = V2)) +
geom_point() +
# make space to accommodate the fake axis
expand_limits(y = -0.05) +
# create a strip of white background under the fake axis
geom_rect(ymin = -5, ymax = 0, xmin = 0, xmax = nrow(X) + 1, fill = "white") +
# fake axis layer, aligned below y = 0
geom_text(aes(colour = v, label = V1), y = 0, vjust = 1.1) +
# specify the font colours for fake axis
scale_colour_manual(values = c("black", "red"), guide = F) +
# hide the actual x-axis text / ticks
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank())
g2 + facet_wrap( ~V3, scales = "free" )
在深入研究了与情节相关的图形 objects(grobs)之后,我发现了一个潜在的破解方法来解决这个问题。虽然不如
我们首先使用
检索 grobgt <- ggplotGrob( g + facet_wrap( ~V3, scales = "free" ) )
## TableGrob (11 x 11) "layout": 20 grobs
## z cells name grob
## 1 0 ( 1-11, 1-11) background rect[plot.background..rect.105]
## 2 1 ( 7- 7, 4- 4) panel-1-1 gTree[panel-1.gTree.17]
## 3 1 ( 7- 7, 8- 8) panel-2-1 gTree[panel-2.gTree.30]
## 4 3 ( 5- 5, 4- 4) axis-t-1-1 zeroGrob[NULL]
## 5 3 ( 5- 5, 8- 8) axis-t-2-1 zeroGrob[NULL]
## 6 3 ( 8- 8, 4- 4) axis-b-1-1 absoluteGrob[GRID.absoluteGrob.43]
## 7 3 ( 8- 8, 8- 8) axis-b-2-1 absoluteGrob[GRID.absoluteGrob.50]
## 8 3 ( 7- 7, 7- 7) axis-l-1-2 absoluteGrob[GRID.absoluteGrob.64]
## 9 3 ( 7- 7, 3- 3) axis-l-1-1 absoluteGrob[GRID.absoluteGrob.57]
## 10 3 ( 7- 7, 9- 9) axis-r-1-2 zeroGrob[NULL]
## 11 3 ( 7- 7, 5- 5) axis-r-1-1 zeroGrob[NULL]
## 12 2 ( 6- 6, 4- 4) strip-t-1-1 gtable[strip]
## 13 2 ( 6- 6, 8- 8) strip-t-2-1 gtable[strip]
## 14 4 ( 4- 4, 4- 8) xlab-t zeroGrob[NULL]
## 15 5 ( 9- 9, 4- 8) xlab-b titleGrob[axis.title.x..titleGrob.33]
## 16 6 ( 7- 7, 2- 2) ylab-l titleGrob[axis.title.y..titleGrob.36]
## 17 7 ( 7- 7,10-10) ylab-r zeroGrob[NULL]
## 18 8 ( 3- 3, 4- 8) subtitle zeroGrob[plot.subtitle..zeroGrob.102]
## 19 9 ( 2- 2, 4- 8) title zeroGrob[plot.title..zeroGrob.101]
## 20 10 (10-10, 4- 8) caption zeroGrob[plot.caption..zeroGrob.103]
Grob 是分层的 objects,遍历这些结构的一般规则分为两类:
- 如果 grob 的类型为
gtable
(如上面的gt
),则可以通过$grobs
访问进入 table 的单个 grob。 - 如果 grob 不是
gtable
类型,它的 children grob 可以通过$children
. 访问
查看上面的 gtable
,我们观察到 grobs 6 和 7 分别对应于面 1 和 2 的底轴。这些轴 grob 中的每一个都是 absoluteGrob
类型,因此使用上面的两个规则,我们可以像这样检查它们是由什么组成的:
gt$grobs[[6]]$children
## (zeroGrob[axis.line.x..zeroGrob.40], gtable[axis])
## and likewise for gt$grobs[[7]]$children
注意到第二个 child 是一个 gtable
,我们可以继续降低 grobs 的层次结构,直到我们到达 gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]
,这是 grob 层次结构的叶子(它的$children
是 NULL
) 并且对应轴文本。让我们检查一下它的图形参数,可以通过 $gp
:
## Double-check that we have the correct text object
gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$label
## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
## Display the summary of graphical parameters
str( gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$gp )
## List of 5
## $ fontsize : num 8.8
## $ col : chr [1:26] "black" "black" "black" "red" ...
## $ fontfamily: chr ""
## $ lineheight: num 0.9
## $ font : Named int 1
## ..- attr(*, "names")= chr "plain"
## - attr(*, "class")= chr "gpar"
请注意,col
属性的长度为 26,并且与问题中的 v
变量完全对应。如果我们查看第二个面 (gt$grobs[[7]]$...
) 的底轴,我们会看到那里也使用了相同的 col
值,导致两个面中的轴文本着色相同(如 Z.Lin的解决方案)。
因此,将这些颜色设置只设置为v
"by hand"对应的部分,可以让我们修改原图,达到想要的效果。
gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$gp$col <- v[1:13]
gt$grobs[[7]]$children[[2]]$grobs[[2]]$children[[1]]$gp$col <- v[14:26]
grid::grid.draw( gt )