Mon Data Frame
ressemble aux trois premières colonnes de cet exemple:
id obs value newCol a 1 uncool NA a 2 cool 1 a 3 uncool NA a 4 uncool NA a 5 cool 2 a 6 uncool NA a 7 cool 1 a 8 uncool NA b 1 cool 0
Ce dont j'ai besoin est une colonne ( newCol ci-dessus) qui compte le nombre de "non refroidis" entre les observations avec la valeur "cool" ou la première ligne du groupe (regroupées par id).
Comment puis-je faire cela (en utilisant dplyr
idéalement)?
4 Réponses :
Outre Ensuite, vous pouvez utiliser id
, vous avez besoin d'une autre variable de regroupement, donnée par grp = cumsum (dat $ value == "cool") - (dat $ value == "cool") code > qui est illustré ci-dessous.
mutate
où nous attribuons sum (value == "uncool")
aux observations où value == " cool "
et NA
sinon dans chaque groupe. dat <- structure(list(id = c("a", "a", "a", "a", "a", "a", "a", "a",
"b"), obs = c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L), value = c("uncool",
"cool", "uncool", "uncool", "cool", "uncool", "cool", "uncool",
"cool"), newCol = c(NA, 1L, NA, NA, 2L, NA, 1L, NA, 0L)), .Names = c("id",
"obs", "value", "newCol"), class = "data.frame", row.names = c(NA,
-9L))
library(dplyr)
dat %>%
group_by(id, grp = cumsum(dat$value == "cool") - (dat$value == "cool")) %>%
mutate(newCool = if_else(value == "cool", sum(value == "uncool"), NA_integer_))
# A tibble: 9 x 6
# Groups: id, grp [5]
id obs value newCol grp newCool
<chr> <int> <chr> <int> <int> <int>
1 a 1 uncool NA 0 NA
2 a 2 cool 1 0 1
3 a 3 uncool NA 1 NA
4 a 4 uncool NA 1 NA
5 a 5 cool 2 1 2
6 a 6 uncool NA 2 NA
7 a 7 cool 1 2 1
8 a 8 uncool NA 3 NA
9 b 1 cool 0 3 0
Ecrire une fonction simple pour résoudre votre problème:
# Running function library(dplyr) data <- data %>% group_by(id) %>% mutate(newCol = cool_counter(value)) # Results data id obs value newCol <chr> <dbl> <chr> <dbl> 1 a 1 uncool NA 2 a 2 cool 1 3 a 3 uncool NA 4 a 4 uncool NA 5 a 5 cool 2 6 a 6 uncool NA 7 a 7 cool 1 8 a 8 uncool NA 9 b 1 cool NA
Cela donne:
# Your data data <- data.frame(id = c("a", "a", "a", "a", "a", "a" ,"a" ,"a", "b"), obs = c(1,2,3,4,5,6,7,8,1), value = c("uncool", "cool", "uncool", "uncool", "cool", "uncool" ,"cool" ,"uncool", "cool"), stringsAsFactors = FALSE) # Function for solving problem cool_counter <- function(vector) { uncool <- FALSE count <- 0 results <- list() for(i in 1:length(vector)) { if(i == 1) { uncool <- vector[i] == "uncool" results[[i]] <- NA if(uncool) { count <- 1 } } if(i > 1) { uncool <- vector[i] == "uncool" if(uncool) { count <- count + 1 results[[i]] <- NA } if(!uncool) { results[[i]] <- count count <- 0 } } } return(unlist(results)) }
Nous pouvons créer une fonction d'assistance qui regroupera la valeur
basée sur cool / uncool
, et compter les cool
s, c'est-à-dire
# A tibble: 9 x 5 id obs value newCol new <fct> <int> <fct> <int> <int> 1 a 1 uncool NA NA 2 a 2 cool 1 1 3 a 3 uncool NA NA 4 a 4 uncool NA NA 5 a 5 cool 2 2 6 a 6 uncool NA NA 7 a 7 cool 1 1 8 a 8 uncool NA NA 9 b 1 cool 0 0
qui donne,
library(tidyverse) f1 <- function(x) { i1 <- which(x == 'cool') v1 <- rep(seq_along(i1), c(i1[1], diff(i1))) if (tail(x, 1) != 'cool') { return(c(v1, tail(v1, 1) + 1)) } else { return(v1) } } df %>% group_by(id) %>% mutate(new_grp = f1(value)) %>% group_by(id, new_grp) %>% mutate(new = length(value[value != 'cool']), new = replace(new, value != 'cool', NA)) %>% ungroup() %>% select(-new_grp)
Nous pouvons définir des groupes en faisant un cumsum
en commençant par le bas, puis utiliser ave
pour construire un vecteur pour chaque groupe:
dat %>% group_by(id,temp = rev(cumsum(rev(value=="cool")))) %>% mutate(newCol = ifelse(value=="cool", n()-1, NA)) %>% ungroup() %>% select(-temp) # # A tibble: 9 x 4 # id obs value newCol # <chr> <int> <chr> <dbl> # 1 a 1 uncool NA # 2 a 2 cool 1 # 3 a 3 uncool NA # 4 a 4 uncool NA # 5 a 5 cool 2 # 6 a 6 uncool NA # 7 a 7 cool 1 # 8 a 8 uncool NA # 9 b 1 cool 0
Avec dplyr :
transform(dat, newCol = ave( value, id, rev(cumsum(rev(value=="cool"))), FUN = function(x) ifelse(x=="cool", length(x)-1, NA))) # id obs value newCol # 1 a 1 uncool <NA> # 2 a 2 cool 1 # 3 a 3 uncool <NA> # 4 a 4 uncool <NA> # 5 a 5 cool 2 # 6 a 6 uncool <NA> # 7 a 7 cool 1 # 8 a 8 uncool <NA> # 9 b 1 cool 0