【问题标题】:Sequences and patterns: how to avoid loops in R序列和模式:如何避免 R 中的循环
【发布时间】:2014-11-12 15:07:28
【问题描述】:

由于我用于多状态分析(传记)的包的格式要求,我需要使用我的受访者的状态序列生成一个路径变量(字符变量)。

这可以使用 Biograph 包中提供的函数“Sequences.ind.0”轻松完成,但由于循环,该函数效率非常低:我的实际数据库需要 20 多个小时(大约 500 万条记录)。我不知道如何在没有循环或更快的情况下获得相同的结果。

这里我展示了一个包含三个状态(N、P、K)的简单示例,其中 K 是吸收状态(即在 K 之后没有额外的跃迁)

dat <- data.table(ID=c(1:5), 
   K=c(NA, 2005, 2004, 2001, 2006), 
   P=c(2001, 1999, 2003, 2003, 1998),
   P=c(2005, 2001, NA, NA, 2001),
   P=c(NA, 2003, NA, NA , 2004),
   N=c(2002, 2000, NA, 2003,2000),
   N=c(NA, 2002, NA, NA, 2003),
   N=c(NA, NA, NA, NA, 2005)) 

我想获得每个人的状态路径和一个随着状态变化发生的时间的矩阵。 “N”是初始状态,所以每个序列都应该以“N”开头。之后,状态序列由转换发生的时间给出。

如果我使用函数“Sequences.ind.0”,我会得到:

library(Biograph)
nsample <- nrow(dat)
namstates <- c("N", "P", "K")
f <- Sequences.ind.0(as.matrix(dat[, 2:8, with=FALSE]),namstates,absorb="K")


$namstates
[1] "N" "P" "K"

$d
     [,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 2001 2002 2005   NA   NA   NA   NA
[2,] 1999 2000 2001 2002 2003 2005   NA
[3,] 2003 2004   NA   NA   NA   NA   NA
[4,] 2001   NA   NA   NA   NA   NA   NA
[5,] 1998 2000 2001 2003 2004 2005 2006

$path
[1] "NPNP"     "NPNPNPK"  "NPK"      "NK"       "NPNPNPNK"

最重要的输出是“path”和“d”。 在这种情况下,是否有人对如何使用 data.table 或其他方法避免循环有任何想法/建议?提前致谢!

【问题讨论】:

  • 澄清一下:基本算法类似于“1)对每一行进行排序。2)对于行中的每个条目,检查年份并记录其对应列的名称(N,P,或 K)"。对吗?
  • 是的!我要补充一点,如果 K 发生(吸收状态,例如死亡),人们可能会忘记这一年剩下的时间和姓名记录。事后这样做可能更有效率。我不知道。
  • 我没有很好的答案,但似乎这不是适合这项工作的数据结构
  • 什么是更好的数据结构?
  • 最好有一个像 list(K=c(2001, 2004, 2005, 2006), ...) 这样的年份和状态的“字典”,但这不是我确定的。我编辑了一个data-structures 标签,也许可以从该领域的专家那里获得一些意见。

标签: r loops data-structures data.table


【解决方案1】:

我没有对此进行基准测试,更多的测试用例可能会更好。

library(reshape2)
dat1 <- melt(dat, id.vars="ID")
dat1[, na := is.na(value)]
setkeyv(dat1, c("na", "value"))
dat1[, ind := seq_along(value), by=ID]
dat1[, value1 := value]
dat1[, tmp := c(FALSE, head(as.logical(cumsum(variable == "K")), -1)), by=ID]
dat1[dat1[, tmp], value1 := NA]
dat1[ID==1,]

dcast.data.table(dat1[, list(ID, ind, value1)], ID ~ ind)
#    ID    1    2    3    4    5    6    7
# 1:  1 2001 2002 2005   NA   NA   NA   NA
# 2:  2 1999 2000 2001 2002 2003 2005   NA
# 3:  3 2003 2004   NA   NA   NA   NA   NA
# 4:  4 2001   NA   NA   NA   NA   NA   NA
# 5:  5 1998 2000 2001 2003 2004 2005 2006

fun <- function(x, b) {
  y <- na.omit(as.character(x)[b])
  if (y[1] != "N") y <- c("N", y)
  paste(y, collapse="")
}
setkey(dat1[, fun(variable, !is.na(value) & !tmp), by=ID], ID)[["V1"]]
#[1] "NPNP"     "NPNPNPK"  "NPK"      "NK"       "NPNPNPNK"

【讨论】:

  • 非常感谢,罗兰。我正在将您的出色解决方案与我的大数据集一起使用。它似乎运行良好且快速。
猜你喜欢
  • 2021-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-20
  • 2020-12-24
  • 1970-01-01
相关资源
最近更新 更多