【问题标题】:Create lagged variable in unbalanced panel data in R在 R 中的不平衡面板数据中创建滞后变量
【发布时间】:2023-03-05 15:38:01
【问题描述】:

我想在一个组中创建一个包含上一年变量值的变量。

     id   date        value
1     1   1992          4.1  
2     1     NA          4.5  
3     1   1991          3.3  
4     1   1990          5.3  
5     1   1994          3.0  
6     2   1992          3.2  
7     2   1991          5.2  

value_lagged 应该在组中缺少前一年时丢失 - 因为它是组中的第一个日期(如第 4、7 行),或者因为数据中存在年份间隔(如第 5 行)。此外,当当前时间缺失时,value_lagged 应该缺失(如第 2 行)。

这给出:

     id   date    value    value_lagged  
1     1   1992      4.1             3.3
2     1     NA      4.5              NA
3     1   1991      3.3             5.3
4     1   1990      5.3              NA
5     1   1994      3.0              NA
6     2   1992      3.2             5.2
7     2   1991      5.2              NA

目前,在 R 中,我使用 data.table

 DT = data.table(id    = c(1,1,1,1,1,2,2),
                 date  = c(1992,NA,1991,1990,1994,1992,1991),
                 value = c(4.1,4.5,3.3,5.3,3.0,3.2,5.2)
                )
 setkey(DT, id, date)
 DT[, value_lagged := DT[J(id, date-1), value], ]
 DT[is.na(date), value_lagged := NA, ]

它很快,但对我来说似乎有点容易出错。我想知道使用data.tabledplyr 或任何其他软件包是否有更好的选择。非常感谢!


Stata,可以这样做:

    tsset id date
    gen value_lagged=L.value

【问题讨论】:

  • 除非您特别希望缺少 value 的行没有匹配的滞后值,否则您可能打算使用 is.na(date) 而不是 is.na(value)
  • 是的,已更正。谢谢。
  • @Matthew 似乎您已经有了一个不错的解决方案 - 您到底想改进什么?
  • 我的解决方案对我来说似乎有些复杂且容易出错 - 但可能是因为我是 R 新手。即使它是一个不错的解决方案,也可能有更简单的方法来做到这一点!无论如何,这是一个重要的问题(至少对于 stata 用户而言),所以我认为应该有一个关于堆栈溢出的明确问题/答案。
  • 这是对最后两个步骤的轻微重写,这可能不太容易出错(因为你没有重复DT 多次),但我真的看不出你还想要什么具有专门设计用于执行您想要的功能(您的 stata 解决方案真正是什么):DT[J(id, date + 1, val = value), val_lag := i.val][is.na(date), val_lag := NA]

标签: r data.table dplyr panel-data


【解决方案1】:

我可能会使用连接来解决这个问题:

library(dplyr)

df <- data.frame(
  id = c(1, 1, 1, 1, 1, 2, 2), 
  date = c(1992, NA, 1991, 1990, 1994, 1992, 1991), 
  value = c(4.1, 4.5, 3.3, 5.3, 3.0, 3.2, 5.2)
)


last_year <- df %>% 
  filter(!is.na(date)) %>%
  mutate(date = date + 1, lagged_value = value, value = NULL)

df %>%
  left_join(last_year)
#> Joining by: c("id", "date")
#>   id date value lagged_value
#> 1  1 1992   4.1          3.3
#> 2  1   NA   4.5           NA
#> 3  1 1991   3.3          5.3
#> 4  1 1990   5.3           NA
#> 5  1 1994   3.0           NA
#> 6  2 1992   3.2          5.2
#> 7  2 1991   5.2           NA

【讨论】:

    【解决方案2】:

    使用1.9.5,连接不需要设置键,可以按如下方式完成:

    require(data.table) # v1.9.5+
    DT[!is.na(date), value_lagged := 
             .SD[.(id = id, date = date - 1), value, on = c("id", "date")]]
    #    id date value value_lagged
    # 1:  1 1992   4.1          3.3
    # 2:  1   NA   4.5           NA
    # 3:  1 1991   3.3          5.3
    # 4:  1 1990   5.3           NA
    # 5:  1 1994   3.0           NA
    # 6:  2 1992   3.2          5.2
    # 7:  2 1991   5.2           NA
    

    这是您的想法的变体。诀窍是直接在i 中使用is.na(),并在j 中使用.SD 而不是DT。我使用了on= 语法,但同样的想法当然也可以通过设置键来完成。 .

    【讨论】:

    • 嗨!使用 2015 年 9 月 11 日安装的 data.table 1.9.5,这会给出错误“[.data.table(.SD, .(id = id, date = date - 1), value, on = c("id ", : 未使用的参数 (on = c("id", "date"))"
    • @JBJ 使用remove.packages() 卸载、重新安装并重试。
    【解决方案3】:

    id定义的组内使用函数tlag

    library(dplyr)
    tlag <- function(x, n = 1L, time) { 
      index <- match(time - n, time, incomparables = NA)
      x[index]
    }
    
    df %>% group_by(id) %>% mutate(value_lagged = tlag(value, 1, time = date))
    

    【讨论】:

    • N=2e6L 非常小。 0.1 vs 0.23 秒并没有那么令人印象深刻.. 你能试试2e7L 甚至2e8L吗?
    • 这确实不令人印象深刻。关键是要有一个更具可读性的解决方案,结果证明 2e6 没有性能成本。我刚刚进行了测试:lag 在 1e7 时仍然快,但纯 data.table 在 1e8 时快两倍
    • 可读性的问题在于它不是一个合适的度量;因人而异。我非常喜欢纯 data.table 解决方案,例如 :)。
    • 可读性因人而异,但这并不意味着它不是一个适当的衡量标准,对吧?你不同意,对于浏览我的代码的同事来说,滞后解决方案更容易阅读吗?混淆 -1 和 -1 怎么办?
    • 您会发布 data.table 的解决方案作为答案吗?我会从我的问题中抑制它,以便人们会看到创建滞后变量的三个不同答案
    【解决方案4】:

    现在,collapse 包提供了一种优雅而快速的通用解决方案,用于计算不规则时间序列和不平衡面板,其函数为 flagfdifffgrowth。请参阅滞后不平衡面板的一般答案here

    现在,在您的特定应用程序中,还有一个罕见的事实,即面板不仅不平衡,而且您的时间变量中存在缺失值,这意味着您不知道观察记录的时间段。在这种情况下,仅应用collapse::flag 是行不通的,但您可以生成一个新的 id 变量,将缺失值与collapse::seqid 放在一个单独的组中。所以我的解决方案是:

    library(collapse)  
    DF = data.frame(id    = c(1,1,1,1,1,2,2),
                    date  = c(1992,NA,1991,1990,1994,1992,1991),
                    value = c(4.1,4.5,3.3,5.3,3.0,3.2,5.2))
    
    settransform(DF, l_value = flag(value, 1, g = seqid(date, order(id, date)), t = date))
    DF
    #>   id date value l_value
    #> 1  1 1992   4.1     3.3
    #> 2  1   NA   4.5      NA
    #> 3  1 1991   3.3     5.3
    #> 4  1 1990   5.3      NA
    #> 5  1 1994   3.0      NA
    #> 6  2 1992   3.2     5.2
    #> 7  2 1991   5.2      NA
    

    reprex package (v0.3.0) 于 2021-07-10 创建

    我很有信心这仍然比data.table 快,但我还没有测试过。同样,这个数据相当奇怪,对于大多数面板不平衡但记录都由 id 和 time 标识的情况,一个简单的flag(value, 1, id, as.integer(date)) 就可以了,并且很快就会引人注目。请注意,您可以通过确保时间变量为整数来提高效率,因为flag 会将非整数时间变量强制转换为因子,这也可以消除不规则性。

    【讨论】:

      猜你喜欢
      • 2023-01-19
      • 2014-12-02
      • 1970-01-01
      • 2021-10-31
      • 2017-11-12
      • 2014-06-18
      • 1970-01-01
      • 2019-06-18
      • 1970-01-01
      相关资源
      最近更新 更多