Временной ряд / Tidyverse: исчисление в зависимости от всех предыдущих строк в данной группе - PullRequest
0 голосов
/ 25 апреля 2020

Цель

Учитывая эти данные:

df <-
structure(list(id = c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L,
3L, 3L, 3L, 3L, 3L), time = c(1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L,
5L, 1L, 2L, 3L, 4L, 5L), val = c(56, 72, 91, 2, 76, 48, 8, 86,
49, 85, 62, 24, 3, 51, 81)), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -15L))
# A tibble: 15 x 3
      id  time   val
   <int> <int> <dbl>
 1     1     1    56
 2     1     2    72
 3     1     3    91
 4     1     4     2
 5     1     5    76
 6     2     1    48
 7     2     2     8
 8     2     3    86
 9     2     4    49
10     2     5    85
11     3     1    62
12     3     2    24
13     3     3     3
14     3     4    51
15     3     5    81

Я хочу создать новый столбец, который будет TRUE, если val когда-либо был выше 60 в любой предыдущий time.

Таким образом, ожидаемый результат должен быть:

# A tibble: 15 x 3
      id  time   val   ever
   <int> <int> <dbl>  <lgl>
 1     1     1    56  FALSE
 2     1     2    72   TRUE
 3     1     3    91   TRUE
 4     1     4     2   TRUE
 5     1     5    76   TRUE
 6     2     1    48  FALSE
 7     2     2     8  FALSE
 8     2     3    86   TRUE
 9     2     4    49   TRUE
10     2     5    85   TRUE
11     3     1    62   TRUE
12     3     2    24   TRUE
13     3     3     3   TRUE
14     3     4    51   TRUE
15     3     5    81   TRUE

То, что я пробовал:

Некоторые варианты:

(
  df
  %>% mutate(high = val > 60)
  %>% group_by(id)
  %>% mutate(ever = F)
  %>% mutate(ever = high || lag(ever))
)

Но цель функции lag() не в том, чтобы использовать ее последний результат для вычисления следующего ...

Ответы [ 2 ]

1 голос
/ 26 апреля 2020

Вы можете использовать cumsum(), примененный к val > 60 следующим образом:

library(tidyverse)

df <- tibble(
  id = c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L),
  time = c(1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L),
  val = c(56, 72, 91, 2, 76, 48, 8, 86, 49, 85, 62, 24, 3, 51, 81)
)

df %>%
  group_by(id) %>%
  mutate(
    high = cumsum(val > 60) > 0
  ) %>%
  ungroup()
#> # A tibble: 15 x 4
#>       id  time   val high 
#>    <int> <int> <dbl> <lgl>
#>  1     1     1    56 FALSE
#>  2     1     2    72 TRUE 
#>  3     1     3    91 TRUE 
#>  4     1     4     2 TRUE 
#>  5     1     5    76 TRUE 
#>  6     2     1    48 FALSE
#>  7     2     2     8 FALSE
#>  8     2     3    86 TRUE 
#>  9     2     4    49 TRUE 
#> 10     2     5    85 TRUE 
#> 11     3     1    62 TRUE 
#> 12     3     2    24 TRUE 
#> 13     3     3     3 TRUE 
#> 14     3     4    51 TRUE 
#> 15     3     5    81 TRUE

Создано в 2020-04-26 пакетом Представить (v0.3.0 )

0 голосов
/ 25 апреля 2020

Я сделал это, но трудно и безобразно. Я уверен, что там лучше:

(
  df
  %>% group_split(id)
  %>% map_df(function(df) {
    df$ever <- NA
    for(i in seq_len(nrow(df))) {
      if( i == 1 ) df$ever[i] <- df$val[i] > 60
      else df$ever[i] <- df$ever[i-1] || df$val[i] > 60 
    }
    df})
)



Редактировать

Я нашел лучший способ (и мой вопрос был дубликатом) здесь

...