【问题标题】:R data.table - group by column includes listR data.table - 按列分组包括列表
【发布时间】:2015-08-22 11:19:48
【问题描述】:

我尝试使用 R 中 data.table 包的分组功能。

start <- as.Date('2014-1-1')
end <- as.Date('2014-1-6')
time.span <- seq(start, end, "days")
a <- data.table(date = time.span, value=c(1,2,3,4,5,6), group=c('a','a','b','b','a','b'))

        date  value group
1   2014-01-01  1   a
2   2014-01-02  2   a
3   2014-01-03  3   b
4   2014-01-04  4   b
5   2014-01-05  5   a
6   2014-01-06  6   b

a[,mean(value),by=group]
> group      V1
 1:   a    2.6667
 2:   b    4.3333

这很好用。

由于我正在使用 Dates,因此可能会发生特殊日期不仅有一个组,而且有两个组。

a <- data.table(date = time.span, value=c(1,2,3,4,5,6), group=list('a',c('a','b'),'b','b','a','b'))

        date   value  group
1   2014-01-01  1   a
2   2014-01-02  2   c("a", "b")
3   2014-01-03  3   b
4   2014-01-04  4   b
5   2014-01-05  5   a
6   2014-01-06  6   b

a[,mean(value),by=group]
> Error in `[.data.table`(a, , mean(value), by = group) : 
  The items in the 'by' or 'keyby' list are length (1,2,1,1,1,1). Each must be same length as rows in x or number of rows returned by i (6).

我希望将两组的分组日期用于计算 a 组和 b 组的平均值。

预期结果:

mean a: 2.6667
mean b: 3.75

data.table 包可以实现吗?

更新

感谢 akrun,我最初的问题已解决。在“拆分”data.table 并在我的情况下计算不同的因素(基于组)之后,我需要将 data.table 恢复为“原始”形式,并根据日期使用唯一的行。到目前为止我的解决方案:

a <- data.table(date = time.span, value=c(1,2,3,4,5,6), group=list('a',c('a','b'),'b','b','a','b'))
b <- a[rep(1:nrow(a), lengths(group))][, group:=unlist(a$group)]

       date   value  group
1   2014-01-01  1   a
2   2014-01-02  2   a
3   2014-01-02  2   b
4   2014-01-03  3   b
5   2014-01-04  4   b
6   2014-01-05  5   a
7   2014-01-06  6   b

# creates new column with mean based on group
b[,factor := mean(value), by=group] 

#creates new data.table c without duplicate rows (based on date) + if a row has group a & b it creates the product of their factors
c <- b[,.(value = unique(value), group = list(group), factor = prod(factor)),by=date]

date     value  group       factor
01/01/14    1   a           2.666666667
02/01/14    2   c("a", "b") 10
03/01/14    3   b           3.75
04/01/14    4   b           3.75
05/01/14    5   a           2.666666667
06/01/14    6   b           3.75

我想这不是完美的方法,但它确实有效。有什么建议可以让我做得更好吗?

替代解决方案(真的很慢!!!):

d <- a[rep(1:nrow(a), lengths(group))][,group:=unlist(a$group)][, mean(value), by = group]
for(i in 1:NROW(a)){
   y1 <- 1
   for(j in a[i,group][[1]]){
       y1 <- y1 * d[group==j, V1]
   }
   a[i, factor := y1]
}

迄今为止我最快的解决方案:

# split rows that more than one group
b <- a[rep(1:nrow(a), lengths(group))][, group:=unlist(a$group)]
# calculate mean of different groups
b <- b[,factor := mean(value), by=group]
# only keep date + factor columns
b <- b[,.(date, factor)]
# summarise rows by date 
b <- b[,lapply(.SD,prod), by=date]
# add summarised factor column to initial data.table
c <- merge(a,b,by='date')

有没有机会让它更快?

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    一种选择是按行序列分组,我们将unlistlist 列('group')、pastelist 元素放在一起(toString(..)),使用来自@ 的cSplit 987654327@ 和direction='long' 将其重塑为'long'格式,然后使用'grp'作为分组变量获取'value'列的mean

    library(data.table)
    library(splitstackshape)
    a[, grp:= toString(unlist(group)), 1:nrow(a)]
    cSplit(a, 'grp', ', ', 'long')[, mean(value), grp]
    #  grp       V1
    #1:   a 2.666667
    #2:   b 3.750000
    

    刚刚意识到使用splitstackshape 的另一个选项是listCol_l,其中unlists 将list 列转换为长格式。由于输出是data.table,我们可以使用data.table 方法来计算mean。获取mean 更加紧凑。

     listCol_l(a, 'group')[, mean(value), group_ul]
     #  group_ul       V1
     #1:        a 2.666667
     #2:        b 3.750000
    

    或者不使用splitstackshape 的另一个选项是通过list 元素的length 复制数据集的行。 lengthssapply(group, length) 的便捷包装器,并且速度更快。然后,我们通过unlisting 'a' 数据集中的原始'group' 更改'group' 列,得到'value' 的mean,按'group' 分组。

     a[rep(1:nrow(a), lengths(group))][,
            group:=unlist(a$group)][, mean(value), by = group]
     #  group       V1
     #1:     a 2.666667
     #2:     b 3.750000
    

    【讨论】:

    • 太棒了!据我了解,“cSplit”将具有两组的行拆分为两个相同的行(一个用于第一组,一个用于第二组),然后我们可以轻松地使用普通的 data.table 函数来计算平均值()。真的很棒的解决方案@akrun - 我对这个“splitstackshape”包一无所知......
    • @RandomDude 是的,它将具有多个元素的行拆分为单独的行。感谢您的反馈。
    • 在哪里可以找到“长度”功能?无法通过谷歌弄清楚,根据我的 R Studio 版本它不可用......
    • @RandomDude 这是一个base R 函数。我认为它是在 R 3.1.2 左右引入的。如果你有更早的版本,可以换成sapply(group, length)
    【解决方案2】:

    @mike-h 在this question 中发布的较短解决方案也使用unlist(),但按其余列分组:

    require(data.table)
    
    a = data.table(date = time.span,
                   value = c(1,2,3,4,5,6),
                   group = list('a',c('a','b'),'b','b','a','b'))
    
    a[ , .(group = unlist(group)), .(date, value)][ , mean(value), group ]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多