是否有简单回归的快速估计(仅具有截距和斜率的回归线)?

dan*_*dan 0 regression r linear-regression lm

该问题涉及机器学习特征选择过程.

我有一个很大的特征矩阵 - 列是主题(行)的特征:

set.seed(1)
features.mat <- matrix(rnorm(10*100),ncol=100)
colnames(features.mat) <- paste("F",1:100,sep="")
rownames(features.mat) <- paste("S",1:10,sep="")
Run Code Online (Sandbox Code Playgroud)

S在不同条件(C)下测量每个受试者()的响应,因此看起来像这样:

response.df <-
data.frame(S = c(sapply(1:10, function(x) rep(paste("S", x, sep = ""),100))),
           C = rep(paste("C", 1:100, sep = ""), 10),
           response = rnorm(1000), stringsAsFactors = F)
Run Code Online (Sandbox Code Playgroud)

所以我匹配的主题是response.df:

match.idx <- match(response.df$S, rownames(features.mat))
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种快速计算每个特征和响应的单变量回归的方法.

比这更快的东西?:

fun <- function(f){
  fit <- lm(response.df$response ~ features.mat[match.idx,f])
  beta <- coef(summary(fit))
  data.frame(feature = colnames(features.mat)[f], effect = beta[2,1],
             p.val = beta[2,4], stringsAsFactors = F))
  }

res <- do.call(rbind, lapply(1:ncol(features.mat), fun))
Run Code Online (Sandbox Code Playgroud)

我对边缘增强感兴趣,即通过mclapply或使用并行计算以外的方法mclapply2.

李哲源*_*李哲源 6

我将提供一个轻量级的玩具例程来估计一个简单的回归模型:y ~ x即一个只有截距和斜率的回归线.可以看出,这比lm+ 快36倍summary.lm.

## toy data
set.seed(0)
x <- runif(50)
y <- 0.3 * x + 0.1 + rnorm(50, sd = 0.05)

## fast estimation of simple linear regression: y ~ x 
simplelm <- function (x, y) {
  ## number of data
  n <- length(x)
  ## centring
  y0 <- sum(y) / length(y); yc <- y - y0
  x0 <- sum(x) / length(x); xc <- x - x0
  ## fitting an intercept-free model: yc ~ xc + 0
  xty <- c(crossprod(xc, yc))
  xtx <- c(crossprod(xc))
  slope <- xty / xtx
  rc <- yc - xc * slope
  ## Pearson estimate of residual standard error
  sigma2 <- c(crossprod(rc)) / (n - 2)
  ## standard error for slope
  slope_se <- sqrt(sigma2 / xtx)
  ## t-score and p-value for slope
  tscore <- slope / slope_se
  pvalue <- 2 * pt(abs(tscore), n - 2, lower.tail = FALSE)
  ## return estimation summary for slope
  c("Estimate" = slope, "Std. Error" = slope_se, "t value" = tscore, "Pr(>|t|)" = pvalue)
  }
Run Code Online (Sandbox Code Playgroud)

我们来测试一下:

simplelm(x, y)

#    Estimate   Std. Error      t value     Pr(>|t|) 
#2.656737e-01 2.279663e-02 1.165408e+01 1.337380e-15
Run Code Online (Sandbox Code Playgroud)

另一方面,lm+ summary.lm给出:

coef(summary(lm(y ~ x)))

#             Estimate Std. Error   t value     Pr(>|t|)
#(Intercept) 0.1154549 0.01373051  8.408633 5.350248e-11
#x           0.2656737 0.02279663 11.654079 1.337380e-15
Run Code Online (Sandbox Code Playgroud)

所以结果匹配.如果你需要R平方并调整R平方,它也可以很容易地计算出来.


我们有一个基准:

set.seed(0)
x <- runif(10000)
y <- 0.3 * x + 0.1 + rnorm(10000, sd = 0.05)

library(microbenchmark)

microbenchmark(coef(summary(lm(y ~ x))), simplelm(x, y))

#Unit: microseconds
#                     expr      min       lq       mean   median       uq
# coef(summary(lm(y ~ x))) 14158.28 14305.28 17545.1544 14444.34 17089.00
#           simplelm(x, y)   235.08   265.72   485.4076   288.20   319.46
#      max neval cld
# 114662.2   100   b
#   3409.6   100  a 
Run Code Online (Sandbox Code Playgroud)

圣!!!我们有36次提升!


备注-1(求解正规方程)

simplelm是基于通过Cholesky分解求解正规方程.但由于它很简单,因此不涉及实际的矩阵计算.如果我们需要使用多个协变量进行回归,我们可以使用lm.chol我的答案中定义的.

也可以通过使用LU分解来求解正规方程.我不会谈到这一点,但如果你感兴趣,这就是它:解决正规方程给出不同的系数使用lm.

备注-2(替代途径cor.test)

simplelmfastsim我的答案蒙特卡罗模拟两个布朗运动(连续随机游走)之间相关性的扩展.另一种方法是基于cor.test.它也比lm+ 快得多summary.lm,但正如答案所示,它比我上面提出的要慢.

备注-3(通过QR方法替代)

基于QR方法也是可行的,在这种情况下,我们想用.lm.fit,对于光称重包装qr.default,qr.coef,qr.fittedqr.resid在C级.以下是我们如何将此选项添加到我们的simplelm:

## fast estimation of simple linear regression: y ~ x 
simplelm <- function (x, y, QR = FALSE) {
  ## number of data
  n <- length(x)
  ## centring
  y0 <- sum(y) / length(y); yc <- y - y0
  x0 <- sum(x) / length(x); xc <- x - x0
  ## fitting intercept free model: yc ~ xc + 0
  if (QR) {
    fit <- .lm.fit(matrix(xc), yc)
    slope <- fit$coefficients
    rc <- fit$residuals
    } else {
    xty <- c(crossprod(xc, yc))
    xtx <- c(crossprod(xc))
    slope <- xty / xtx
    rc <- yc - xc * slope
    }
  ## Pearson estimate of residual standard error
  sigma2 <- c(crossprod(rc)) / (n - 2)
  ## standard error for slope
  if (QR) {
    slope_se <- sqrt(sigma2) / abs(fit$qr[1])
    } else {
    slope_se <- sqrt(sigma2 / xtx)
    }
  ## t-score and p-value for slope
  tscore <- slope / slope_se
  pvalue <- 2 * pt(abs(tscore), n - 2, lower.tail = FALSE)
  ## return estimation summary for slope
  c("Estimate" = slope, "Std. Error" = slope_se, "t value" = tscore, "Pr(>|t|)" = pvalue)
  }
Run Code Online (Sandbox Code Playgroud)

对于我们的玩具数据,QR方法和Cholesky方法都给出了相同的结果:

set.seed(0)
x <- runif(50)
y <- 0.3 * x + 0.1 + rnorm(50, sd = 0.05)

simplelm(x, y, TRUE)

#    Estimate   Std. Error      t value     Pr(>|t|) 
#2.656737e-01 2.279663e-02 1.165408e+01 1.337380e-15 

simplelm(x, y, FALSE)

#    Estimate   Std. Error      t value     Pr(>|t|) 
#2.656737e-01 2.279663e-02 1.165408e+01 1.337380e-15
Run Code Online (Sandbox Code Playgroud)

已知QR方法比Cholesky方法慢2~3倍(阅读我的答案为什么内置的lm函数在R中如此慢?详细解释).这是一个快速检查:

set.seed(0)
x <- runif(10000)
y <- 0.3 * x + 0.1 + rnorm(10000, sd = 0.05)

library(microbenchmark)

microbenchmark(simplelm(x, y, TRUE), simplelm(x, y))

#Unit: microseconds
#                 expr    min     lq      mean median     uq     max neval cld
# simplelm(x, y, TRUE) 776.88 873.26 1073.1944 908.72 933.82 3420.92   100   b
#       simplelm(x, y) 238.32 292.02  441.9292 310.44 319.32 3515.08   100  a 
Run Code Online (Sandbox Code Playgroud)

的确如此908 / 310 = 2.93.

备注-4(GLM的简单回归)

如果我们继续使用GLM,还有一个快速,轻量级的版本glm.fit.你可以阅读我的答案R循环帮助:省略一个观察并一次运行glm一个变量并使用f那里定义的函数.目前f是针对逻辑回归定制的,但我们可以轻松地将其推广到其他响应.