2017-12-06 44 views
5

Bu sorunun varyasyonlarını buldum ve modulos'un kullanılabileceğini biliyorum ama hepsini bir araya getirmekte zorlanıyorum.Koşul karşılandığında diziyi tekrarlama

Kimlik ve saniyelerle bir dizi gözlem var. Id artışlarla 5 saniyelik büyüklüklerin saniye cinsinden ne zaman, sayıyı yeniden başlatmak istiyorum. Birisi bu soruyu dplyr'de cevaplamama yardımcı olabilir mi?

Orijinal df

df <- data.frame(id = c(1,1,1,1,1,2,2,2,2,3,3,3,3), 
       val = c(2,10,12,15,17,2,4,7,8,12,15,20,25)) 

df 
    id val 
1 1 2 
2 1 10 
3 1 12 
4 1 15 
5 1 17 
6 2 2 
7 2 4 
8 2 7 
9 2 8 
10 3 12 
11 3 15 
12 3 20 
13 3 25 

İstenilen Sonuç yanıtları için

finalResult 
    id val reset 
1 1 2  1 
2 1 10  2 
3 1 12  2 
4 1 15  3 
5 1 17  3 
6 2 2  1 
7 2 4  1 
8 2 7  2 
9 2 8  2 
10 3 12  1 
11 3 15  1 
12 3 20  2 
13 3 25  3 

Düzenleme

Teşekkür dün ama verilen çözümlerle bazı sorunlar karşılaştı.

Bu veri seti üzerinde, kod bazı durumlarda çalışır. Kullanılan

sub.df <- structure(list(`ID` = c("1", 
               "1", "1", 
               "1", "1", 
               "1", "1", 
               "1", "1" 
), dateFormat = structure(c(1479955726, 1479955726, 1483703713, 
          1495190809, 1495190809, 1497265079, 1497265079, 1474023059, 1474023061 
), class = c("POSIXct", "POSIXt"), tzone = "America/Chicago")), .Names = c("ID", 
                      "dateFormat"), row.names = c(NA, -9L), class = c("tbl_df", "tbl", 
                                  "data.frame")) 

Çözüm:

jj <- sub.df %>% 
    group_by(`ID`) %>% 
    arrange(`ID`,`dateFormat`)%>% 
    mutate(totalTimeInt = difftime(dateFormat,first(dateFormat),units = 'secs'))%>% 
    mutate(totalTimeFormat = as.numeric(totalTimeInt))%>% 
    mutate(reset = cumsum(
    Reduce(
     function(x, y) 
     if (x + y >= 5) 0 
     else x + y, 

     diff(totalTimeFormat), init = 0, accumulate = TRUE 
    ) == 0 
))%>% 
    mutate(reset_2 = cumsum(
    accumulate(
     diff(totalTimeFormat), 
     ~if (.x + .y >= 5) 0 else .x + .y, 
     .init = 0 
    ) == 0 
)) 

Sonuç

# A tibble: 9 x 6 
# Groups: ID [1] 
    ID   dateFormat totalTimeInt totalTimeFormat reset reset_2 
    <chr>    <dttm>  <time>   <dbl> <int> <int> 
1  1 2016-09-16 05:50:59  0 secs    0  1  1 
2  1 2016-09-16 05:51:01  2 secs    2  1  1 
3  1 2016-11-23 20:48:46 5932667 secs   5932667  2  2 
4  1 2016-11-23 20:48:46 5932667 secs   5932667  3  3 
5  1 2017-01-06 05:55:13 9680654 secs   9680654  4  4 
6  1 2017-05-19 05:46:49 21167750 secs  21167750  5  5 
7  1 2017-05-19 05:46:49 21167750 secs  21167750  6  6 
8  1 2017-06-12 05:57:59 23242020 secs  23242020  7  7 
9  1 2017-06-12 05:57:59 23242020 secs  23242020  8  8 

ne olur ilk iki gözlem için doğru olduğunu sayar olmasıdır 1 örneği olarak. Üçüncü ve dördüncü gözlemlere ulaştığında, bu sadece iki gözlem olarak sayılmalıdır, çünkü bu iki örnek arasında geçen hiçbir zaman yoktur.

Doğru Çıktı: (: Şimdi burada bu yaklaşımı terk ediyorum rağmen alistaire 'ın brilliant answer, ben yanlış kanıtlanmış oldu DÜZENLEME), ama bu bir olduğunu düşünüyorum

# A tibble: 9 x 6 
# Groups: ID [1] 
    ID   dateFormat totalTimeInt totalTimeFormat reset reset_2 
    <chr>    <dttm>  <time>   <dbl> <int> <int> 
1  1 2016-09-16 05:50:59  0 secs    0  1  1 
2  1 2016-09-16 05:51:01  2 secs    2  1  1 
3  1 2016-11-23 20:48:46 5932667 secs   5932667  2  2 
4  1 2016-11-23 20:48:46 5932667 secs   5932667  2  2 
5  1 2017-01-06 05:55:13 9680654 secs   9680654  3  3 
6  1 2017-05-19 05:46:49 21167750 secs  21167750  4  4 
7  1 2017-05-19 05:46:49 21167750 secs  21167750  4  4 
8  1 2017-06-12 05:57:59 23242020 secs  23242020  5  5 
9  1 2017-06-12 05:57:59 23242020 secs  23242020  5  5 
+0

# 1 numaralı kimlik grubunda, değer 12'den 15'e geçtiğinde, sıfırlama değiştiğine, ancak # 3 grubunda değil. Aşağıdaki cevabım, ilk gruptaki mantıkla tutarlıdır. –

+2

@JosephWood Bu noktada # 1 grubunda, sıfırlama için referans 10, grup 3 ise "12" iken, "12" – duckmayr

+0

@duckmayr, bu noktayı açıklığa kavuşturduğunuz için teşekkür ederiz (yani referans noktası sadece ilk değer değil. Grup, ancak önceki referans farkının 5'ten büyük veya eşit olduğu değer. Benim şimdi silinen cevap naif ve yanlış sadece o gruptaki ilk değere atıfta bulundu. Bu cevap için –

cevap

4

(İsterseniz veya purrr::accumulate) Eğer accumulate = TRUE ile Reduce kullanıyorsanız, sıfırlayabilir 5'ten büyük veya eşit olduğunda, farkın çalıştırılması 0 toplamının 0 olup olmadığına dair cumsum çağrılması sıfırlama sayısını döndürür.

library(tidyverse) 

df <- data.frame(id = c(1,1,1,1,1,2,2,2,2,3,3,3,3), 
       val = c(2,10,12,15,17,2,4,7,8,12,15,20,25)) 

df %>% 
    group_by(id) %>% 
    mutate(reset = cumsum(
     Reduce(
      function(x, y) if (x + y >= 5) 0 else x + y, 
      diff(val), init = 0, accumulate = TRUE 
     ) == 0 
    )) 
#> # A tibble: 13 x 3 
#> # Groups: id [3] 
#>  id val reset 
#> <dbl> <dbl> <int> 
#> 1  1  2  1 
#> 2  1 10  2 
#> 3  1 12  2 
#> 4  1 15  3 
#> 5  1 17  3 
#> 6  2  2  1 
#> 7  2  4  1 
#> 8  2  7  2 
#> 9  2  8  2 
#> 10  3 12  1 
#> 11  3 15  1 
#> 12  3 20  2 
#> 13  3 25  3 

veya purrr::accumulate ile , düzenleme ile ilgili olarak

df %>% 
    group_by(id) %>% 
    mutate(reset = cumsum(
     accumulate(
      diff(val), 
      ~if (.x + .y >= 5) 0 else .x + .y, 
      .init = 0 
     ) == 0 
    )) 
#> # A tibble: 13 x 3 
#> # Groups: id [3] 
#>  id val reset 
#> <dbl> <dbl> <int> 
#> 1  1  2  1 
#> 2  1 10  2 
#> 3  1 12  2 
#> 4  1 15  3 
#> 5  1 17  3 
#> 6  2  2  1 
#> 7  2  4  1 
#> 8  2  7  2 
#> 9  2  8  2 
#> 10  3 12  1 
#> 11  3 15  1 
#> 12  3 20  2 
#> 13  3 25  3 

, mesele fark dosyaları bazı onu sıfırlama görmek sayım ne aynı olan, 0 olmasıdır. En basit çözüm bir sıfırlama değeri olarak NA yerine sıfır kullanmaktır: herhangi bir değerin aslında NA ise, bu benzer artar olarak
library(tidyverse) 

sub.df <- structure(list(`ID` = c("1", "1", "1", "1", "1", "1", "1", "1", "1"), 
         dateFormat = structure(c(1479955726, 1479955726, 1483703713, 
          1495190809, 1495190809, 1497265079, 1497265079, 1474023059, 1474023061), 
          class = c("POSIXct", "POSIXt"), tzone = "America/Chicago")), 
        .Names = c("ID", "dateFormat"), row.names = c(NA, -9L), 
        class = c("tbl_df", "tbl", "data.frame")) 

sub.df %>% 
    group_by(ID) %>% 
    arrange(ID, dateFormat) %>% 
    mutate(reset = cumsum(is.na(
       accumulate(diff(dateFormat), 
          ~{ 
           s <- sum(.x, .y, na.rm = TRUE); 
           if (s >= 5) NA else s 
          }, 
          .init = NA) 
    ))) 
#> # A tibble: 9 x 3 
#> # Groups: ID [1] 
#>  ID   dateFormat reset 
#> <chr>    <dttm> <int> 
#> 1  1 2016-09-16 05:50:59  1 
#> 2  1 2016-09-16 05:51:01  1 
#> 3  1 2016-11-23 20:48:46  2 
#> 4  1 2016-11-23 20:48:46  2 
#> 5  1 2017-01-06 05:55:13  3 
#> 6  1 2017-05-19 05:46:49  4 
#> 7  1 2017-05-19 05:46:49  4 
#> 8  1 2017-06-12 05:57:59  5 
#> 9  1 2017-06-12 05:57:59  5 

Sonuçta bu yaklaşım olsa da, sınırlamaları ile karşı karşıyadır. Daha sağlam bir çözüm, her yinelemeden iki öğenin bir listesini, sıfırlama sayısıyla toplamı ve sıfırlama sayısı için bir tane döndürmek olacaktır.

sub.df %>% 
    group_by(ID) %>% 
    arrange(ID, dateFormat) %>% 
    mutate(total_reset = accumulate(
     transpose(list(total = diff(dateFormat), reset = rep(0, n() - 1))), 
     ~{ 
      s <- .x$total + .y$total; 
      if (s >= 5) { 
       data_frame(total = 0, reset = .x$reset + 1) 
      } else { 
       data_frame(total = s, reset = .x$reset) 
      } 
     }, 
     .init = data_frame(total = 0, reset = 1) 
    )) %>% 
    unnest() 
#> # A tibble: 9 x 4 
#> # Groups: ID [1] 
#>  ID   dateFormat total reset 
#> <chr>    <dttm> <dbl> <dbl> 
#> 1  1 2016-09-16 05:50:59  0  1 
#> 2  1 2016-09-16 05:51:01  2  1 
#> 3  1 2016-11-23 20:48:46  0  2 
#> 4  1 2016-11-23 20:48:46  0  2 
#> 5  1 2017-01-06 05:55:13  0  3 
#> 6  1 2017-05-19 05:46:49  0  4 
#> 7  1 2017-05-19 05:46:49  0  4 
#> 8  1 2017-06-12 05:57:59  0  5 
#> 9  1 2017-06-12 05:57:59  0  5 

toplam biraz saçma görünüyor, ama fark bakarsanız, aslında doğru verilmiştir: Bu da, bu konularda daha fazla iştir.

+0

teşekkürler. Zekice çalışır. Ama "Azalt" fonksiyonunu açıklayabilir misiniz? O kısmı anlamıyorum. – DataTx

+2

'Azalt ', bir vektörün birbirini izleyen koşullarına bir ikili (2 değişkenli) işlev uygular. Varsayılan olarak, her şeyi tek bir noktaya kadar daraltır, bu nedenle '(Azalt (\ '+ \', 1: 4) ',' (1: 4)' ile aynıdır, ancak '((1 +) olarak hesaplar. 2) + 3) + 4) '. Ancak, "accumulate = TRUE" eklerseniz, aracı terimleri kaydeder, bu nedenle "Reduce (\' + \ ', 1: 4, birikir = TRUE)', "cumsum (1: 4)' e eşittir. Listeleri (veri çerçeveleri dahil) iyi işleyebilir, örn. 'Azalt (\' + \ ', mtcars)' ve herhangi bir karmaşıklığın ikili işlevini kabul eder. Eğer init 'verilirse, vektörün ilk değeri olarak kullanılır. – alistaire

+0

Çözümle ilgili bir sorunla karşılaştım. Nasıl düzelteceğinizi biliyorsanız, herhangi bir girişi takdir ediyorum. Teşekkür ederim. – DataTx

2

Yanılıyor olabilirim Aslında bir döngüye gereksinim duyacağınız örneklerin her satırdaki reset değeri önceki satırlarda ne olduğuna bağlı olarak değişecektir. Umarım Joseph Wood bundan daha akıllı bir şeyle gelecektir, ama bu arada burada istendiği gibi dplyr kullanan saf bir yaklaşımdır. Aşağıdaki fonksiyonu

count_resets <- function(x) { 
    N <- length(x) 
    value <- 1 
    result <- rep(1, N) 
    threshold <- x[1] 
    for (i in 2:N) { 
     if (abs(x[i] - threshold) >= 5) { 
      value <- value + 1 
      threshold <- x[i] 
     } 
     result[i] <- value 
    } 
    return(result) 
} 

yapmak Ve dplyr 'ın group_by() kullanılarak id bunu uygulayabilirsiniz:

library(dplyr) 

df %>% 
    group_by(id) %>% 
    mutate(reset = count_resets(val)) 

# A tibble: 13 x 3 
# Groups: id [3] 
     id val reset 
    <dbl> <dbl> <dbl> 
1  1  2  1 
2  1 10  2 
3  1 12  2 
4  1 15  3 
5  1 17  3 
6  2  2  1 
7  2  4  1 
8  2  7  2 
9  2  8  2 
10  3 12  1 
11  3 15  1 
12  3 20  2 
13  3 25  3 
İlgili konular