使用dplyr将行添加到数据框

32

我有这个样本数据:

cvar <- c("2015-11-01","2015-11-02","All")
nvar1 <- c(12,10,5)
nvar2 <- c(7,5,6)
data <- cbind.data.frame(cvar,nvar1,nvar2)

我只想在data.frame中添加一行,该行包含nvar1和nvar2的总和以及一个字符。使用基本R,我只需使用以下命令:

data[nrow(data)+1,] <- c("add",sum(data[,2]),sum(data[,3]))

或者使用lapply等更聪明的方法,但这只是为了展示我所寻找的内容。

我希望在管道环境中使用这个简单的命令,所以data %>% ...会给我上面的结果。

感谢任何帮助,谢谢。


你这里有任何 data.table 吗? - user3710546
只要你将其放入管道中,它就会变成一个 data.table,不是吗? - Sebastian
data.table是一个不同的包。如果要转换为data.table,应该使用data.table(cvar, ....)。如果你正在使用dplyr,那么尝试使用bind_rows - akrun
你可能正在指的是 tbl_df - Pierre L
啊,好的,我很抱歉,我会编辑这个。 - Sebastian
5个回答

46

11
这个能够运行是因为向量是在全局环境中定义的。如果列名有惰性求值就更好了。已经打开了一个问题(issue)。链接:https://github.com/tidyverse/tibble/issues/200 - aurelien
1
现在使用列名的惰性求值,这个方法在我看来显然是最好的 tidyverse 选项! - Arthur Spoon

22

也许是这样的:

data %>% 
     rbind(c("add",sum(nvar1),sum(nvar2)))
#        cvar nvar1 nvar2
#1 2015-11-01    12     7
#2 2015-11-02    10     5
#3        All     5     6
#4        add    27    18

编辑:

根据您的评论,这将起作用:

data %>% 
  mutate(nvar3 = nvar1) %>% 
  rbind(c("add",sum(nvar1),sum(nvar2),sum(.$nvar3))) 

使用 . 将允许rbind找到nvar3

编辑2:

提供新行作为列表,它将保持列类:

> str(
+ data %>% 
+   mutate(nvar3 = nvar1) %>% 
+   rbind(list("add",sum(nvar1),sum(nvar2),sum(.$nvar3))) 
+ )
'data.frame':   4 obs. of  4 variables:
 $ cvar : chr  "2015-11-01" "2015-11-02" "All" "add"
 $ nvar1: num  12 10 5 27
 $ nvar2: num  7 5 6 18
 $ nvar3: num  12 10 5 27

谢谢LyzandeR,这确实解决了上述问题,+1。但在管道内它对我不起作用,例如:data %>% mutate(nvar3 = nvar1) %>% rbind(c("add",sum(nvar1),sum(nvar2),sum(nvar3))),我需要在一系列长时间的转换之后使用rbind,抱歉在问题中没有提到这一点。 - Sebastian
@Sebastian,是的,在您上面描述的情况下,nvar3 无法在 data 中找到,因此会出现错误。您应该这样做:data %>% mutate(nvar3 = nvar1) %>% rbind(c("add",sum(nvar1),sum(nvar2),sum(.$nvar3))),它会起作用。 - LyzandeR
@AnandaMahto 我完全同意。看起来原帖作者需要它作为更复杂情况中的一步。 - LyzandeR
@Frank 是的,那是真的。也许楼主在提问时并没有注意到这一点,因为他使用了 [ 得到了相同的结果。 - LyzandeR
我在下一步中使用了mutate_each(as.numeric,...),有没有更好的方法可以保留类(class)在第一次变换时就不被改变?对于我的情况,目前代码中只有几百行数据,所以这个没问题,但对于其他情况来说,这可能会很有用。 - Sebastian
显示剩余2条评论

14

只使用dplyr,您可以执行以下操作

data %<>%
  summarise(cvar = "add",
            nvar1 = sum(nvar1),
            nvar2 = sum(nvar2)) %>%
  bind_rows(data)

导致了

        cvar nvar1 nvar2
1        add    27    18
2 2015-11-01    12     7
3 2015-11-02    10     5
4        All     5     6
请注意,这种方法添加的新行是添加在原始数据框的开头而不是末尾。
如果你想要把新行添加到末尾,可以使用以下代码(感谢krlmlr指出):
data %<>%
  summarise(cvar = "add",
            nvar1 = sum(nvar1),
            nvar2 = sum(nvar2)) %>%
  bind_rows(data, .)

导致的结果是

        cvar nvar1 nvar2
1 2015-11-01    12     7
2 2015-11-02    10     5
3        All     5     6
4        add    27    18

3
使用bind_rows(data, .)将数据添加到末尾。 - krlmlr
1
@krlmlr 谢谢!我没想到在末尾添加新行是那么容易的。有趣的是,在执行此操作时会出现警告消息 In bind_rows_(x, .id) : binding factor and character vector, coercing into character vector,但是反过来却不会出现(bind_rows(., data))。你知道为什么吗? - Salim B
更简单的示例:bind_rows(data_frame(a = "a"), data_frame(a = factor("a")))bind_rows(data_frame(a = factor("a")), data_frame(a = "a"))。似乎取决于顺序,也许我的第一个示例中缺少警告是一个简单的疏忽。请仔细检查dplyr NEWS以确保。 - krlmlr
2
是的,我注意到这取决于顺序...但对我来说,这种行为似乎不一致,所以我在 GitHub 上打开了相应的问题 :) - Salim B

6

使用 summarise_all()bind_rows() 的一个选项可能是:

data %>% 
 bind_rows(summarise_all(., ~ if (is.numeric(.)) sum(.) else "add"))

        cvar nvar1 nvar2
1 2015-11-01    12     7
2 2015-11-02    10     5
3        All     5     6
4        add    27    18

或者可以添加行,在使用if_else()函数仅对最后一行进行求和:

data %>%
 add_row(cvar = "add") %>%
 mutate_at(-1, ~ if_else(row_number() == max(row_number()), sum(., na.rm = TRUE), .))

当变量不在全局环境中时,以下是对 @Rickard 答案的另一种替代方案:

data %>% 
 add_row(cvar = "add", nvar1 = sum(data$nvar1), nvar2 = sum(data$nvar2))

请使用sum(., na.rm = T)进行求和。谢谢! - SJGD
1
建议在RStudio中使用括号以避免警告: data %>% bind_rows(summarise_all(., ~ if (is.numeric(.)) {sum(., na.rm = T)} else {"Total"})) - SJGD
通过使用上面展示的summarise_all()和bind_rows()选项,如何有条件地添加一行?例如,如果sum(nvar1+nvar2)<15,则添加一行(例如,cvar="Others"),并删除所有(nvar1+nvar2)<15的单独行? - Prasanna S

0
如果有人仍在寻找通用解决方案,我建议使用以下方法:
cvar <- c("2015-11-01","2015-11-02","All")
nvar1 <- c(12,10,5)
nvar2 <- c(7,5,6)
data <- tibble::tibble(cvar,nvar1,nvar2)

purrr::map_df(data, ~c(.x, ifelse(is.numeric(.x), sum(.x, na.rm=TRUE), NA)))

顺便说一下,我使用tibble来保留字符,因为数据框会将它们转换为因子,而base::c会“破坏”它们。


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