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