`rlang::exec` 因 `WRS2::rmmcp` 而失败

`rlang::exec` fails with `WRS2::rmmcp`

我从未遇到过 rlang::exec 的问题,但 WRS2::rmmcp 似乎神秘地失败了,我不确定为什么或如何解决它。

# setup
set.seed(123)
library(WRS2)
library(rlang)

# works
WRS2::rmmcp(
  y = WineTasting$Taste,
  groups = WineTasting$Wine,
  blocks = WineTasting$Taster
)
#> Call:
#> WRS2::rmmcp(y = WineTasting$Taste, groups = WineTasting$Wine, 
#>     blocks = WineTasting$Taster)
#> 
#>                    psihat ci.lower ci.upper p.value p.crit   sig
#> Wine A vs. Wine B 0.02143 -0.02164  0.06449 0.19500 0.0500 FALSE
#> Wine A vs. Wine C 0.11429  0.02148  0.20710 0.00492 0.0169  TRUE
#> Wine B vs. Wine C 0.08214  0.00891  0.15538 0.00878 0.0250  TRUE

# doesn't work
rlang::exec(
  .fn = WRS2::rmmcp,
  y = WineTasting$Taste,
  groups = WineTasting$Wine,
  blocks = WineTasting$Taster
)
#> Error in names(x) <- value: 'names' attribute [18] must be the same length as the vector [3]

为什么会失败,如何解决?

如果您查看 WRS2::rmmcp 的源代码,前几行显示了错误的原因。

WRS2::rmmcp

function (y, groups, blocks, tr = 0.2, alpha = 0.05) 
{
    cols1 <- deparse(substitute(y))
    cols2 <- deparse(substitute(groups))
    cols3 <- deparse(substitute(blocks))
    dat <- data.frame(y, groups, blocks)
    colnames(dat) <- c(cols1, cols2, cols3)
    #...
    #...
}

deparse(substitute()) 代码在与 rlang::exec 一起使用时无法按预期工作。我们可以对列名进行硬编码以使其与 rlang::exec.

一起工作
tmp <- function (y, groups, blocks, tr = 0.2, alpha = 0.05) 
{
  cols1 <- 'col1' #Change
  cols2 <- 'col2' #Change
  cols3 <- 'col3' #Change
  dat <- data.frame(y, groups, blocks)
  colnames(dat) <- c(cols1, cols2, cols3)
  cl <- match.call()
  x <- reshape(dat, idvar = cols3, timevar = cols2, direction = "wide")[-1]
  grp <- c(1:length(x))
  con = 0
  dif = TRUE
  flagcon = F
  if (!is.matrix(x)) 
    x <- matl(x)
  if (!is.matrix(x)) 
    stop("Data must be stored in a matrix or in list mode.")
  con <- as.matrix(con)
  J <- ncol(x)
  xbar <- vector("numeric", J)
  x <- elimna(x)
  nval <- nrow(x)
  h1 <- nrow(x) - 2 * floor(tr * nrow(x))
  df <- h1 - 1
  for (j in 1:J) xbar[j] <- mean(x[, j], tr)
  if (sum(con^2 != 0)) 
    CC <- ncol(con)
  if (sum(con^2) == 0) 
    CC <- (J^2 - J)/2
  ncon <- CC
  if (alpha == 0.05) {
    dvec <- c(0.05, 0.025, 0.0169, 0.0127, 0.0102, 0.00851, 
              0.0073, 0.00639, 0.00568, 0.00511)
    if (ncon > 10) {
      avec <- 0.05/c(11:ncon)
      dvec <- c(dvec, avec)
    }
  }
  if (alpha == 0.01) {
    dvec <- c(0.01, 0.005, 0.00334, 0.00251, 0.00201, 0.00167, 
              0.00143, 0.00126, 0.00112, 0.00101)
    if (ncon > 10) {
      avec <- 0.01/c(11:ncon)
      dvec <- c(dvec, avec)
    }
  }
  if (alpha != 0.05 && alpha != 0.01) 
    dvec <- alpha/c(1:ncon)
  if (sum(con^2) == 0) {
    flagcon <- T
    psihat <- matrix(0, CC, 5)
    dimnames(psihat) <- list(NULL, c("Group", "Group", "psihat", 
                                     "ci.lower", "ci.upper"))
    test <- matrix(NA, CC, 6)
    dimnames(test) <- list(NULL, c("Group", "Group", "test", 
                                   "p.value", "p.crit", "se"))
    temp1 <- 0
    jcom <- 0
    for (j in 1:J) {
      for (k in 1:J) {
        if (j < k) {
          jcom <- jcom + 1
          q1 <- (nrow(x) - 1) * winvar(x[, j], tr)
          q2 <- (nrow(x) - 1) * winvar(x[, k], tr)
          q3 <- (nrow(x) - 1) * wincor(x[, j], x[, k], 
                                       tr)$cov
          sejk <- sqrt((q1 + q2 - 2 * q3)/(h1 * (h1 - 
                                                   1)))
          if (!dif) {
            test[jcom, 6] <- sejk
            test[jcom, 3] <- (xbar[j] - xbar[k])/sejk
            temp1[jcom] <- 2 * (1 - pt(abs(test[jcom, 
                                                3]), df))
            test[jcom, 4] <- temp1[jcom]
            psihat[jcom, 1] <- j
            psihat[jcom, 2] <- k
            test[jcom, 1] <- j
            test[jcom, 2] <- k
            psihat[jcom, 3] <- (xbar[j] - xbar[k])
          }
          if (dif) {
            dv <- x[, j] - x[, k]
            test[jcom, 6] <- trimse(dv, tr)
            temp <- trimci(dv, alpha = alpha/CC, pr = FALSE, 
                           tr = tr)
            test[jcom, 3] <- temp$test.stat
            temp1[jcom] <- temp$p.value
            test[jcom, 4] <- temp1[jcom]
            psihat[jcom, 1] <- j
            psihat[jcom, 2] <- k
            test[jcom, 1] <- j
            test[jcom, 2] <- k
            psihat[jcom, 3] <- mean(dv, tr = tr)
            psihat[jcom, 4] <- temp$ci[1]
            psihat[jcom, 5] <- temp$ci[2]
          }
        }
      }
    }
    temp2 <- order(0 - temp1)
    zvec <- dvec[1:ncon]
    sigvec <- (test[temp2] >= zvec)
    if (sum(sigvec) < ncon) {
      dd <- ncon - sum(sigvec)
      ddd <- sum(sigvec) + 1
      zvec[ddd:ncon] <- dvec[ddd]
    }
    test[temp2, 5] <- zvec
    if (!dif) {
      psihat[, 4] <- psihat[, 3] - qt(1 - alpha/(2 * CC), 
                                      df) * test[, 6]
      psihat[, 5] <- psihat[, 3] + qt(1 - alpha/(2 * CC), 
                                      df) * test[, 6]
    }
  }
  if (sum(con^2) > 0) {
    if (nrow(con) != ncol(x)) 
      warning("The number of groups does not match the number of contrast coefficients.")
    ncon <- ncol(con)
    psihat <- matrix(0, ncol(con), 4)
    dimnames(psihat) <- list(NULL, c("con.num", "psihat", 
                                     "ci.lower", "ci.upper"))
    test <- matrix(0, ncol(con), 5)
    dimnames(test) <- list(NULL, c("con.num", "test", "p.value", 
                                   "p.crit", "se"))
    temp1 <- NA
    for (d in 1:ncol(con)) {
      psihat[d, 1] <- d
      if (!dif) {
        psihat[d, 2] <- sum(con[, d] * xbar)
        sejk <- 0
        for (j in 1:J) {
          for (k in 1:J) {
            djk <- (nval - 1) * wincor(x[, j], x[, k], 
                                       tr)$cov/(h1 * (h1 - 1))
            sejk <- sejk + con[j, d] * con[k, d] * djk
          }
        }
        sejk <- sqrt(sejk)
        test[d, 1] <- d
        test[d, 2] <- sum(con[, d] * xbar)/sejk
        test[d, 5] <- sejk
        temp1[d] <- 2 * (1 - pt(abs(test[d, 2]), df))
      }
      if (dif) {
        for (j in 1:J) {
          if (j == 1) 
            dval <- con[j, d] * x[, j]
          if (j > 1) 
            dval <- dval + con[j, d] * x[, j]
        }
        temp1[d] <- trimci(dval, tr = tr, pr = FALSE)$p.value
        test[d, 1] <- d
        test[d, 2] <- trimci(dval, tr = tr, pr = FALSE)$test.stat
        test[d, 5] <- trimse(dval, tr = tr)
        psihat[d, 2] <- mean(dval, tr = tr)
      }
    }
    test[, 3] <- temp1
    temp2 <- order(0 - temp1)
    zvec <- dvec[1:ncon]
    sigvec <- (test[temp2, 3] >= zvec)
    if (sum(sigvec) < ncon) {
      dd <- ncon - sum(sigvec)
      ddd <- sum(sigvec) + 1
    }
    test[temp2, 4] <- zvec
    psihat[, 3] <- psihat[, 2] - qt(1 - test[, 4]/2, df) * 
      test[, 5]
    psihat[, 4] <- psihat[, 2] + qt(1 - test[, 4]/2, df) * 
      test[, 5]
  }
  if (flagcon) 
    num.sig <- sum(test[, 4] <= test[, 5])
  if (!flagcon) 
    num.sig <- sum(test[, 3] <= test[, 4])
  fnames <- as.character(unique(groups))
  psihat1 <- cbind(psihat, test[, 4:5])
  result <- list(comp = psihat1, fnames = fnames, call = cl)
  class(result) <- "mcp2"
  result
}

请注意,为了可重现性,我复制了整个代码,此函数中的更改仅是第 1 3 行。

在 运行 函数之后,您可以将其用作 :

tmp(
  y = WineTasting$Taste,
  groups = WineTasting$Wine,
  blocks = WineTasting$Taster
)

#Call:
#tmp(y = WineTasting$Taste, groups = WineTasting$Wine, blocks = WineTasting$Taster)

#                   psihat ci.lower ci.upper p.value p.crit   sig
#Wine A vs. Wine B 0.02143 -0.02164  0.06449 0.19500 0.0500 FALSE
#Wine A vs. Wine C 0.11429  0.02148  0.20710 0.00492 0.0169  TRUE
#Wine B vs. Wine C 0.08214  0.00891  0.15538 0.00878 0.0250  TRUE

rlang::exec

res <- rlang::exec(
  .fn = tmp,
  y = WineTasting$Taste,
  groups = WineTasting$Wine,
  blocks = WineTasting$Taster
) 

res$comp
#     Group Group     psihat     ci.lower   ci.upper     p.value p.crit
#[1,]     1     2 0.02142857 -0.021636832 0.06449397 0.195004531 0.0500
#[2,]     1     3 0.11428571  0.021475579 0.20709585 0.004915566 0.0169
#[3,]     2     3 0.08214286  0.008910564 0.15537515 0.008777396 0.0250

res$fnames
#[1] "Wine A" "Wine B" "Wine C"

(尽管与 rlang::exec 一起使用会损坏 res$call。不知道为什么!)

虽然 运行 我得到了像 could not find function elimnacould not find function matl 这样的错误,这很奇怪,因为这些函数来自我加载的包 WRS2 但它仍然给出了错误。我不得不将函数从 https://github.com/cran/WRS2/tree/master/R 复制到我的会话中,然后它就如上所示工作。

exec 的一种替代方法是手动构建调用,然后对其求值:

mycall <- rlang::call2( "rmmcp", .ns="WRS2",
                       y = quote(WineTasting$Taste),
                       groups = quote(WineTasting$Wine),
                       blocks = quote(WineTasting$Taster) )
# WRS2::rmmcp(y = WineTasting$Taste, groups = WineTasting$Wine,
#     blocks = WineTasting$Taster)

eval(mycall)   # Works