¿Cómo puedo graficar con 2 ejes y diferentes?

122

Me gustaría superponer dos diagramas de dispersión en R para que cada conjunto de puntos tenga su propio eje y (diferente) (es decir, en las posiciones 2 y 4 de la figura) pero los puntos aparezcan superpuestos en la misma figura.

¿Es posible hacer esto con plot?

Editar código de ejemplo que muestra el 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
fuente
Proporcione datos de muestra. Esta es generalmente una mala idea desde una perspectiva estética.
Chase
3
respuestas y discusión en el caso específico de ggplot2: stackoverflow.com/questions/3099219/… (buscando SO para [r] two y-axeso [r] twoord.plot) - hay algunas otras respuestas relacionadas, aunque (para mi sorpresa, ya que es una R FAQ) nada idéntico
Ben Bolker
@chase: agregué un ejemplo práctico del problema. Gracias por la advertencia sobre los problemas estéticos.
DQdlM

Respuestas:

126

actualización : material copiado que estaba en la wiki de R en http://rwiki.sciviews.org/doku.php?id=tips:graphics-base:2yaxes , enlace ahora roto: también disponible en la máquina wayback

Dos ejes y diferentes en la misma gráfica

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

Tenga en cuenta que hay muy pocas situaciones en las que sea apropiado utilizar dos escalas diferentes en el mismo gráfico. Es muy fácil engañar al espectador del gráfico. Consulte los siguientes dos ejemplos y comentarios sobre este tema ( ejemplo1 , ejemplo2 de Junk Charts ), así como este artículo de Stephen Few (que concluye "Ciertamente no puedo concluir, de una vez por todas, que los gráficos con ejes de doble escala nunca útil; solo que no puedo pensar en una situación que los justifique a la luz de otras soluciones mejores ”). Vea también el punto # 4 en esta caricatura ...

Si está decidido, la receta básica es crear su primer gráfico, configurado par(new=TRUE)para evitar que R borre el dispositivo gráfico, crear el segundo gráfico con axes=FALSE(y configurar xlaby ylabestar en blanco - ann=FALSEtambién debería funcionar) y luego usar axis(side=4)para agregar un nuevo eje en el lado derecho, y mtext(...,side=4)para agregar una etiqueta de eje en el lado derecho. Aquí hay un ejemplo que usa un poco de datos 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()en el plotrixpaquete automatiza este proceso, al igual que doubleYScale()en el latticeExtrapaquete.

Otro ejemplo (adaptado de una publicación de la lista de correo de R por 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"))

ingrese la descripción de la imagen aquí

Se pueden usar recetas similares para superponer gráficos de diferentes tipos: gráficos de barras, histogramas, etc.

Ben Bolker
fuente
esta es la razón por la que las respuestas de solo enlace son una mala idea ... wiki.r-project.org parece desaparecido, lo pregunto en [email protected].
Ben Bolker
@BenBolker, ¡tu solución es genial! sin embargo, tengo una pregunta. Si hay más de una línea en ambos lados, todavía puedo usar este método, pero solo con símbolos. Cuando trato de usar line = plot, intenta trazarlos continuamente. ¿Podría sugerir un truco para solucionar este problema?
wthimdh
1
@BenBolker ¿Qué pasa si la hora es el formato de fecha del tipo: "2019-01-01". ¿Cómo cambiarás la axis(1,pretty(range(time),10))línea?
k.dkhk
35

Como sugiere su nombre, twoord.plot()en el paquete plotrix se grafican con dos ejes de ordenadas.

library(plotrix)
example(twoord.plot)

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

kmm
fuente
3
resbaloso. gracias por el camión lleno de ejemplos. Me encantaría ver uno de esos ejemplos de líneas y barras con valores negativos también. También estaría bien apilar.
Matt Bannert
5

Una opción es hacer dos parcelas una al lado de la otra. ggplot2proporciona una buena opción para esto con 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")
Persecución
fuente
4

Si puede renunciar a las escalas / etiquetas de eje, puede cambiar la escala de los datos al intervalo (0, 1). Esto funciona, por ejemplo, para diferentes trakcs 'wiggle' en cromosomas, cuando generalmente está interesado en correlaciones locales entre las pistas y tienen diferentes escalas (cobertura en miles, 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
}

Entonces, tener una trama de datos con chrom, position, coveragey fstcolumnas, puede hacer algo como:

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

La ventaja de esto es que no está limitado a dos trakcs.

liborm
fuente
4

Yo también sugiero, twoord.stackplot()en los plotrixgráficos del paquete con más de dos ejes de ordenadas.

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

Otra alternativa que es similar a la respuesta aceptada por @BenBolker es redefinir las coordenadas de la parcela existente al agregar un segundo conjunto de puntos.

Aquí hay un ejemplo mínimo.

Datos:

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

Trama:

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

ejemplo

Karolis Koncevičius
fuente