【问题标题】:Why does `fastLm()` return results when I run a regression with one observation?当我对一个观察值进行回归时,为什么“fastLm()”会返回结果?
【发布时间】:2016-10-06 00:28:10
【问题描述】:

当我对一个观察值运行回归时,为什么 fastLm() 返回结果?

在下面,为什么lm()fastLm() 结果不相等?

library(Rcpp)
library(RcppArmadillo)
library(data.table)
set.seed(1)
DT <- data.table(y = rnorm(5), x1 = rnorm(5), x2 = rnorm(5), my.key = 1:5)
#             y         x1         x2 my.key
# 1: -0.6264538 -0.8204684  1.5117812      1
# 2:  0.1836433  0.4874291  0.3898432      2
# 3: -0.8356286  0.7383247 -0.6212406      3
# 4:  1.5952808  0.5757814 -2.2146999      4
# 5:  0.3295078 -0.3053884  1.1249309      5

lm(y ~ 1 + x1 + x2, data = DT[my.key == 1])
# Coefficients:
# (Intercept)           x1           x2  
#     -0.6265           NA           NA

fastLm(X = model.matrix(y ~ 1 + x1 + x2, data = DT[my.key == 1]), y = DT[my.key == 1]$y)
# Coefficients:
# (Intercept)          x1          x2 
#    -0.15825     0.12984    -0.23924 

model.matrix(y ~ 1 + x1 + x2, data = DT[my.key == 1])
#   (Intercept)        x1       x2
#             1 -0.8204684 1.511781
# attr(,"assign")
# [1] 0 1 2

DT[my.key == 1]$y
# [1] -0.6264538

当我使用整个 DT 时,结果是相等的:

 all.equal(fastLm(X = model.matrix(y ~ 1 + x1 + x2, data = DT), y = DT$y)$coef, 
           lm.fit(x = model.matrix(y ~ 1 + x1 + x2, data = DT), y = DT$y)$coef)
# [1] TRUE

从带有修改的fLmTwoCastsRcppArmadillo 库中,我也得到了相同的行为:

src <- '
Rcpp::List fLmTwoCastsOnlyCoefficients(Rcpp::NumericMatrix Xr, Rcpp::NumericVector yr) {
    int n = Xr.nrow(), k = Xr.ncol();
    arma::mat X(Xr.begin(), n, k, false);
    arma::colvec y(yr.begin(), yr.size(), false);
    arma::colvec coef = arma::solve(X, y);
    return Rcpp::List::create(Rcpp::Named("coefficients")=trans(coef));
}
'
cppFunction(code=src, depends="RcppArmadillo")

XX <- model.matrix(y ~ 1 + x1 + x2, data = DT[my.key == 1])
YY <- DT[my.key == 1]$y
fLmTwoCastsOnlyCoefficients(XX, YY)$coef
#            [,1]      [,2]       [,3]
# [1,] -0.1582493 0.1298386 -0.2392384

使用整个DT,系数应该是相同的:

lm(y ~ 1 + x1 + x2, data = DT)$coef
# (Intercept)          x1          x2 
#   0.2587933  -0.7709158  -0.6648270

XX.whole <- model.matrix(y ~ 1 + x1 + x2, data = DT)
YY.whole <- DT$y
fLmTwoCastsOnlyCoefficients(XX.whole, YY.whole)
#           [,1]       [,2]      [,3]
# [1,] 0.2587933 -0.7709158 -0.664827

【问题讨论】:

  • “但是,Armadillo 要么失败,要么更糟糕的是,在秩不足的模型矩阵上产生完全错误的答案,而由于修改了 Linpack 代码,stats 包中的函数将正确处理它们。”跨度>

标签: r rcpp armadillo


【解决方案1】:

因为fastLm 不担心排名不足;这是您为速度付出的代价的一部分。

来自?fastLm

... Armadillo 可以比 stats 包中的函数更快地执行类似 lm.fit 的操作的原因是,Armadillo 使用 QR 分解的 Lapack 版本,而 stats 包使用修改后的 Linpack 版本。因此,Armadillo 使用 3 级 BLAS 代码,而 stats 包使用 1 级 BLAS。然而,犰狳会失败,或者更糟的是,在秩不足的模型矩阵上产生完全错误的答案,而由于修改了 Linpack 代码,stats 包中的函数将正确处理它们。

看代码here,代码的胆子是

 arma::colvec coef = arma::solve(X, y);

这会进行 QR 分解。我们可以将lmFast 结果与来自base R 的qr() 匹配(这里我不只使用base R 构造,而是依赖data.table):

set.seed(1)
dd <- data.frame(y = rnorm(5), 
      x1 = rnorm(5), x2 = rnorm(5), my.key = 1:5)

X <- model.matrix(~1+x1+x2, data=subset(dd,my.key==1))
qr(X,dd$y)
## $qr
##   (Intercept)         x1       x2
## 1           1 -0.8204684 1.511781

你可以看lm.fit()的代码,看看R在拟合线性模型时对秩亏做了什么;它调用的底层 BLAS 算法通过旋转来执行 QR ...

如果你想标记这些情况,我认为Matrix::rankMatrix() 可以解决问题:

library(Matrix)
rankMatrix(X) < ncol(X)  ## TRUE
X1 <- model.matrix(~1+x1+x2, data=dd)
rankMatrix(X1) < ncol(X1)  ## FALSE

【讨论】:

  • 希望这是唯一的原因,对吧?因此,如果我检查 X'X 的确定性将阻止我考虑此类情况。即library(matrixcalc); is.positive.definite(crossprod(XX))FALSE,而is.positive.definite(crossprod(XX))TRUE,这种情况我不考虑第一种情况(XX只有一个观察,而XX.whole 5)。
  • 我认为不是,但我不知道。这是arma::solve 的文档 ...arma.sourceforge.net/docs.html#solve
  • FWIW RcppEigen 包在其fastLm() 示例中具有更完整的分解集。但正如 Ben 雄辩地指​​出的那样:“这是你为速度付出的一部分代价。”。我们给你足够的绳子让你上吊。如果您想保护自己免受伤害,请坚持使用 lm()lm.fit(),或者创建一个混合“快速但安全”的版本。
猜你喜欢
  • 2021-10-31
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-16
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
相关资源
最近更新 更多