如何使用 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
说明
- 该函数以 data.table 和目标状态作为参数。
- 对于每个
ID
,State
被移动(滞后)以获得先前的状态。对于每个ID
组的第一个周期,目标状态是填充的。因此,不需要对第一个周期进行特殊处理。
- 然后将修改后的 data.table 传递给
dcast()
,在那里它在转换之前针对目标状态进行子集化。
- 之前的状态和时期在转换公式中被转化为因素以允许完成缺失值。
drop = FALSE
通过包含所有缺失的组合进行转换。这是必要的,因为子集化会删除一些组合。
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
我是 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
说明
- 该函数以 data.table 和目标状态作为参数。
- 对于每个
ID
,State
被移动(滞后)以获得先前的状态。对于每个ID
组的第一个周期,目标状态是填充的。因此,不需要对第一个周期进行特殊处理。 - 然后将修改后的 data.table 传递给
dcast()
,在那里它在转换之前针对目标状态进行子集化。 - 之前的状态和时期在转换公式中被转化为因素以允许完成缺失值。
drop = FALSE
通过包含所有缺失的组合进行转换。这是必要的,因为子集化会删除一些组合。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