2017-05-26 57 views
5

我正在使用处理应用于动物的一些信息R。首先我想描述我的信息结构(我将在最后添加dput()版本)。我的数据是DF,它看起来像这样:考虑数据框中不同组的行之间的计算日期差异

Treatment_ID Start_Date  Valid 
1   0031 2011-05-01 2011-05-30 
2   0031 2011-05-01 2011-06-30 
3   0045 2012-02-01 2012-03-01 
4   0057 2012-04-01 2012-04-30 
5   0057 2012-04-01 2012-05-30 
6   0098 2012-10-01 2012-10-30 

它有56行和三个变量Treatment_ID(5种类型的治疗),Start_Date(治疗的开始日期)和Valid(治疗的截止日期)。例如Treatment_ID0031有两个意见,因为这个观测开始于2011年5月,并于2011年6月完成。然后一个新的处理0045开始于2012年2月,并于2012年3月完成(仅一个观测)。 DF中的所有组均采用相同的结构。我需要使用一些条件来计算每次治疗和每次治疗后的月份差异。我将使用第一两种处理,以显示这一点:

Treatment_ID Start_Date  Valid 
1   0031 2011-05-01 2011-05-30 
2   0031 2011-05-01 2011-06-30 
3   0045 2012-02-01 2012-03-01 
4   0057 2012-04-01 2012-04-30 

对于这个例子,我有两行,其中Treatment_Id变量等于第一次治疗。当发生这种情况时,必须计算变量Valid的月份差异。当出现新的治疗方法时,必须计算Start_DateValid之间的月差。请注意,当治疗有多个观察时,通过对该组中的观察使用Valid变量获得差异,但当Treatment_Id发生变化时,则必须使用Start_DateValid变量获得差异。为了得到这个变量Break_Months我使用了一个结构:

DF$Break_Months=NA 

for(i in c(2:(length(DF$Break_Months)))) 
{ 
    DF$Break_Months[i]=ifelse(DF$Treatment_ID[i]==DF$Treatment_ID[i-1],round(as.numeric(DF$Valid[i]-DF$Valid[i-1])/30,0), 
          round(as.numeric(DF$Start_Date[i]-DF$Valid[i-1])/30,0)) 
} 

forTreatment_Id相等计算实际行与Valid变量,当它们是不同的区别是使用Start_Date计算和以前的区别ValidBreak_Months的第一个值是NA,因为没有以前的值进行比较。当我使用前面的代码行时,问题出现在DF的末尾。

Treatment_ID Start_Date  Valid Break_Months 
47   0098 2012-10-01 2016-07-30   1 
48   0098 2012-10-01 2016-08-31   1 
49   0031 2016-09-01 2016-09-30   0 
50   0031 2016-09-01 2016-10-30   1 
51   0031 2016-09-01 2016-11-30   1 
52   0031 2016-09-01 2016-12-30   1 
53   0031 2016-09-01 2017-01-30   1 
54   0031 2016-09-01 2017-03-02   1 
55   0031 2016-09-01 2017-03-30   1 
56   0012 2017-03-01 2017-03-30   -1 

Treatment_Id0012只有一个观察,因为它是新的,Valid日期是相同的是治疗0031的最后一次观察。由于Treatment_Id0031已在其他几个月使用,那么计算差异是与治疗内的以前的观察。在0012的情况下,这是不可能的,因为Valid的日期与最后一次观察00310012没有更多的观察结果是一样的,因为它是新的。当发生这种情况时,必须使用0031之前的组的最后一次观察进行比较,这是0098。通过使用由于0012概念不等于0098Break_Months由差2017-03-01Start_Date)和2016-08-31Valid)由相同的机械在结构for给出6一个值,而不是-1之间计算。

我的问题是关于如何将这个考虑纳入for。尝试这样做非常复杂,因为我不知道如何整合与日期相关的比较(如果它们与上例相同),并且在包含相同日期的日期之前寻找前一组。我曾尝试使用dplyr封装的lag函数来避免for,但结果并不相同。该dput()版本的DF是下一个:

DF<-structure(list(Treatment_ID = c("0031", "0031", "0045", "0057", 
"0057", "0098", "0098", "0098", "0098", "0098", "0098", "0098", 
"0098", "0098", "0098", "0098", "0098", "0098", "0098", "0098", 
"0098", "0098", "0098", "0098", "0098", "0098", "0098", "0098", 
"0098", "0098", "0098", "0098", "0098", "0098", "0098", "0098", 
"0098", "0098", "0098", "0098", "0098", "0098", "0098", "0098", 
"0098", "0098", "0098", "0098", "0031", "0031", "0031", "0031", 
"0031", "0031", "0031", "0012"), Start_Date = structure(c(1304208000, 
1304208000, 1328054400, 1333238400, 1333238400, 1349049600, 1349049600, 
1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 
1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 
1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 
1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 
1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 
1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 
1349049600, 1349049600, 1349049600, 1349049600, 1349049600, 1472688000, 
1472688000, 1472688000, 1472688000, 1472688000, 1472688000, 1472688000, 
1488326400), class = c("POSIXct", "POSIXt"), tzone = "UTC"), 
    Valid = structure(c(1306713600, 1309392000, 1330560000, 1335744000, 
    1338336000, 1351555200, 1354233600, 1356825600, 1359504000, 
    1362182400, 1364601600, 1367280000, 1369872000, 1372550400, 
    1375142400, 1377820800, 1380499200, 1383091200, 1385769600, 
    1388361600, 1391040000, 1393718400, 1396137600, 1398816000, 
    1401408000, 1404086400, 1412035200, 1414627200, 1417305600, 
    1419897600, 1422576000, 1425254400, 1427673600, 1432944000, 
    1435622400, 1440892800, 1443571200, 1446163200, 1448841600, 
    1451433600, 1454112000, 1456790400, 1459296000, 1461974400, 
    1464566400, 1467244800, 1469836800, 1472601600, 1475193600, 
    1477785600, 1480464000, 1483056000, 1485734400, 1488412800, 
    1490832000, 1490832000), class = c("POSIXct", "POSIXt"), tzone = "UTC")), .Names = c("Treatment_ID", 
"Start_Date", "Valid"), row.names = c(NA, -56L), class = "data.frame") 

感谢您的帮助。

更新 其中一种解决方案非常完美。现在我有一个小问题,当我必须计算一个类似的变量。首先我计算变量Elapsed,它是ValidStart_Date之间的差值。我使用下面的代码:

DF$Elapsed=round(as.numeric(DF$Valid-DF$Start_Date)/30,0) 

然后出现困境。我必须计算下两个变量Last1Last2。我用的是接下来的代码如下:

#Compute Last1 
DF$Last1=NA 
DF$Last1[1]=0 
for(j in c(2:length(DF$Last1))) 
{ 
    DF$Last1[j]=ifelse(DF$Treatment_ID[j]==DF$Treatment_ID[j-1],DF$Last1[j-1], 
        ifelse(DF$Treatment_ID[j]!=DF$Treatment_ID[j-1],DF$Elapsed[j-1],0)) 
} 

的代码工作parcially因为我有相关的变量Break_Months类似的问题。在这种情况下,因为00310012具有相同的Valid值,因此Treatment_Id的比较不得使用0031的最后一个值完成,其中由于循环的逻辑(变量Elapsed)而分配了7。在这种情况下,正确的值是48,因为比较必须与组0098的最后一次观察相结合,然后我们得到48.我试图用last_obs_index修改,但我无法得到正确的结果。

Treatment_ID Start_Date  Valid Break_Months Elapsed Last1 
47   0098 2012-10-01 2016-07-30   1  47  2 
48   0098 2012-10-01 2016-08-31   1  48  2 
49   0031 2016-09-01 2016-09-30   0  1 48 
50   0031 2016-09-01 2016-10-30   1  2 48 
51   0031 2016-09-01 2016-11-30   1  3 48 
52   0031 2016-09-01 2016-12-30   1  4 48 
53   0031 2016-09-01 2017-01-30   1  5 48 
54   0031 2016-09-01 2017-03-02   1  6 48 
55   0031 2016-09-01 2017-03-30   1  7 48 
56   0012 2017-03-01 2017-03-30   6  1  7 

对于变量Last2我用下面的代码:

#Compute Last2 
DF$Last2=NA 
DF$Last2[1]=0 
for(k in c(2:length(DF$Last2))) 
{ 
    DF$Last2[k]=ifelse(DF$Treatment_ID[k]==DF$Treatment_ID[k-1],DF$Last2[k-1], 
        ifelse(DF$Treatment_ID[k]!=DF$Treatment_ID[k-1],DF$Break_Months[k],0)) 
} 

在这种情况下,它似乎是工作,但事实并非如此。尽管6是正确的,但比较还没有很好的定义,因为00120031的日期和日期相同,最佳比较是使用最后一次观察0098组。因此,指定了值Break_Months。再次,我无法修复与last_obs_index定义的适当的逻辑循环。

Treatment_ID Start_Date  Valid Break_Months Elapsed Last1 Last2 
47   0098 2012-10-01 2016-07-30   1  47  2  4 
48   0098 2012-10-01 2016-08-31   1  48  2  4 
49   0031 2016-09-01 2016-09-30   0  1 48  0 
50   0031 2016-09-01 2016-10-30   1  2 48  0 
51   0031 2016-09-01 2016-11-30   1  3 48  0 
52   0031 2016-09-01 2016-12-30   1  4 48  0 
53   0031 2016-09-01 2017-01-30   1  5 48  0 
54   0031 2016-09-01 2017-03-02   1  6 48  0 
55   0031 2016-09-01 2017-03-30   1  7 48  0 
56   0012 2017-03-01 2017-03-30   6  1  7  6 

感谢所有帮助这个时候,才有可能获得关于如何适应循环得到比较正确的方式的建议。

+0

根据你提供的代码,'Treatment_ID == 0045'具有'Break_Months == 7',对于'2012-02-01'和''' 2012-03-01'?那么'for'循环在除了结尾之外的其他地方不起作用吗? – johnckane

+1

在样本数据中,治疗0012在治疗0031结束之前的整个月开始。那么最后一次输入的-1不是正确的结果吗? – lebelinoz

+0

尊敬的@johnckane @lebelinoz在0012和0031的样本中都有效到2017-03-30发生这种情况时,需要与前一个群组0098的最后一次观察完成区别。然后,由于0012和0098不同,我们计算' Start_Date' - 'Valid'给出6并没有其他值。这是我循环中的问题。 – Duck

回答

0

要在for循环就需要额外的条件添加到您的计算时的Treatment_ID值从先前的观测不同的做到这一点。

如果Treatment_ID值是不一样的先前的观测,计算Start_Date之间的这种观察和Valid为最近Treatment_ID的最后一个观察值的区别在哪里的Valid的最后一个值也不同。

要做到这一点,您需要知道DF的索引,其中Treatment_ID的值发生变化,Valid的值发生变化。你需要从Hmisc

library(Hmisc) 
new_obs_index <- which(DF$Treatment_ID != Lag(DF$Treatment_ID,1) & DF$Valid != Lag(DF$Valid)) 

这提供了在新的观测开始的索引Lag功能,我们真正想要的最后观察到在此之前的最后Treatment_ID索引。

last_obs_index <- new_obs_index - 1 

这些是Valid值的符合的最后一次观察的标准Treatment_ID使得在接下来的观测Valid的值变化,以及索引。

然后在for循环中,当Treatment_ID变化值减去Start与符合我们标准的最近值Valid之间的差值。我们通过指定我们想要

DF$Valid[last_obs_index[max(which(last_obs_index < i))]] 

所以for循环看起来像这样实现:

for(i in c(2:(length(DF$Break_Months)))){ 
    DF$Break_Months[i]=ifelse(DF$Treatment_ID[i]==DF$Treatment_ID[i-1], 
round(as.numeric(DF$Valid[i]-DF$Valid[i-1])/30,0),round(as.numeric(DF$Start_Date[i]-DF$Valid[last_obs_index[max(which(last_obs_index < i))]])/30,0)) 
} 

这使在DF最后一个观察所期望的结果。

Treatment_ID Start_Date  Valid Break_Months 
51   0031 2016-09-01 2016-11-30   1 
52   0031 2016-09-01 2016-12-30   1 
53   0031 2016-09-01 2017-01-30   1 
54   0031 2016-09-01 2017-03-02   1 
55   0031 2016-09-01 2017-03-30   1 
56   0012 2017-03-01 2017-03-30   6 

这么干脆,实施必要的代码是

library(Hmisc) 
new_obs_index <- which(DF$Treatment_ID != Lag(DF$Treatment_ID,1) & DF$Valid != Lag(DF$Valid)) 
last_obs_index <- new_obs_index - 1 
for(i in c(2:(length(DF$Break_Months)))){ 
DF$Break_Months[i]=ifelse(DF$Treatment_ID[i]==DF$Treatment_ID[i-1],round(as.numeric(DF$Valid[i]-DF$Valid[i-1])/30,0),round(as.numeric(DF$Start_Date[i]-DF$Valid[last_obs_index[max(which(last_obs_index < i))]])/30,0)) 
} 

UPDATE 对于变量Last1你可以访问你的愿望用矢量last_obs_index使用此语法值:

for(j in c(2:length(DF$Last1))){ 
DF$Last1[j]=ifelse(DF$Treatment_ID[j]==DF$Treatment_ID[j-1],DF$Last1[j-1],ifelse(DF$Treatment_ID[j]!=DF$Treatment_ID[j-1],DF$Elapsed[last_obs_index[max(which(last_obs_index < i))]],0))} 

对于变量Last2如果我在站在你的正确位置我认为你的实施将永远给你想要的答案。我认为一个新的治疗值足以使用Break_Months的值,并且您也不需要使用它的不同值Valid

+0

尊敬的@johnckane您的解决方案为我工作。我会接受你的回答,但是我做了更新,因为我有类似的问题,你需要将相同的逻辑合并到新的循环中。你能否请这个额外的考虑帮助。非常感谢。 – Duck

+0

刚刚更新了我的回复,我不认为你需要改变'Last2'变量的语法。 – johnckane

+0

感谢@johnckane,但我想保留'Last2'的循环内部与'last_obs_index'的比较逻辑相同,因为新的处理可能会出现(多于两个)并且具有相同的'Valid'日期。可能吗?你的解决方案太棒了。 – Duck

3

这里有一个方法使用一些额外的dplyr功能,如lagif_else。它计算出比较日期(保存在此处进行健全性检查),从中减去以前的有效日期,然后转换为“月”(30天期间)的舍入数。

library(dplyr) 

    mutate(DF, 
    comparison_date = if_else(Treatment_ID == lag(Treatment_ID), Valid, Start_Date), 
    Break_Months = difftime(comparison_date, lag(Valid), units = "days"), 
    Break_Months = as.numeric(round(Break_Months/30))) 

#> Treatment_ID Start_Date  Valid comparison_date Break_Months 
#> 1   0031 2011-05-01 2011-05-30   <NA>   NA 
#> 2   0031 2011-05-01 2011-06-30  2011-06-30   1 
#> 3   0045 2012-02-01 2012-03-01  2012-02-01   7 
#> 4   0057 2012-04-01 2012-04-30  2012-04-01   1 
#> 5   0057 2012-04-01 2012-05-30  2012-05-30   1 
#> 6   0098 2012-10-01 2012-10-30  2012-10-01   4 
#> 7   0098 2012-10-01 2012-11-30  2012-11-30   1 
#> 8   0098 2012-10-01 2012-12-30  2012-12-30   1 
#> 9   0098 2012-10-01 2013-01-30  2013-01-30   1 
#> 10   0098 2012-10-01 2013-03-02  2013-03-02   1 
... 
相关问题