J'ai un dataframe avec une colonne sur le temps et cette colonne contient du NA . Je voudrais remplir ces cellules avec l'année avant + 1 (si la cellule manquante n'est pas le début de la série). Voici un exemple reproductible:
df <- data.frame(x = c("A", "B", "C", "A", "B", "C"),
y = c(2000, 2001, 2002, 2000, 2001, 2002))
J'ai essayé de suivre ce message
df <- df %>% complete(y = seq(min(y), max(y), by = "year"))
mais je ne trouve pas comment faire. Une idée?
Modifier: résultat attendu:
df <- data.frame(x = c("A", "B", "C", "A", "B", "C"),
y = c(2000, NA, NA, 2000, 2001, 2002))
Remarque: je préférerais un Solution dplyr .
Note 2 (23 octobre 2019): Les trois réponses à ce jour sont bonnes mais assez compliquées. Je suis vraiment surpris qu'il ne soit pas possible de faire cela simplement (par exemple, avoir la possibilité d'ajouter un décalage dans la fonction fill serait vraiment utile je pense).
p>
3 Réponses :
Dans base , vous pouvez utiliser ave en combinaison avec cumsum pour diviser votre ensemble de données et y appliquer seq , comme vous l'avez déjà essayé.
df <- data.frame(x = c("A", "B", "C", "A", "B", "C"),
y = c(2000, NA, NA, 2000, 2001, 2002))
dfExpected <- data.frame(x = c("A", "B", "C", "A", "B", "C"),
y = c(2000, 2001, 2002, 2000, 2001, 2002))
df2 <- data.frame(x = c("A", "B", "C", "A", "B", "C"),
y = c(NA, NA, NA, 2000, 2001, 2002))
Dans le cas où il commence par NA et que vous voulez le laisser commencer par 2000 , vous pouvez utiliser replace:
df2$y <-ave(df2$y, cumsum(!is.na(df2$y)), FUN=function(x) seq(replace(x[1],is.na(x[1]),2000), length.out = length(x))) identical(df2, dfExpected) #[1] TRUE
Données:
df$y <- ave(df$y, cumsum(!is.na(df$y)), FUN=function(x)
seq(x[1], length.out = length(x)))
identical(df, dfExpected)
#[1] TRUE
df$y
#[1] 2000 2001 2002 2000 2001 2002
cela fonctionne, mais je préférerais une solution dplyr (je validerais votre réponse plus tard s'il n'y a plus de réponses proposées)
Ceci utilise les fonctions dplyr case_when () et lag combinées avec une boucle while dans une fonction personnalisée.
La sortie est comme prévu, essayez-le.
library(dplyr)
lag_years <- function(df){
while (anyNA(df$y))
{
df %>%
mutate(y = case_when(is.na(y)&!is.na(lag(y))~lag(y)+1,TRUE~y)) %>%
{.} -> df
}
return(df)
}
lag_years(df) %>%
head()
votre code fonctionne, mais quand je l'applique à un dataframe de plusieurs milliers de lignes, cela prend beaucoup trop de temps (de mon point de vue)
@bretauv Je le craignais autant, la boucle while est vraiment de la force brute. Il existe peut-être un moyen d'optimiser la solution, mais je crains que la méthode de GKi ne soit meilleure pour le moment.
Cette solution est un peu ennuyeuse mais complètement vectorisée dans dplyr . J'ai doublé votre df en un nouveau df2 pour essayer sur quelques occurrences espacées.
df2 %>% group_by(grp = cumsum(!is.na(y) & lag(is.na(y), default = FALSE))) %>% mutate(add_year = cumsum(is.na(y))) %>% fill(y) %>% mutate(y = y + add_year) %>% ungroup() %>% select(-grp, -add_year)
En gros, vous devez créer des groupes à travers les blocs avec NA . Ensuite, vous pouvez calculer un cumsum au sein du groupe et utiliser fill pour faire glisser la valeur précédente vers le bas. C'est ennuyeux à cause de toutes les lignes.
library(tidyr)
library(dplyr)
df <- data.frame(x = c("A", "B", "C", "A", "B", "C"),
y = c(2000, NA, NA, 2000, 2001, 2002))
df2 <- bind_rows(df, df)
il doit y avoir une erreur dans votre code d'édition, la sortie pour df2 $ y est: [1] 2000 2001 2002 2000 2000 2000 2001 2002 2002 2002 2002
Ah ouais je pense qu'il y en a peut-être. J'ai marqué cela rapidement. Je vais simplement effacer le code d'édition.
Salut, Pouvez-vous aussi publier la sortie souhaitée?
bien sûr, je l'ai ajouté
ce que vous voulez se passer n'est pas clair lorsque vous dites que la première valeur de y est un
NA? Ou dites-vous que cela ne peut pas arriver?Aurez-vous un tel écart ou plusieurs lacunes de scuh?
quand ils sont NA quelque part, il y en a sur la série complète. Ici, j'ai mis 2000 pour avoir un point à partir duquel je pourrais remplir l'autre NA mais à l'origine, le dataframe était de la forme:
df <- data.frame (x = c ("A", "B", " C "," A "," B "," C "), y = c (NA, NA, NA, 2000, 2001, 2002))donc si vous avez une solution à remplir pour tous les NA, je la prendrai mais j'ai pensé qu'il serait plus facile de trouver une solution si je changeais manuellement le point de départ de la série et de remplir l'autre
NA code > à partir de ce point