【问题标题】:r: group by multiple columns and countr:按多列分组并计数
【发布时间】:2017-05-09 11:10:16
【问题描述】:

我有以下数据框,df:

LeftOrRight SpeedCategory   NumThruLanes
R           25to45          3             
L           45to62          2           
R           Gt62            1           

我想按 SpeedCategory 对其进行分组,然后循环遍历其他列,以获取每个速度类别中每个唯一代码的频率——如下所示:

                 25to45 45to62 Gt62
LeftOrRight    L      0      1    0
               R      1      0    1
NumThruLanes   1      0      0    1
               2      0      1    0
               3      1      0    0

我能做到的最接近的是:

for (col in df){
tbl <- table(col, df$SpeedCategory)
print(tbl)
}

打印出以下内容(首先是 SpeedCategory,然后是 NumThruLanes):

col   25to45 45to62 Gt62
  L        0      1    0
  R        1      0    1

col   25to45 45to62 Gt62
  1        0      0    1
  2        0      1    0
  3        1      0    0

我很确定我可以使用aggregate()dplyr 的group_by 来实现我的目标,但我是R 新手,无法弄清楚语法。在 pandas 我会使用 MultiIndex 但我不知道 R 等价物是什么所以很难用谷歌搜索。

我想尝试一次完成所有事情,或者循环完成,因为我有十几列要通过。

【问题讨论】:

    标签: r dataframe dplyr


    【解决方案1】:

    使用 reshape2 包中的dcast,您可以这样做:

    library("reshape2")
    
    DF=read.table(text="LeftOrRight SpeedCategory   NumThruLanes
    R           25to45          3             
    L           45to62          2           
    R           Gt62            1",header=TRUE,stringsAsFactors=FALSE)
    
    LR_Stat = dcast(DF,LeftOrRight ~ SpeedCategory,length,fill=0)
    LR_Stat
    #  LeftOrRight 25to45 45to62 Gt62
    #1           L      0      1    0
    #2           R      1      0    1
    
    Lanes_Stat = dcast(DF,NumThruLanes ~ SpeedCategory,length,fill=0)
    Lanes_Stat
    #  NumThruLanes 25to45 45to62 Gt62
    #1            1      0      0    1
    #2            2      0      1    0
    #3            3      1      0    0
    

    请注意,LR_Stat 在您的预期输出中应该在 45 到 62 范围内包含 1

    【讨论】:

    • 已修复,谢谢!这行得通,但我有很多专栏要通过。有没有办法在不明确命名列的情况下做到这一点?我尝试循环并将每个对象附加到一个空白数据框,但这似乎不起作用......
    【解决方案2】:

    您可以使用 lapply() 而不是 for 循环一次完成所有操作:

    tab_list <- lapply(df[, -2], function(col) table(col, df$SpeedCategory))
    tab_list
    ## $LeftOrRight
    ##    
    ## col 25to45 45to62 Gt62
    ##   L      0      1    0
    ##   R      1      0    1
    ## 
    ## $NumThruLanes
    ##    
    ## col 25to45 45to62 Gt62
    ##   1      0      0    1
    ##   2      0      1    0
    ##   3      1      0    0
    

    然后您可以使用rbind()do.call() 将这些表合并为一个:

    do.call(rbind, tab_list)
    ##   25to45 45to62 Gt62
    ## L      0      1    0
    ## R      1      0    1
    ## 1      0      0    1
    ## 2      0      1    0
    ## 3      1      0    0
    

    可以在输出表中得到一个列,该列指示原始数据框中的列名。要实现这一点,您需要使用更复杂的函数来lapply() 覆盖列名:

    tab_list <- lapply(names(df)[-2], function(col) {
      tab <- table(df[, col], df[, "SpeedCategory"])
      name_col <- c(col, rep("", nrow(tab) - 1))
      mat <- cbind(name_col, rownames(tab), tab)
      as.data.frame(mat)
      })
    do.call(rbind, tab_list)
    ##       name_col V2 25to45 45to62 Gt62
    ## L  LeftOrRight  L      0      1    0
    ## R               R      1      0    1
    ## 1 NumThruLanes  1      0      0    1
    ## 2               2      0      1    0
    ## 3               3      1      0    0
    

    【讨论】:

    • 这看起来很有希望。有没有办法在 do.call() 中维护每个行细分(LeftOrRight、NumThruLanes 等)的列名(而不是手动添加列),使其看起来更像我想要的输出?跨度>
    【解决方案3】:

    tables 包可以很容易地以非常具体的方式格式化表格。语法需要一些时间来适应,但对于这个问题,它非常简单:

    exd <- read.table(text = "LeftOrRight SpeedCategory   NumThruLanes
    R           25to45          3             
    L           45to62          2           
    R           Gt62            1", header = TRUE)       
    
    ## to get counts by default we need everything to be categorical
    exd$SpeedCategory <- factor(exd$SpeedCategory)
    
    library(tables)
    tabular(LeftOrRight + NumThruLanes ~ SpeedCategory, data = exd)
    
    ##                SpeedCategory            
    ##                25to45        45to62 Gt62
    ## LeftOrRight  L 0             1      0   
    ##              R 1             0      1   
    ## NumThruLanes 1 0             0      1   
    ##              2 0             1      0   
    ##              3 1             0      0
    

    如果您有很多列要迭代,您可以以编程方式构造公式,例如,

    tabular(as.formula(paste(paste(names(exd)[-2], collapse = " + "),
                             names(exd)[2], sep = " ~ ")),
            data = exd)
    

    另外还有htmllatex 方法,可以轻松标记您的表格以包含在文章或报告中。

    【讨论】:

    • 这正是我所需要的,谢谢!我最终不得不使用 lapply(df, factor) 将所有列转换为因子,之后效果很好。
    猜你喜欢
    • 2011-03-13
    • 2022-07-22
    • 1970-01-01
    • 2015-12-02
    • 1970-01-01
    • 1970-01-01
    • 2021-05-07
    • 2020-02-10
    • 1970-01-01
    相关资源
    最近更新 更多