web-dev-qa-db-fra.com

remplacer NA dans une chaîne dplyr

La question a été modifiée à partir de l'original .

Après avoir lu cet intéressant discussion je me demandais comment remplacer les NA dans une colonne en utilisant dplyr dans, par exemple, les données de frappeurs de Lahman:

Source: local data frame [96,600 x 3]
Groups: teamID

   yearID teamID G_batting
1    2004    SFN        11
2    2006    CHN        43
3    2007    CHA         2
4    2008    BOS         5
5    2009    SEA         3
6    2010    SEA         4
7    2012    NYA        NA

Ce qui suit ne fonctionne pas comme prévu

library(dplyr)
library(Lahman)

df <- Batting[ c("yearID", "teamID", "G_batting") ]
df <- group_by(df, teamID )
df$G_batting[is.na(df$G_batting)] <- mean(df$G_batting, na.rm = TRUE)

Source: trame de données locale [20 x 3] Groupes: yearID, teamID

   yearID teamID G_batting
1    2004    SFN  11.00000
2    2006    CHN  43.00000
3    2007    CHA   2.00000
4    2008    BOS   5.00000
5    2009    SEA   3.00000
6    2010    SEA   4.00000
7    2012    NYA  **49.07894**

> mean(Batting$G_battin, na.rm = TRUE)
[1] **49.07894**

En fait, il a imputé la moyenne globale et non la moyenne du groupe. Comment feriez-vous cela dans une chaîne dplyr? L'utilisation de transform à partir de la base R ne fonctionne pas non plus car elle impute la moyenne globale et non la moyenne du groupe. Cette approche convertit également les données en données régulières. un cadre. Y a-t-il une meilleure manière de faire cela?

df %.% 
  group_by( yearID ) %.%
  transform(G_batting = ifelse(is.na(G_batting), 
    mean(G_batting, na.rm = TRUE), 
    G_batting)
  )

Modifier: le remplacement de transform par mutate donne l'erreur suivante

Error in mutate_impl(.data, named_dots(...), environment()) : 
  INTEGER() can only be applied to a 'integer', not a 'double'

Edit: L'ajout de as.integer semble résoudre l'erreur et produit le résultat attendu. Voir aussi la réponse de @ eddi.

df %.% 
  group_by( teamID ) %.%
  mutate(G_batting = ifelse(is.na(G_batting), as.integer(mean(G_batting, na.rm = TRUE)), G_batting))

Source: local data frame [96,600 x 3]
Groups: teamID

   yearID teamID G_batting
1    2004    SFN        11
2    2006    CHN        43
3    2007    CHA         2
4    2008    BOS         5
5    2009    SEA         3
6    2010    SEA         4
7    2012    NYA        47

> mean_NYA <- mean(filter(df, teamID == "NYA")$G_batting, na.rm = TRUE)
> as.integer(mean_NYA)
[1] 47

Edit: Suite au commentaire de @ Romain, j'ai installé dplyr depuis github:

> head(df,10)
   yearID teamID G_batting
1    2004    SFN        11
2    2006    CHN        43
3    2007    CHA         2
4    2008    BOS         5
5    2009    SEA         3
6    2010    SEA         4
7    2012    NYA        NA
8    1954    ML1       122
9    1955    ML1       153
10   1956    ML1       153

> df %.% 
+   group_by(teamID)  %.%
+   mutate(G_batting = ifelse(is.na(G_batting), mean(G_batting, na.rm = TRUE), G_batting))
Source: local data frame [96,600 x 3]
Groups: teamID

   yearID teamID  G_batting
1    2004    SFN          0
2    2006    CHN          0
3    2007    CHA          0
4    2008    BOS          0
5    2009    SEA          0
6    2010    SEA 1074266112
7    2012    NYA   90693125
8    1954    ML1        122
9    1955    ML1        153
10   1956    ML1        153
..    ...    ...        ...

Donc je n'ai pas eu l'erreur (bonne) mais j'ai obtenu un résultat (apparemment) étrange.

37
Vincent

Le principal problème que vous rencontrez est que mean renvoie un double tandis que la colonne G_batting Est un entier. Donc, envelopper la moyenne dans as.integer Fonctionnerait, ou vous auriez besoin de convertir la colonne entière en numeric je suppose.

Cela dit, voici quelques alternatives data.table - Je n'ai pas vérifié laquelle est la plus rapide.

library(data.table)

# using ifelse
dt = data.table(a = 1:2, b = c(1,2,NA,NA,3,4,5,6,7,8))
dt[, b := ifelse(is.na(b), mean(b, na.rm = T), b), by = a]

# using a temporary column
dt = data.table(a = 1:2, b = c(1,2,NA,NA,3,4,5,6,7,8))
dt[, b.mean := mean(b, na.rm = T), by = a][is.na(b), b := b.mean][, b.mean := NULL]

Et c'est ce que je voudrais faire idéalement ( il y a un FR à ce sujet):

# again, atm this is pure fantasy and will not work
dt[, b[is.na(b)] := mean(b, na.rm = T), by = a]

La version dplyr du ifelse est (comme dans OP):

dt %>% group_by(a) %>% mutate(b = ifelse(is.na(b), mean(b, na.rm = T), b))

Je ne sais pas comment implémenter la deuxième idée data.table Sur une seule ligne dans dplyr. Je ne sais pas non plus comment vous pouvez empêcher dplyr de brouiller/ordonner les données (à part créer une colonne d'index).

32
eddi