【问题标题】:Iteratively create columns based on grouped variables基于分组变量迭代创建列
【发布时间】:2014-11-07 15:18:24
【问题描述】:

我有一些数据(如下),我想通过某个分组变量根据当前列的总和迭代地添加列,并且我想将列命名为当前名称的粘贴值 +“_tot”。我认为 dplyr 和 lapply 的组合是解决它的方法,但我无法使结构正确。

set.seed(1234)
data <- data.frame(
    biz = sample(c("telco","shipping","tech"), 50, replace = TRUE),
    region = sample(c("mideast","americas"), 50, replace = TRUE),
    june = sample(1:50, 50, replace=TRUE),
    july = sample(100:150, 50, replace=TRUE)
    )

所以,我想要做的是 1)按“区域”对这些数据进行分组,然后为接下来的每个月添加一个新列,即该月值的总和(在实际数据框中,有很多时期接下来)。

基本上,我想应用这个功能

library(dplyr)
data %>% group_by(region) %>% mutate(june_tot = sum(june))

跨越每个月,无需指定“六月”或“七月”。我最初的看法:

testfun <- function(df, col) {
    name <- paste(col, "_tot", sep="")
    data2 <- df %>% group_by(region) %>% summarise(name=sum(col))
    return(data2)
}

但是应用这个不起作用,因为我必须指定要调用初始函数的列。当然,仅仅从初始函数中删除“col”参数也不起作用。

任何想法如何应用这种论点?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    以下是使用dplyr 解决您的问题的可能解决方案(首先,因为这是您尝试过的),然后是data.tablebase R 解决方案:

    dplyr:

    cols <- lapply(names(data)[-(1:2)], as.name)
    names(cols) <- paste0(names(data)[-(1:2)], "_tot")
    data %>% group_by(region) %>% mutate_each_q(funs(sum), cols)
    

    假设每一列,但前两列是月度数据。逐行解释:

    1. 我们使用as.namelapply 来生成我们想要mutate 作为符号的列名列表
    2. 我们将我们想要的新名称(即 month_tot)赋予从 1 开始的符号列表。
    3. 我们使用mutate_each_q(在dplyr 0.3.0.2 中称为mutate_each_)将sum 应用于我们在1. 和2 中创建的表达式列表。

    这是(样本)结果:

    Source: local data frame [50 x 6]
    Groups: region
    
            biz   region june july june_tot july_tot
    1  shipping  mideast   17  124      780     3339
    2     telco americas   11  101      465     2901
    3     telco  mideast   27  131      780     3339
    4      tech americas   24  135      465     2901
    ... rows omitted
    

    数据表:

    new.names <- paste0(tail(names(data), 2L), "_tot")  # Make new names
    data.table(data)[,
      (new.names):=lapply(.SD, sum),    # `lapply` `sum` to the selected columns (those in .SD), and assign to `new.names` columns
      by=region, .SDcols=-1             # group by `region`, and exclude first column from `.SD` (note `region` is excluded as well by reason of being in `by`
    ][]                                 # extra `[]` just to force printing
    

    在这里,类似的逻辑,除了我们使用特殊的 .SD 对象来表示 data.table 中我们不分组的每一列。

    基础:

    do.call(
      cbind, 
      list(
        data, 
        setNames(
          lapply(data[-(1:2)], function(x) ave(x, data$region, FUN=sum)),
          paste0(names(data[-(1:2)]), "_tot")
    ) ) )
    

    这里我们使用ave 计算每个区域的总和,使用lapplyave 应用于每一列,并使用do.call(cbind, ...) 重构最终数据帧。

    【讨论】:

    • +1,不错的答案和有趣的方法。我希望这将以不同的方式实现,以便在将来的版本中可以轻松地在 mutate_each 中定义它。 (顺便说一句,在 dplyr 0.3.0.2 mutate_each_q 被替换为 mutate_each_
    • @beginneR,同意,这是我能想到的最好的。回复_q,我意识到了,但是我懒得升级 R 无法使用最新版本的dplyr,所以我坚持使用 0.2...
    • summarise_each_ 输出的更灵活命名似乎是dplyr 下一版本的主题,请参阅here
    • 抱歉,回复晚了,但方法 1 正是我想要的(虽然很高兴了解 data.table 和 base 选项)。不知道“mutate_each_q”的存在......
    【解决方案2】:

    试试:

    > for(i in 3:4) print(tapply(data[[i]], data$region, sum))
    americas  mideast 
         563      768 
    americas  mideast 
        2538     3802 
    

    如果需要,您可以在列表中获取所有输出。

    【讨论】:

      【解决方案3】:

      重组数据很有效。

      require(tidyr)
      # wide to long
      d2 <- gather(data = data,key = month,value = monthval,-c(biz,region))
      
      # get totals and rename month
      month_tots <- aggregate(x = list(total = d2$monthval),by = list(region = d2$region,month = d2$month),sum)
      month_tots$month <- paste0(month_tots$month,'_tot')
      
      # long to wide
      month_tots <- spread(data = month_tots,key = month,value = total)
      
      # recombine
      merge(data,month_tots,by = 'region',all.x = T)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-02
        • 2017-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-24
        • 1970-01-01
        • 2022-01-05
        相关资源
        最近更新 更多