Как я могу построить график с двумя разными осями 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)
DQdlM
источник
Пожалуйста, предоставьте образцы данных. Как правило, это плохая идея с эстетической точки зрения.
Чейз
3
ответы и обсуждение в конкретном случае ggplot2: stackoverflow.com/questions/3099219/… (поиск SO для [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.2006, 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)

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

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

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

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

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

KMM
источник
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)

Преимущество этого в том, что вы не ограничены двумя тракками.

liborm
источник
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) )
Juannes
источник
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")

пример

Каролис Концевичюс
источник