【问题标题】:Summarize and Rank Data Frame汇总和排名数据框
【发布时间】:2013-04-22 13:34:50
【问题描述】:

使用 R,我需要为每个部门支出最多的前 2 名员工构建一个报告,并为该部门的其他员工添加一个“其他”。例如,我需要一份与此类似的报告。

Dept.      EmployeeId     Expense
Marketing       12345         100
Marketing       12346          90
Marketing      Others         200
Sales           12347          50 <-- There's just one employee with expenses
Research        12348        2000
Research        12349         900
Research       Others       10000

换句话说,我需要总结数据,重点关注支出最多的前 2 名员工。费用栏的总和应该是公司的费用总额。

employeIds <- sample(1000:9999, 20)
depts <- sample(c('Sales', 'Marketing', 'Research'), 20, replace = TRUE)
expenses <- sample(1:1000, 20, replace = TRUE)

df <- data.frame(employeIds, depts, expenses)

# Based on that data, how do I build a table with the top 2 employees with the most expenses in each department, including an "Other" employee per department.

我是 R 新手,我不确定如何处理这个问题。在 SQL 中,我本来可以使用 RANK() 函数和 JOIN,但这里不是一个选项。

【问题讨论】:

    标签: r dataframe aggregate


    【解决方案1】:

    这是data.table 解决方案:

    创建数据:我还制作了不会发生“其他”的情况(该部门的条目数为:1

    set.seed(45)
    employeIds <- sample(1000:9999, 20)
    depts <- sample(c('Sales', 'Marketing', 'Research'), 20, replace = TRUE)
    expenses <- sample(1:1000, 20, replace = TRUE)
    
    df <- data.frame(employeIds, depts, expenses)
    df <- df[-c(6,10,12,18,19), ]
    

    data.table 解决方案:

    require(data.table)
    dt <- data.table(df, key=c("depts", "expenses"))
    k <- 2
    dt[, if(.N > k) {
            idx <- (seq_len(.N)-1) %/% max(k, (.N - k)) == 1
            list(EmployeeIds = c(employeIds[idx], "Others"), 
               Expenses = c(expenses[idx], sum(expenses[!idx])))
         } else {
            list(EmployeeIds = as.character(employeIds), Expenses = expenses)
         }, by = depts]
    
    #        depts EmployeeIds Expenses
    # 1: Marketing        4870      567
    # 2: Marketing        3167      591
    # 3: Marketing      Others     2285
    # 4:  Research        5989      878
    # 5:  Research        9667      930
    # 6:  Research      Others     1301
    # 7:     Sales        6700      129
    # 8:     Sales        3857      714
    

    想法:使用key = depts, expenses 创建dt 的第一步确保expenses 按升序排序。然后,根据每个 dept 的条目数,我们要么创建“Others”条目,要么不创建。

    【讨论】:

    • 令人印象深刻的答案阿伦!这正是我一直在寻找的! data.frame 和 date.table 有什么区别?什么代表.N?如果我想要每个部门的前 5 名员工怎么办?非常感谢您的回答!
    • @Martin,我通过设置与 前 k 名员工 对应的变量 k 修改了答案。您可以将其设置为 2 或 5 以获得适当的结果。 data.table 是一个建立在data.frame 之上的外部包,但是非常快速和高效。你可以先看看vignetteshere
    【解决方案2】:

    可能不是最优雅的,但它是一个解决方案:

    func <- function(data) {
     data1 <- aggregate(data$expenses, list(employeIds=data$employeIds), sum)
     # rank without ties.method = "first" will screw things up with identical values
     data1$employeIds[!(rank(data1$x, ties.method="first") %in% 1:2)] <- 'Others'
     data1 <- aggregate(data.frame(expenses=data1$x), list(employeIds=data1$employeIds), sum)
    }
    
    do.call(rbind, by(df, df$depts, func))
    

    【讨论】:

    • 如果两个值相同,rank 没有ties.method = "first" 将给出平均值作为排名。在我的示例df 中,执行以下操作:df$expenses[1] &lt;- 714,然后尝试您的代码。相应地进行了编辑。
    【解决方案3】:

    另一种data.table 方法(可能更接近您所知道的 SQL 风格):

    dt <- data.table(employeIds, depts, expenses)
    dt[, rank:=rank(-expenses), by=depts][,
        list("Expenses"=sum(expenses)),
        keyby=list(depts, "Employee"=ifelse(rank<=2,employeIds,"Other"))
    ]
           depts Employee Expenses
    1: Marketing     6988      986
    2: Marketing     7011      940
    3: Marketing    Other     2614
    4:  Research     2434      763
    5:  Research     9852      731
    6:  Research    Other     3397
    7:     Sales     3120      581
    8:     Sales     6069      868
    

    【讨论】:

      【解决方案4】:
      df <- split(df, df$depts)
      df <- lapply(df, FUN=function(x){
        x <- x[order(x$expenses, decreasing=TRUE), ]
        x$total.expenses <- sum(x$expenses)
        x$group <- 1:nrow(x)
        x$group <- ifelse(x$group <= 2, x$group, "Other")
        x
      })
      df <- do.call(rbind, df)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-05
        • 1970-01-01
        相关资源
        最近更新 更多