Редактировать 2:
По второму вопросу ОП в комментарии:
- Нет необходимости добавлять
geom_hline()
для отображения оси, просто добавьте axis.line
к theme()
и panel.spacing.x=unit(0, "lines")
, чтобы сделать его непрерывным по всем граням
gg <- ggplot(aes(x=as.factor(Site), y=Average, fill=as.factor(Site)), data=data)
gg <- gg + geom_bar(stat = 'identity')
gg <- gg + scale_fill_discrete(guide_legend(title = 'Site')) # just to get 'site' instead of 'as.factor(Site)' as legend title
# gg <- gg + scale_fill_manual(values=c('black', 'grey85'), guide_legend(title = 'Site')) # to get bars in black and grey instead of ggplot's default colors
# gg <- gg + theme_classic() # get white background and black axis.line for x- and y-axis
gg <- gg + geom_errorbar(aes(ymin=Average-SEM, ymax=Average+SEM), width=.3)
gg <- gg + facet_wrap(~Season*Exposure, strip.position=c('bottom'), nrow=1, drop=F)
gg <- gg + scale_y_continuous(expand = expand_scale(mult = c(0, .05))) # remove space below zero
gg <- gg + theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.title.x = element_blank(),
axis.line = element_line(color='black'),
strip.placement = 'outside', # place x-axis above (factor-label-) strips
panel.spacing.x=unit(0, "lines"), # remove space between facets (for continuous x-axis)
panel.grid.major.x = element_blank(), # remove vertical grid lines
# panel.grid = element_blank(), # remove all grid lines
# panel.background = element_rect(fill='white'), # choose background color for plot area
strip.background = element_rect(fill='white', color='white') # choose background for factor labels, color just matters for theme_classic()
)
- Чтобы разместить метки экспозиции над метками сезона в полосках фасет, вы можете изменить накладку на каждую полосу
# facet factor levels
season.levels <- levels(data$Season)
exposure.levels <- levels(data$Exposure)
# convert to gtable
g <- ggplotGrob(gg)
# find the grobs of the strips in the original plot
grob.numbers <- grep("strip-b", g$layout$name)
# filter strips from layout
b.strips <- gtable_filter(g, "strip-b", trim = FALSE)
# b.strips$layout shows the strips position in the cell grid of the plot
# b.strips$layout
season.left.panels <- seq(1, by=length(levels(data$Exposure)), length.out = length(season.levels))
season.right.panels <- seq(length(exposure.levels), by=length(exposure.levels), length.out = length(season.levels))
left <- b.strips$layout$l[season.left.panels]
right <- b.strips$layout$r[season.right.panels]
top <- b.strips$layout$t[1]
bottom <- b.strips$layout$b[1]
# create empty matrix as basis to overly new gtable on the strip
mat <- matrix(vector("list", length = 10), nrow = 2)
mat[] <- list(zeroGrob())
# add new gtable matrix above each strip
for (i in 1:length(season.levels)) {
res <- gtable_matrix("season.strip", mat, unit(c(1, 0, 1, 0, 1), "null"), unit(c(1, 1), "null"))
season.left <- season.left.panels[i]
# place season labels below exposure labels in row 2 of the overlayed gtable for strips
res <- gtable_add_grob(res, g$grobs[[grob.numbers[season.left]]]$grobs[[1]], 2, 1, 2, 5)
# move exposure labels to row 1 of the overlayed gtable for strips
for (j in 0:2) {
exposure.x <- season.left+j
res$grobs[[c(1, 5, 9)[j+1]]] <- g$grobs[[grob.numbers[exposure.x]]]$grobs[[2]]
}
new.grob.name <- paste0(levels(data$Season)[i], '-strip')
g <- gtable_add_grob(g, res, t = top, l = left[i], b = top, r = right[i], name = c(new.grob.name))
new.grob.no <- grep(new.grob.name, g$layout$name)[1]
g$grobs[[new.grob.no]]$grobs[[nrow(g$grobs[[new.grob.no]]$layout)]]$children[[2]]$children[[1]]$gp <- gpar(fontface='bold')
}
grid.newpage()
grid.draw(g)
Результат выглядит так:
- Чтобы также получить столбцы черного и серого цветов, как на вашем примере изображения, измените ggplot следующим образом:
gg <- ggplot(aes(x=as.factor(Site), y=Average, fill=as.factor(Site)), data=data)
gg <- gg + geom_bar(stat = 'identity')
# gg <- gg + scale_fill_discrete(guide_legend(title = 'Site')) # just to get 'site' instead of 'as.factor(Site)' as legend title
gg <- gg + scale_fill_manual(values=c('black', 'grey85'), guide_legend(title = 'Site')) # to get bars in black and grey instead of ggplot's default colors
gg <- gg + theme_classic() # get white background and black axis.line for x- and y-axis
gg <- gg + geom_errorbar(aes(ymin=Average-SEM, ymax=Average+SEM), width=.3)
gg <- gg + facet_wrap(~Season*Exposure, strip.position=c('bottom'), nrow=1, drop=F)
gg <- gg + scale_y_continuous(expand = expand_scale(mult = c(0, .05))) # remove space below zero
gg <- gg + theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.title.x = element_blank(),
axis.line = element_line(color='black'),
strip.placement = 'outside', # place x-axis above (factor-label-) strips
panel.spacing.x=unit(0, "lines"), # remove space between facets (for continuous x-axis)
panel.grid.major.x = element_blank(), # remove vertical grid lines
# panel.grid = element_blank(), # remove all grid lines
# panel.background = element_rect(fill='white'), # choose background color for plot area
strip.background = element_rect(fill='white', color='white') # choose background for factor labels, color just matters for theme_classic()
)
Результат должен выглядеть следующим образом:
Редактировать:
На вопрос ОП в комментарии:
- Удаление линий сетки можно сделать с помощью
ggplot
theme()
:
gg <- ggplot(aes(x=as.factor(Site), y=Average, fill=as.factor(Site)), data=data)
gg <- gg + geom_bar(stat = 'identity')
gg <- gg + geom_errorbar(aes(ymin=Average-SEM, ymax=Average+SEM), width=.3)
gg <- gg + facet_wrap(~Season*Exposure, strip.position=c('bottom'), nrow=1, drop=F)
gg <- gg + scale_fill_discrete(guide_legend(title = 'Site'))
gg <- gg + theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.title.x = element_blank(),
panel.grid.major.x = element_blank(), # remove vertical grid lines
# panel.grid = element_blank(), # remove al grid lines
# panel.background = element_rect(fill='white'), # choose background color for plot area
strip.background = element_rect(fill='white') # choose background for factor labels
)
- Иметь только один лейбл на каждый сезон немного сложнее. Вам нужно будет отредактировать
gtable
из ggplot
.
Один из способов сделать это будет следующим:
# facet factor levels
season.levels <- levels(data$Season)
exposure.levels <- levels(data$Exposure)
# convert to gtable
g <- ggplotGrob(gg)
# find the grobs of the strips in the original plot
grob.numbers <- grep("strip-b", g$layout$name)
# filter strips from layout
b.strips <- gtable_filter(g, "strip-b", trim = FALSE)
# b.strips$layout shows the strips position in the cell grid of the plot
b.strips$layout
season.left.panels <- seq(1, by=length(levels(data$Exposure)), length.out = length(season.levels))
season.right.panels <- seq(length(exposure.levels), by=length(exposure.levels), length.out = length(season.levels))
left <- b.strips$layout$l[season.left.panels]
right <- b.strips$layout$r[season.right.panels]
top <- b.strips$layout$t[1]
bottom <- b.strips$layout$b[1]
# create empty matrix as basis to overly new gtable on the strip
mat <- matrix(vector("list", length = 10), nrow = 2)
mat[] <- list(zeroGrob())
# add new gtable matrix above each strip
for (i in 1:length(season.levels)) {
res <- gtable_matrix("season.strip", mat, unit(c(1, 0, 1, 0, 1), "null"), unit(c(1, 1), "null"))
res <- gtable_add_grob(res, g$grobs[[grob.numbers[season.left.panels[i]]]]$grobs[[1]], 1, 1, 1, 5)
new.grob.name <- paste0(levels(data$Season)[i], '-strip')
g <- gtable_add_grob(g, res, t = top, l = left[i], b = top, r = right[i], name = c(new.grob.name))
new.grob.no <- grep(new.grob.name, g$layout$name)
g$grobs[[new.grob.no]]$grobs[[nrow(g$grobs[[new.grob.no]]$layout)]]$children[[2]]$children[[1]]$gp <- gpar(fontface='bold')
}
grid.newpage()
grid.draw(g)
Оригинальный ответ
Я думаю, что то, что вы ищете, может быть наилучшим образом достигнуто с помощью ggplot()
.
data <- expand.grid(c('Spring', 'Summer', 'Autumn', 'Winter'), c('Sheltered', 'Moderately exposed', 'Exposed'), c(1, 2))
names(data) <- c('Season', 'Exposure', 'Site')
# adding some arbitrary values
set.seed(42)
data$Average <- sample(c(rep(3, 3), rep(2, 2), rep(1, 2), rep(NA, 17)))
data$SEM <- NA
SEM <- sample(c(rep(0.5, 3), rep(0.3, 2), rep(.1, 2)))
data$SEM[which(!is.na(data$Average))] <- SEM
gg <- ggplot(aes(x=as.factor(Site), y=Average, fill=as.factor(Site)), data=data)
gg <- gg + geom_bar(stat = 'identity')
gg <- gg + geom_errorbar(aes(ymin=Average-SEM, ymax=Average+SEM), width=.3)
gg <- gg + facet_wrap(~Season*Exposure, strip.position=c('bottom'), nrow=1, drop=F)
gg <- gg + scale_fill_discrete(guide_legend(title = 'Site'))
gg <- gg + theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.title.x = element_blank())
print(gg)