【问题标题】:Efficiently sum across multiple columns in R在 R 中的多列之间有效地求和
【发布时间】:2015-03-12 09:32:27
【问题描述】:

我有以下精简数据集:

a<-as.data.frame(c(2000:2005))
a$Col1<-c(1:6)
a$Col2<-seq(2,12,2)

colnames(a)<-c("year","Col1","Col2")

for (i in 1:2){
  a[[paste("Var_", i, sep="")]]<-i*a[[paste("Col", i, sep="")]]
}

我想对我使用的 Var1 和 Var2 列求和:

a$sum<-a$Var_1 + a$Var_2

实际上,我的数据集要大得多 - 我想从 Var_1 到 Var_n 求和(n 可以达到 20)。必须有比以下更有效的方法:

 a$sum<-a$Var_1 + ... + a$Var_n

【问题讨论】:

  • 试试 apply: a$sum
  • 谢谢,在以下情况下效果很好,其中 i 是 Var_1 的列索引,j 是 Var_n 的列索引a$sum &lt;- apply(a[,c(i:j)], 1, sum)
  • 并进一步自动化流程(使用stackoverflow.com/questions/9277363/…):a$sum &lt;- apply(a[,c(match("Var_1",names(a)):match("Var_n",names(a)))], 1, sum)
  • a$Col3 &lt;- rowSums(a[,2:3])

标签: r sum


【解决方案1】:

这是使用tidyverse 的解决方案。您可以使用select() 函数将其扩展到任意多的列,以在mutate() 中选择适当的列。

library(tidyverse)

a<-as.data.frame(c(2000:2005))
a$Col1<-c(1:6)
a$Col2<-seq(2,12,2)

colnames(a)<-c("year","Col1","Col2")

for (i in 1:2){
    a[[paste("Var_", i, sep="")]]<-i*a[[paste("Col", i, sep="")]]
}
a
#>   year Col1 Col2 Var_1 Var_2
#> 1 2000    1    2     1     4
#> 2 2001    2    4     2     8
#> 3 2002    3    6     3    12
#> 4 2003    4    8     4    16
#> 5 2004    5   10     5    20
#> 6 2005    6   12     6    24

# Tidyverse solution
a %>%
    mutate(Total = select(., Var_1:Var_2) %>% rowSums(na.rm = TRUE))
#>   year Col1 Col2 Var_1 Var_2 Total
#> 1 2000    1    2     1     4     5
#> 2 2001    2    4     2     8    10
#> 3 2002    3    6     3    12    15
#> 4 2003    4    8     4    16    20
#> 5 2004    5   10     5    20    25
#> 6 2005    6   12     6    24    30

reprex package (v0.2.1) 于 2019 年 1 月 1 日创建

【讨论】:

  • 如何计算每列的百分比(Col1/total, caol2/total)?
  • @Masoud - 我会添加一个 mutate_at(),它会改变 Col1:Col2,并带有一个除以总列的函数。
【解决方案2】:

您可以使用colSums(a[,c("Var1", "Var2")])rowSums(a[,c("Var_1", "Var_2")])。在你的情况下,你想要后者。

【讨论】:

    【解决方案3】:

    你可以使用 dplyr

    a %>%
    rowwise() %>%
    mutate(sum = sum(Col1,Col1, na.rm = T))
    

    或者更高效

    a %>%
    rowwise() %>%
    mutate(sum = sum(across(starts_with("Col")), na.rm = T))
    

    【讨论】:

      【解决方案4】:

      如果您正在处理一个非常大的数据集,rowSums 可能会很慢。

      另一种方法是Rfast 包中的rowsums 函数。这要求您在此过程中将数据转换为 matrix 并使用列索引而不是名称。这是基于您的代码的示例:

      ## load Rfast
      library(Rfast)
      
      ## create dataset
      a <- as.data.frame(c(2000:2005))
      a$Col1 <- c(1:6)
      a$Col2 <- seq(2,12,2)
      
      colnames(a) <- c("year","Col1","Col2")
      
      for (i in 1:2){
        a[[paste("Var_", i, sep="")]] <- i*a[[paste("Col", i, sep="")]]
      }
      
      ## get column indices based on names
      col_st <- grep("Var_1", colnames(a))  # index of "Var_1" col
      col_en <- grep("Var_2", colnames(a))  # index of "Var_2" col
      cols   <- c(col_st:col_en)  # indices of all cols from "Var_1" to "Var_2"
      
      ## sum rows 4 to 5
      a$Total <- rowsums(as.matrix(a[,cols]))
      

      【讨论】:

        【解决方案5】:

        你可以用这个:

        library(dplyr)
        a$Sum <- apply(a[,select(a, starts_with("Var_"))], 1, sum)
        

        【讨论】:

          【解决方案6】:

          在基础 R 中:

          你可以简单地使用sapply:

          sapply(unique(sub(".$", "", colnames(a))), function(x) rowSums(a[startsWith(colnames(a), x)]))
          

          这非常可靠,它适用于任何事情。

          【讨论】:

            【解决方案7】:

            基准测试似乎表明普通的Reduce('+', ...) 是最快的。图书馆只是让它(至少稍微)变慢,至少对于mtcars,即使我将它扩展得很大。

            Unit: milliseconds
                     expr        min         lq       mean     median         uq        max
                  rowSums   8.672061   9.014344  13.708022   9.602312  10.672726  148.47183
                   Reduce   2.994240   3.157500   6.331503   3.223612   3.616555   99.49181
                    apply 524.488376 651.549401 771.095002 743.286441 857.993418 1235.53153
                    Rfast   5.649006   5.901787  11.110896   6.387990   9.727408   66.03151
               DT_rowSums   9.209539   9.566574  20.955033  10.131163  12.967030  294.32911
                DT_Reduce   3.590719   3.774761  10.595256   3.924592   4.259343  340.52855
             tidy_rowSums  15.532917  15.997649  33.736883  17.316108  27.072343  343.21254
              tidy_Reduce   8.627810   8.960008  12.271105   9.603124  11.089334   79.98853
            

            代码:

            library('data.table')
            library('tidyverse')
            library('Rfast')
            DFcars = data.table::copy(mtcars)
            DFcars = do.call("rbind", replicate(10000, DFcars, simplify = FALSE))
            DT_cars = data.table::copy(DFcars)
            DFcars2 = data.table::copy(DFcars)
            setDT(DT_cars)
            colnms = c("mpg", "cyl", "disp", "hp", "drat")
            
            microbenchmark::microbenchmark(
                rowSums =
                    {
                        DFcars$new_col = rowSums(DFcars[, colnms])
                        (as.numeric(DFcars$new_col))
                    },
                Reduce =
                    {
                        DFcars$new_col = Reduce('+', DFcars[, colnms])
                        (as.numeric(DFcars$new_col))
                    },
                apply =
                    {
                        DFcars$new_col = apply(DFcars[, 1:5], 1, sum)
                        (as.numeric(DFcars$new_col))
                    },
                Rfast =
                    {
                        DFcars$new_col = rowsums(as.matrix(DFcars[, colnms]))
                        (as.numeric(DFcars$new_col))
                    },
                DT_rowSums =
                    {
                        DT_cars[, new_col := rowSums(.SD), .SDcols = colnms]
                        (as.numeric(DT_cars$new_col))
                    },
                DT_Reduce =
                    {
                        DT_cars[, new_col := Reduce('+', .SD), .SDcols = colnms]
                        (as.numeric(DT_cars$new_col))
                    },
                tidy_rowSums =
                    {
                        DFcars2 = DFcars2 %>% mutate(new_col = select(., colnms) %>% rowSums())
                        (as.numeric(DFcars2$new_col))
                    },
                tidy_Reduce =
                    {
                        DFcars2 = DFcars2 %>% mutate(new_col = select(., colnms) %>% Reduce('+', .))
                        (as.numeric(DFcars2$new_col))
                    },
                check = 'equivalent'
            )
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2022-11-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-04-13
              • 2014-05-05
              • 1970-01-01
              相关资源
              最近更新 更多