如何使用 data.table 构建一个新的数据框,显示根据前一行元素的值流入指定的过渡状态?

How to use data.table to build a new dataframe showing inflows into a specified transition state based on the value of an element in a prior row?

我是 data.table 包的新手,也是 R 的半新手,我想使用 data.table 因为它在处理非常大的数据集时速度很快。

假设我们从这个名为“data”的数据帧开始,由它下面的代码生成:

> data
   ID Period_1 Period_2 Values State
1:  1        1  2020-01      5    X0
2:  1        2  2020-02     10    X1
3:  1        3  2020-03     15    X2
4:  2        1  2020-04      0    X0
5:  2        2  2020-05      2    X2
6:  2        3  2020-06      4    X0
7:  3        1  2020-02      3    X2
8:  3        2  2020-03      6    X1
9:  3        3  2020-04      9    X0

data <- 
  data.frame(
    ID = c(1,1,1,2,2,2,3,3,3),
    Period_1 = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
    Period_2 = c("2020-01","2020-02","2020-03","2020-04","2020-05","2020-06","2020-02","2020-03","2020-04"),
    Values = c(5, 10, 15, 0, 2, 4, 3, 6, 9),
    State = c("X0","X1","X2","X0","X2","X0", "X2","X1","X0")
  )

我想创建一个新的数据框,显示随时间流向用户指定的目标状态(称为“X”)的所有“状态”,如 Period_1 在这个 [=12] 中测量的那样=] 数据框。对于 Period_1 = 1,我们简单地计算目标状态 X 的所有实例。对于所有 Period_1 > 1,对于状态 = X 的任何行,X 的所有实例都被计算并放入相应的行反映前一个 Period_1 状态的数据帧(对于相同的 ID)。如何使用 data.table 有效地完成此操作?

下图更好地说明了这一点,其中新派生的数据帧显示所有状态随时间流入目标状态 x0:

我在 data 中包括了其他列(Period_2 和值),以供此函数以后发展时使用,或者将时间范围定义为 Period_2 并汇总值而不是在状态实例中计数。在有人用上面的请求启动我之后,我应该能够自己做这些。

这是一种选择:

f <- function(s) {
  dcast(
    rbind(unique(data[,.(State,Period_1,N=0)]),
        data[, priorState:=lag(State), by = ID] %>% 
          .[State==s] %>% 
          .[!is.na(priorState), State:=priorState] %>% 
          .[, .N, .(State,Period_1)]
    ),
    State~Period_1, value.var="N", fun.aggregate=sum
  )
}

setDT(data)

f("X0")

输出:

    State     1     2     3
   <char> <num> <num> <num>
1:     X0     2     0     0
2:     X1     0     0     1
3:     X2     0     0     1

2022 年 5 月 10 日编辑: 简化代码并调整说明。

为了完整起见,这里是一个函数定义,它使用了 dcast() 函数的一些更奇特的参数。最终扩展此功能,实现OP更高级的需求

简单的功能

library(data.table)
state_inflow <- function(mydat, target_state) {
  dcast(
    setDT(mydat)[, Previous_State := shift(State, fill = target_state), by = ID],
    factor(Previous_State) ~ factor(Period_1), length, value.var = "Values",
    subset = .(State == target_state), drop = FALSE
  )
} 

调用函数

state_inflow(data, "X0")

returns 预期结果:

   Previous_State 1 2 3
1:             X0 2 0 0
2:             X1 0 0 1
3:             X2 0 0 1

说明

  1. 该函数以 data.table 和目标状态作为参数。
  2. 对于每个 IDState 被移动(滞后)以获得先前的状态。对于每个ID组的第一个周期,目标状态是填充的。因此,不需要对第一个周期进行特殊处理。
  3. 然后将修改后的 data.table 传递给 dcast(),在那里它在转换之前针对目标状态进行子集化。
  4. 之前的状态和时期在转换公式中被转化为因素以允许完成缺失值。
  5. drop = FALSE 通过包含所有缺失的组合进行转换。这是必要的,因为子集化会删除一些组合。
  6. Values 列用作值变量。

高级功能

OP 计划改进功能

for alternatively defining the time horizon as Period_2 and for summing the flow of Values rather than counting in instances of state

这可以通过向函数添加更多参数来实现:

state_inflow <- function(mydat, target_state, period_col_name, fct) {
  dcast(
    setDT(mydat)[, Previous_State := shift(State, fill = target_state), by = ID],
    as.formula(sprintf("factor(Previous_State) ~ factor(%s)", period_col_name)), fct, 
    value.var = "Values", subset = .(State == target_state), drop = FALSE
  )
}

一些例子:

state_inflow(data, "X0", "Period_1", sum)
   Previous_State 1 2 3
1:             X0 5 0 0
2:             X1 0 0 9
3:             X2 0 0 4
state_inflow(data, "X2", "Period_2", length)
   Previous_State 2020-01 2020-02 2020-03 2020-04 2020-05 2020-06
1:             X0       0       0       0       0       1       0
2:             X1       0       0       1       0       0       0
3:             X2       0       1       0       0       0       0