Я пытаюсь построить график с несколькими сложенными столбиками и точкой (соответствующей их сумме). Однако у меня возникли проблемы с размещением ключей легенды в нужных мне позициях. В частности у меня два вопроса:
Вот воспроизводимый пример:
plot_data <- structure(list(unit = c("AA", "AA", "AA", "AA", "AA", "AA", "AA"
), group = c("A", "B", "C", "D", "E", "F", "G"), varZ = c(0.315095655620098,
8.47993358969688, 1.07286661490798, 0.451091277599335, -3.06214834600687,
0.356725811958313, 2.84848970770836), varA = c(0.0098613808164373,
0.641465996578336, 0.361015032231808, 0.213144088536501, 0.389915533736348,
0.0736596005037427, 0.110731973079965), varC = c(0.341579372435808,
0.330122645944357, 0.45065661072731, 0.092761187441647, 0.368695167452097,
0.542871625348926, 0.563651240617037), varD = c(-0.00427791306283325,
0.323448045179248, 0.017125289858086, 0.0177044845186174, 0.0418251685332507,
0.0367124438518658, 0.0115989629644901), varB = c(-0.101550728082657,
1.68335139006376, 0.36021358249709, -0.529840487614274, -0.199755599349737,
0.260609032586217, 0.358311578631401), varE = c(0.0694835543632507,
5.50154550075531, -0.116143900901079, 0.657321977615356, -3.66282860934734,
-0.5571269325912, 1.80419590175152)), row.names = c(NA, -7L), class = c("data.table",
"data.frame"))
## Split in two melted data tables. One with data for the stacked bars, one for the dots
bar_data <- plot_data[][, list(unit, group, varA, varB, varC, varD, varE)]
bar_data <- melt(bar_data, id.vars= c("unit", "group"))
line_data <- plot_data[][, list(unit, group, varZ)]
line_data <- melt(line_data, id.vars=c("unit", "group"))
p <-
ggplot() +
# Stacked bars
geom_col( data = bar_data,
aes( y = value,
x = group,
fill = variable
)
) +
# Point
geom_point( data = line_data,
aes( y = value,
x = group,
colour = "Z"
)
) +
theme(
legend.key.height = unit(.15,"cm"),
legend.key.width = unit(.8,"cm"),
legend.position = "bottom",
legend.title = element_blank()
) +
scale_fill_manual(values = c("#005390", "#B6C5DE", "#082A4A", "#7AACF6", "#00B0F0"),
labels = c("A", "B", "C", "D", "E")
) +
scale_color_manual(values = c("Z" = "black")) +
guides(fill = guide_legend(ncol = 2))
Просто измените colour
на fill
в geom_point
и измените shape
.
Пара замечаний:
show.legend = FALSE
в geom_point
, чтобы не показывать точки на каждой метке легенды. Если вы попытаетесь удалить его, вы поймете, что я имею в виду.scale_fill_manual
, добавив Z
и 'black'
.Так:
ggplot() +
# Stacked bars
geom_col( data = bar_data,
aes( y = value,
x = group,
fill = variable
)
) +
# Point
geom_point( data = line_data,
aes( y = value,
x = group,
fill = "Z"
), shape = 20, size = 3, show.legend = FALSE
) +
theme(
legend.key.height = unit(.15,"cm"),
legend.key.width = unit(.8,"cm"),
legend.position = "bottom",
legend.title = element_blank()
) +
scale_fill_manual(values = c("#005390", "#B6C5DE", "#082A4A", "#7AACF6", "#00B0F0", "black"),
labels = c("A", "B", "C", "D", "E", "Z")
) +
guides(fill = guide_legend(ncol = 2))
Обновлено: основываясь на принятом решении, я оставлю здесь очень похожий код, но только на основе data.table
. Кроме того, я написал другой ввод guide_legend
, поскольку тот, который указан в принятом решении, не работал на моей машине.
library(data.table)
library(ggplot2)
data <- melt(plot_data, id.vars = c("unit", "group"))
ggplot() +
# Stacked bars
geom_col(
data = data[variable != "varZ"],
aes(y = value,
x = group,
fill = variable)
) +
# Point
geom_point(
data = data[variable == "varZ"],
aes(y = value,
x = group,
colour = variable)
) +
theme(
legend.key.height = unit(.15,"cm"),
legend.key.width = unit(.8,"cm"),
legend.position = "bottom",
legend.title = element_blank()
) +
scale_fill_manual(
values = c("#005390", "#B6C5DE", "#082A4A", "#7AACF6", "#00B0F0", "black"),
labels = c("A", "B", "C", "D", "E", "Z"),
aesthetics = c("colour", "fill"),
guide = guide_legend(ncol = 2, byrow = TRUE)
)
Я не понимаю первый вопрос. Вы скопировали мой код? конкретно часть Scale_fill_manual? Обратите внимание, что я удаляю файл Scale_colour_manual. Вторая проблема, я не уверен, разрешима. Мне нужно проверить. Для третьего я оставил заказ, как вы на своей картинке. Какой заказ вы хотите?
Я точно скопировал ваши данные, но Z отображается белым прямоугольником вместо черного. Говоря о порядке, я имею в виду, что он должен идти слева направо, а не сверху вниз.
Чтобы переупорядочить элементы легенды, проще просто изменить порядок факторов, прежде чем создавать график. В вашем примере вы можете сделать это с помощью простой строки кода, например:
bar_data$variable <-factor(bar_data$variable,levels=c('varE', 'varC', 'varA','varB', 'varD'))
С точки зрения объединения точки и цветов для вашего графика, это может быть сложно при использовании двух типов переменных (но, как правило, это можно обойти). Если Z представляет собой сумму обработок (и, следовательно, отличается от переменных A-E), у меня возникнет соблазн вообще не включать ее в легенду (поскольку она технически не эквивалентна другим буквам), а вместо этого объяснить, что означают черные точки. подпись под рисунком.
Если я ошибаюсь, и Z эквивалентен другим буквам, то, возможно, рассмотрите возможность добавления его в тот же фрейм данных bar_data и построения его единым способом.
Вы правы (Z — сумма других переменных). То, что вы предлагаете, может быть вариантом. Тем не менее, меня все равно очень заинтересовало бы хитрое решение — добавить его в то же поле легенды, что и столбцы. Как это можно сделать?
Вы можете легко добиться желаемого результата, работая с одним набором данных, включающим все категории, и преобразуя variable
в factor
. Затем используйте отфильтрованный набор данных для каждого из geom
, сопоставьте variable
с color
aes также в geom_point
и, наконец, примените один и тот же масштаб как к color
, так и к fill
aes, чего можно добиться с помощью аргумента aesthetics=
.
Для порядка категорий в легенде. Чтобы упорядочить категории по строкам, вы можете установить legend.byrow=TRUE
, а для достижения любого желаемого порядка вы можете использовать аргумент limits
. В последнем случае (но я бы предложил это в целом) используйте именованный вектор цветов и меток, чтобы они были назначены правильным категориям при изменении ограничений.
library(ggplot2)
library(tidyr)
library(dplyr, warn = FALSE)
plot_data <- plot_data |>
tidyr::pivot_longer(!c(unit, group), names_to = "variable") |>
dplyr::mutate(variable = factor(variable))
pal_color <- c("#005390", "#B6C5DE", "#082A4A", "#7AACF6", "#00B0F0", "black")
labels_color <- c("A", "B", "C", "D", "E", "Z")
names(pal_color) <- names(labels_color) <- levels(plot_data$variable)
p <- ggplot(mapping = aes(
y = value,
x = group
)) +
geom_col(
data = dplyr::filter(plot_data, variable != "varZ"),
aes(
fill = variable
)
) +
geom_point(
data = dplyr::filter(plot_data, variable == "varZ"),
aes(
color = variable
)
) +
theme(
legend.key.height = unit(.15, "cm"),
legend.key.width = unit(.8, "cm"),
legend.position = "bottom",
legend.title = element_blank()
)
p +
scale_fill_manual(
values = pal_color,
labels = labels_color,
aesthetics = c("color", "fill"),
guide = guide_legend(ncol = 2, theme = theme(legend.byrow = TRUE))
)
p +
scale_fill_manual(
values = pal_color,
labels = labels_color,
aesthetics = c("color", "fill"),
guide = guide_legend(ncol = 2),
limits = c("varD", "varE", "varZ", "varA", "varB", "varC")
)
Большое спасибо! Это работает очень хорошо, за исключением: как мне убедиться в том, что в строке labels_color <- c("A", "B", "C", "D", "E", "Z")
метка «A» соответствует «varA», «B» соответствует «varB» и т. д.? Я понимаю, что это зависит только от того, как сортируются данные, поэтому в зависимости от того, как они сортируются, метка «A» может быть присвоена другой переменной, чем «varA».
@stefan, тебе не нужна вся эта часть с tidyverse
. Просто измените форму plot_data
с помощью data <- melt(plot_data, id.vars=c("unit", "group"))
в начале. Затем накормите geom_col
с помощью data[variable != "varZ"]
и geom_point
с помощью data[variable == "varZ"]
. Это делает раствор чище и облегчает его повторное использование. (проголосовал, кстати!)
отвечая на мой собственный вопрос: чтобы убедиться, что маркировка соответствует правильным переменным, я объявляю уровни факторов, предложенные Эллиотом, а затем гарантирую использование того же порядка в маркировке, т. е. plot_data[, variable := factor(variable, levels=c("A", "B", "C", "D", "E", "Z"))]
, а затем labels_color <- c("A", "B", "C", "D", "E", "Z")
Базовое решение R:
cols <- c("#005390", "#B6C5DE", "#082A4A", "#7AACF6", "#00B0F0")
plot_data.neg <- plot_data.pos <- plot_data[,c(2,4:8)]
plot_data.pos[,2:6][plot_data[,4:8] <0] <- 0
plot_data.neg[,2:6][plot_data[,4:8]>=0] <- 0
ylim <- c(min(colSums(plot_data.neg[,2:6])) * 1.15,
max(colSums(plot_data.pos[,2:6])) * 1.15)
par(mar=c(3,2,1,1), las=1)
bp <- barplot(cbind(varE, varD, varC, varB, varA)~group,
data=plot_data.pos,
ylim=ylim,
legend.text=c("Z", LETTERS[5:1]),
args.legend=list(x = "topright", nc=2, box.lty=0,
fill=c(cols, NA),
border=NA,
pch=c(rep(NA, 5), 21),
pt.bg = "red", col = "black"),
col=rev(cols),
axis.lty = 1
)
barplot(cbind(varE, varD, varC, varB, varA)~group,
data=plot_data.neg,
ylim=rev(ylim),
col=rev(cols),
add=TRUE
)
points(bp, plot_data$varZ, pch=21, bg = "red", col = "black")
(у вас потрясающее понимание графиков базы R! поздравляю!) однако что-то выглядит не так: столбцы A D E должны быть частично положительными и отрицательными, но они стоят только с одной стороны. Действительно, Z, которая является суммой, не находится сверху A и D или внизу E.
@Edo Может быть, я сделал это очень быстро. Я еще раз проверю, когда будет больше времени…
Большое спасибо! Однако у меня все еще есть три проблемы: 1) с вашим кодом Z теперь появляется вместе с другими клавишами, но с белым (невидимым) полем, а не с черным, как на вашем рисунке. 2) Есть ли способ показать Z точкой, как на графике, а не столбиком? 3) Почему легенда расположена вертикально, а не горизонтально? (т.е. B должен располагаться справа от A, а не под ним и т. д.)