【问题标题】:replace NA in a dplyr chain在 dplyr 链中替换 NA
【发布时间】:2014-03-10 00:10:30
【问题描述】:

问题已根据原文进行了修改

在阅读了这个有趣的discussion 之后,我想知道如何使用 dplyr 替换列中的 NA,例如在 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 链中执行此操作?使用基础 R 中的 transform 也确实 not 起作用,因为它估算了整体平均值而不是组平均值。这种方法也将数据转换为常规数据。一个框架。有没有更好的方法来做到这一点?

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 的评论,我从 github 安装了 dplyr:

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

所以我没有得到错误(很好),但我得到了一个(看似)奇怪的结果。

【问题讨论】:

  • 错误信息令人困惑,但问题的根源在于 ifelse 的语义混乱。我添加了一个问题,以确保我考虑更多github.com/hadley/dplyr/issues/254
  • 我无法使用 dplyr 的开发版本重现该错误。
  • 谢谢哈德利。 @Romain,感谢您的建议。我从 hadley/dplyr 安装了 master 分支并得到了上面的结果。与您在问题 #254 中的结果不同

标签: r dplyr


【解决方案1】:

您遇到的主要问题是 mean 返回一个双精度,而 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]

这就是我理想中想要做的事情(there is an FR 关于这个):

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

ifelsedplyr 版本是(如在 OP 中):

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

我不确定如何在dplyr 的一行中实现第二个data.table 想法。我也不确定如何阻止 dplyr 对数据进行加扰/排序(除了创建索引列)。

【讨论】:

  • 我认为有一个 NaN 因为与 teamID, yearID 对应的所有值都是 NA
  • 感谢 @eddi 的 data.table 替代品。在某些时候,我真的必须弄清楚所有 [] 是如何工作的(您的临时列选项)。
  • mutateifelse 也正是我所需要的。但是我可以在不明确命名列的情况下替换整个数据框中的所有 NA 吗?
  • @Bobby in dplyr 有一些遍历列的函数——我不记得名字但应该很容易找到。在data.table 中,你会做常规的 R 循环:dt[, names(dt) := lapply(.SD, function(x) ifelse(is.na(x), mean(x, na.rm = T), x)), by = a]
  • @Bobby,您可以用 df %&gt;% mutate_all(.funs = funs(ifelse(is.na(.), 0, .))) 替换 dplyr 中的 df 中的所有 NA 。或者带有df %&gt;% mutate_at(.vars = vars(yearID, G_batting), .funs = funs(ifelse(is.na(.), 0, .)))的列子集
猜你喜欢
  • 2018-06-12
  • 1970-01-01
  • 2018-10-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 2020-11-19
  • 1970-01-01
  • 2014-11-30
相关资源
最近更新 更多