在3D回归图中将网格线与轴刻度匹配(persp和rockchalk)

时间:2016-11-22 18:19:58

标签: r 3d axis scatter-plot multiple-regression

我试图将我的网格线与此图中的轴刻度相匹配,因此数字在轴上是均匀的,并且易于在两者之间读取。有谁知道怎么做?由于我同时使用两个不同的软件包(persp和rockchalk),我发现找到绝对有用的代码非常困难。

Sample       Coral.cover   Coral.richness   Water.Temp    fish.rich
 r.2002.c.1        59              5             23.90         57
 r.2002.c.2        71              9             33.43         70
 r.2002.c.3        55              10            23.33         55
 r.2002.c.4        58              10            22.14         56
 r.2002.c.5        62              8             26.82         61
 r.2002.c.6        65              10            29.07         64
 r.2002.s.1        86              10            38.76         84
 r.2002.s.2        71              10            30.28         68
 r.2002.s.3        91              6             27.85         89
 r.2002.s.4        63              0             28.03         62
 r.2002.s.5        75              2             31.67         75
 r.2002.s.6        63              2             29.43         63

require(Hmisc)
require(car)
require(vegan)
require(rockchalk)
require(reshape)
require(persp)

mod3 <- mcGraph3(Coral.cover, Coral.richness, fish.rich, 
             interaction = F,
             theta = -40 ,
             phi =20,
             x1lab = "",
             x2lab = "",
             ylab = "",
             x1lim = c(46,100),
             ylim = c(-2.5, 12.5),
             zlim =c(40, 100),
             r=10,
             col = 'white',
             border = 'black',
             box = T,
             axes = T,
             ticktype = "detailed",
             ntick = 4)

这是我目前所拥有的(忽略了标签的照片):

3D Regression Plot with unaligned axis and ticks

感谢任何帮助。

由于

数据
df <- structure(list(Sample = structure(1:12, .Label = c("r.2002.c.1", 
  "r.2002.c.2", "r.2002.c.3", "r.2002.c.4", "r.2002.c.5", "r.2002.c.6", 
  "r.2002.s.1", "r.2002.s.2", "r.2002.s.3", "r.2002.s.4", "r.2002.s.5", 
  "r.2002.s.6"), class = "factor"), Coral.cover = c(59L, 71L, 55L, 
  58L, 62L, 65L, 86L, 71L, 91L, 63L, 75L, 63L), Coral.richness = c(5L, 
  9L, 10L, 10L, 8L, 10L, 10L, 10L, 6L, 0L, 2L, 2L), Water.Temp = c(23.9, 
  33.43, 23.33, 22.14, 26.82, 29.07, 38.76, 30.28, 27.85, 28.03, 
  31.67, 29.43), fish.rich = c(57L, 70L, 55L, 56L, 61L, 64L, 84L, 
  68L, 89L, 62L, 75L, 63L)), .Names = c("Sample", "Coral.cover", 
  "Coral.richness", "Water.Temp", "fish.rich"), class = "data.frame", row.names = c(NA, 
  -12L))

Coral.cover <- df[,2]
Coral.richness <- df[,3]
fish.rich <- df[,5]

1 个答案:

答案 0 :(得分:1)

我认为在没有rockchalk包的情况下绘制图表并手动添加内容是个好主意。我使用了plot3D包(它提供了persp)的扩展函数。

 ## preparation of some values for mesh of fitted value
fit <- lm(fish.rich ~ Coral.cover + Coral.richness)  # model
x.p <- seq(46, 100, length = 20)                     # x-grid of mesh
y.p <- seq(-2.5, 12.5, length = 20)                  # y-grid of mesh
z.p <- matrix(predict(fit, expand.grid(Coral.cover = x.p, Coral.richness = y.p)), 20) # prediction from xy-grid

library(plot3D)
  # box, grid, bottom points, and so on
scatter3D(Coral.cover, Coral.richness, rep(40, 12), colvar = NA, bty = "b2", 
          xlim = c(46,100), ylim = c(-2.5, 12.5), zlim = c(40,100), theta = -40, phi = 20, 
          r = 10, ticktype = "detailed", pch = 19, col = "gray", nticks = 4)
  # mesh, real points
scatter3D(Coral.cover, Coral.richness, fish.rich, add = T, colvar = NA, col = "blue",
          surf = list(x = x.p, y = y.p, z = z.p, facets = NA, col = "gray80"))
  # arrow from prediction to observation
arrows3D(x0 = Coral.cover, y0 = Coral.richness, z0 = fit$fitted.values, z1 = fish.rich, 
         type = "simple", lty = 2, add = T, col = "red")


 ### [bonus] persp() version
pmat <- persp(x.p, y.p, z.p, xlim = c(46,100), ylim = c(-2.5, 12.5), zlim = c(40,100), theta = -40, phi = 20, 
              r = 10, ticktype = "detailed", pch = 19, col = NA, border = "gray", nticks = 4)
for (ix in seq(50, 100, 10)) lines (trans3d(x = ix, y = c(-2.5, 12.5), z= 40, pmat = pmat), col = "black") 
for (iy in seq(0, 10, 5)) lines (trans3d(x = c(46, 100), y = iy, z= 40, pmat = pmat), col = "black")

points(trans3d(Coral.cover, Coral.richness, rep(40, 12), pmat = pmat), col = "gray", pch = 19)
points(trans3d(Coral.cover, Coral.richness, fish.rich, pmat = pmat), col = "blue")

xy0 <- trans3d(Coral.cover, Coral.richness, fit$fitted.values, pmat = pmat)
xy1 <- trans3d(Coral.cover, Coral.richness, fish.rich, pmat = pmat)
arrows(xy0[[1]], xy0[[2]], xy1[[1]], xy1[[2]], col = "red", lty = 2, length = 0.1)

enter image description here

[回应评论]
你的评论是有道理的。但mcGraph3()没有与网格相关的选项,也不能将add = T作为参数。所以我展示了修改后的mcGraph3()(这是一种hacky方式)和我绘制网格的功能。

功能:my_mcGraph3persp_grid(将此代码保存为.R文件并按source("file_name.R")读取可能是个好主意)

my_mcGraph3 <- function (x1, x2, y, interaction = FALSE, drawArrows = TRUE, 
                         x1lab, x2lab, ylab, col = "white", border = "black", x1lim = NULL, x2lim = NULL, 
                         grid = TRUE, meshcol = "black", ...)   # <-- new arguments
{
  x1range <- magRange(x1, 1.25)
  x2range <- magRange(x2, 1.25)
  yrange <- magRange(y, 1.5)
  if (missing(x1lab)) 
    x1lab <- gsub(".*\\$", "", deparse(substitute(x1)))
  if (missing(x2lab)) 
    x2lab <- gsub(".*\\$", "", deparse(substitute(x2)))
  if (missing(ylab)) 
    ylab <- gsub(".*\\$", "", deparse(substitute(y)))
  if (grid) {
    res <- perspEmpty(x1 = plotSeq(x1range, 5), x2 = plotSeq(x2range, 5),
                      y = yrange, x1lab = x1lab, x2lab = x2lab, ylab = ylab, ...)
  } else {
    if (is.null(x1lim)) x1lim <- x1range
    if (is.null(x2lim)) x2lim <- x2range
    res <- persp(x = x1range, y = x2range, z = rbind(yrange, yrange), 
                 xlab = x1lab, ylab = x2lab, zlab = ylab, xlim = x1lim, ylim = x2lim, col = "#00000000", border = NA, ...)
  }
  mypoints1 <- trans3d(x1, x2, yrange[1], pmat = res)
  points(mypoints1, pch = 16, col = gray(0.8))
  mypoints2 <- trans3d(x1, x2, y, pmat = res)
  points(mypoints2, pch = 1, col = "blue")
  if (interaction) m1 <- lm(y ~ x1 * x2) else m1 <- lm(y ~ x1 + x2)
  x1seq <- plotSeq(x1range, length.out = 20)
  x2seq <- plotSeq(x2range, length.out = 20)
  zplane <- outer(x1seq, x2seq, function(a, b) {
    predict(m1, newdata = data.frame(x1 = a, x2 = b))
  })
  for (i in 1:length(x1seq)) {
    lines(trans3d(x1seq[i], x2seq, zplane[i, ], pmat = res), lwd = 0.3, col = meshcol)
  }
  for (j in 1:length(x2seq)) {
    lines(trans3d(x1seq, x2seq[j], zplane[, j], pmat = res), lwd = 0.3, col = meshcol)
  }
  mypoints4 <- trans3d(x1, x2, fitted(m1), pmat = res)
  newy <- ifelse(fitted(m1) < y, fitted(m1) + 0.8 * (y - fitted(m1)), 
                 fitted(m1) + 0.8 * (y - fitted(m1)))
  mypoints2s <- trans3d(x1, x2, newy, pmat = res)
  if (drawArrows) 
    arrows(mypoints4$x, mypoints4$y, mypoints2s$x, mypoints2s$y, 
           col = "red", lty = 4, lwd = 0.3, length = 0.1)
  invisible(list(lm = m1, res = res))
}


persp_grid <- function(xlim, ylim, zlim, pmat, pos = c("z-", "z+", "x-", "x+", "y-", "y+"), n = 5, ...) {
  px <- pretty(xlim, n)[xlim[1] < pretty(xlim, n) & pretty(xlim, n) < xlim[2]]
  py <- pretty(ylim, n)[ylim[1] < pretty(ylim, n) & pretty(ylim, n) < ylim[2]]
  pz <- pretty(zlim, n)[zlim[1] < pretty(zlim, n) & pretty(zlim, n) < zlim[2]]
  if (any(pos == "z-" | pos == "z+")){
    zval <- ifelse(any(pos == "z-"), zlim[1], zlim[2])
    for (ix in px) lines (trans3d(x = ix, y = ylim, z = zval, pmat = pmat), ...) 
    for (iy in py) lines (trans3d(x = xlim, y = iy, z = zval, pmat = pmat), ...)
  }
  if (any(pos == "x-" | pos == "x+")){
    xval <- ifelse(any(pos == "x-"), xlim[1], xlim[2])
    for (iz in pz) lines (trans3d(x = xval, y = ylim, z = iz, pmat = pmat), ...) 
    for (iy in py) lines (trans3d(x = xval, y = iy, z = zlim, pmat = pmat), ...)
  }
  if (any(pos == "y-" | pos == "y+")){
    yval <- ifelse(any(pos == "y-"), ylim[1], ylim[2])
    for (ix in px) lines (trans3d(x = ix, y = yval, z = zlim, pmat = pmat), ...) 
    for (iz in pz) lines (trans3d(x = xlim, y = yval, z = iz, pmat = pmat), ...)
  }
}

使用它们(如果我没有犯错,my_mcGraph3(..., meshcol = "black", grid = T)相当于mcGraph3(...))。

require(rockchalk)

mod3 <- my_mcGraph3(Coral.cover, Coral.richness, fish.rich,
                 interaction = F,
                 theta = -40 ,
                 phi =20,
                 x1lab = "",
                 x2lab = "",
                 ylab = "",
                 x1lim = c(46,100),
                 x2lim = c(-2.5, 12.5),
                 zlim =c(40, 100),
                 r = 10,
                 col = 'white',
                 border = 'black',
                 box = T,
                 axes = T,
                 ticktype = "detailed",
                 ntick = 4, 
                 meshcol = "gray",    # <<- new argument
                 grid = F)            # <<- new argument

persp_grid(xlim = c(46, 100), ylim = c(-2.5, 12.5), zlim = c(40, 100), 
           pmat = mod3$res, pos = c("z-", "y+", "x+"), col = "green", lty = 2)
  # if you want only bottom grid, persp_grid(..., pos = "z-", ...)

# note
magRange(fish.rich, 1.5)   # c(38, 106) is larger than zlim, so warning message comes.

persp的函数使用base绘图绘制图形,换句话说,首先它们将3d坐标转换为2d坐标并将其赋予base绘图的函数。您可以通过trans3d(3d_coodinates, pmat)从三维坐标获得二维坐标。比如说,您想从x=46, y=-2.5, z=100x=46, y=12.5, z=100画线。您可以pmatpmat <- persp(...)获得mod <- mcGraph3(...); pmat <- mod$res。 (请在上面的代码运行后使用下面的代码)

coords_2d_0 <- trans3d(46, -2.5, 100, pmat = mod3$res)  # 2d_coordinates of the start point
coords_2d_1 <- trans3d(46, 12.5, 100, pmat = mod3$res)  # 2d_coordinates of the end point
points(coords_2d_0, col = 2, pch = 19); points(coords_2d_1, col = 2, pch = 19)

xx <- c(coords_2d_0$x, coords_2d_1$x)
yy <- c(coords_2d_0$y, coords_2d_1$y)
lines(xx, yy, col = "blue", lwd = 3)

enter image description here