【问题标题】:Faster Method for R While Loop in least squares function最小二乘函数中 R While 循环的更快方法
【发布时间】:2012-04-23 15:24:36
【问题描述】:

我正在尝试加速下面的函数(用于以后的引导),该函数执行直线的最小二乘拟合,x 和 y 都有误差。我认为主要的挂断是在while循环中。该函数的输入值是观测值xy 以及这些值sxsy 中的绝对不确定性。

york <- function(x, y, sx, sy){

    x <- cbind(x)
    y <- cbind(y)

    # initial least squares regression estimation
    fit <- lm(y ~ x)
    a1 <- as.numeric(fit$coefficients[1])   # intercept
    b1 <- as.numeric(fit$coefficients[2])   # slope
    e1 <- cbind(as.numeric(fit$residuals))  # residuals
    theta.fit <- rbind(a1, b1)

    # constants
    rho.xy <- 0     # correlation between x and y

    # initialize york regression
    X <- cbind(1, x)
    a <- a1
    b <- b1
    tol <- 1e-15    # tolerance
    d <- tol
    i = 0

    # york regression
    while (d > tol || d == tol){
        i <- i + 1
        a2 <- a
        b2 <- b
        theta2 <- rbind(a2, b2)
        e <- y - X %*% theta2
        w <- 1 / sqrt((sy^2) + (b2^2 * sx^2) - (2 * b2 * sx * sy * rho.xy))
        W <- diag(w)
        theta <- solve(t(X) %*% (W %*% W) %*% X) %*% t(X) %*% (W %*% W) %*% y

        a <- theta[1]
        b <- theta[2]

        mswd <- (t(e) %*% (W%*%W) %*% e)/(length(x) - 2)
        sfit <- sqrt(mswd)
        Vo <- solve(t(X) %*% (W %*% W) %*% X)
        dif <- b - b2
        d <- abs(dif)
        }

    # format results to data.frame
    th <- data.frame(a, b)
    names(th) <- c("intercept", "slope")
    ft <- data.frame(mswd, sfit)
    names(ft) <- c("mswd", "sfit")
    df <- data.frame(x, y, sx, sy, as.vector(e), diag(W))
    names(df) <- c("x", "y", "sx", "sy", "e", "W")

    # store output results
    list(coefficients = th,
        vcov = Vo,
        fit = ft,
        df = df)
}

【问题讨论】:

  • 只是出于兴趣,你的向量有多大,代码运行需要多长时间?
  • 在循环之前为结果向量分配内存,然后避免 cbind 和 rbind。
  • 这可能不是一个特别令人满意的答案,但这正是您应该在从 R 调用的编译代码中执行 while 循环的那种函数。
  • 运行缓慢的数据并不多。如果是我,我可能会进行一些仔细的调试,以查看耗时这么长的 while 循环中发生了什么。特别是关于您的公差设置和 d 的连续值。
  • 还可以尝试使用 R 的内置函数进行加权回归,而不是自己滚动;它可能更快也可能不会更快,但肯定更可靠。 theta &lt;- coef(lsfit(x,y,wt=w^2))

标签: r


【解决方案1】:

您可以通过一些简单的更改来加快您的功能。首先,您应该将不需要的任何内容移出 while 循环。例如,您对同一数据运行两次solve。此外,当您仅在 while 循环的最后一次迭代中使用 sfit 时,您会在每次迭代中计算它。

这是我的代码:

york.fast <- function(x, y, sx, sy, tol=1e-15){
    # initial least squares regression estimation
    fit <- lm(y ~ x)
    theta <- fit$coefficients
    # initialize york regression
    X <- cbind(1, x)
    d <- tol
    # york regression
    while (d >= tol){
        b2 <- theta[2]
        # w <- 1 / sqrt((sy^2) + (b2^2 * sx^2) - (2 * b2 * sx * sy * rho.xy)) # rho.xy is always zero!
        w <- 1 / sqrt(sy^2 + (b2^2 * sx^2))  # rho.xy is always zero!
        # W <- diag(w)
        # w2 <- W %*% W
        w2 <- diag(w^2) # As suggested in the comments.
        base <- crossprod(X,w2)
        Vo <- solve(base %*% X)
        theta <- Vo %*% base %*% y
        d <- abs(theta[2] - b2)
     }
     e <- y - X %*% theta
     mswd <- (crossprod(e,w2) %*% e) / (length(x) - 2)
     sfit <- sqrt(mswd)

    # format results to data.frame
    th <- data.frame(intercept=theta[1], slope=theta[2])
    ft <- data.frame(mswd=mswd, sfit=sfit)
    df <- data.frame(x=x, y=y, sx=sx, sy=sy, e=as.vector(e), W=diag(diag(w)))

    # store output results
    list(coefficients = th, vcov = Vo, fit = ft, df = df)
}

一个小测试:

n=225
set.seed(1)
x=rnorm(n)
y=rnorm(n)
sx=rnorm(n)
sy=rnorm(n)

system.time(test<-york.fast(x,y,sx,sy)) # 0.37 s
system.time(gold<-york(x,y,sx,sy)) # 1.28 s

我注意到rho.xy 始终固定为零。这可能是一个错误吗?

我还注意到,您经常使用cbindvector 转换为具有一列的matrix。所有向量都被自动视为一列矩阵,因此您可以避免大量额外代码。

正如@joran 提到的,容差水平设置得太小,以至于需要很长时间才能收敛;考虑使用更大的公差。

【讨论】:

  • 感谢大家的帮助。我显然还有一些东西要学,但这些建议非常有用。
  • +1:不过,我肯定更喜欢使用现有的例程进行拟合。尽管如此,这样做还有一个额外的加速:叉积可以通过使用常规乘法而不是矩阵乘法来加速,因为第二项是对角矩阵。至少是w2 &lt;- diag(w^2),而不是W%*%W
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-02
  • 1970-01-01
  • 2010-11-03
  • 2018-10-21
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
相关资源
最近更新 更多