具有许多 'fixed effects' / 个别特定截距的大型面板的良好包装

Good packages for large panels with many 'fixed effects' / individual specific intercepts

我在 plm() 这样的小数据集中取得了很好的经验:

library("data.table")
library("plm")
set.seed(123)
smalldata <- data.table(id = rep(1:100, each = 10), x = rnorm(1000))
smalldata[, y := x + 0.3*x + (rnorm(1000)/100)]

plm(smalldata,
    formula = y ~ x,
    effect = c("individual"),
    model = c("within"),
    index = "id")

但是如果我有一个更大的数据集,我很快就会 运行 进入 ram 问题并且事情变得 非常(!)慢

set.seed(123)
largedata <- data.table(id = rep(1:100000, each = 10), x = rnorm(1000000))
largedata[, y := x + 0.3*x + (rnorm(1000000)/100)]

time_pre <- Sys.time()
plm(largedata,
    formula = y ~ x,
    effect = c("individual"),
    model = c("within"),
    index = "id")
time_post <- Sys.time()
time_post-time_pre

这在我的机器上已经花费了超过一分钟的时间,而且我现实生活中的数据集远大于 100 万个观察值。如果您想尝试一下,只需添加几个 0,然后等到您的 RAM 填满并且代码不再 运行 为止。当然,随着更多级别的固定效应的添加,问题变得更糟(您可以在 plm 中使用 as.factor() 虚拟回归来做到这一点)等 pp 但问题已经在这个简单的示例中可见。

这种缓慢也很奇怪,因为 plm() 文档告诉我它在应该很快的转换中使用。他们一定是使用复杂的函数来做到这一点,因为这里是同一个 'fixed effects' 面板,在转换中使用 data.table() 然后 lm():

time_pre_dt <- Sys.time()
largedata[, x_demeaned := x - mean(x), by = id]
largedata[, y_demeaned := y - mean(y), by = id]
lm(largedata, formula = y_demeaned ~ -1 + x_demeaned)
time_post_dt <- Sys.time()
time_post_dt - time_pre_dt
as.double(time_post-time_pre, unit = "secs")/as.double(time_post_dt - time_pre_dt, unit = "secs")

在我的机器上,速度提高了 70-125 倍。

所以更快面板的成分已经存在,但 plm() 似乎没有利用它们。是否还有其他面板包对大型 'fixed effects' 面板更有用?我对解决方法犹豫不决(比如在转换中),因为我并不总是确定在不同版本的 coeftest() 等中如何计算标准误差(Stata 中的相同问题,样本调整等)。我正在寻找开发的软件包,并希望避免从头开始编写所有代码。

编辑:

为了使用下面@Helix123 的答案进行基准测试,这里尝试使用 plm() 'fast':

install.packages("collapse")
install.packages("fixest")
install.packages("lfe")

# (restart R session)

options("plm.fast" = TRUE)

time_pre_fast <- Sys.time()
plm(largedata,
    formula = y ~ x,
    effect = c("individual"),
    model = c("within"),
    index = "id")
time_post_fast <- Sys.time()

time_post_fast - time_pre_fast

as.double(time_post_fast - time_pre_fast, unit = "secs")/as.double(time_post_dt - time_pre_dt, unit = "secs")

内部转换示例在我的机器上仍然快 90 倍。然而,felm() 确实大大加快了速度!这里的例子:

library("lfe")

time_pre_felm <- Sys.time()
felm(largedata, formula = y ~ x | id)
time_post_felm <- Sys.time()

as.double(time_post_felm - time_pre_felm, unit = "secs")/as.double(time_post_dt - time_pre_dt, unit = "secs")

这只需要 data.table() 内部转换的两倍,然后是 lm()

feols() 最固定的包是赢家,请参阅下面的答案。

edit: plm 版本 2.6-0 默认启用快速模式。如果还安装了软件包 fixest(或 lfe),则可以进一步加快 plm 的运行速度。对于 ?plm.fast 中的基准测试示例,这提供了高达 28 倍的加速。

旧答案 自 plm 2.4-0 版本以来,有一个可选的快速模式,请参阅 2.4-0 和 2.4-2 的新闻条目:https://cran.r-project.org/web/packages/plm/news/news.html

要启用快速模式,请设置此选项:

options("plm.fast" = TRUE)

您可以在脚本或 .Rprofile 文件中手动设置此选项。

您还需要安装包 collapse,可选包 fixestlfe 以在某些情况下进一步加快速度,尤其是。从 plm 2.4-2 开始的双向固定效应。

有关基准示例,请参阅 ?plm.fast 的帮助。

在您的示例中,创建 pdata.frame(plm 使用的数据格式)花费了大量时间,如果未首先显式转换数据,它会在对 plm 的调用中隐式发生。因此,可以将格式拆分为 pdata.frame 和模型估计。因此,plm() 的执行时间明显更短。此外,这将加快数据集上第一个模型之后的任何进一步模型估计。

# convert data set to a pdata.frame:
plargedata <- pdata.frame(largedata, index = "id")

# use the pdata.frame for model estimation:
plm(plargedata,
     formula = y ~ x,
     effect = c("individual"),
     model = c("within"))

您还可以查看软件包 fixestlfe 以获得固定效应估计;它们的语法略有不同。如果您不想更改代码,plm 的快速模式应该可以帮助您。但是,如果您有两个以上的固定效应,则需要将它们包含在 plm 的公式中 +factor(fe3) + factor(fe4) + ... 并且这些情况可能会更快 lfefixest 如果有很多具有多个级别的固定效果。

根据@Helix123 的提示查看 fixst 包,答案如下:

library("fixest")

time_pre_fixest <- Sys.time()
feols(y ~ x | id, largedata)
time_post_fixest <- Sys.time()

as.double(time_post_fixest - time_pre_fixest, unit = "secs")/as.double(time_post_dt - time_pre_dt, unit = "secs")

用我问题中最快版本的十分之一。

编辑:当然,您仍然可以 运行 解决 RAM 问题,但这种可能性要小得多。