[英]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.