英文:
Headers above entries in bar chart with ggplot
问题
我想用ggplot制作类似以下图表的效果([来源][1])。 (我目前不关心颜色或其他审美细节,也不关心数据,只关心复制整体布局。)
我更喜欢在属性级别的顶部显示属性名称,而不是在侧面显示。换句话说,我想在16的顶部有"attr2",在1的顶部有"attr1"。我找不到如何做到这一点的方法。
如果管理两个组与我的目标不兼容,那么也可以提供一个不考虑两个组的解决方案。如果ggplot2无法实现这个目标,也可以接受使用其他工具。
[1]: https://thorax.bmj.com/content/75/9/735#F3
英文:
I would like to produce something like the following graph (source) with ggplot. (I do not care at this stage about the colors or other aesthetic details, nor about the data, just about reproducing the overall layout.)
Here is my closest result.
library(tidyverse)
library(ggh4x)
df <- tribble(
~group, ~level, ~value, ~attribute,
"g1", 0, 1, "attr1",
"g1", 1, 2, "attr1",
"g1", 0, 0, "attr2",
"g1", 14, 3, "attr2",
"g1", 16, 4, "attr2",
"g2", 0, 3, "attr1",
"g2", 1, 1, "attr1",
"g2", 0, 0, "attr2",
"g2", 14, 2, "attr2",
"g2", 16, 3, "attr2"
)
ggplot(df, aes(x = interaction(level, attribute), y = value)) +
geom_bar(stat = "identity") +
ggtitle("Title") +
facet_wrap(~group) +
coord_flip() +
guides(y = "axis_nested") +
xlab("") +
ylab("Marginal utility")
I would prefer to display the attribute names on top (rather than on the side) of the attribute levels. In other words, I would like to have "attr2" on top of 16 and "attr1" on top of 1. I couldn't find how to do that.
A solution that does not consider two groups would also be useful if managing two groups is incompatible with my goal. Using something else than ggplot2 is acceptable as well if ggplot2 cannot achieve this goal.
答案1
得分: 2
一个潜在的解决方案是使用 facet_grid()
替代 facet_wrap()
和 {ggh4x},然后更改 y 轴的分面标签外观:
library(tidyverse)
df <- tribble(
~group, ~level, ~value, ~attribute,
"g1", 0, 1, "attr1",
"g1", 1, 2, "attr1",
"g1", 0, 0, "attr2",
"g1", 14, 3, "attr2",
"g1", 16, 4, "attr2",
"g2", 0, 3, "attr1",
"g2", 1, 1, "attr1",
"g2", 0, 0, "attr2",
"g2", 14, 2, "attr2",
"g2", 16, 3, "attr2"
)
ggplot(df, aes(x = value, y = factor(level))) +
geom_col() +
ggtitle("标题") +
facet_grid(attribute~group, scales = "free_y", switch ="y") +
ylab("") +
xlab("边际效用") +
theme_minimal() +
theme(plot.background = element_rect(
fill = "transparent", colour = "transparent"
),
axis.text.y = element_text(margin = margin(r = -25)),
strip.text.y.left = element_text(
angle = 0, face = "bold", vjust = 1
))
这将得到:
英文:
One potential solution is to use facet_grid()
instead of facet_wrap()
and {ggh4x}, and then change the way the facet labels look for the y-axis:
library(tidyverse)
df <- tribble(
~group, ~level, ~value, ~attribute,
"g1", 0, 1, "attr1",
"g1", 1, 2, "attr1",
"g1", 0, 0, "attr2",
"g1", 14, 3, "attr2",
"g1", 16, 4, "attr2",
"g2", 0, 3, "attr1",
"g2", 1, 1, "attr1",
"g2", 0, 0, "attr2",
"g2", 14, 2, "attr2",
"g2", 16, 3, "attr2"
)
ggplot(df, aes(x = value, y = factor(level))) +
geom_col() +
ggtitle("Title") +
facet_grid(attribute~group, scales = "free_y", switch ="y") +
ylab("") +
xlab("Marginal utility") +
theme_minimal() +
theme(plot.background = element_rect(
fill = "transparent", colour = "transparent"
),
axis.text.y = element_text(margin = margin(r = -25)),
strip.text.y.left = element_text(
angle = 0, face = "bold", vjust = 1
))
which gives:
答案2
得分: 1
这是一个可能实现你想要的效果的技巧。它基于以下三个假设:
- 你希望这个效果应用在y轴上。
- 你的条带标签比y轴文本更宽。
- 你不太关心条带和轴文本的右边距:刻度标记和标签之间的间距。
首先,我们将定义一个新的文本主题元素,只需重新定义 element_text()
。
library(ggplot2)
element_text_zerowidth <- function(...) {
el <- element_text(...)
class(el) <- c("zw_text", class(el))
el
}
然后,我们可以使用这个类来编写一个自定义的 element_grob()
的S3方法。我们欺骗ggplot2,让它认为这个文本没有宽度。
element_grob.zw_text <- function(...) {
grob <- NextMethod()
grob$widths <- rep(unit(0, "pt"), length(grob$widths))
grob
}
现在只要条带标题比轴文本长,你可以使用这个新元素来绘制零宽度的轴文本,这将使条带标签靠近轴。请注意,我们在这里移除了条带文本的边距,因为从轴线到文本的距离现在由刻度长度确定。
library(tibble)
df <- tribble(
~group, ~level, ~value, ~attribute,
"g1", "some reference level", 1, "some long strip text",
"g1", "another level", 2, "some long strip text",
"g1", "zero units", 0, "another long strip text",
"g1", "many units", 3, "another long strip text",
"g1", "even more units", 4, "another long strip text",
"g2", "some reference level", 3, "some long strip text",
"g2", "another level", 1, "some long strip text",
"g2", "zero units", 0, "another long strip text",
"g2", "many units", 2, "another long strip text",
"g2", "even more units", 3, "another long strip text"
)
ggplot(df, aes(x = value, y = factor(level))) +
geom_col() +
ggtitle("Title") +
facet_grid(attribute~group, scales = "free_y", switch ="y") +
ylab("") +
xlab("Marginal utility") +
theme_minimal() +
theme(strip.placement = "outside",
strip.text.y.left = element_text(angle = 0, face = "bold", vjust = 1, margin = margin()),
axis.text.y.left = element_text_zerowidth())
于2023年07月06日使用 reprex v2.0.2 创建
英文:
Here is trick that you might pull to get the effect you're after. It assumes 3 things:
- You want this on the y-axis.
- Your strip labels are wider than the y-axis text.
- You don't particularly care deeply about the right-most margin of the strip and axis text: the spacing between the tick marks and labels.
First, we'll define a new text theme element that just re-classes element_text()
.
library(ggplot2)
element_text_zerowidth <- function(...) {
el <- element_text(...)
class(el) <- c("zw_text", class(el))
el
}
We can then use this class to write a custom S3 method for element_grob()
. We fool ggplot2 into thinking that this text has no width.
element_grob.zw_text <- function(...) {
grob <- NextMethod()
grob$widths <- rep(unit(0, "pt"), length(grob$widths))
grob
}
Now as long as the strip titles are longer than the axis text, you can use this new element to draw zero-width axis text, which will make the strip labels adjacent to the axis. Note that we remove the margin of the strip text here, since distance from the axis line to the text is now determined by the tick length.
library(tibble)
df <- tribble(
~group, ~level, ~value, ~attribute,
"g1", "some reference level", 1, "some long strip text",
"g1", "another level", 2, "some long strip text",
"g1", "zero units", 0, "another long strip text",
"g1", "many units", 3, "another long strip text",
"g1", "even more units", 4, "another long strip text",
"g2", "some reference level", 3, "some long strip text",
"g2", "another level", 1, "some long strip text",
"g2", "zero units", 0, "another long strip text",
"g2", "many units", 2, "another long strip text",
"g2", "even more units", 3, "another long strip text"
)
ggplot(df, aes(x = value, y = factor(level))) +
geom_col() +
ggtitle("Title") +
facet_grid(attribute~group, scales = "free_y", switch ="y") +
ylab("") +
xlab("Marginal utility") +
theme_minimal() +
theme(strip.placement = "outside",
strip.text.y.left = element_text(angle = 0, face = "bold", vjust = 1, margin = margin()),
axis.text.y.left = element_text_zerowidth())
<!-- -->
<sup>Created on 2023-07-06 with reprex v2.0.2</sup>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论