Як я можу побудувати графік за допомогою двох різних осей y?


122

Я хотів би накласти два графіки розсіювання в R так, щоб кожен набір точок мав власну (різну) вісь y (тобто у позиціях 2 та 4 на рисунку), але точки виглядають на одній фігурі.

Чи можна це зробити plot?

Редагувати приклад коду, що показує проблему

# example code for SO question
y1 <- rnorm(10, 100, 20)
y2 <- rnorm(10, 1, 1)
x <- 1:10
# in this plot y2 is plotted on what is clearly an inappropriate scale
plot(y1 ~ x, ylim = c(-1, 150))
points(y2 ~ x, pch = 2)

Будь ласка, надайте вибіркові дані. Це взагалі погана ідея з естетичної точки зору.
Чейз

3
відповіді та обговорення в конкретному випадку ggplot2: stackoverflow.com/questions/3099219/… (шукає ТА [r] two y-axesабо [r] twoord.plot) - є кілька інших пов’язаних відповідей, хоча (на моє здивування, оскільки це R FAQ) нічого ідентичного
Бен Болкер

@chase - я додав робочий приклад проблеми. Дякуємо за попередження з естетичних питань.
DQdlM

Відповіді:


126

оновлення : скопійований матеріал, який знаходився на вікі R за адресою http://rwiki.sciviews.org/doku.php?id=tips:graphics-base:2yaxes , посилання зараз розірвано: також доступне з машини зворотного звороту

Дві різні осі y на одній ділянці

(деякі матеріали спочатку Даніель Райдл 31/03/31 15:26)

Зверніть увагу, що існує дуже мало ситуацій, коли доцільно використовувати дві різні шкали на одній ділянці. Дуже легко ввести в оману глядача графіки. Перевірте наступні два приклади та коментарі до цього питання ( example1 , example2 з Junk Charts ), а також цю статтю Стівена Фьюя (яка робить висновок: «Я, звичайно, не можу зробити висновок раз і назавжди, що графіки з осями з подвійною шкалою ніколи не бувають корисно; лише те, що я не можу придумати ситуацію, яка вимагає їх у світлі інших кращих рішень. ") Також дивіться пункт 4 у цьому мультфільмі ...

Якщо ви налаштовані, основний рецепт - створити свій перший сюжет, встановлений par(new=TRUE)для того, щоб R не очистив графічний пристрій, створив другий сюжет за допомогою axes=FALSE(і встановив, xlabі ylabвін буде порожнім - ann=FALSEтакож повинен працювати), а потім використовувати axis(side=4)для додавання нової осі праворуч та mtext(...,side=4)додати мітку осі на правій частині. Ось приклад використання трохи складених даних:

set.seed(101)
x <- 1:10
y <- rnorm(10)
## second data set on a very different scale
z <- runif(10, min=1000, max=10000) 
par(mar = c(5, 4, 4, 4) + 0.3)  # Leave space for z axis
plot(x, y) # first plot
par(new = TRUE)
plot(x, z, type = "l", axes = FALSE, bty = "n", xlab = "", ylab = "")
axis(side=4, at = pretty(range(z)))
mtext("z", side=4, line=3)

twoord.plot()в plotrixпакеті автоматизує цей процес, як і doubleYScale()в latticeExtraпакеті.

Інший приклад (адаптований із публікації R-списку розсилки Роберта В. Баера):

## set up some fake test data
time <- seq(0,72,12)
betagal.abs <- c(0.05,0.18,0.25,0.31,0.32,0.34,0.35)
cell.density <- c(0,1000,2000,3000,4000,5000,6000)

## add extra space to right margin of plot within frame
par(mar=c(5, 4, 4, 6) + 0.1)

## Plot first set of data and draw its axis
plot(time, betagal.abs, pch=16, axes=FALSE, ylim=c(0,1), xlab="", ylab="", 
   type="b",col="black", main="Mike's test data")
axis(2, ylim=c(0,1),col="black",las=1)  ## las=1 makes horizontal labels
mtext("Beta Gal Absorbance",side=2,line=2.5)
box()

## Allow a second plot on the same graph
par(new=TRUE)

## Plot the second plot and put axis scale on right
plot(time, cell.density, pch=15,  xlab="", ylab="", ylim=c(0,7000), 
    axes=FALSE, type="b", col="red")
## a little farther out (line=4) to make room for labels
mtext("Cell Density",side=4,col="red",line=4) 
axis(4, ylim=c(0,7000), col="red",col.axis="red",las=1)

## Draw the time axis
axis(1,pretty(range(time),10))
mtext("Time (Hours)",side=1,col="black",line=2.5)  

## Add Legend
legend("topleft",legend=c("Beta Gal","Cell Density"),
  text.col=c("black","red"),pch=c(16,15),col=c("black","red"))

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

Подібні рецепти можна використовувати для накладення сюжетів різних типів - смугових сюжетів, гістограм тощо.


ось чому відповіді, що стосуються лише посилань, є поганою ідеєю ... wiki.r-project.org видається непрацездатним, я прошу на r-devel@r-project.org.
Бен Болкер

@BenBolker ваше рішення геніальне! однак у мене є одне питання. Якщо обох сторін більше, ніж один рядок, я все ще можу використовувати цей метод, але тільки з символами. Коли я намагаюся використовувати line = plot, він намагається будувати їх постійно. Чи не вдасться ви запропонувати хитрість, щоб виправити це?
wthimdh

1
@BenBolker Що робити, якщо час - це формат дати типу: "2019-01-01". Як ти зміниш axis(1,pretty(range(time),10))лінію?
к.дхк

35

Як випливає з назви, twoord.plot()у пакеті plotrix зображуються графіки з двома ординатними осями.

library(plotrix)
example(twoord.plot)

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

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

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

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

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


3
гладкий. спасибі за вантажний приклад прикладів. Я також хотів би побачити один із таких прикладів рядків і рядків з негативними значеннями. Також кладка була б непоганою.
Метт Баннерт

5

Один варіант - зробити дві ділянки поруч. ggplot2надає хороший варіант для цього за допомогою facet_wrap():

dat <- data.frame(x = c(rnorm(100), rnorm(100, 10, 2))
  , y = c(rnorm(100), rlnorm(100, 9, 2))
  , index = rep(1:2, each = 100)
  )

require(ggplot2)
ggplot(dat, aes(x,y)) + 
geom_point() + 
facet_wrap(~ index, scales = "free_y")

4

Якщо ви можете відмовитись від міток масштабу / осі, ви можете змінити масштаб даних до інтервалу (0, 1). Це працює, наприклад, для різних "хитаються" траксів на хромосомах, коли вас, як правило, цікавлять локальні кореляції між треками, і вони мають різні масштаби (охоплення в тисячах, Fst 0-1).

# rescale numeric vector into (0, 1) interval
# clip everything outside the range 
rescale <- function(vec, lims=range(vec), clip=c(0, 1)) {
  # find the coeficients of transforming linear equation
  # that maps the lims range to (0, 1)
  slope <- (1 - 0) / (lims[2] - lims[1])
  intercept <- - slope * lims[1]

  xformed <- slope * vec + intercept

  # do the clipping
  xformed[xformed < 0] <- clip[1]
  xformed[xformed > 1] <- clip[2]

  xformed
}

Потім, маючи фрейм даних з chrom, position, coverageі fstстовпців, ви можете зробити що - щось на кшталт:

ggplot(d, aes(position)) + 
  geom_line(aes(y = rescale(fst))) + 
  geom_line(aes(y = rescale(coverage))) +
  facet_wrap(~chrom)

Перевагою цього є те, що ви не обмежені двома trakcs.


4

Я теж припускаю, що twoord.stackplot()в plotrixпакеті подаються графіки з двома ординатними осями.

data<-read.table(text=
"e0AL fxAL e0CO fxCO e0BR fxBR anos
 51.8  5.9 50.6  6.8 51.0  6.2 1955
 54.7  5.9 55.2  6.8 53.5  6.2 1960
 57.1  6.0 57.9  6.8 55.9  6.2 1965
 59.1  5.6 60.1  6.2 57.9  5.4 1970
 61.2  5.1 61.8  5.0 59.8  4.7 1975
 63.4  4.5 64.0  4.3 61.8  4.3 1980
 65.4  3.9 66.9  3.7 63.5  3.8 1985
 67.3  3.4 68.0  3.2 65.5  3.1 1990
 69.1  3.0 68.7  3.0 67.5  2.6 1995
 70.9  2.8 70.3  2.8 69.5  2.5 2000
 72.4  2.5 71.7  2.6 71.1  2.3 2005
 73.3  2.3 72.9  2.5 72.1  1.9 2010
 74.3  2.2 73.8  2.4 73.2  1.8 2015
 75.2  2.0 74.6  2.3 74.2  1.7 2020
 76.0  2.0 75.4  2.2 75.2  1.6 2025
 76.8  1.9 76.2  2.1 76.1  1.6 2030
 77.6  1.9 76.9  2.1 77.1  1.6 2035
 78.4  1.9 77.6  2.0 77.9  1.7 2040
 79.1  1.8 78.3  1.9 78.7  1.7 2045
 79.8  1.8 79.0  1.9 79.5  1.7 2050
 80.5  1.8 79.7  1.9 80.3  1.7 2055
 81.1  1.8 80.3  1.8 80.9  1.8 2060
 81.7  1.8 80.9  1.8 81.6  1.8 2065
 82.3  1.8 81.4  1.8 82.2  1.8 2070
 82.8  1.8 82.0  1.7 82.8  1.8 2075
 83.3  1.8 82.5  1.7 83.4  1.9 2080
 83.8  1.8 83.0  1.7 83.9  1.9 2085
 84.3  1.9 83.5  1.8 84.4  1.9 2090
 84.7  1.9 83.9  1.8 84.9  1.9 2095
 85.1  1.9 84.3  1.8 85.4  1.9 2100", header=T)

require(plotrix)
twoord.stackplot(lx=data$anos, rx=data$anos, 
                 ldata=cbind(data$e0AL, data$e0BR, data$e0CO),
                 rdata=cbind(data$fxAL, data$fxBR, data$fxCO),
                 lcol=c("black","red", "blue"),
                 rcol=c("black","red", "blue"), 
                 ltype=c("l","o","b"),
                 rtype=c("l","o","b"), 
                 lylab="Años de Vida", rylab="Hijos x Mujer", 
                 xlab="Tiempo",
                 main="Mortalidad/Fecundidad:1950–2100",
                 border="grey80")
legend("bottomright", c(paste("Proy:", 
                      c("A. Latina", "Brasil", "Colombia"))), cex=1,
        col=c("black","red", "blue"), lwd=2, bty="n",  
        lty=c(1,1,2), pch=c(NA,1,1) )

1

Ще одна альтернатива, схожа на прийняту відповідь @BenBolker, - це переосмислення координат існуючого сюжету при додаванні другого набору точок.

Ось мінімальний приклад.

Дані:

x  <- 1:10
y1 <- rnorm(10, 100, 20)
y2 <- rnorm(10, 1, 1)

Сюжет:

par(mar=c(5,5,5,5)+0.1, las=1)

plot.new()
plot.window(xlim=range(x), ylim=range(y1))
points(x, y1, col="red", pch=19)
axis(1)
axis(2, col.axis="red")
box()

plot.window(xlim=range(x), ylim=range(y2))
points(x, y2, col="limegreen", pch=19)
axis(4, col.axis="limegreen")

приклад

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.