Подтвердить что ты не робот

Замените NA в цепочке dplyr

Вопрос отредактирован из оригинала.

Прочитав этот интересный обсуждение, мне было интересно, как заменить NA в столбце, используя dplyr in, например, данные о вахме 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

Ниже показано, как не работает

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)

Источник: локальный фрейм данных [20 x 3] Группы: 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**

Фактически он вменяет общее среднее, а не среднее значение группы. Как бы вы это сделали в цепочке dplyr? Использование transform из базы R также работает не, поскольку оно вменяет общее среднее, а не среднее значение группы. Также этот подход преобразует данные в регулярные данные. рама. Есть ли лучший способ сделать это?

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

Изменить: Замена transform на mutate дает следующую ошибку

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

Изменить: добавление as.integer, кажется, разрешает ошибку, а делает результат ожидаемого результата. См. Также ответ @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

Изменить: после комментария @Romain я установил dplyr из 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
..    ...    ...        ...

Итак, я не получил ошибку (хорошо), но у меня появился (по-видимому) странный результат.

4b9b3361

Ответ 1

Основная проблема, с которой вы сталкиваетесь, заключается в том, что mean возвращает double, а столбец G_batting - целое число. Таким образом, обертывание среднего значения в as.integer будет работать, или вам нужно будет преобразовать весь столбец в numeric, я думаю.

Тем не менее, вот пара альтернатив data.table - я не проверял, какой из них быстрее.

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]

И это то, что я хотел бы сделать в идеале (есть FR об этом):

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

Версия dplyr ifelse (как в OP):

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

Я не уверен, как реализовать вторую идею data.table в одной строке в dplyr. Я также не уверен, как вы можете остановить dplyr от скремблирования/упорядочения данных (кроме создания столбца индекса).