繁体   English   中英

使用累积总和重塑data.table

[英]Reshaping data.table with cumulative sum

我想重塑data.table,并包含每个变量的历史(累计求和)信息。 No变量表示对象ID的测量的时间顺序。 在每次测量时,都会找到其他信息。 我想在每个时间戳No聚合对象ID的已知信息。

让我举个例子来证明:

对于以下data.table:

df <- data.table(ID=c(1,1,1,2,2,2,2),
                 No=c(1,2,3,1,2,3,4), 
                 Variable=c('a','b', 'a', 'c', 'a', 'a', 'b'),
                 Value=c(2,1,3,3,2,1,5))
df
   ID No Variable Value
1:  1  1        a     2
2:  1  2        b     1
3:  1  3        a     3
4:  2  1        c     3
5:  2  2        a     2
6:  2  3        a     1
7:  2  4        b     5

我想重塑它:

       ID No  a  b  c
    1:  1  1  2 NA NA
    2:  1  2  2  1 NA
    3:  1  3  5  1 NA
    4:  2  1 NA NA  3
    5:  2  2  2 NA  3
    6:  2  3  3 NA  3
    7:  2  4  3  5  3

所以Value的总和值,每个Variable乘以(ID, No) ,累计超过No

我可以在没有累积部分的情况下得到结果

dcast(df, ID+No~Variable, value.var="Value")

这导致非累积变体:

   ID No  a  b  c
1:  1  1  2 NA NA
2:  1  2 NA  1 NA
3:  1  3  3 NA NA
4:  2  1 NA NA  3
5:  2  2  2 NA NA
6:  2  3  1 NA NA
7:  2  4 NA  5 NA

任何想法如何使这累积? 原始data.table有超过250,000行,因此效率很重要。

编辑:我只是用a,b,c作为例子,原始文件有大约40个不同的级别。 此外, NA是重要的; 还有Value -values为0,这意味着除了NA其他东西

可能的解决方案

好的,所以我找到了一个有效的解决方案。 它远没有效率,因为它扩大了原始表格。

我们的想法是复制每一行TotalNo - No次,其中TotalNo是最大No每个ID 然后可以使用原始dcast函数来提取数据帧。 所以在代码中:

df[,TotalNo := .N, by=ID]
df2 <- df[rep(seq(nrow(df)), (df$TotalNo - df$No + 1))] #create duplicates
df3 <- df2[order(ID, No)]#, No:= seq_len(.N), by=.(ID, No)]
df3[,No:= seq(from=No[1], to=TotalNo[1], by=1), by=.(ID, No)]
df4<- dcast(df3, 
            formula = ID + No ~ Variable, 
            value.var = "Value", fill=NA, fun.aggregate = sum)

它不是很好,因为重复的创建使用更多的内存。 我认为它可以进一步优化,但到目前为止它适用于我的目的。 在示例代码中,它从7行到16行,在原始文件中从241,670行到高达978,331。 这超过了4倍。

解决方案 Eddi在完整数据集中改进了我的计算时间解决方案(Eddi的2.08秒,而我的4.36秒)。 这些是我可以使用的数字! 谢谢大家!

您的解决方案很好,但是您添加了太多行,如果您事先计算了cumsum ,那么这些行是不必要的:

# add useful columns
df[, TotalNo := .N, by = ID][, CumValue := cumsum(Value), by = .(ID, Variable)]

# do a rolling join to extend the missing values, and then dcast
dcast(df[df[, .(No = seq(No[1], TotalNo[1])), by = .(ID, Variable)],
         on = c('ID', 'Variable', 'No'), roll = TRUE],
      ID + No ~ Variable, value.var = 'CumValue')
#   ID No  a  b  c
#1:  1  1  2 NA NA
#2:  1  2  2  1 NA
#3:  1  3  5  1 NA
#4:  2  1 NA NA  3
#5:  2  2  2 NA  3
#6:  2  3  3 NA  3
#7:  2  4  3  5  3

这是一种标准方式:

library(zoo)

df[, cv := cumsum(Value), by = .(ID, Variable)]
DT   = dcast(df, ID + No ~ Variable, value.var="cv")

lvls = sort(unique(df$Variable))
DT[, (lvls) := lapply(.SD, na.locf, na.rm = FALSE), by=ID, .SDcols=lvls]


   ID No  a  b  c
1:  1  1  2 NA NA
2:  1  2  2  1 NA
3:  1  3  5  1 NA
4:  2  1 NA NA  3
5:  2  2  2 NA  3
6:  2  3  3 NA  3
7:  2  4  3  5  3

另一种方法是使用自定义构建的累积和函数。 这正是@David Arenburg评论中的方法,但在自定义累积汇总函数中替代。

编辑:使用@ eddi更有效的自定义累积和函数。

cumsum.na <- function(z){
 Reduce(function(x, y) if (is.na(x) && is.na(y)) NA else sum(x, y, na.rm = T), z, accumulate = T)
}

cols <- sort(unique(df$Variable))
res <- dcast(df, ID + No ~ Variable, value.var = "Value")[, (cols) := lapply(.SD, cumsum.na), .SDcols = cols, by = ID]
res

   ID No  a  b  c
1:  1  1  2 NA NA
2:  1  2  2  1 NA
3:  1  3  5  1 NA
4:  2  1 NA NA  3
5:  2  2  2 NA  3
6:  2  3  3 NA  3
7:  2  4  3  5  3

这绝对不是最有效的,但是它可以完成工作,并为您提供一个非常慢的非常缓慢的累积汇总函数,以您希望的方式处理NAs。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM