Зарисовать легенду за пределами области черчения в базовой графике?


185

Как написано в заголовке: как можно использовать легенду за пределами области печати при использовании базовой графики?

Я думал о том, чтобы возиться layoutи создать пустой график, содержащий только легенду, но мне было бы интересно использовать способ, использующий только базовый график, и, например, выделить par(mar = )место справа от графика для легенды.


Вот пример:

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")
legend(1,-1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

производит:

альтернативный текст

Но, как я уже сказал, я бы хотел, чтобы легенда находилась вне области построения (например, справа от графика / графика).


... вы также можете взломать поддельный контейнер для легенды, простой и довольно удобный время от времени. Подобный вопрос здесь .
HHH

3
@hhh Ссылка больше не работает. Можете ли вы обновить его или опубликовать ответ, используя этот подход?
Хенрик

Ответы:


111

Возможно, вам нужно par(xpd=TRUE)сделать так, чтобы вещи рисовались за пределами области сюжета. Так что, если вы сделаете основной сюжет, у bty='L'вас будет место справа для легенды. Обычно это обрезается до области графика, но сделайте это, par(xpd=TRUE)и с небольшой корректировкой вы можете получить легенду настолько далеко, насколько это возможно:

 set.seed(1) # just to get the same random numbers
 par(xpd=FALSE) # this is usually the default

 plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2), bty='L')
 # this legend gets clipped:
 legend(2.8,0,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

 # so turn off clipping:
 par(xpd=TRUE)
 legend(2.8,-1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

33
Обратите внимание, что вы можете передать xpd непосредственно в условные обозначения, поэтому вам не нужно беспокоиться о том, чтобы впоследствии сбросить номинал. Также см. GrconvertX & Y, чтобы узнать способ указания местоположения легенды, не зависящий от ограничений выводимых вами данных.
Чарльз

6
поскольку этот вопрос и ответ по-прежнему очень популярны, par(xpd=NA)он еще более силен (т. е. готовит участки к большему количеству регионов).
Хенрик

+1. Следует отметить, что имеет смысл провести отдельный parзвонок прямо перед легендой. В своем сюжете я использовал par(new=T)несколько других случаев и просто хотел добавить xpdпараметр в тот же вызов, что вызывает проблемы.
Мэтт Баннер

146

Никто не упомянул использование отрицательных insetзначений для legend. Вот пример, где легенда находится справа от графика и выровнена по верху (используя ключевое слово "topright").

# Random data to plot:
A <- data.frame(x=rnorm(100, 20, 2), y=rnorm(100, 20, 2))
B <- data.frame(x=rnorm(100, 21, 1), y=rnorm(100, 21, 1))

# Add extra space to right of plot area; change clipping to figure
par(mar=c(5.1, 4.1, 4.1, 8.1), xpd=TRUE)

# Plot both groups
plot(y ~ x, A, ylim=range(c(A$y, B$y)), xlim=range(c(A$x, B$x)), pch=1,
               main="Scatter plot of two groups")
points(y ~ x, B, pch=3)

# Add legend to top right, outside plot region
legend("topright", inset=c(-0.2,0), legend=c("A","B"), pch=c(1,3), title="Group")

Первое значение inset=c(-0.2,0)может потребоваться настроить в зависимости от ширины легенды.

legend_right


14
@ Генрик Нет, это не работает без xpd = TRUE. Также обратите внимание, что лучше установить xpd = TRUE в качестве аргумента функции legend ().
Стефан Лоран

1
Иногда xpdдолжно быть установлено, чтобы TRUEотрицательная вставка работала. Но иногда нет. С командой args.legend=list(x="bottom", horiz=TRUE, inset=-0.2)внутри barplot(...она, кажется, не нужна, xpd=TRUEно только legend(x="bottom", horiz=TRUE, inset=-0.2)с необходимостью xpd=TRUE. Есть идеи? Я просто запутался в передаче моих аргументов?
user3386170

28

Другое решение, кроме уже упомянутых (с помощью layoutили par(xpd=TRUE)), состоит в том, чтобы наложить свой график прозрачным графиком на все устройство, а затем добавить к нему легенду.

Хитрость заключается в том, чтобы наложить (пустой) график на всю область построения и добавить к нему легенду. Мы можем использовать par(fig=...)опцию. Сначала мы инструктируем R создать новый график на всем графическом устройстве:

par(fig=c(0, 1, 0, 1), oma=c(0, 0, 0, 0), mar=c(0, 0, 0, 0), new=TRUE)

Настройка omaи marнеобходима, так как мы хотим, чтобы интерьер сюжета охватывал все устройство. new=TRUEнеобходим для предотвращения запуска R нового устройства. Затем мы можем добавить пустой график:

plot(0, 0, type='n', bty='n', xaxt='n', yaxt='n')

И мы готовы добавить легенду:

legend("bottomright", ...)

добавит легенду в правом нижнем углу устройства. Аналогично, мы можем добавить легенду к верхнему или правому полю. Единственное, что нам нужно, это убедиться, что поле оригинального сюжета достаточно велико, чтобы вместить легенду.

Поместить все это в функцию;

add_legend <- function(...) {
  opar <- par(fig=c(0, 1, 0, 1), oma=c(0, 0, 0, 0), 
    mar=c(0, 0, 0, 0), new=TRUE)
  on.exit(par(opar))
  plot(0, 0, type='n', bty='n', xaxt='n', yaxt='n')
  legend(...)
}

И пример. Сначала создайте сюжет, убедившись, что у нас достаточно места внизу, чтобы добавить легенду:

par(mar = c(5, 4, 1.4, 0.2))
plot(rnorm(50), rnorm(50), col=c("steelblue", "indianred"), pch=20)

Затем добавьте легенду

add_legend("topright", legend=c("Foo", "Bar"), pch=20, 
   col=c("steelblue", "indianred"),
   horiz=TRUE, bty='n', cex=0.8)

В результате чего:

Пример рисунка показывает легенду в верхнем поле


2
Отличное дополнение к списку здесь. Там есть объяснение о том , как сделать эту работу с несколькими участками в графике здесь .
Ши

Ян, есть ли способ увеличить размер шрифта в легенде без обрезки текста? Например, у меня есть 4 разных типа этикеток, но между ними много пустого пространства.
Старик в море.

Я написал вопрос с более подробной информацией stackoverflow.com/questions/42707308/…
Старик в море.

16

Извините, что воскресил старую ветку, но сегодня у меня была такая же проблема. Самый простой способ, который я нашел, заключается в следующем:

# Expand right side of clipping rect to make room for the legend
par(xpd=T, mar=par()$mar+c(0,0,0,6))

# Plot graph normally
plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

# Plot legend where you want
legend(3.2,1,c("group A", "group B"), pch = c(1,2), lty = c(1,2))

# Restore default clipping rect
par(mar=c(5, 4, 4, 2) + 0.1)

Найдено здесь: http://www.harding.edu/fmccown/R/


4
Еще лучше - oldpar <- par (xpd = T, mar = par () $ mar + c (0,0,0,6)) ... par (oldpar) (см. Справку по par)
rakensi

Это решение лучше, потому что место для легенды фиксируется независимо от длины строк легенды
Серхио

15

Мне нравится делать это так:

par(oma=c(0, 0, 0, 5))
plot(1:3, rnorm(3), pch=1, lty=1, type="o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch=2, lty=2, type="o")
legend(par('usr')[2], par('usr')[4], bty='n', xpd=NA,
       c("group A", "group B"), pch=c(1, 2), lty=c(1,2))

введите описание изображения здесь

Единственная необходимая настройка заключается в установке правого поля, которое должно быть достаточно широким для размещения легенды.

Однако это также может быть автоматизировано:

dev.off() # to reset the graphics pars to defaults
par(mar=c(par('mar')[1:3], 0)) # optional, removes extraneous right inner margin space
plot.new()
l <- legend(0, 0, bty='n', c("group A", "group B"), 
            plot=FALSE, pch=c(1, 2), lty=c(1, 2))
# calculate right margin width in ndc
w <- grconvertX(l$rect$w, to='ndc') - grconvertX(0, to='ndc')
par(omd=c(0, 1-w, 0, 1))
plot(1:3, rnorm(3), pch=1, lty=1, type="o", ylim=c(-2, 2))
lines(1:3, rnorm(3), pch=2, lty=2, type="o")
legend(par('usr')[2], par('usr')[4], bty='n', xpd=NA,
       c("group A", "group B"), pch=c(1, 2), lty=c(1, 2))

введите описание изображения здесь


Использование xpd = T или xpd = NA не препятствует обрезанию моего 'main' (title) при растяжении, чтобы попытаться использовать область, добавленную с широким правым полем.
Фил Гетц

@PhilGoetz Вы уверены, что прокладываете основной в пределах участка? Возможно ли, что у вас недостаточно линий разметки для построения графика?
Jbaums

10

Недавно я нашел очень простую и интересную функцию для печати легенды за пределами области сюжета, где вы хотите.

Сделайте внешнее поле с правой стороны графика.

par(xpd=T, mar=par()$mar+c(0,0,0,5))

Создать сюжет

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

Добавьте легенду и просто используйте функцию locator (1), как показано ниже. Затем вам нужно просто нажать, где вы хотите после загрузки следующего скрипта.

legend(locator(1),c("group A", "group B"), pch = c(1,2), lty = c(1,2))

Попытайся


9

Я могу предложить только пример уже упомянутого решения по макету.

layout(matrix(c(1,2), nrow = 1), widths = c(0.7, 0.3))
par(mar = c(5, 4, 4, 2) + 0.1)
plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")
par(mar = c(5, 0, 4, 2) + 0.1)
plot(1:3, rnorm(3), pch = 1, lty = 1, ylim=c(-2,2), type = "n", axes = FALSE, ann = FALSE)
legend(1, 1, c("group A", "group B"), pch = c(1,2), lty = c(1,2))

ужасная картина: S


9

Добавление еще одной простой альтернативы, которая, на мой взгляд, довольно элегантна.

Ваш сюжет:

plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

Условные обозначения:

legend("bottomright", c("group A", "group B"), pch=c(1,2), lty=c(1,2),
       inset=c(0,1), xpd=TRUE, horiz=TRUE, bty="n"
       )

Результат:

картина с легендой

Здесь только вторая строка легенды была добавлена ​​к вашему примеру. В очереди:

  • inset=c(0,1)- перемещает легенду на долю области сюжета в направлениях (x, y). В этом случае легенда находится на "bottomright"месте. Он перемещается на 0 областей построения в направлении x (таким образом, остается «справа») и на 1 область построения в направлении y (снизу вверх). И так получилось, что он появляется прямо над сюжетом.
  • xpd=TRUE - пусть легенда появится за пределами области черчения.
  • horiz=TRUE - поручает произвести горизонтальную легенду.
  • bty="n" - деталь стиля, чтобы избавиться от ограничивающей рамки легенды.

То же самое относится и при добавлении легенды на сторону:

par(mar=c(5,4,2,6))
plot(1:3, rnorm(3), pch = 1, lty = 1, type = "o", ylim=c(-2,2))
lines(1:3, rnorm(3), pch = 2, lty = 2, type="o")

legend("topleft", c("group A", "group B"), pch=c(1,2), lty=c(1,2),
       inset=c(1,0), xpd=TRUE, bty="n"
       )

Здесь мы просто скорректировали положение легенды и добавили дополнительное пространство для полей справа от графика. Результат:

картинка с легендой 2


4

Это можно сделать с помощью API Plotly R , с помощью кода или из графического интерфейса, перетащив легенду туда, где вы хотите.

Вот пример. График и код также здесь .

x = c(0,1,2,3,4,5,6,7,8) 
y = c(0,3,6,4,5,2,3,5,4) 
x2 = c(0,1,2,3,4,5,6,7,8) 
y2 = c(0,4,7,8,3,6,3,3,4)

Легенду можно расположить за пределами графика, присвоив одному из значений x и y значение 100 или -100.

legendstyle = list("x"=100, "y"=1)
layoutstyle = list(legend=legendstyle)

Вот другие варианты:

  • list("x" = 100, "y" = 0) для наружного правого дна
  • list("x" = 100, "y"= 1) Снаружи справа сверху
  • list("x" = 100, "y" = .5) Снаружи правый средний
  • list("x" = 0, "y" = -100) Под левым
  • list("x" = 0.5, "y" = -100) В центре
  • list("x" = 1, "y" = -100) Под правым

Тогда ответ.

response = p$plotly(x,y,x2,y2, kwargs=list(layout=layoutstyle));

Plotly возвращает URL с вашим графиком, когда вы делаете звонок. Вы можете получить к нему более быстрый доступ, позвонив, browseURL(response$url)чтобы он открыл ваш график в вашем браузере для вас.

url = response$url
filename = response$filename

Это дает нам этот график. Вы также можете переместить легенду из GUI, и тогда график будет соответственно масштабирован. Полное раскрытие: я в команде Plotly.

Легенда на стороне графика


2

Попробуйте то, layout()что я использовал для этого в прошлом, просто создав пустой график ниже, правильно масштабированный примерно на 1/4 или около того, и поместив в него части легенды вручную.

Здесь есть несколько старых вопросов, с legend()которых стоит начать.


Как уже было сказано в вопросе, я тоже об этом думал. Но было бы идеально, если бы был другой путь. Так или иначе, я полагаю, что нет.
Хенрик,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.