【问题标题】:How to maintain original row names when binding rows from different data frames?绑定来自不同数据帧的行时如何保持原始行名?
【发布时间】:2018-02-04 11:10:32
【问题描述】:

使用 dply::summarise 时如何保留其中一个分组名称?或者,是否有更好的方法来保留其中一个组名?我可能这样做的效率很低。

我有一个 data.frame (df):

dput(head(df, n = 20))
structure(list(file_src = c("CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", 
"CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", 
"CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", 
"CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", 
"CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", "CBG_EFD.xlsx", 
"CBG_EFD.xlsx"), AU = c("CBD", "CBD", "CBD", "CBD", "CBD", "CBD", 
"CBD", "CBD", "CBD", "CBD", "CBD", "CBD", "CBD", "CBD", "CBD", 
"CBD", "CBD", "CBD", "CBD", "CBD"), BU = c("OAO", "Constr", "Retail", 
"OAO", "Constr", "Retail", "OAO", "Constr", "Retail", "OAO", 
"Constr", "Retail", "OAO", "Constr", "Retail", "OAO", "Constr", 
"Retail", "OAO", "Constr"), CC = c("AUDIT", "AUDIT", "AUDIT", 
"AUDIT", "AUDIT", "AUDIT", "CORC", "CORC", "CORC", "CORC", "CORC", 
"CORC", "CORC", "CORC", "CORC", "CORC", "CORC", "CORC", "CORC", 
"CORC"), CA_LVL = c("AUDIT01", "AUDIT01", "AUDIT01", "AUDIT02", 
"AUDIT02", "AUDIT02", "CORC01", "CORC01", "CORC01", "CORC02", 
"CORC02", "CORC02", "CORC03", "CORC03", "CORC03", "CORC04", "CORC04", 
"CORC04", "CORC05", "CORC05"), Score = c(1, 1, 2, 1, 3, 3, 1, 
3, 2, 2, 4, 2, 2, 3, 1, 4, 2, 3, 3, 2)), .Names = c("file_src", 
"AU", "BU", "CC", "CA_LVL", "Score"), row.names = c(NA, -20L), class = c("tbl_df", 
"tbl", "data.frame"))

定义 其中 AU 是一组五 (5) 个“组”,BU 是一组五十五 (55) 个单元,所有这些单元都属于五个 AU 中的一个。亲子关系。分数是一个原始数字 0-4。 Control_Category 是一个有六个(字符串值)的变量。

目前,我的代码被分解为脚本执行两个级别的分组和聚合分数以给出一个简单的平均值。我首先在 AU 级别进行分组,以获得给定组的所有单元各自类别 (CC) 的简单平均值。最后,我有五个 data.frames(cbg.au.stat.wide、cbd.au.stat.wide 等)。这些 dfs 表示给定组的所有单元中给定类别的平均分数。

# Group1 assessment unit scores
cbg.au.stat.wide <- df %>%
  group_by(AU, CC) %>%
  filter(AU == "CBG") %>%
  summarise(avg = mean(Score, na.rm = TRUE)) %>%
  dcast(AU ~ CC, value.var = "avg") %>%
  print()  # end chain

产生:

cbg.au.stat.wide
   AU AUDIT     CORC GOV      PPS     TMSC    TRAIN
1 CBG     3 2.733333 2.2 2.666667 1.583333 2.666667

稍后,所有“AU 级别”数据帧都使用 dplyr::bind_rows 进行组合

au.avg.scores <- bind_rows(
  bsa.au.stat.wide,bsg.au.stat.wide,cbd.au.stat.wide,
  cbg.au.stat.wide,wmg.au.stat.wide)

au.avg.scores
         AU    AUDIT     CORC      GOV      PPS     TMSC    TRAIN
1 BSA Admin 2.833333 2.000000 2.733333 2.000000 1.750000 2.333333
2       BSG 2.833333 0.000000 2.733333 2.000000 1.750000 2.333333
3       CBD 1.833333 2.533333 2.466667 2.000000 2.500000 2.166667
4       CBG 3.000000 2.733333 2.200000 2.666667 1.583333 2.666667
5       WMG 2.625000 1.816667 2.533333 2.166667 1.895833 2.375000

然后我执行类似的分组和总结活动。只有这一次,我不是在 AU 级别(父级),而是在每个类别(CC)的 BU 级别进行。因此,对于一个给定的 AU,我知道有一个 BU 平均分数表,其中包含他们的控制类别。

# Group1 business units by Control Category
cbg.bu.stat.wide <- df %>%
  group_by(BU, CC) %>%
  filter(AU == "CBG") %>%
  summarise(avg = mean(Score, na.rm = TRUE)) %>%
  dcast(BU ~ CC, value.var = "avg") %>%
  print() # end chain

产生:

                      BU AUDIT CORC GOV PPS TMSC TRAIN
1        Capital Markets     3  3.2 1.6   4 1.00     3
2                    EFD     4  2.6 1.6   3 1.75     3
3 Global Trade Solutions     3  2.4 3.4   1 2.00     2
4         Investigations     1   NA  NA  NA   NA    NA

我认为您会在此处的“BU”级别注意到“AU”级别已被删除。最后,我想将所有这些 BU 合并到一个大表中,显示 BU 和 AU 的来源

所以它最终会看起来像这样:

> bu.avg.scores
AU BU AUDIT CORC GOV PPS TMSC TRAIN
CBG Adherence   3.0  1.4 3.2   1 1.50   3.0
CBG CTR   2.0  2.8 2.0   4 1.50   2.5
CBG HRCU   3.5  1.8 3.0   1 2.25   1.5
CBD Investigations   2.0   NA  NA  NA   NA    NA
BSG ACH   2.0  0.0 2.0   4 1.50   2.5

【问题讨论】:

    标签: r dplyr rows


    【解决方案1】:

    更新答案

    这是基于评论线程的更新答案。我们分别通过AUBU 进行汇总,并将结果存储在一个列表中。然后,我们将展示如何将汇总组合成一个数据框并将汇总输出为表格。

    library(tidyverse)
    
    # Summarize by AU and (separately) by BU and store each summary in a list
    dfs = list(AU = df %>% 
                 group_by(AU, CC) %>% 
                 summarise(avg=mean(Score, na.rm=TRUE)),
               BU = df %>% 
                 group_by(BU, CC) %>% 
                 summarise(avg=mean(Score, na.rm=TRUE)))
    

    现在每个摘要都存储在一个单独的列表元素中。这将两个不同级别的摘要分开,但存储在一个对象中,以便进一步处理。

    dfs
    
    $AU
         AU    CC      avg
    1   CBD AUDIT 1.833333
    2   CBD  CORC 2.428571
    
    $BU
          BU    CC   avg
    1 Constr AUDIT   2.0
    2 Constr  CORC   2.8
    3    OAO AUDIT   1.0
    4    OAO  CORC   2.4
    5 Retail AUDIT   2.5
    6 Retail  CORC   2.0
    

    如果你想要一个单一的数据框,你可以这样做:

    # Combine into a single table and spread
    df.table = bind_rows(dfs, .id="Unit Level") %>% 
      replace(., is.na(.), "") %>%  # To avoid "NA" values when we "unite" below
      unite(Unit, AU, BU, sep="") %>% 
      spread(CC, avg)
    
    df.table
    
      `Unit Level`   Unit    AUDIT     CORC
    1           AU    CBD 1.833333 2.428571
    2           BU Constr 2.000000 2.800000
    3           BU    OAO 1.000000 2.400000
    4           BU Retail 2.500000 2.000000
    

    如果您要在 rmarkdown 中创建报告,则可以将其转换为输出表。这是我们删除重复行标识符的示例:

    ```{r}
    knitr::kable(df.table %>% 
                   mutate(`Unit Level` = replace(`Unit Level`, duplicated(`Unit Level`), "")))
    ```
    

    这是表格在 PDF 文件中输出时的样子:

    或者,如果您想添加一条中线来分隔 AUBU 平均值,您可以这样做:

    ```{r, results="asis"}
    library(xtable)
    options(xtable.include.rownames=FALSE, xtable.comment=FALSE)
    
    print(xtable(df.table %>% 
                   mutate(`Unit Level` = replace(`Unit Level`, duplicated(`Unit Level`), ""))),
                 hline.after=c(-1,0,cumsum(table(df.table["Unit Level"]))))
    ```
    

    原答案

    在下面的代码中,我们首先计算AUBU 级别的平均值。然后我们计算AU 级别的平均值,并使用bind_rows 合并两个级别的平均值。然后我们可以将spread生成的数据帧转为宽格式。

    library(tidyverse)
    
    # Get averages at the AU-BU level
    dfs = df %>%
      group_by(AU, BU, CC) %>%
      summarise(avg = mean(Score, na.rm = TRUE)) 
    
    dfs
    
         AU     BU    CC     n   avg
    1   CBD Constr AUDIT     2   2.0
    2   CBD Constr  CORC     5   2.8
    3   CBD    OAO AUDIT     2   1.0
    4   CBD    OAO  CORC     5   2.4
    5   CBD Retail AUDIT     2   2.5
    6   CBD Retail  CORC     4   2.0
    
    # Combine with averages at the AU level
    dfs = bind_rows(dfs, 
                    df %>%
                      group_by(AU, CC) %>%
                      summarise(avg = mean(Score, na.rm = TRUE)) %>% 
                      mutate(BU = paste("All", AU,"BU")))
    
    dfs   
    
         AU         BU    CC      avg
    1   CBD     Constr AUDIT 2.000000
    2   CBD     Constr  CORC 2.800000
    3   CBD        OAO AUDIT 1.000000
    4   CBD        OAO  CORC 2.400000
    5   CBD     Retail AUDIT 2.500000
    6   CBD     Retail  CORC 2.000000
    7   CBD All CBD BU AUDIT 1.833333
    8   CBD All CBD BU  CORC 2.428571
    
    # Spread (does same thing as dcast, but using tidyr spread function)
    dfs %>% spread(CC, avg)
    
         AU         BU    AUDIT     CORC
    1   CBD All CBD BU 1.833333 2.428571
    2   CBD     Constr 2.000000 2.800000
    3   CBD        OAO 1.000000 2.400000
    4   CBD     Retail 2.500000 2.000000
    

    这可以组合成一条链:

    dfs = df %>%
      group_by(AU, BU, CC) %>%
      summarise(avg = mean(Score, na.rm = TRUE)) %>% 
      bind_rows(
        df %>%
          group_by(AU, CC) %>%
          summarise(avg = mean(Score, na.rm = TRUE)) %>% 
          mutate(BU = paste("All", AU,"BU"))
      ) %>% 
      spread(CC, avg)
    

    【讨论】:

    • 我需要展示两个结果:AU 级别的类别平均值(其中有五个)和 BU 级别的类别平均值(总共有 55 个)。如果您显示的“所有 CBD”是 AU 级别的平均值,而基础行是 BU 级别的平均值,那么我认为这很合适!
    • 当您说“结合两个平均水平”时。您是指将 AU 平均水平(55 个 BU 的每个类别的平均数)叠加在 BU 平均水平(特定 BU 的一个类别的平均数)之上?
    • 在这种情况下,我叠加了两组平均值:(1) 每个 AU 的平均值和 (2) AUBU 的每个组合的平均值。如果您想要所有AU 中每个BU 的平均值,那么您可以使用group_by(BU, CC) 而不是group_by(AU, BU, CC)。但是,由于平均值不再是分层的,您还需要更改设置决赛表的方式,以明确每个平均值来自哪个级别。让我知道这是否是您正在寻找的内容,我可以更新我的答案。
    • 是的。这就对了。我需要分别呈现 AU 平均值,然后分别呈现 BU 平均值。如果你想看,所有的代码都在这里:github.com/wfio/ctrl-score
    • 是的,没错。将事物保存在一个列表中,无论是多个相似的数据框还是相关对象的集合(如 R 建模函数的列表输出,例如 lmglm),都可以更轻松地进行进一步的分析和处理,也可以保留您的工作空间整洁,因为你有一个列表而不是 10 或 20 或任何单独的对象来跟踪。
    猜你喜欢
    • 2020-03-15
    • 2021-01-30
    • 2011-01-24
    • 1970-01-01
    • 2019-09-07
    • 2013-07-26
    • 2018-11-13
    相关资源
    最近更新 更多