group_by() 和 into fill() 不按预期工作

13

我正在尝试使用dplyrtidyr对一些格式不良的数据进行最后观察结果前向操作。但它并没有像我预期的那样工作。

library(dplyr)
library(tidyr)

df <- data.frame(id=c(1,1,2,2,3,3),
                 email=c('bob@email.com', NA, 'joe@email.com', NA, NA, NA))
df2 <- df %>% group_by(id) %>% fill(email)

这将导致:

Source: local data frame [6 x 2]
Groups: id [3]

     id         email
  (dbl)        (fctr)
1     1 bob@email.com
2     1 bob@email.com
3     2 joe@email.com
4     2 joe@email.com
5     3 joe@email.com
6     3 joe@email.com

我希望它是:

Source: local data frame [6 x 2]
Groups: id [3]

     id         email
  (dbl)        (fctr)
1     1 bob@email.com
2     1 bob@email.com
3     2 joe@email.com
4     2 joe@email.com
5     3 NA
6     3 NA
我期望的是后者,因为group_by的文档说:“group_by函数接受一个现有的tbl并将其转换为分组的tbl,在其中执行“按组”操作。” 在这种情况下,组由id变量确定,并且以下操作是fill(email)。 然而,很明显它并没有这样做。

在有人问之前,如果字段都是character而不是numericfactor也没有任何区别。


更新 @aosmith指出在Github上这个未解决问题。 我要说,在那个问题解决之前,这个问题不会有一个适当的解决方案。其他所有方法都只是权宜之计。所以,如果有人成功地提交了解决该问题的PR并在此处发布了它,我将很乐意将其标记为解决方案。


1
看起来在 Github 存储库上有一个关于此问题的未解决问题 - aosmith
如果你的问题是如何在dplyr中实现这个功能而不使用fill(目前似乎不支持分组),那么这里有两个重复的问题:这里这里 - aosmith
感谢提供Github问题链接!我最终使用ddply()fill()制作了一个解决方法,但是我想这个问题存在的事实意味着一个适当的解决方案只能作为该问题的解决办法。 - doicomehereoften1
作为一个不需要 zoo 的解决方法,请参见这里Wojciech的答案 - Gregor Thomas
6个回答

11

看起来在 tidyr 的开发版本中已经修复了这个问题。现在,使用 tidyr_0.3.1.9000 中的 fill,您可以按 id 获取预期的结果。

df %>% group_by(id) %>% fill(email)

Source: local data frame [6 x 2]
Groups: id [3]

     id         email
  (dbl)        (fctr)
1     1 bob@email.com
2     1 bob@email.com
3     2 joe@email.com
4     2 joe@email.com
5     3            NA
6     3            NA

确实,这个问题已经在可在CRAN上获取的新版本0.4.0中得到了修复。 - Erwan LE PENNEC

8
幸运的是,你仍然可以使用 zoo::na.locf 来实现这一点:
df %>% 
    group_by(id) %>% 
    mutate(email = zoo::na.locf(email, na.rm = FALSE))  
# Source: local data frame [6 x 2]
# Groups: id [3]
# 
#      id         email
#   (dbl)        (fctr)
# 1     1 bob@email.com
# 2     1 bob@email.com
# 3     2 joe@email.com
# 4     2 joe@email.com
# 5     3            NA
# 6     3            NA

1
好主意,但我和我的同事认为使用一个主要用于时间序列分析的包来进行数据归一化是不好的实践。如果这是一个时间序列问题,那么使用该包就可以了,但在这里不行。 - doicomehereoften1

2

另一种选择是使用dplyr中的do函数:

df3 <- df %>% group_by(id) %>% do(fill(.,email))

这可能是列出的最佳解决方法。然而,我仍然会坚持我的上面的注释,直到有一个包的修复为止,它才能得到适当的回答。谢谢! - doicomehereoften1

0

我已经遇到这个问题很多次了,我担心使用它。

df2 <- df %>% group_by(id) %>% fill(email)

在大数据集上,我有过各种各样的结果,所以我发现以下解决方法非常有用。使用map_df函数的split函数确保您将要执行的任何操作应用于每个id的特定df,并且map_df然后像魔术一样重新绑定所有单独的df。在许多其他情况下,它也被证明是方便的。虽然这个问题已经被解决,但仍然是一个有用的替代方案,可以避免使用group_by()。

df %>% split(.$id) %>% map_df(function(x){ x %>% fill(email)})


0

这有点丑陋,但它是另一种选项,使用 dplyr 并适用于您的示例数据

df %>%
   group_by(id) %>%
   mutate(email = email[ !is.na(email) ][1])

0

两个问题,是否必须重复,以及是否必须使用 dplyrtidyr

也许这可以是一个解决方案?

(
bar <- data.frame(id=c(1,1,2,2,3,3),
                 email=c('bob@email.com', NA, 'joe@email.com', NA, NA, NA))
)                 
#> id         email
#>  1 bob@email.com
#>  1          <NA>
#>  2 joe@email.com
#>  2          <NA>
#>  3          <NA>
#>  3          <NA>

(                 
foo <- bar[!duplicated(bar$id),]
)
#> id         email
#>  1 bob@email.com
#>  2 joe@email.com
#>  3          <NA>

2
我正在处理的实际数据中,右侧还有更多的数据,我只是想以最简洁的方式说明问题。 - doicomehereoften1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接