首页 > 解决方案 > 生存分析:如何将人口数据框转换为具有 tidyverse / 没有 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))
}

我在循环中有几个需要解决但无法弄清楚的错误/任务:

  1. 这里有一个Date问题:new_df[2,c(1:5)]=df[2,c(1:5)]我不理解为class(df$Date_sample)返回“日期”cf this post。我曾尝试使用new_df[1,3]=ymd(df[[2,3]])or new_df[1,3]=as_date(df[[2,3]])as described 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) 我认为这是格式问题,因为它返回“1409443200”并且 as_date(1409443200) 不相关(“3860894-05-31” )。我也读过这个这个,但我想避免在循环中或之前创建一个函数。我还检查了lubridate包装以找到一个优雅的选项,但无法弄清楚。如果有人对此选项有任何想法,那就太好了。

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

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

  5. 例如,会有一个非循环选项来做同样的事情tidyverse吗?我尽量避免循环,但在这里我不知道如何管理它。

  6. 最后但同样重要的是:网站上还是新的,我应该在单独的帖子中问我的问题吗?

编辑

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

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

我一直在处理其他错误,但仍未解决。

标签: rloopstidyrsurvival

解决方案


我可以让循环工作,这解决了第 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))
}

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

我仍然有这个问题:有没有办法在没有循环的情况下使用tidyverse?


推荐阅读