【问题标题】:Plotting newton-raphson/fisher scoring iterations in R在 R 中绘制 newton-raphson/fisher 评分迭代
【发布时间】:2016-02-12 09:00:52
【问题描述】:

在拟合 glm 模型(来自 stats 包)时,R 中是否有绘制 newton-raphson/fisher 评分迭代的包?

【问题讨论】:

  • 据我所知,没有任何特殊的绘图包,因此,您显然可以使用 ggplot2 等。这是一个具有大量功能的出色软件包。有关 R 绘图包的更多详细信息,请查看此处:stackoverflow.com/questions/3750153/…
  • 感谢您的提示。但我更多的是寻找一个包或 R 代码,它在拟合 glm 并绘制它们时提取迭代

标签: r glm newtons-method


【解决方案1】:

answered a very similar question yesterday。但是,在您的情况下,事情要简单一些。

请注意,当您调用glm 时,它最终会调用glm.fit(或您指定给glm 的任何其他method 参数)计算循环中从第78 行到第170 行的解决方案路径。当前迭代的在第 97 行,使用 .Call 到 C 函数 C_Cdqrls 计算系数的值。作为 hack,您可以在此循环中通过修改 glm.fit 函数将系数的当前值提取到全局环境 (fit$coefficients),如下所示:

glm.fit.new = function (x, y, weights = rep(1, nobs), start = NULL, etastart = NULL, 
                        mustart = NULL, offset = rep(0, nobs), family = gaussian(), 
                        control = list(), intercept = TRUE) {
                          control <- do.call("glm.control", control)
                          x <- as.matrix(x)
                          xnames <- dimnames(x)[[2L]]
                          ynames <- if (is.matrix(y)) 
                            rownames(y)
                          else names(y)
                          conv <- FALSE
                          nobs <- NROW(y)
                          nvars <- ncol(x)
                          EMPTY <- nvars == 0
                          if (is.null(weights)) 
                            weights <- rep.int(1, nobs)
                          if (is.null(offset)) 
                            offset <- rep.int(0, nobs)
                          variance <- family$variance
                          linkinv <- family$linkinv
                          if (!is.function(variance) || !is.function(linkinv)) 
                            stop("'family' argument seems not to be a valid family object", 
                                 call. = FALSE)
                          dev.resids <- family$dev.resids
                          aic <- family$aic
                          mu.eta <- family$mu.eta
                          unless.null <- function(x, if.null) if (is.null(x)) 
                            if.null
                          else x
                          valideta <- unless.null(family$valideta, function(eta) TRUE)
                          validmu <- unless.null(family$validmu, function(mu) TRUE)
                          if (is.null(mustart)) {
                            eval(family$initialize)
                          }
                          else {
                            mukeep <- mustart
                            eval(family$initialize)
                            mustart <- mukeep
                          }
                          if (EMPTY) {
                            eta <- rep.int(0, nobs) + offset
                            if (!valideta(eta)) 
                              stop("invalid linear predictor values in empty model", 
                                   call. = FALSE)
                            mu <- linkinv(eta)
                            if (!validmu(mu)) 
                              stop("invalid fitted means in empty model", call. = FALSE)
                            dev <- sum(dev.resids(y, mu, weights))
                            w <- ((weights * mu.eta(eta)^2)/variance(mu))^0.5
                            residuals <- (y - mu)/mu.eta(eta)
                            good <- rep_len(TRUE, length(residuals))
                            boundary <- conv <- TRUE
                            coef <- numeric()
                            iter <- 0L
                          }
                          else {
                            coefold <- NULL
                            eta <- if (!is.null(etastart)) 
                              etastart
                            else if (!is.null(start)) 
                              if (length(start) != nvars) 
                                stop(gettextf("length of 'start' should equal %d and correspond to initial coefs for %s", 
                                              nvars, paste(deparse(xnames), collapse = ", ")), 
                                     domain = NA)
                            else {
                              coefold <- start
                              offset + as.vector(if (NCOL(x) == 1L) 
                                x * start
                                else x %*% start)
                            }
                            else family$linkfun(mustart)
                            mu <- linkinv(eta)
                            if (!(validmu(mu) && valideta(eta))) 
                              stop("cannot find valid starting values: please specify some", 
                                   call. = FALSE)
                            devold <- sum(dev.resids(y, mu, weights))
                            boundary <- conv <- FALSE

                            # EDIT: counter to create track of iterations
                            i <<- 1
                            for (iter in 1L:control$maxit) {
                              good <- weights > 0
                              varmu <- variance(mu)[good]
                              if (anyNA(varmu)) 
                                stop("NAs in V(mu)")
                              if (any(varmu == 0)) 
                                stop("0s in V(mu)")
                              mu.eta.val <- mu.eta(eta)
                              if (any(is.na(mu.eta.val[good]))) 
                                stop("NAs in d(mu)/d(eta)")
                              good <- (weights > 0) & (mu.eta.val != 0)
                              if (all(!good)) {
                                conv <- FALSE
                                warning(gettextf("no observations informative at iteration %d", 
                                                 iter), domain = NA)
                                break
                              }
                              z <- (eta - offset)[good] + (y - mu)[good]/mu.eta.val[good]
                              w <- sqrt((weights[good] * mu.eta.val[good]^2)/variance(mu)[good])
                              fit <- .Call(stats:::C_Cdqrls, x[good, , drop = FALSE] * 
                                             w, z * w, min(1e-07, control$epsilon/1000), check = FALSE)

                              #======================================================
                              # EDIT: assign the coefficients to variables in the global namespace
                              #======================================================
                              assign(paste0("iteration_x_", i), fit$coefficients, 
                                     envir = .GlobalEnv)
                              i <<- i + 1   # increase the counter

                              if (any(!is.finite(fit$coefficients))) {
                                conv <- FALSE
                                warning(gettextf("non-finite coefficients at iteration %d", 
                                                 iter), domain = NA)
                                break
                              }
                              if (nobs < fit$rank) 
                                stop(sprintf(ngettext(nobs, "X matrix has rank %d, but only %d observation", 
                                                      "X matrix has rank %d, but only %d observations"), 
                                             fit$rank, nobs), domain = NA)
                              start[fit$pivot] <- fit$coefficients
                              eta <- drop(x %*% start)
                              mu <- linkinv(eta <- eta + offset)
                              dev <- sum(dev.resids(y, mu, weights))
                              if (control$trace) 
                                cat("Deviance = ", dev, " Iterations - ", iter, 
                                    "\n", sep = "")
                              boundary <- FALSE
                              if (!is.finite(dev)) {
                                if (is.null(coefold)) 
                                  stop("no valid set of coefficients has been found: please supply starting values", 
                                       call. = FALSE)
                                warning("step size truncated due to divergence", 
                                        call. = FALSE)
                                ii <- 1
                                while (!is.finite(dev)) {
                                  if (ii > control$maxit) 
                                    stop("inner loop 1; cannot correct step size", 
                                         call. = FALSE)
                                  ii <- ii + 1
                                  start <- (start + coefold)/2
                                  eta <- drop(x %*% start)
                                  mu <- linkinv(eta <- eta + offset)
                                  dev <- sum(dev.resids(y, mu, weights))
                                }
                                boundary <- TRUE
                                if (control$trace) 
                                  cat("Step halved: new deviance = ", dev, "\n", 
                                      sep = "")
                              }
                              if (!(valideta(eta) && validmu(mu))) {
                                if (is.null(coefold)) 
                                  stop("no valid set of coefficients has been found: please supply starting values", 
                                       call. = FALSE)
                                warning("step size truncated: out of bounds", 
                                        call. = FALSE)
                                ii <- 1
                                while (!(valideta(eta) && validmu(mu))) {
                                  if (ii > control$maxit) 
                                    stop("inner loop 2; cannot correct step size", 
                                         call. = FALSE)
                                  ii <- ii + 1
                                  start <- (start + coefold)/2
                                  eta <- drop(x %*% start)
                                  mu <- linkinv(eta <- eta + offset)
                                }
                                boundary <- TRUE
                                dev <- sum(dev.resids(y, mu, weights))
                                if (control$trace) 
                                  cat("Step halved: new deviance = ", dev, "\n", 
                                      sep = "")
                              }
                              if (abs(dev - devold)/(0.1 + abs(dev)) < control$epsilon) {
                                conv <- TRUE
                                coef <- start
                                break
                              }
                              else {
                                devold <- dev
                                coef <- coefold <- start
                              }
                            }
                            if (!conv) 
                              warning("glm.fit: algorithm did not converge", call. = FALSE)
                            if (boundary) 
                              warning("glm.fit: algorithm stopped at boundary value", 
                                      call. = FALSE)
                            eps <- 10 * .Machine$double.eps
                            if (family$family == "binomial") {
                              if (any(mu > 1 - eps) || any(mu < eps)) 
                                warning("glm.fit: fitted probabilities numerically 0 or 1 occurred", 
                                        call. = FALSE)
                            }
                            if (family$family == "poisson") {
                              if (any(mu < eps)) 
                                warning("glm.fit: fitted rates numerically 0 occurred", 
                                        call. = FALSE)
                            }
                            if (fit$rank < nvars) 
                              coef[fit$pivot][seq.int(fit$rank + 1, nvars)] <- NA
                            xxnames <- xnames[fit$pivot]
                            residuals <- (y - mu)/mu.eta(eta)
                            fit$qr <- as.matrix(fit$qr)
                            nr <- min(sum(good), nvars)
                            if (nr < nvars) {
                              Rmat <- diag(nvars)
                              Rmat[1L:nr, 1L:nvars] <- fit$qr[1L:nr, 1L:nvars]
                            }
                            else Rmat <- fit$qr[1L:nvars, 1L:nvars]
                            Rmat <- as.matrix(Rmat)
                            Rmat[row(Rmat) > col(Rmat)] <- 0
                            names(coef) <- xnames
                            colnames(fit$qr) <- xxnames
                            dimnames(Rmat) <- list(xxnames, xxnames)
                          }
                          names(residuals) <- ynames
                          names(mu) <- ynames
                          names(eta) <- ynames
                          wt <- rep.int(0, nobs)
                          wt[good] <- w^2
                          names(wt) <- ynames
                          names(weights) <- ynames
                          names(y) <- ynames
                          if (!EMPTY) 
                            names(fit$effects) <- c(xxnames[seq_len(fit$rank)], rep.int("", 
                                                                                        sum(good) - fit$rank))
                          wtdmu <- if (intercept) 
                            sum(weights * y)/sum(weights)
                          else linkinv(offset)
                          nulldev <- sum(dev.resids(y, wtdmu, weights))
                          n.ok <- nobs - sum(weights == 0)
                          nulldf <- n.ok - as.integer(intercept)
                          rank <- if (EMPTY) 
                            0
                          else fit$rank
                          resdf <- n.ok - rank
                          aic.model <- aic(y, n, mu, weights, dev) + 2 * rank
                          list(coefficients = coef, residuals = residuals, fitted.values = mu, 
                               effects = if (!EMPTY) fit$effects, R = if (!EMPTY) Rmat, 
                               rank = rank, qr = if (!EMPTY) structure(fit[c("qr", "rank", 
                                                                             "qraux", "pivot", "tol")], class = "qr"), family = family, 
                               linear.predictors = eta, deviance = dev, aic = aic.model, 
                               null.deviance = nulldev, iter = iter, weights = wt, prior.weights = weights, 
                               df.residual = resdf, df.null = nulldf, y = y, converged = conv, 
                               boundary = boundary)
                        }

请注意,这是一个 hack,有几个原因:
1.函数C_Cdrqls不是stats包导出的,所以我们必须在namespace:package:stats中查找。
2. 这会通过调用glm.fit.new 的副作用用迭代值污染您的全局环境,每次迭代创建一个向量。在 R 等函数式语言中,副作用通常不受欢迎。您可以通过创建矩阵或data.frame 并在其中分配来清理多个对象。

但是,一旦您提取了迭代值,您就可以对它们做任何您想做的事情,包括绘制它们。

下面是使用新定义的glm.fit.new 方法调用glm 的样子:

counts = c(18,17,15,20,10,20,25,13,12)
outcome = gl(3,1,9)
treatment = gl(3,3)
print(d.AD = data.frame(treatment, outcome, counts))
glm.D93 = glm(counts ~ outcome + treatment, family = poisson(), 
               control = list(trace = TRUE, epsilon = 1e-16), method = "glm.fit.new")

您可以检查迭代参数值是否确实已填充到全局环境中:

> ls(pattern = "iteration_x_")
 [1] "iteration_x_1"  "iteration_x_10" "iteration_x_11" "iteration_x_2" 
 [5] "iteration_x_3"  "iteration_x_4"  "iteration_x_5"  "iteration_x_6" 
 [9] "iteration_x_7"  "iteration_x_8"  "iteration_x_9" 

【讨论】:

  • 首先,非常感谢您快速详细的回答。一个快速的问题。当我遵循您的建议并使用新方法 typ 拟合 glm 时,我只会得到每次迭代的偏差。您是否偶然知道,我如何自己获得估计的参数?因为,我想绘制它们,这将非常有帮助!再次感谢,非常感谢!
  • @PatrickBalada 我的猜测是您正在查看打印到控制台的内容。正如我在回答中提到的那样——参数没有打印出来,并且包含在名称为 iteration_x_* 的对象中。
  • @PatrickBalada 试试t(sapply(ls(pattern = "iteration_x"), function(x) eval(parse(text = x)), simplify = TRUE))
  • 太棒了 - 完美!太感谢了!很好的帮助!
猜你喜欢
  • 2023-04-06
  • 2017-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-10
  • 1970-01-01
  • 2013-01-02
  • 2015-05-08
相关资源
最近更新 更多