使用Dplyr对每一行中列右侧的所有单元格求和

时间:2019-07-31 15:24:07

标签: r dplyr sum row

因此,我在此问题的通用版本上看到了很多页面,但是在这里我要对特定列之后的一行中的所有值求和。

假设我们有这个df:

id    city      identity   q1   q2   q3
0110  detroit   ella       2    4    3
0111  boston    fitz       0    0    0
0112  philly    gerald     3    1    0
0113  new_york  doowop     8    11   2
0114  ontario   wazaaa     NA   11   NA

现在与我一起使用的df通常不带有3个“ q”变量,它们会有所不同。因此,我想对每一行进行求和,但仅求和列identity之后的行。

带有NA的行将被忽略。

最终,我想将总和为0的行删除,并以如下所示的df结尾:

id    city      identity   q1   q2   q3
0110  detroit   ella       2    4    3
0112  philly    gerald     3    1    0
0113  new_york  doowop     8    11   2

在dplyr中执行此操作是首选,但不是必需的。

编辑:

我在下面添加了该解决方案无法使用的数据,对于造成混淆的消息深表歉意。

df <- structure(list(Program = c("3002", "111", "2455", "2929", "NA", 
"NA", NA), Project_ID = c("299", "11", "271", "780", "207", "222", 
NA), Advance_Identifier = c(14, 24, 12, 15, NA, 11, NA), Sequence = c(6, 
4, 4, 5, 2, 3, 79), Item = c("payment", "hero", "prepayment_2", 
"UPS", "period", "prepayment", "yeet"), q1 = c("500", "12", "-1", 
"0", NA, "0", "0"), q2 = c("500", "12", "-1", "0", NA, "0", "1"
), q3 = c("500", "12", "2", "0", NA, "0", "2"), q4 = c("500", 
"13", "0", "0", NA, "0", "3")), row.names = c(NA, -7L), class = c("tbl_df", 
"tbl", "data.frame"))

4 个答案:

答案 0 :(得分:9)

零附加依赖项的Base R版本:

[编辑:我总是忘记rowSums存在]

> df1$new = rowSums(
    df1[,(1+which(names(df1)=="identity")):ncol(df1),drop=FALSE]
    )


> df1
   id     city identity q1 q2 q3 new
1 110  detroit     ella  2  4  3   9
2 111   boston     fitz  0  0  0   0
3 112   philly   gerald  3  1  0   4
4 113 new_york   doowop  8 11  2  21

如果您需要将字符转换为数字,请将applyas.numeric结合使用:

df$new = apply(df[,(1+which(names(df)=="Item")):ncol(df),drop=FALSE], 1, function(col){sum(as.numeric(col))})

要弄清楚它们是否真的是因素,因为这会失败,这就是为什么在执行其他操作之前先将看起来像数字的东西转换成数字是一件好事。

基准

如果您担心速度,这是针对当前接受的解决方案对我的功能进行的基准测试:

akrun = function(df1){df1 %>%
   mutate(new = rowSums(select(., ((match('identity', names(.)) + 
           1):ncol(.))), na.rm = TRUE))}

baz = function(df1){rowSums(
    df1[,(1+which(names(df1)=="identity")):ncol(df1),drop=FALSE]
    )}

样本数据

df = data.frame(id=sample(100,100), city=sample(LETTERS,100,TRUE), identity=sample(letters,100,TRUE), q1=runif(100), q2=runif(100),q3=runif(100))

测试-请注意,每次我都会从源数据框中删除new列,否则代码会不断向其中添加其中的一个(尽管akrun不会适当地修改df它可以在baz通过在基准代码中为其分配新列来对其进行修改后开始运行。

> microbenchmark({df$new=NULL;df2 = akrun(df)},{df$new=NULL;df$new=baz(df)})
Unit: microseconds
                                       expr      min       lq       mean
  {     df$new = NULL     df2 = akrun(df) } 1300.682 1328.941 1396.63477
 {     df$new = NULL     df$new = baz(df) }   63.102   72.721   87.78668
    median        uq      max neval
 1376.9425 1398.5880 2075.894   100
   84.3655   86.7005  685.594   100

tidyverse版本的时间是基本R版本的16倍。

答案 1 :(得分:4)

我们可以使用

out <- df1 %>%
   mutate(new = rowSums(select(., ((match('identity', names(.)) + 
           1):ncol(.))), na.rm = TRUE))
out
#    id     city identity q1 q2 q3 new
#1 110  detroit     ella  2  4  3   9
#2 111   boston     fitz  0  0  0   0
#3 112   philly   gerald  3  1  0   4
#4 113 new_york   doowop  8 11  2  21

,然后filter删除“新建”中具有0的行

out %>%
    filter(new >0)

在OP的更新数据集中,列typecharacter。我们可以自动将type转换为相应的类型

df %>%
    #type.convert %>% # base R
    # or with `readr::type_convert
     type_convert %>%
    ... 

注意:标题和描述中关于tidyverse选项的OP。这不是效率问题。

此外,rowSums是一个base R选项。在这里,我们展示了如何在tidyverse链中使用它。使用相同的选项,我本可以以base R的方式写得早一些。

如果我们删除select,它将变成base R,即

df1$new < rowSums(df1[(match('identity', names(df1)) + 1):ncol(df1)], na.rm = TRUE)

基准

df = data.frame(id=sample(100,100), city=sample(LETTERS,100,TRUE), 
      identity=sample(letters,100,TRUE), q1=runif(100), q2=runif(100),q3=runif(100))
akrun = function(df1){
 rowSums(df1[(match('identity', names(df1)) + 1):ncol(df1)], na.rm = TRUE)
}



baz = function(df1){rowSums(
    df1[,(1+which(names(df1)=="identity")):ncol(df1),drop=FALSE]
    )}

microbenchmark({df$new=NULL;df2 = akrun(df)},{df$new=NULL;df$new=baz(df)})
#Unit: microseconds
#                                       expr    min     lq     mean  median      uq      max neval
#  {     df$new = NULL     df2 = akrun(df) } 69.926 73.244 112.2078 75.4335 78.7625 3539.921   100
# {     df$new = NULL     df$new = baz(df) } 73.670 77.945 118.3875 80.5045 83.5100 3767.812   100

数据

df1 <- structure(list(id = 110:113, city = c("detroit", "boston", "philly", 
"new_york"), identity = c("ella", "fitz", "gerald", "doowop"), 
    q1 = c(2L, 0L, 3L, 8L), q2 = c(4L, 0L, 1L, 11L), q3 = c(3L, 
    0L, 0L, 2L)), class = "data.frame", row.names = c(NA, -4L
))

答案 2 :(得分:2)

类似于akrun,您可以尝试

df %>% 
  mutate_at(vars(starts_with("q")),funs(as.numeric)) %>% 
  mutate(sum_new = rowSums(select(., starts_with("q")), na.rm = TRUE)) %>% 
  filter(sum_new>0)

答案 3 :(得分:1)

在这里,我使用reduce中的purrr对行求和,这是最快的方法。

library(tidyverse)
data %>% filter_at(vars(starts_with('q')),~!is.na(.)) %>% 
        mutate( Sum = reduce(select(., starts_with("q")), `+`)) %>% 
        filter(Sum > 0)