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