【发布时间】:2015-12-22 23:47:18
【问题描述】:
我正在使用 R 中的 apply() 系列函数,并尝试使用 apply() 编写一个对数似然函数。
这是假设高斯干扰的线性回归模型的对数似然:
# Likelihood function for the standard linear regression model
logL <- function(theta, data){
# Return minus the log likelihood function for the standard linear regression model
# y: endogenous variable
# x: matrix of regressors
y <- data[, 1]
x <- data[, -1]
N <- nrow(data)
# This is the contribution to the log-likelihood of individual i. Initialized at 0.
contrib <- 0
beta <- head(theta, -1) # Every element but the last one
sigma <- tail(theta, 1) # Only the last element
for (i in 1:N){
contrib <- contrib + (y[i] - beta%*%x[i,])**2
}
sigma <- abs(sigma)
L <- -(1/(2*sigma^2)*contrib) - 1/2 * N * log(2*pi) - N * log(sigma)
return(-L)
}
下面我们模拟一些数据,最小化负对数似然(相当于最大化对数似然)。
# Simulate some data
N <- 1000
x <- cbind(1, rnorm(N,0,sd=1), rnorm(N, 0, sd=2))
true_theta <- c(2, 3, 2, 4)
y <- true_theta[1:3]%*%t(x) + rnorm(N, mean = 0, sd = true_theta[4])
my_data <- cbind(t(y),x)
optim(c(1,1,1, 1), fn = logL, data = my_data,
method = "L-BFGS-B",upper = c(Inf, Inf, Inf), lower=c(-Inf, -Inf, 0.01))
到目前为止一切顺利,我们得到的结果与用于模拟数据的结果相同。通过使用 rbenchmark 包,我得到优化步骤的 10 次复制在我的计算机上大约需要 4 秒。
benchmark(optim(c(1,1,1, 1), fn = logL, data = my_data,
method = "L-BFGS-B",upper = c(Inf, Inf, Inf), lower=c(-Inf, -Inf, 0.01)),
replications=10)
现在我尝试用 apply 函数替换 for 循环。为此,我将 contrib 定义为一个函数:
contrib <- function(beta, one_obs){
y <- one_obs[1]
x <- one_obs[-1]
return((y - beta%*%x)**2)
}
还有新的对数似然函数:
logL2 <- function(theta, data){
# Return minus the log likelihood function for the standard linear regression model
# y: endogenous variable
# x: matrix of regressors
N <- nrow(data)
beta <- head(theta, -1) # Every element but the last one
sigma <- tail(theta, 1) # Only the last element
sigma <- abs(sigma)
L <- -(1/(2*sigma^2)*sum(apply(data, FUN=contrib, beta = beta, 1)))
- 1/2 * N * log(2*pi) - N * log(sigma)
return(-L)
}
这几乎是原来的两倍。现在,我可能误解了 apply 系列函数的作用,因为它们应该用于代码清晰而不是性能。但是,它们不应该比 for 循环慢,对吧?那么我的代码发生了什么?是否正在进行某种类型转换?我检查并 logL 返回一个矩阵, logL2 返回一个数字。我尝试使用 vapply() 因为它允许指定返回对象的类型,但 vapply() 似乎通过将每一列堆叠在一起将我的数据矩阵转换为向量。这会导致 contrib 函数不再工作:
logL2 <- function(theta, data){
# Return minus the log likelihood function for the standard linear regression model
# y: endogenous variable
# x: matrix of regressors
N <- nrow(data)
beta <- head(theta, -1) # Every element but the last one
sigma <- tail(theta, 1) # Only the last element
sigma <- abs(sigma)
L <- -(1/(2*sigma^2)*sum(vapply(data, FUN=contrib, beta = beta, FUN.VALUE = matrix(1)))) - 1/2 * N * log(2*pi) - N * log(sigma)
return(-L)
}
这就是我得到的结果:
class(logL2(theta = c(1,2,2,2), my_data))
Error in beta %*% x : non-conformable arguments
那么我如何使用 apply 系列函数来使我的代码更具可读性,并且至少与使用 for 循环一样快?
【问题讨论】:
标签: r