【问题标题】:Reshaping data.table with cumulative sum用累积和重塑 data.table
【发布时间】:2016-08-28 12:11:57
【问题描述】:

我想重塑一个 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 个不同的级别。此外,NAs 很重要;还有Value-values 为0,这意味着不是NA

可能的解决方案

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

这个想法是复制每一行TotalNo - No 次,其中TotalNo 是每个ID 的最大值No。然后可以使用原始的 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 秒)。这些是我可以使用的数字!谢谢大家!

【问题讨论】:

  • 如果你不介意用零代替NAs,你可以使用cols &lt;- sort(unique(df$Variable)) ; res &lt;- dcast(df, ID + No ~ Variable, value.var = "Value", fill = 0L)[, (cols) := lapply(.SD, cumsum), .SDcols = cols, by = ID]
  • 零不是一个选项;还有一些值 0 与 NA 具有不同的含义。感谢您的贡献!

标签: r data.table


【解决方案1】:

另一种方法是使用自定义构建的累积和函数。这正是@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

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

【讨论】:

  • 这会非常慢 - 你的函数中有一个双循环
  • 感谢您的评论,但这并没有给我预期的输出...我尝试用 cumsum.na &lt;- function(z) {Reduce(function(x, y) sum(x, y, na.rm = T), z, accumulate = T)} 替换,我在第 2 行和第 3 行中得到 0 而不是 NA 列“c” .
  • true,它不能正常工作 - 然后添加一个指标来跟踪循环中的第一个非 NA 值或 smth,而不是添加第二个循环(只是为了澄清 - 第二个循环是all(is.na(x[1:i])))
  • Reduce 的修复是Reduce(function(x, y) if (is.na(x) &amp;&amp; is.na(y)) NA else sum(x, y, na.rm = T), your_vec, accumulate = T)
  • 你还有另一个内部循环 - sum(x[1:i])
【解决方案2】:

您的解决方案很好,但您添加了太多行,如果您事先计算 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

【讨论】:

    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 2016-02-19
      • 2020-09-20
      • 1970-01-01
      • 2018-08-07
      • 1970-01-01
      • 1970-01-01
      • 2019-01-10
      • 1970-01-01
      • 2019-02-28
      相关资源
      最近更新 更多