Как рисовать линии за пределами области графика в ggplot2?
Я создал этот сюжет с ggplot2:
Внешние линии должны соответствовать шкале Y (т. Е. Позиция Y строк для Text1 должна быть 100 и 85). Единственный способ сделать это - нарисовать пустой график справа от фигуры с тем же масштабом, что и на диаграмме, а затем использовать функцию аннотирования для рисования линий. Другой подход состоит в том, чтобы просто "вручную" нарисовать линии с grid.lines
Однако координаты grid.lines
не будет соответствовать шкале Y графика.
Можно ли как-то нарисовать эти линии, используя другой подход? Я предполагаю, что это должно быть сделано с grid.lines
, Как я мог передать координаты Y диаграммы grid.lines
?
Ниже приведен минимальный код, используемый для создания этой фигуры:
library (ggplot2)
test= data.frame(
group=c(rep(1,6), rep(2,6)),
subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
category=c( rep(1:3, 4)),
count=c( 10,80,10,5,90,5, 10,80,10,5,90,5 )
)
qplot(subgroup,
count,
data=test,
geom="bar",
stat="identity",
fill =category,
facets = .~ group, width=0.9)+
opts(legend.position="none",
plot.margin = unit(c(0,9,2,0), "lines"))
Как я могу нарисовать линии справа от столбцов?
Недавно я задал вопрос о рисовании текста вне области печати в ggplot2, и решение было использовать gt$layout
а также grid.draw
,
Отображение текста под графиком, сгенерированным ggplot2
Можно ли использовать подобный подход здесь? Насколько я понимаю, annotation_custom только для текста и не будет работать с другими графическими элементами. Спасибо
3 ответа
Обновить
Используется оригинальное решение annotation_custom
, но проблема с annotation_custom
является то, что он рисует аннотации на всех панелях. Тем не менее, с простой модификацией, annotation_custom
рисовать можно только на одной панели (взято из ответа Баптиста здесь)
annotation_custom2 <-
function (grob, xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf, data)
{
layer(data = data, stat = StatIdentity, position = PositionIdentity,
geom = ggplot2:::GeomCustomAnn,
inherit.aes = TRUE, params = list(grob = grob,
xmin = xmin, xmax = xmax,
ymin = ymin, ymax = ymax))
}
library(ggplot2)
library(grid)
#Some data
test = data.frame(
group=c(rep(1,6), rep(2,6)),
subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
category=c( rep(1:3, 4)),
count=c( 10,80,10,5,90,5, 10,80,10,5,90,5 )
)
# base plot
p <- ggplot(test) +
geom_bar(aes(subgroup, count, fill = category), stat = "identity") +
facet_grid(. ~ group) +
theme(legend.position = "none",
plot.margin = unit(c(1,5,1,1), "lines"))
# Create the text Grobs
Text1 = textGrob("Text 1")
Text2 = textGrob("Text 2")
Text4 = textGrob("Text 4")
## Add the annotations
# Which panel to attach the annotations
data = data.frame(group=2)
# Text 1
p1 = p + annotation_custom2(Text1, xmin = 3., xmax = 3., ymin = 85, ymax = 100, data = data) +
annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 100, ymax = 100, data = data) +
annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 85, ymax = 85, data = data) +
annotation_custom2(linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 85, ymax = 100, data = data)
# Text 2
p1 = p1 + annotation_custom2(Text2, xmin = 3, xmax = 3, ymin = 20, ymax = 80, data = data) +
annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 80, ymax = 80, data = data) +
annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 20, ymax = 20, data = data) +
annotation_custom2(linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 20, ymax = 80, data = data)
# Text 4
p1 = p1 + annotation_custom2(Text4, xmin = 3, xmax = 3, ymin = 0, ymax = 15, data = data) +
annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 15, ymax = 15, data = data) +
annotation_custom2(linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 0, ymax = 0, data = data) +
annotation_custom2(linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 0, ymax = 15, data = data)
# Code to override clipping
gt <- ggplotGrob(p1)
gt$layout[grepl("panel", gt$layout$name), ]$clip <- "off"
# Draw the plot
grid.newpage()
grid.draw(gt)
Оригинальное решение
Я думаю, что почти любой Grob создан с использованием grid()
может быть использован в annotation_custom()
, Там могут быть более аккуратные способы сделать это, но вот способ использования grid
, annotation_custom
и код @baptiste отсюда, чтобы переопределить отсечение (как в предыдущем посте).
library (ggplot2)
library(grid)
test= data.frame(
group=c(rep(1,6), rep(2,6)),
subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
category=c( rep(1:3, 4)),
count=c( 10,80,10,5,90,5, 10,80,10,5,90,5 )
)
## EDIT: Updated qplot() command
p <- qplot(subgroup, count,
data = test, geom = "bar", stat = "identity",
fill = category,
facets = .~ group, width = 0.9)+
theme(legend.position="none", plot.margin = unit(c(0,9,2,0), "lines"))
# Create the text Grobs
Text1 = textGrob("Text 1")
Text2 = textGrob("Text 2")
Text4 = textGrob("Text 4")
# Draw the plot
# Text 1
p1 = p + annotation_custom(grob = Text1, xmin = 3., xmax = 3., ymin = 85, ymax = 100) +
annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 100, ymax = 100) +
annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 85, ymax = 85) +
annotation_custom(grob = linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 85, ymax = 100)
# Text 2
p1 = p1 + annotation_custom(grob = Text2, xmin = 3, xmax = 3, ymin = 20, ymax = 80) +
annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 80, ymax = 80) +
annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 20, ymax = 20) +
annotation_custom(grob = linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 20, ymax = 80)
# Text 4
p1 = p1 + annotation_custom(grob = Text4, xmin = 3, xmax = 3, ymin = 0, ymax = 15) +
annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 15, ymax = 15) +
annotation_custom(grob = linesGrob(), xmin = 2.6, xmax = 2.75, ymin = 0, ymax = 0) +
annotation_custom(grob = linesGrob(), xmin = 2.75, xmax = 2.75, ymin = 0, ymax = 15)
p1
# Code to override clipping
gt <- ggplot_gtable(ggplot_build(p1))
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid.draw(gt)
обновленный opts
устарела; использование theme
вместо.
Вот еще одно решение. Это обходит проблему annotation_custom()
рисование гробов на обеих панелях. Он рисует два графика: первый - это ваш гистограмма; вторая содержит только аннотации. Затем два соединяются с помощью grid.arrange()
от gridExtra
пакет. Тем не менее, ваша проблема ломаной линии остается.
Существует проблема получения одинаковых масштабов оси Y на двух графиках. Но с осторожностью это можно сделать. Обратите внимание, что на графике, содержащем аннотации, элементы, которые могут повлиять на масштаб по оси Y, не удаляются (с помощью theme_blank()
, а скорее скрыты (используя colour = NA
).
library(ggplot2)
library(gridExtra)
library(grid)
test= data.frame(
group=c(rep(1,6), rep(2,6)),
subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
category=c( rep(1:3, 4)),
count=c( 10,80,10,5,90,5, 10,80,10,5,90,5))
# The bar plot
p1 <- ggplot(test, aes(subgroup, count, fill = category)) +
geom_bar(stat = "identity") +
facet_grid(.~ group) +
theme(legend.position = "none",
plot.margin = unit(c(1,0,2,0), "lines"))
p1 <- p1 + ylim(0, 100)
# The empty plot to contain the annotations
p2 = ggplot(data.frame(x = c(1,2), y = c(0,100), z = c(1,1)), aes(x,y)) + theme_bw() + facet_wrap(~ z) +
theme(axis.title.y = element_blank(),
axis.title.x = element_text(colour = NA),
axis.text.y = element_blank(),
axis.text.x = element_text(colour = NA),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.ticks = element_line(colour = NA),
panel.border = element_rect(colour = NA),
strip.background = element_rect(colour = NA, fill = NA),
strip.text.x = element_text(colour = NA),
plot.margin = unit(c(1,0,2,0), "lines"))
# The annotations
Text1 = textGrob("Text 1")
Text2 = textGrob("Text 2")
Text4 = textGrob("Text 4")
p2 = p2 + annotation_custom(grob = Text1, xmin = 1.4, xmax = 1.4, ymin = 85, ymax = 100) +
annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 100, ymax = 100) +
annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 85, ymax = 85) +
annotation_custom(grob = linesGrob(), xmin = 1.1, xmax = 1.1, ymin = 85, ymax = 100)
p2 = p2 + annotation_custom(grob = Text2, xmin = 1.4, xmax = 1.4, ymin = 20, ymax = 80) +
annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 80, ymax = 80) +
annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 20, ymax = 20) +
annotation_custom(grob = linesGrob(), xmin = 1.1, xmax = 1.1, ymin = 20, ymax = 80)
p2 = p2 + annotation_custom(grob = Text4, xmin = 1.4, xmax = 1.4, ymin = 0, ymax = 15) +
annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 15, ymax = 15) +
annotation_custom(grob = linesGrob(), xmin = 1, xmax = 1.1, ymin = 0, ymax = 0) +
annotation_custom(grob = linesGrob(), xmin = 1.1, xmax = 1.1, ymin = 0, ymax = 15)
# Putting the two plots together
plot = arrangeGrob(p1, p2, ncol = 2, widths = unit(c(10, 2), c("null", "null")))
grid.draw(plot)
Я добавил строки / текст, используя код из этой ссылки: Используя grconvertX/grconvertY в ggplot2. Этот подход использует grid.text и grid.lines вместо grobs. Я не уверен, какой подход лучше.
Я думаю, что grid.lines можно объединить в оператор grid.polyline или, возможно, сделать через цикл. Позиции x и y могут быть установлены в одну переменную вместо жесткого кодирования в каждой строке.
Единственное возможное осложнение - передача шкалы в окно просмотра. Однако, если в GGPLOT и в окне просмотра используется тот же масштаб, этот код должен работать. Обратите внимание, что область просмотра использует всю высоту графика от 0 до 100.
library (ggplot2)
library(grid)
library(gridBase)
test= data.frame(
group=c(rep(1,6), rep(2,6)),
subgroup=c( 1,1,1,2,2,2,1,1,1,2,2,2),
category=c( rep(1:3, 4)),
count=c( 10,80,10,5,90,5, 10,80,10,5,90,5 )
)
qplot(subgroup, count,
data=test, geom="bar", stat="identity",
fill =category,
facets = .~ group, width=0.9)+
opts(legend.position="none", plot.margin = unit(c(0,9,2,0), "lines"))
current.vpTree()
downViewport('panel-4-6')
pushViewport(dataViewport( yscale=c(0,100), clip='off',xscale=c(0,1)))
grid.text(x=1.21, y = 90, default.units='native' ,label="Text 1")
grid.text(x=1.21, y = 55, default.units='native' ,label="Text 2")
grid.text(x=1.21, y = 10, default.units='native' ,label="Text 3")
grid.lines(x=c(1.02,1.12), y = c(95,95), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(85, 85), default.units='native' )
grid.lines(x=c(1.12,1.12), y = c(85, 95), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(80, 80), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(20, 20), default.units='native' )
grid.lines(x=c(1.12,1.12), y = c(80, 20), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(5, 5), default.units='native' )
grid.lines(x=c(1.02,1.12), y = c(15, 15), default.units='native' )
grid.lines(x=c(1.12,1.12), y = c(5, 15), default.units='native' )
Извиняюсь за любые проблемы форматирования - я просто вставил свой код и использовал кнопку кода, чтобы сделать отступ.