2017-03-23 42 views
3

我試圖根據ID計數連續的幾天無效(consecDaysInactive)。在R中創建一個計數器變量,並按條件重置

我已經創建了一個指示變量inactive,在ID爲非活動狀態的日期爲1,在活動狀態時爲0。我也有一個id變量和一個日期變量。我的分析數據集將包含數十萬行,因此效率將非常重要。

我試圖創建的邏輯如下:

  • 每個ID,如果用戶是活動的,consecDaysInactive = 0
  • 每個ID,如果用戶是不活動的,並且是在先前的天活性,consecDaysInactive = 1
  • 每個ID,如果用戶是在前一天無活性的,consecDaysInactive = 1 +#先前不活動的連續天
  • consecDaysInactive應該重置爲0的id新值。

我已經能夠創建一個累計總和,但無法得到它重置在0後非活動== 0行。

我已經在下面說明了我想要的結果(consecDaysInactive)以及我能夠以編程方式實現的結果(bad_consecDaysInactive)。

library(dplyr) 
d <- data.frame(id = c(1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2), date=as.Date(c('2017-01-01','2017-01-02','2017-01-03','2017-01-04','2017-01-05','2017-01-06','2017-01-07','2017-01-08','2017-01-01','2017-01-02','2017-01-03','2017-01-04','2017-01-05','2017-01-06','2017-01-07','2017-01-08')), inactive=c(0,0,0,1,1,1,0,1,0,1,1,1,1,0,0,1), consecDaysInactive=c(0,0,0,1,2,3,0,1,0,1,2,3,4,0,0,1)) 

d <- d %>% 
    group_by(id) %>% 
    arrange(id, date) %>% 
    do(data.frame(., bad_consecDaysInactive = cumsum(ifelse(.$inactive==1, 1,0)) 
) 
) 
d 

其中consecDaysInactive迭代由1對每個連續的天無活性的,但重置爲0的每個日期用戶是活動的,並且復位爲0的id新值。由於輸出如下所示,我無法獲得bad_consecDaysInactive重置爲0 - 例如排

  id  date inactive consecDaysInactive bad_consecDaysInactive 
     <dbl>  <date> <dbl>    <dbl>     <dbl> 
    1  1 2017-01-01  0     0      0 
    2  1 2017-01-02  0     0      0 
    3  1 2017-01-03  0     0      0 
    4  1 2017-01-04  1     1      1 
    5  1 2017-01-05  1     2      2 
    6  1 2017-01-06  1     3      3 
    7  1 2017-01-07  0     0      3 
    8  1 2017-01-08  1     1      4 
    9  2 2017-01-01  0     0      0 
    10  2 2017-01-02  1     1      1 
    11  2 2017-01-03  1     2      2 
    12  2 2017-01-04  1     3      3 
    13  2 2017-01-05  1     4      4 
    14  2 2017-01-06  0     0      4 
    15  2 2017-01-07  0     0      4 
    16  2 2017-01-08  1     1      5 

我也算(而且試過)內group_by() & do()遞增的變量,但由於do()是不重複的,我不能讓我的櫃檯,讓過去2:

d2 <- d %>% 
    group_by(id) %>% 
    do(data.frame(., bad_consecDaysInactive2 = ifelse(.$inactive == 0, 0, ifelse(.$inactive==1,.$inactive+lag(.$inactive), .$inactive)))) 
d2 

其產生,如上文所述:

 id  date inactive consecDaysInactive bad_consecDaysInactive bad_consecDaysInactive2 
    <dbl>  <date> <dbl>    <dbl>     <dbl>     <dbl> 
1  1 2017-01-01  0     0      0      0 
2  1 2017-01-02  0     0      0      0 
3  1 2017-01-03  0     0      0      0 
4  1 2017-01-04  1     1      1      1 
5  1 2017-01-05  1     2      2      2 
6  1 2017-01-06  1     3      3      2 
7  1 2017-01-07  0     0      3      0 
8  1 2017-01-08  1     1      4      1 
9  2 2017-01-01  0     0      0      0 
10  2 2017-01-02  1     1      1      1 
11  2 2017-01-03  1     2      2      2 
12  2 2017-01-04  1     3      3      2 
13  2 2017-01-05  1     4      4      2 
14  2 2017-01-06  0     0      4      0 
15  2 2017-01-07  0     0      4      0 
16  2 2017-01-08  1     1      5      1 

正如你看到的,我的迭代器bad_consecDaysInactive2復位在0,但不會增加過去2!如果有data.table解決方案,我也很樂意聽到它。

+0

像這個時間差? '庫(data.table); setDT(d)[,consecDaysInactive2:= cumsum(inactive),by =。(id,cumsum(!inactive))]' – chinsoon12

+0

'library(data.table); setDT(d)[,v:= if(inactive [1])seq.int(.N)else 0L,by = rleid(inactive)]' – Frank

+0

感謝chinsoon12和Frank - 這兩個都很好。我將以此爲契機探索data.table庫。 @Frank,關於你將這篇文章標記爲重複,我認爲這與你標記的文章不同,OP要求在dplyr中使用data.table函數,目標是使一個常量增加不同的ID值。我嘗試的操作是不同的,我沒有要求在dplyr中使用data.table方法; dplyr是我嘗試過的方法,但無法實現我的目標,因此是個問題。再次感謝您的幫助。 – rsty

回答

2

下面是一個for循環做一個可愛的方式:

a <- c(1,1,1,1,0,0,1,0,1,1,1,0,0) 
b <- rep(NA, length(a)) 
b[1] <- a[1] 
for(i in 2:length(a)){ 
    b[i] <- a[i]*(a[i]+b[i-1]) 
} 
a 
b 

它可能不是最有效的方式做到這一點,但是這將是相當不錯的快。我的電腦上有一千萬行數據量爲11.7秒。

a <- round(runif(10000000,0,1)) 
b <- rep(NA, length(a)) 
b[1] <- a[1] 
t <- Sys.time() 
for(i in 2:length(a)){ 
    b[i] <- a[i]*(a[i]+b[i-1]) 
} 
b 
Sys.time()-t 

的11.73612秒

但這個時間差不考慮需要做每個ID的事情。這很容易解決,效率損失最小。您的示例數據框按ID排序。如果您的實際數據尚未排序,請執行此操作。然後:

a <- round(runif(10000000,0,1)) 
id <- round(runif(10000000,1,1000)) 
id <- id[order(id)] 
b <- rep(NA, length(a)) 
b[1] <- a[1] 
t <- Sys.time() 
for(i in 2:length(a)){ 
    b[i] <- a[i]*(a[i]+b[i-1]) 
    if(id[i] != id[i-1]){ 
    b[i] <- a[i] 
    } 
} 
b 
Sys.time()-t 

的13.54373秒

如果在包括有花時間的時間差排序id,那麼時間差接近19秒。還不錯!

我們可以在OP的評論中使用Frank的答案來節省多少效率?

d <- data.frame(inactive=a, id=id) 

t2 <- Sys.time() 
b <- setDT(d)[, v := if (inactive[1]) seq.int(.N) else 0L, by=rleid(inactive)] 
Sys.time()-t2 

的2.233547秒

+0

謝謝Jacob!這與小的調整工作,把它放在數據框架的形式。這是一個很好的循環解決方案;我的R很生鏽,這將會很有用。 – rsty

相關問題