J'ai une trame de données comme suit:

df <- data.frame(
Item=c("A","A","A","A","A","B","B","B","B","B"),
Date=c("2018-1-1","2018-2-1","2018-3-1","2018-4-1","2018-5-1","2018-1-1","2018-2-1",
      "2018-3-1","2018-4-1","2018-5-1"),
Value=rnorm(10))

Je veux muter une nouvelle colonne groupée par Item, pour compter le nombre de valeurs supérieures à 0 dans la fenêtre de 3 (ou tout autre entier que je spécifie).

Je connais bien tidyverse, par conséquent, une solution dplyr serait la bienvenue.

0
Felix Zhao 20 nov. 2018 à 15:51

3 réponses

Meilleure réponse
  Item  Date       Value
   <fct> <date>     <int>
 1 A     2018-01-01     3
 2 B     2018-01-01     2
 3 B     2018-02-01    -5
 4 A     2018-02-01    -3
 5 A     2018-03-01     4
 6 B     2018-03-01    -2
 7 A     2018-04-01     5
 8 B     2018-04-01     0
 9 A     2018-05-01     1
10 B     2018-05-01    -4

Exemple de rnorm modifié pour plus de clarté, échantillon utilisé (-5: 5):

> df <- df %>% mutate(greater_than = (Value>0)*Value) %>%
group_by(Item) %>% arrange(Date) %>% mutate(greater_than = 
zoo::rollapplyr(greater_than, 3, sum, partial = T))
df %>% arrange(Item) %>% head(10)

Devrait ressembler à ceci:

 1 A     2018-01-01     3            3
 2 A     2018-02-01    -3            3
 3 A     2018-03-01     4            7
 4 A     2018-04-01     5            9
 5 A     2018-05-01     1           10
 6 B     2018-01-01     2            2
 7 B     2018-02-01    -5            2
 8 B     2018-03-01    -2            2
 9 B     2018-04-01     0            0
10 B     2018-05-01    -4            0
0
Matheus Deister Veiga 20 nov. 2018 à 13:37

Pensez au package zoo:: si vous voulez lancer quoi que ce soit.

df$new<-
zoo::rollsum( df$Value > 0, 3, fill = NA )

#   Item     Date      Value new
#1     A 2018-1-1  0.5852699  NA
#2     A 2018-2-1 -0.7383377   1
#3     A 2018-3-1 -0.3157693   1
#4     A 2018-4-1  1.2475237   1
#5     A 2018-5-1 -1.5479757   1
#6     B 2018-1-1 -0.6913331   0
#7     B 2018-2-1 -0.2423809   0
#8     B 2018-3-1 -1.6363024   0
#9     B 2018-4-1 -0.3256263   1
#10    B 2018-5-1  0.3563144  NA

Vous avez une option de la "fenêtre-position". Regardez de plus près l'argument align = c("center", "left", "right").


Donc en tant que chaîne dplyr:

df %>% group_by(Item) %>% dplyr::mutate( new = zoo::rollsum( Value > 0, 3, fill = NA ))
3
Andre Elrico 20 nov. 2018 à 13:16

Vous pouvez utiliser le package RcppRoll.

require(RcppRoll)
df$new <- df$new <- RcppRoll::roll_sum(df$Value > 0, 3, fill = NA)

Utilisation de Tidyverse:

df %>% 
  group_by(Item) %>% 
  dplyr::mutate(new = RcppRoll::roll_sum(Value > 0, 3, fill = NA))

Speedwise, c'est plus rapide que le package zoo:

n <- 10000
df <- data.frame(
  Item = sample(LETTERS, n, replace = TRUE),
  Value = rnorm(n))

df_grouped <- df %>% 
  group_by(Item)
microbenchmark::microbenchmark(
  RcppRoll = df_grouped <- df_grouped %>% dplyr::mutate(new_RcppRoll = RcppRoll::roll_sum(Value > 0, 3, fill = NA)),
  zoo = df_grouped <- df_grouped %>% dplyr::mutate(new_zoo = zoo::rollsum( Value > 0, 3, fill = NA ))
)

Résulte en:

Unit: milliseconds
     expr       min        lq      mean   median        uq       max neval
 RcppRoll  2.509003  2.741993  2.929227  2.83913  2.983726  5.832962   100
      zoo 11.172920 11.785113 13.288970 12.43320 13.607826 25.879754   100

Et

all.equal(df_grouped$new_RcppRoll, df_grouped$new_zoo)
TRUE
1
Rentrop 20 nov. 2018 à 13:33