使用dplyr和add_row()在每个组中添加行。

33

如果我添加了一个新行到iris数据集中:

iris <- as_tibble(iris)

> iris %>% 
    add_row(.before=0)

# A tibble: 151 × 5
    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          <dbl>       <dbl>        <dbl>       <dbl>   <chr>
1            NA          NA           NA          NA    <NA> <--- Good!
2           5.1         3.5          1.4         0.2  setosa
3           4.9         3.0          1.4         0.2  setosa

它有效。那么,为什么我不能在每个“子集”的顶部添加新行呢?
iris %>% 
 group_by(Species) %>% 
 add_row(.before=0)

Error: is.data.frame(df) is not TRUE

5
升级你的tibble版本,这个错误信息至少有三个月了(请参考链接https://github.com/tidyverse/tibble/blame/b32c2b952afdeff93d422512a132ec6d0a2e2fbc/R/add.R#L35-L37)。新的错误信息是“无法添加行到分组数据框”,这回答了你为什么不能工作的问题。 - r2evans
17
您可以使用do函数向每个组添加行:iris %>% group_by(Species) %>% do(add_row(., .before=0)) - JasonWang
感谢JasonWang和r2evans。我已经更新了我的软件包,使用do()就可以解决问题了。 - Dan
3个回答

31

一个更近期的版本将使用group_modify()代替do()

iris %>%
  as_tibble() %>%
  group_by(Species) %>% 
  group_modify(~ add_row(.x,.before=0))
#> # A tibble: 153 x 5
#> # Groups:   Species [3]
#>    Species Sepal.Length Sepal.Width Petal.Length Petal.Width
#>    <fct>          <dbl>       <dbl>        <dbl>       <dbl>
#>  1 setosa          NA          NA           NA          NA  
#>  2 setosa           5.1         3.5          1.4         0.2
#>  3 setosa           4.9         3            1.4         0.2

1
现在应该使用group_modify而不是@JasonWang在OP的评论中提出的do调用。 group_modify在创建新行时保留组名,而do则不会,这会使用户在分组变量上获得NA值。 - hmhensen
2
在我发布问题多年后,只是添加了一条评论:截至2022年5月,group_modify仍处于实验阶段。感谢Alexlok的答案。 - Dan
@Alexlok,我想要在NA行中的Species列中粘贴'_blank':group_modify(~ add_row(.x %>% mutate(Species=paste0(Species,'_blank')), .before=0))。但是它失败了,我该如何修复?谢谢! - undefined
1
@anderwyang 在不进行任何修改的情况下运行上述代码,然后添加以下这行代码:mutate(Species = if_else(is.na(Sepal.Length), paste0(Species,'_blank'), Species)) - undefined

20
如果您想使用分组操作,您需要像JasonWang在他的评论中描述的那样使用 do ,因为其他函数(如 mutate summarise )期望一个与分组数据框(在您的情况下为50)具有相同行数的结果,或者只有一行(例如在汇总时)。

通常情况下, do 可能会很慢,如果您无法通过其他方式获得结果,则应该将其作为最后的手段。您的任务非常简单,因为它仅涉及向数据框添加额外行,这可以通过简单的索引完成,例如查看 iris [NA,] 的输出。

您想要的实质上是创建一个向量

indices <- c(NA, 1:50, NA, 51:100, NA, 101:150)

(因为第一组在1到50行,第二组在51到100行,第三组在101到150行)。

结果为iris[indices, ]

构建此向量的更通用方法使用group_indices

indices <- seq(nrow(iris)) %>% 
    split(group_indices(iris, Species)) %>% 
    map(~c(NA, .x)) %>%
    unlist

(map来自于purrr包,我假设你已经加载了它,因为你使用了tidyverse标签)

(map来自purrr,我假设你已经导入了tidyverse并加载了该包)


1
哇,感谢@konvas提供的详细答案。顺便说一下,我不知道do是慢的,也不知道purrr/map的替代方法。这就是SO的伟大之处。现在我知道在哪里寻找解决这个问题的答案了。谢谢。 - Dan

6

稍作变动,也可以这样做:

library(purrr)
library(tibble)

iris %>%
  group_split(Species) %>%
  map_dfr(~ .x %>%
            add_row(.before = 1))

# A tibble: 153 x 5
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          <dbl>       <dbl>        <dbl>       <dbl> <fct>  
 1         NA          NA           NA          NA   NA     
 2          5.1         3.5          1.4         0.2 setosa 
 3          4.9         3            1.4         0.2 setosa 
 4          4.7         3.2          1.3         0.2 setosa 
 5          4.6         3.1          1.5         0.2 setosa 
 6          5           3.6          1.4         0.2 setosa 
 7          5.4         3.9          1.7         0.4 setosa 
 8          4.6         3.4          1.4         0.3 setosa 
 9          5           3.4          1.5         0.2 setosa 
10          4.4         2.9          1.4         0.2 setosa 
# ... with 143 more rows

这也可以用于分组的数据框,但是有点啰嗦:

library(dplyr)

iris %>%
  group_by(Species) %>%
  summarise(Sepal.Length = c(NA, Sepal.Length), 
            Sepal.Width = c(NA, Sepal.Width), 
            Petal.Length = c(NA, Petal.Length),
            Petal.Width = c(NA, Petal.Width), 
            Species = c(NA, Species))

1
有趣的方法,使用summarise。不知道它是否保留了组名。 - Dan
1
summarise()方法对我所尝试的工作非常完美。基本上用first()或其他值替换那些NA即可,例如c(first(Sepal.Length), Sepal.Length) - samsamara

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