Створіть легенду поза площею графіки в базовій графіці?


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))

виробляє:

alt текст

Але, як було сказано, я хотів би, щоб легенда знаходилася поза площею графіку (наприклад, праворуч від графіка / сюжету.


... ви також можете зламати парі з фіктивним контейнером для легенди, простий і досить зручний час від часу. Подібне питання тут .
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 безпосередньо в легенду, тому вам не потрібно буде турбуватися про скидання par після цього. Також дивіться у 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
@Henrik ні, це не працює без xpd = ІСТИНА. Також зауважте, що краще встановити 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. Будь-які уявлення? Я просто розгублений, передаючи свої аргументи?
користувач3386170

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 не заважає моєму "головному" (заголовку) відсікатися, коли він розтягується, щоб спробувати використовувати область, додану з широким правим полем.
Філ Гец

@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")

Додайте легенду і просто використовуйте функцію локатора (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.