R data.table - 按列分组包括列表

Ran*_*ude 5 r data.table

我尝试在 R 中使用 data.table 包的 group by 函数。

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
Run Code Online (Sandbox Code Playgroud)

这工作正常。

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

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).
Run Code Online (Sandbox Code Playgroud)

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

预期成绩:

mean a: 2.6667
mean b: 3.75
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

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

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]
}
Run Code Online (Sandbox Code Playgroud)

到目前为止我最快的解决方案:

# 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')
Run Code Online (Sandbox Code Playgroud)

有机会让它更快吗?

akr*_*run 4

一种选择是按行序列进行分组,我们将unlist列list(“组”)、paste元素list放在一起(toString(..)),使用cSplitfrom splitstackshapewithdirection='long'将其重塑为“长”格式,然后使用mean“获取”值”列的grp' 作为分组变量。

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
Run Code Online (Sandbox Code Playgroud)

刚刚意识到使用的另一个选项splitstackshape是将listCol_lwhich unlistsalist列转换为长形式。由于输出是 a 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
Run Code Online (Sandbox Code Playgroud)

或者不使用的另一个选项是通过元素的splitstackshape复制数据集的行。这是一个方便的包装并且速度要快得多。然后,我们通过从“a”数据集中获取原始“组”来更改“组”列,并获取按“组”分组的“值”。lengthlistlengthssapply(group, length)unlistmean

 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
Run Code Online (Sandbox Code Playgroud)