Is there a way to align 2 independent axes in an R graph?

余生颓废 提交于 2019-12-20 05:35:07

问题


I'm plotting a graph with different axis. The problem is that I want the 2 axes to be crossing one point, the rest doesn't really matter. Is it possible?

Here is a reproducible code:

plot(x = -10:10, y = -10:10)
abline(v=0,lty = 2)
par(new =TRUE)
plot(x = -10:50, y = seq(-5,5,length.out = length(-10:50)), xaxt = "n", yaxt = "n", bty ="n")
abline(v=0,lty = 3)
axis(3, col="red",col.axis="red",las=2, cex.axis = 1)
axis(4, col="red",col.axis="red",las=2, cex.axis = 1)

Here is the output:

I basically want the vertical lines to be crossing each other at 0.

Is there another way to write this (which is not really accurate, I want something that can be done automatically, other than setting a xlim manually, since I have a lot of them to do. Also, when the image is resized, it's losing the alignment with the 2 0s):

plot(x = -10:10, y = -10:10)
abline(v=0,lty = 2)
par(new =TRUE)
plot(x = -10:50, y = seq(-5,5,length.out = length(-10:50)), 
     xaxt = "n", yaxt = "n", bty ="n",
     xlim = c(-50,50))
abline(v=0,lty = 3, lwd = 5)
axis(3, col="red",col.axis="red",las=2, cex.axis = 1)
axis(4, col="red",col.axis="red",las=2, cex.axis = 1)

The output should use something similar to what biplot.prcomp is using to align the arrows and the axis:

EDITED

With a PCA, it's only working for the y axis, not the x axis.

new_lim <- function(a, type = 1) {
  newdata_ratio <-  NULL
  i <- type * 2 - 1
  old_lim <- par("usr")[i:(i+1)] + c(diff(par("usr")[i:(i+1)]) * 0.04 / 1.08, 
                                     diff(par("usr")[i:(i+1)]) * -0.04 / 1.08)
  old_ratio <- old_lim[1] / old_lim[2]
  newdata_ratio <- if (max(a) <= 0) -1.0e+6 else min(a) / max(a)
  if (old_ratio >= newdata_ratio ) {
    new_min <- min(a)
    new_max <- min(a) / old_ratio
  } else {
    new_min <- max(a) * old_ratio
    new_max <- max(a)
  }
  c(new_min, new_max)
}
s1= rnorm(50,mean = 12)
s2= rnorm(50, mean = 17)
s3= rnorm(50, mean = 20)
library(vegan)
pca=rda(cbind(s1,s2,s3))
pca.scoop=scores(pca, scaling = 2)
biplot(pca)

par(mar=c(4, 4, 4, 4))
plot(pca, xlab = "x1", ylab = "y1",
     type = c("p"),
     main= "main",
     scaling = 2,
     choices = c(1,2),
     xlim =c(min(pca.scoop$sites[,1]),max(pca.scoop$sites[,1])),
     ylim = c(min(pca.scoop$sites[,2]),max(pca.scoop$sites[,2])),
     bty = "o",#"l"
     pch=4)
abline(v = 0, lty = 2); abline(h = 0, lty = 2)

x2 <- -10:20
y2 <- seq(40, 10, length.out = length(x2))
par(new =TRUE)
plot(x2, y2, 
     xlim = new_lim(x2), 
     ylim = new_lim(y2, 2), axes = F, ann = F)
axis(3, col = "red", col.axis = "red") # axes=F is equivalent to xaxt="n", yaxt="n" and bty="n"
axis(4, col = "red", col.axis = "red") # ann=F is equivalent to xlab=NA and ylab=NA
mtext("x2", side = 3, line = 2.5, col = "red")
mtext("y2", side = 4, line = 2.5, col = "red")
# box(bty="7", col="red")  # if you want.


回答1:


Is it possible to align the second graph based on the first one?

Yes, it is. But it's a litte complex because of needing logical judgment.

This function, new_lim(a, type), calculates first graph's xlim (or ylim) and ratio of minus to plus from graphic parameters. And it judges which value it uses as second graph's parameter, min(second data) or max, and calculate another value from the first graph's ratio. The output are second graph's min and max value, _lim. The argument a is a second x or y data. type = 1 (default; omittable) is for xlim, type = 2 (type = is omittable) is for ylim.

new_lim <- function(a, type = 1) {
  newdata_ratio <-  NULL
  i <- type * 2 - 1
  old_lim <- par("usr")[i:(i+1)] + c(diff(par("usr")[i:(i+1)]) * 0.04 / 1.08, 
                                     diff(par("usr")[i:(i+1)]) * -0.04 / 1.08)
  old_ratio <- old_lim[1] / old_lim[2]
  newdata_ratio <- if (max(a) <= 0) -1.0e+6 else min(a) / max(a)
  if (old_ratio >= newdata_ratio ) {
    new_min <- min(a)
    new_max <- min(a) / old_ratio
  } else {
    new_min <- max(a) * old_ratio
    new_max <- max(a)
  }
  c(new_min, new_max)
}

[Note] This function needs to exist of first graph and include zero of first data's range. It is no problem that second data's range doesn't include zero.

x2 <- -40:20
y2 <- seq(40, 10, length.out = length(-40:20))

par(mar=c(4, 4, 4, 4))
plot(x = -15:5, y = -5:15, xlab = "x1", ylab = "y1")
abline(v = 0, lty = 2); abline(h = 0, lty = 2)

par(new =TRUE)
plot(x2, y2, xlim = new_lim(x2), ylim = new_lim(y2, 2), axes = F, ann = F)
axis(3, col = "red", col.axis = "red") # axes=F is equivalent to xaxt="n", yaxt="n" and bty="n"
axis(4, col = "red", col.axis = "red") # ann=F is equivalent to xlab=NA and ylab=NA
mtext("x2", side = 3, line = 2.5, col = "red")
mtext("y2", side = 4, line = 2.5, col = "red")
# box(bty="7", col="red")  # if you want.

EDITED [CAUTION]

When you use this function with plot(class.rda) and change the aspect by Rsutdio, the output becomes different from what you want.

x2 <- -10:20
y2 <- seq(40, 10, length.out = length(x2))
library(vegan) 
s1= rnorm(50,mean = 12); s2= rnorm(50, mean = 17); s3= rnorm(50, mean = 20)
pca=rda(cbind(s1,s2,s3))
pca.scoop=scores(pca, scaling = 2)
biplot(pca)
par(new =TRUE)
plot(x2, y2, 
     xlim = new_lim(x2), 
     ylim = new_lim(y2, 2), axes = F, ann = F)
axis(3, col = "red", col.axis = "red")
axis(4, col = "red", col.axis = "red")
mtext("x2", side = 3, line = 2, col = "red")
mtext("y2", side = 4, line = 2, col = "red")




回答2:


It's not a beautiful solution, but as long as your x-axes always cross the 0 value, you can set the xlim=c(-max(c(-min(x),max(x))),max(c(-min(x),max(x)))) for both plots. This will make the x-axis always center at 0for both the top and bottom axes. The same can be done with the y-axes if you so choose.

# Set graphing parameters
par(mar = c(5,4,4,4) + 0.1) # This can be reset later with "dev.off()"

# Plot the first graph
plot(x = -10:10, y = -10:10, 
     xlim=c(-max(c(-min(x),max(x))),max(c(-min(x),max(x)))),
     xlab="x-label-1",ylab="y-label-1") # Added labels to be changed
abline(v=0,lty = 2)

# Plot the second graph using new axes
par(new =TRUE)
plot(x = -10:50, y = seq(-5,5,length.out = length(-10:50)), 
     xaxt = "n", yaxt = "n", bty ="n",
     xlim=c(-max(c(-min(x),max(x))),max(c(-min(x),max(x)))),
     xlab=NA,ylab=NA) # Remove x- and y-axis labels
abline(v=0,lty = 3)
axis(3, col="red",col.axis="red",las=2, cex.axis = 1)
axis(4, col="red",col.axis="red",las=2, cex.axis = 1)
mtext("x-label-2", side = 3, line = 3, cex = par("cex.lab")) # Labeled secondary x-axis
mtext("y-label-2", side = 4, line = 3, cex = par("cex.lab")) # Labeled secondary y-axis

which gives:

I hope this works! You'll need to play around with the values for par(mar()) if you want to label your graphs with a title.



来源:https://stackoverflow.com/questions/38133485/is-there-a-way-to-align-2-independent-axes-in-an-r-graph

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!