生存分析:如何在 R 中使用 tidyverse / 无循环将人口数据框转换为数据框?

Survival analysis: how to transform a population data frame into a data frame with the tidyverse / without a loop in R?

我需要将包含每个抽样日期的人口信息的数据框转换为包含个人信息的数据框,以便 运行 进行生存分析。我的数据如下所示:

Place=c(rep("Europe",6))
Age=c(rep("Newborn",3),rep("Young",3))
Date_sample=as.Date(c('2014-03-18','2014-10-01','2015-01-15','2014-06-16','2014-12-21','2015-01-15'))
Number_indiv_status1=c(0,2,1,0,2,2)
Number_indiv_status2=c(10,8,7,7,5,3)
df<-data.table(Place,Age,Date_sample,Number_indiv_status1,Number_indiv_status2)

> df
    Place     Age Date_sample Number_indiv_status1 Number_indiv_status2
1: Europe Newborn  2014-03-18                    0                   10
2: Europe Newborn  2014-10-01                    2                    8
3: Europe Newborn  2015-01-15                    1                    7
4: Europe   Young  2014-06-16                    0                    7
5: Europe   Young  2014-12-21                    2                    5
6: Europe   Young  2015-01-15                    2                    3

我需要得到这个:

> new_df
     Place     Age Date_sample Number_indiv_status1 Number_indiv_status2 Status date_event
 1: Europe Newborn  2014-10-01                    2                    8      1 2014-05-30
 2: Europe Newborn  2014-10-01                    2                    8      1 2014-08-15
 3: Europe Newborn  2015-01-15                    1                    7      1 2014-12-17
 4: Europe Newborn  2015-01-15                    1                    7      2 2015-01-15
 5: Europe Newborn  2015-01-15                    1                    7      2 2015-01-15
 6: Europe Newborn  2015-01-15                    1                    7      2 2015-01-15
 7: Europe Newborn  2015-01-15                    1                    7      2 2015-01-15
 8: Europe Newborn  2015-01-15                    1                    7      2 2015-01-15
 9: Europe Newborn  2015-01-15                    1                    7      2 2015-01-15
10: Europe Newborn  2015-01-15                    1                    7      2 2015-01-15
11: Europe   Young  2014-12-21                    2                    5      1 2014-09-01
12: Europe   Young  2014-12-21                    2                    5      1 2014-09-21
13: Europe   Young  2015-01-15                    2                    3      1 2014-12-29
14: Europe   Young  2015-01-15                    2                    3      1 2015-01-02
15: Europe   Young  2015-01-15                    2                    3      2 2015-01-15
16: Europe   Young  2015-01-15                    2                    3      2 2015-01-15
17: Europe   Young  2015-01-15                    2                    3      2 2015-01-15

我写了下面的代码,它不起作用:

tot_lines <- df %>% group_by(Age) %>%  slice(1) %>% ungroup() %>% summarise(tot_lines=sum(Number_indiv_status2))
new_df <- data.frame(matrix(NA, nrow = tot_lines[[1]], ncol = 7))
colnames(new_df)=c(colnames(df),"Status","date_event")
k=0
for (i in 1:nrow(df)) {
  if(df[i,"Number_indiv_status1"]>0){
    for (j in 1:df[[i,"Number_indiv_status1"]]){
      new_df[k+j,c(1:5)]=df[i,c(1:5)]
      new_df[k+j,6]=1
      new_df[k+j,7]=sample(seq.POSIXt(as.POSIXct(df[[i-1,3]]), as.POSIXct(df[[i,3]]),by="day"), size = 1)   #random date between df[i,3] and df[i+1,3]
      k=sum(complete.cases(new_df))    
      }
    } else {
    }
  if(i==sum(df$Age=="Newborn")) {
    for (l in 1:df[i,"Number_indiv_status2"]) {
      new_df[k+l,c(1:5)]=df[l,c(1:5)]
      new_df[k+l,6]=2
      new_df[k+l,7]=df[i,3]
    } else {
    }
  }
  k=sum(complete.cases(new_df))
}

我在循环中有几个 errors/tasks 需要解决但无法弄清楚:

  1. 这里有一个 Date 问题:new_df[2,c(1:5)]=df[2,c(1:5)] 我不理解为 class(df$Date_sample) returns “日期” cf this . I have tried to use new_df[1,3]=ymd(df[[2,3]]) or new_df[1,3]=as_date(df[[2,3]]) as mentioned here,没有成功。我仍然得到“16344”而不是“2014-10-01”(这是匹配的整数,但不是日期格式)。为什么以及如何解决这个问题?

  2. 我尝试在 之后的时间间隔内分配一个随机日期,这在这里不起作用: new_df[1,7]=sample(seq.POSIXt(as.POSIXct(df[[1,3]]), as.POSIXct(df[[2,3]]),by="day"), size = 1) 我认为这是格式问题,因为它 returns“1409443200”和 as_date(1409443200) 不相关(“3860894-05-31”)。我也读过 this and 但我想避免在循环中或循环之前创建一个函数。我还检查了 lubridate 包以找到一个优雅的选项,但无法弄清楚。如果有人对那个选项有想法,那就太好了。

  3. 由于我的循环不起作用,我不确定我的索引(i、j、k 和 l)是否编码正确,是否放置在正确的位置。

  4. 一旦循环工作:有没有办法将它插入管道 %>% 例如?我实际上有不止一个地点,还有不止 2 个年龄 类,所以我需要 group_by 来按地点和年龄进行操作,但附加一个新的数据框 new_df。

  5. 是否有一个非循环选项来做同样的事情,例如 tidyverse?我试图避免循环,但在这里我看不到如何管理它。

  6. 最后但并非最不重要的一点:网站还是新网站,我应该在单独的帖子中提问吗?

编辑

  1. 我找到了第 1 点的解决方案:设置 new_df$Date_sample <- as.Date(new_df$Date_sample)k=0 之前并进入循环解决了 new_df 的格式问题。我仍然不知道为什么在循环中使用 ymd()as_date 不起作用。

  2. 我找到了一种在两个采样时间间隔内分配随机日期的方法。我的代码基于 python 建议 here (第一个答案)得到这个: sample(unclass(as.Date(df[[i,3]]))-unclass(as.Date(df[[i-1,3]])),1)+df[[i-1,3]] 它还需要在k=0和循环之前设置new_df$date_event <- as.Date(new_df$date_event),否则和之前一样结果是正确的但不是日期格式。

我继续处理其他错误,它们仍未解决。

我可以让循环工作,这解决了第 1-3 点。 在数据框中,我需要将年龄编码为因子: Age=as_factor(c(rep("Newborn",3),rep("Young",3)))

然后,这就完成了工作:

k=0
Age_fact=1
for (i in 1:nrow(df)) {
  if(df[i,"Number_indiv_status1"]>0){
    for (j in 1:df[[i,"Number_indiv_status1"]]){
      new_df[k+j,c(1:5)]=df[i,c(1:5)]
      new_df[k+j,6]=1
      new_df[k+j,7]=sample(unclass(as.Date(df[[i,3]]))-unclass(as.Date(df[[i-1,3]])),1)+df[[i-1,3]]
    }
    k=sum(complete.cases(new_df)) 
    } 
  if(i==tail(which(df$Age == levels(df$Age)[Age_fact]),1)) {
    for (l in 1:df[[i,"Number_indiv_status2"]]) {
      new_df[k+l,c(1:5)]=df[i,c(1:5)]
      new_df[k+l,6]=2
      new_df[k+l,7]=df[i,3]
    }
    k=sum(complete.cases(new_df))
    } 
  if (i==tail(which(df$Age == levels(df$Age)[Age_fact]),1)) {
    Age_fact=Age_fact+1
  }
  k=sum(complete.cases(new_df))
}

但有一个限制:年龄现在按因素索引(1 或 2)显示在 new_df 中,而不是级别名称。和设定 new_df$Age <- as.factor(new_df$Age)之前的循环没有解决。稍后我仍然可以更改它,但由于我的数据集比这大得多,所以让副本作为因子工作会很棒。

我还有这个问题:有没有一种不用循环的方法,使用 tidyverse?