1
votes

R: remplissez les cellules ci-dessous en ajoutant 1 à chaque fois

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>

r

6 commentaires

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 à partir de ce point


3 Réponses :


1
votes

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


1 commentaires

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)



0
votes

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()


2 commentaires

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.



1
votes

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) 


2 commentaires

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.