Como plotar com 2 eixos y diferentes?

122

Gostaria de sobrepor dois gráficos de dispersão em R para que cada conjunto de pontos tenha seu próprio eixo y (diferente) (ie, nas posições 2 e 4 da figura), mas os pontos apareçam sobrepostos na mesma figura.

É possível fazer isso plot?

Editar código de exemplo mostrando o problema

# 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
fonte
Forneça dados de amostra. Geralmente, é uma má ideia do ponto de vista estético.
Chase
3
respostas e discussão no caso específico de ggplot2: stackoverflow.com/questions/3099219/… (pesquisando SO [r] two y-axesou [r] twoord.plot) - existem algumas outras respostas relacionadas, embora (para minha surpresa, pois seja uma R FAQ) nada idêntico
Ben Bolker
@chase - Adicionei um exemplo de trabalho do problema. Obrigado pelo aviso sobre as questões estéticas.
DQdlM 26/05

Respostas:

126

update : Material copiado que estava no wiki do R em http://rwiki.sciviews.org/doku.php?id=tips:graphics-base:2yaxes , link agora quebrado: também disponível na máquina wayback

Dois eixos y diferentes no mesmo gráfico

(algum material originalmente de Daniel Rajdl 31/03/2006 15:26)

Observe que há muito poucas situações em que é apropriado usar duas escalas diferentes no mesmo gráfico. É muito fácil enganar o visualizador do gráfico. Verifique os dois exemplos e comentários a seguir sobre esse problema ( exemplo1 , exemplo2 da Junk Charts ), bem como este artigo de Stephen Few (que conclui “Certamente não posso concluir, de uma vez por todas, que gráficos com eixos de escala dupla nunca são útil, só que não consigo pensar em uma situação que os justifique à luz de outras soluções melhores. ”) Veja também o ponto 4 deste desenho animado ...

Se você estiver determinado, a receita básica é criar seu primeiro gráfico, definido par(new=TRUE)para impedir que R limpe o dispositivo gráfico, criando o segundo gráfico com axes=FALSE(e definindo xlabe ficando ylabem branco - ann=FALSEtambém deve funcionar) e, em seguida, usando axis(side=4)para adicionar um novo eixo no lado direito e mtext(...,side=4)para adicionar um rótulo de eixo no lado direito. Aqui está um exemplo usando um pouco de dados inventados:

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()no plotrixpacote automatiza esse processo, como acontece doubleYScale()no latticeExtrapacote.

Outro exemplo (adaptado de uma postagem da lista de discussão de Robert W. Baer):

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

insira a descrição da imagem aqui

Receitas semelhantes podem ser usadas para sobrepor gráficos de diferentes tipos - gráficos de barras, histogramas etc.

Ben Bolker
fonte
é por isso que respostas somente para links são uma má idéia ... wiki.r-project.org parece extinto, estou perguntando em [email protected].
Ben Bolker
@BenBolker, sua solução é genial! no entanto, eu tenho uma pergunta. Se houver mais de uma linha nos dois lados, ainda posso usar esse método, mas apenas com símbolos. Quando tento usar line = plot, ele tenta plotá-los continuamente. Você sugeriria um truque para corrigir isso?
precisa saber é o seguinte
1
@BenBolker What if Time é o formato de data do tipo: "01-01-2019". Como você vai mudar a axis(1,pretty(range(time),10))linha?
precisa saber é
35

Como o próprio nome sugere, twoord.plot()no pacote plotrix plota com dois eixos ordenados.

library(plotrix)
example(twoord.plot)

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

kmm
fonte
3
liso. obrigado pela carga de caminhões de exemplos. Eu adoraria ver um desses exemplos de linha e barra com valores negativos também. Também empilhamento seria bom.
Matt Bannert
5

Uma opção é fazer duas parcelas lado a lado. ggplot2fornece uma boa opção para isso com 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")
correr atrás
fonte
4

Se você pode desistir dos rótulos de escalas / eixos, pode redimensionar os dados para o intervalo (0, 1). Isso funciona, por exemplo, para diferentes traços de 'manobra' nos cromossomos, quando você geralmente está interessado em correlações locais entre as faixas e elas têm escalas diferentes (cobertura em milhares, 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
}

Então, ter um quadro de dados com chrom, position, coveragee fstcolunas, você pode fazer algo como:

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

A vantagem disso é que você não está limitado a dois trakcs.

liborm
fonte
4

Eu também sugiro, twoord.stackplot()nas plotrixparcelas do pacote com mais de dois eixos ordenados.

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
fonte
1

Outra alternativa semelhante à resposta aceita por @BenBolker é redefinir as coordenadas do gráfico existente ao adicionar um segundo conjunto de pontos.

Aqui está um exemplo mínimo.

Dados:

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

Enredo:

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

exemplo

Karolis Koncevičius
fonte