【问题标题】:Summing Multiple Groups of Columns对多组列求和
【发布时间】:2018-10-31 18:50:00
【问题描述】:

我的数据框包含图像分析的结果,其中列是图像中存在的特定类的比例,因此示例数据框 class_df 看起来像:

id    A    B    C    D    E    F
 1 0.20 0.30 0.10 0.15 0.25 0.00 
 2 0.05 0.10 0.05 0.30 0.10 0.40
 3 0.10 0.10 0.10 0.20 0.20 0.30

这些类中的每一个都属于一个功能组,我想创建新列,其中每个功能组的比例都是从类中计算出来的。映射示例class_fg

class         fg
    A          Z
    B          Z
    C          Z
    D          Y
    E          Y
    F          X

所需的结果将是(添加行以显示所需的新列):

id    A    B    C    D    E    F |    X    Y    Z
 1 0.20 0.30 0.10 0.15 0.25 0.00 | 0.00 0.40 0.60
 2 0.05 0.10 0.05 0.30 0.10 0.40 | 0.40 0.40 0.20
 3 0.10 0.10 0.10 0.20 0.20 0.30 | 0.30 0.40 0.30

我可以一次使用一个功能组来完成

first_fg <- class_fg %>%
  filter(fg == "Z") %>%
  select(class) %>%
  unlist()

class_df <- class_df %>%
  mutate(Z = rowSums(select(., one_of(first_fg))))

当然有更好的方法来做到这一点,我可以计算每个功能组的行总和,而不必为每个组重复此代码?也许使用 purrr?

【问题讨论】:

  • 是的,这称为聚合,然后是汇总。做class_fg %&gt;% group_by(fg) %&gt;% summarize(...your summary code goes here...)
  • 抱歉@Ronak,已正确更新。 “标签”应该是“类”
  • @smci - 我不明白这将如何让我为 class_df 创建一个我真正想要总结的摘要?
  • 你的代码很混乱,因为你从来没有命名你的 df. (是叫class_fg还是class_df?什么是class_df?)不管怎样,你想要的解决方案是whatever_your_df_is_actually_called %&gt;% group_by(fg) %&gt;% summarize(...your summary code goes here...)
  • 我已更新以使示例更清晰。我正在处理一个非常大的数据集,我不确定我是否可以共享(并且在这里使用它作为示例会太大),所以希望改进的示例有所帮助。

标签: r group-by dplyr purrr summarization


【解决方案1】:

在列子集上使用rowSums 的另一个tidyverse 解决方案:

library(tidyverse)
class_fg %>%
  group_by(fg) %>% 
  summarize(list(rowSums(class_df[class]))) %>%
  spread(1,2) %>%
  unnest() %>%
  bind_cols(class_df, .)

#>   id    A   B    C    D    E   F   X   Y   Z
#> 1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
#> 2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
#> 3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3

或者为了代码高尔夫的荣耀:

x <- with(class_fg, tapply(class, fg, reformulate))
mutate(class_df, !!!map(x, ~as.list(.)[[2]]))
#>   id    A   B    C    D    E   F   X   Y   Z
#> 1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
#> 2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
#> 3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3

【讨论】:

    【解决方案2】:

    我通常的方法是坚持base R,只要数据集不会变得太大。在您的情况下,base R 解决方案将是:

    class_df=as.data.frame(
      c(class_df,
        lapply(split(class_fg,class_fg$fg),
               function(x) rowSums(class_df[,x$class,drop=FALSE]))))
    class_df
    #  id    A   B    C    D    E   F   X   Y   Z
    #1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
    #2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
    #3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3
    

    如果数据集太大,我使用data.tabledata.table 解决您的问题:

    library(data.table)
    
    class_dt=data.table(class_df)
    grps=split(class_fg,class_fg$fg)
    
    for (g in grps) class_dt[,c(g$fg[1]):=rowSums(.SD),.SDcols=g$class,]
    class_dt
    #   id    A   B    C    D    E   F   X   Y   Z
    #1:  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
    #2:  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
    #3:  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3
    

    【讨论】:

      【解决方案3】:

      另一个选项,连同已经贡献的工作解决方案, 将使用quasiquotation 使用 rlang 包构建表达式以计算每个中的总和 组。

      library(tidyverse)
      

      首先,定义一个辅助函数来对向量进行元素求和:

      psum <- function(...) reduce(list(...), `+`)
      

      class_fg 中提取分组到一个列表中,然后我们可以构造 计算每组总和的表达式列表:

      sum_exprs <- with(class_fg, split(class, fg)) %>% 
        map(~ rlang::expr(psum(!!!rlang::syms(.x))))
      
      sum_exprs
      #> $X
      #> psum(F)
      #> 
      #> $Y
      #> psum(D, E)
      #> 
      #> $Z
      #> psum(A, B, C)
      

      准备好表达式列表后,我们可以将"bang-bang-bang" (!!!)mutate 一起放入数据中:

      class_df %>%
        mutate(!!!sum_exprs)
      #>   id    A   B    C    D    E   F   X   Y   Z
      #> 1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
      #> 2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
      #> 3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3
      

      (我使用@MKR 在他的回答中提供的代码来创建数据)。

      reprex package (v0.2.0) 于 2018 年 5 月 22 日创建。

      【讨论】:

      • 想说声谢谢,因为将所有这些答案添加到一个小型示例库中,这样我以后可以更加灵活。
      【解决方案4】:

      处理长格式数据总是更容易。因此,使用tidyr:gatherclass_df 更改为长格式并加入class_fg。对您的数据执行长格式分析。最后,以宽格式展开以匹配预期结果。

      library(tidyverse)
      
      class_df %>% gather(key, value, -id) %>% 
        inner_join(class_fg, by=c("key" = "class")) %>%
        group_by(id, fg) %>%
        summarise(value = sum(value)) %>%
        spread(fg, value) %>%
        inner_join(class_df, by="id") %>% as.data.frame()
      
      #   id   X   Y   Z    A   B    C    D    E   F
      # 1  1 0.0 0.4 0.6 0.20 0.3 0.10 0.15 0.25 0.0
      # 2  2 0.4 0.4 0.2 0.05 0.1 0.05 0.30 0.10 0.4
      # 3  3 0.3 0.4 0.3 0.10 0.1 0.10 0.20 0.20 0.3
      

      数据:

      class_fg <- read.table(text = 
      "class         fg
                       A          Z
                       B          Z
                       C          Z
                       D          Y
                       E          Y
                       F          X",
      header = TRUE, stringsAsFactors = FALSE)
      
      class_df  <- read.table(text = 
      "id    A    B    C    D    E    F
      1 0.20 0.30 0.10 0.15 0.25 0.00 
      2 0.05 0.10 0.05 0.30 0.10 0.40
      3 0.10 0.10 0.10 0.20 0.20 0.30",
      header = TRUE, stringsAsFactors = FALSE)
      

      【讨论】:

      • 想说声谢谢,因为将所有这些答案添加到一个小型示例库中,这样我以后可以更加灵活。
      【解决方案5】:

      我们可以split 'class_df' by 'class',循环通过list 元素和mapselect 'class_df' 的列并得到rowSums

      library(tidyverse)
      class_fg %>%
          split(.$fg) %>% 
          map_df(~ class_df %>%
                      select(one_of(.x$class)) %>% 
                      rowSums) %>%
          bind_cols(class_df, .)
      #  id    A   B    C    D    E   F   X   Y   Z
      #1  1 0.20 0.3 0.10 0.15 0.25 0.0 0.0 0.4 0.6
      #2  2 0.05 0.1 0.05 0.30 0.10 0.4 0.4 0.4 0.2
      #3  3 0.10 0.1 0.10 0.20 0.20 0.3 0.3 0.4 0.3
      

      或者通过nesting 进行分组,然后通过mapping 对list 元素进行rowSums

      class_fg %>% 
         group_by(fg) %>%
         nest %>%
         mutate(out = map(data, ~  class_df %>%
                                     select(one_of(.x$class)) %>% 
                                     rowSums)) %>% 
         select(-data)  %>%
         unnest %>% 
         unstack(., out ~ fg) %>% 
         bind_cols(class_df, .)
      

      【讨论】:

      • 谢谢,我有这样的感觉,用这样的单一语句是可能的。我仍在学习如何有效嵌套而不立即求助于创建长格式表。
      猜你喜欢
      • 2018-04-11
      • 2019-11-13
      • 2015-05-17
      • 2021-02-28
      • 2012-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多