2017-05-26 102 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 
...