使用dplyr跨多列求和


98

我的问题涉及跨数据框的多个列求和,并使用来创建与该求和相对应的新列dplyr。列中的数据条目为binary(0,1)。我正在考虑的summarise_eachmutate_each函数的逐行模拟dplyr。以下是数据框的最小示例:

library(dplyr)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))

> df
   x1 x2 x3 x4 x5
1   1  1  0  1  1
2   0  1  1  0  1
3   0 NA  0 NA NA
4  NA  1  1  1  1
5   0  1  1  0  1
6   1  0  0  0  1
7   1 NA NA NA NA
8  NA NA NA  0  1
9   0  0  0  0  0
10  1  1  1  1  1

我可以使用类似:

df <- df %>% mutate(sumrow= x1 + x2 + x3 + x4 + x5)

但这将涉及写出每列的名称。我喜欢50列。另外,列名会在要实现此操作的循环的不同迭代中更改,因此我想尽量避免必须提供任何列名。

我怎样才能最有效地做到这一点?任何帮助将不胜感激。


11
为什么dplyr呢 为什么不只是df$sumrow <- rowSums(df, na.rm = TRUE)从R开始的简单?或者,df$sumrow <- Reduce(`+`, df)如果您想复制所做的确切操作dplyr
David Arenburg

7
你可以两者都做dplyr太作为df %>% mutate(sumrow = Reduce(`+`, .))df %>% mutate(sumrow = rowSums(.))
大卫Arenburg

2
更新到最新dplyr版本,它将起作用。
David Arenburg'3

1
升级包后,由大卫Arenburg建议工作dplyr @DavidArenburg
AMO

1
@boern David Arenburgs的评论是最好的答案,也是最直接的解决方案。您的答案会奏效,但它涉及到将NA值替换为零的额外步骤,这在某些情况下可能不合适。
2016年

Answers:


112

怎么样

总结每一列

df %>%
   replace(is.na(.), 0) %>%
   summarise_all(funs(sum))

总结每一行

df %>%
   replace(is.na(.), 0) %>%
   mutate(sum = rowSums(.[1:5]))

8
summarise_each沿每一列求和,而沿每一行求和
amo

1
我正在尝试实现相同的目的,但是我的DF有一个列是字符,因此我无法对所有列求和。我想我应该修改该(.[1:5])部分,但是不幸的是我不熟悉语法,也不知道如何寻求帮助。尝试过mutate(sum = rowSums(is.numeric(.)))但没有用。
ccamara

5
我懂了。您可能要尝试df %>% replace(is.na(.), 0) %>% select_if(is.numeric) %>% summarise_each(funs(sum))一下?
布尔恩'17

2
使用summarise_all而不是summarise_each已弃用的。
hmhensen

2
mutate(sum = rowSums(.[,-1]))如果您不知道需要处理多少列,则语法可能会派上用场。
Paulo S. Abreu

32

如果您只想对某些列求和,我将使用以下方法:

library(dplyr)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))
df %>% select(x3:x5) %>% rowSums(na.rm=TRUE) -> df$x3x5.total
head(df)

这样您就可以使用dplyr::select的语法。


我喜欢这种方法,因为它不需要将NA强制为0
Michael Bellhouse

并且比grep更好,因为它更容易处理x4:x11之类的东西
Dov Rosenberg

32

我将使用正则表达式匹配来总结具有某些模式名称的变量。例如:

df <- df %>% mutate(sum1 = rowSums(.[grep("x[3-5]", names(.))], na.rm = TRUE),
                    sum_all = rowSums(.[grep("x", names(.))], na.rm = TRUE))

这样,您可以创建多个变量作为数据帧中某些变量组的总和。


很好的解决方案!我一直在寻找一个特定的dplyr函数在最近的发行版中执行此操作,但是找不到
agenis

这个解决方案很棒。如果有不想包含的列,则只需设计grep()语句来选择与特定模式匹配的列。
特伦顿·霍夫曼

1
@TrentonHoffman这是取消选择列的特定模式的位。只需要-签名:rowSums(.[-grep("x[3-5]", names(.))], na.rm = TRUE)
alexb523

22

我经常遇到此问题,最简单的方法是apply()mutate命令中使用该函数。

library(tidyverse)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))

df %>%
  mutate(sum = select(., x1:x5) %>% apply(1, sum, na.rm=TRUE))

在这里,您可以使用标准dplyr技巧(例如starts_with()contains())来选择列。通过在单个mutate命令中完成所有工作,此操作可以在一系列dplyr处理步骤中的任何位置发生。最后,通过使用该apply()函数,您可以灵活地使用所需的任何摘要,包括您自己构建的摘要功能。

或者,如果使用非排字功能的想法不受欢迎,那么您可以收集各列,对其进行汇总,最后将结果合并回原始数据框。

df <- df %>% mutate( id = 1:n() )   # Need some ID column for this to work

df <- df %>%
  group_by(id) %>%
  gather('Key', 'value', starts_with('x')) %>%
  summarise( Key.Sum = sum(value) ) %>%
  left_join( df, . )

在这里,我使用该starts_with()函数选择列并计算总和,您可以对NA值进行任何操作。这种方法的缺点是,尽管它非常灵活,但实际上并不适合dplyr数据清理步骤。


3
apply当这是rowSums设计目的时,似乎很傻。
zacdav '18

6
在这种情况下rowSums,它确实可以很好地工作rowMeans,但是我总是对“如果我要计算的东西不是总和或均值怎么办?”感到奇怪。但是,有99%的时间我必须做这样的事情,它要么是求和,要么是平均值,因此使用通用apply函数时可能没有多余的灵活性。
Derek Sonderegger '18

22

使用reduce()frompurrr的速度比rowSums最终要快apply,并且肯定比快,因为您避免了遍历所有行,而只是利用了矢量化操作:

library(purrr)
library(dplyr)
iris %>% mutate(Petal = reduce(select(., starts_with("Petal")), `+`))

看到这个计时


我喜欢这个,但是当需要时您会怎么做na.rm = TRUE
参阅

@ see24我不确定我知道你的意思。这将使向量a + b + c都具有相同的长度。由于每个向量在不同位置可能有或没有NA,因此您不能忽略它们。这将使向量不对齐。如果要删除NA值,则必须在以后执行,例如drop_na
skd

我最终做了,rowSums(select(., matches("myregex")) , na.rm = TRUE))因为那是我忽略NA所需要的。因此,如果sum(NA, 5)结果的数字为5,但是您说reduce的效果比rowSums我好,我想知道在这种情况下是否可以使用它?
参阅24

我懂了。如果您想要总和并确定地忽略NA值,则该rowSums版本可能是最好的版本。主要缺点是只有rowSumsrowMeans可用(它比reduce慢很多,但幅度不大)。如果您需要执行其他操作(而不是总和),则reduce版本可能是唯一的选择。只是避免apply在这种情况下使用。
skd

1

在较新的版本中,dplyr您可以使用rowwise()c_across来对没有特定的按行变体的功能执行按行聚合,但是如果存在按行变体,则应该更快。

因为rowwise()这只是一种特殊的分组形式,并且会改变动词的工作方式,所以您可能需要ungroup()在执行逐行操作后将其传递给管道。

要选择行范围:

df %>%
  dplyr::rowwise() %>% 
  dplyr::mutate(sumrange = sum(dplyr::c_across(x1:x5), na.rm = T))
# %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()

要按类型选择行:

df %>%
  dplyr::rowwise() %>% 
  dplyr::mutate(sumnumeric = sum(c_across(where(is.numeric)), na.rm = T))
# %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()

在您的特定情况下,存在逐行变体,因此您可以执行以下操作(注意使用across代替):

df %>%
  dplyr::mutate(sumrow = rowSums(dplyr::across(x1:x5), na.rm = T))
# %>% dplyr::ungroup() # you'll likely want to ungroup after using rowwise()

欲了解更多信息,请参阅页面横行

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.