2016-05-03 6 views
6

私はdata.tableの形を変え、各変数の歴史的な(累積合計)情報を入れたいと思っています。 No変数は、オブジェクトIDの測定の時系列を示します。各測定において、追加情報が見出される。私はオブジェクトのIDの各タイムスタンプNoで既知の情報を集約したいと思います。累計でdata.tableを作り直す

私は例を示しましょう:以下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である。

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 

任意のアイデアをどのようにこれは累積的にするために:

私は非累積バリアントになり

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

を行うことによって、累積一部せずに結果を得ることができますか?元のdata.tableには25万行以上のデータが格納されているため、効率が重要です。

EDIT:例としてa、b、cを使用しました。オリジナルファイルのレベルは約40種類です。さらに、NAが重要です。そこ

オーケーNA

POSSIBLE SOLUTION以外の何かを意味0のValue - 値は、またしているので、私は実用的なソリューションを見つけました。それは元のテーブルを拡大するので効率的ではありません。

各行をTotalNo - No回複製することです。TotalNoNo/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倍以上の大きさです。

SOLUTION エディは、完全なデータセット(鉱山の4.36秒対エディの2.08秒)で計算時間で私の解決策を改善しました。それらは私が一緒に働くことができる数です!みんなありがとう!ここで

+1

あなたが潜在的に '行う可能性をcolsの< - ソート(ユニーク(DF $変数) ); res = IDL、ID = 0、ID = 0、ID = 0、id = 0、id =あなたが 'NA'の代わりにゼロを持つ気にならない場合 –

+0

ゼロはオプションではありません。 NAとは異なる意味を持つ0の値もある。しかし、寄付いただきありがとうございます! –

答えて

3

あなたのソリューションは良いですが、あなたは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 
2

は、標準的な方法です:

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 
1

それを行うための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にあなたがしたい方法を扱う確かに非常に遅い非常に遅い累積要約機能を提供します。

+1

これは狂って遅くなるでしょう - あなたの関数の中にダブルループがあります – eddi

+0

コメントありがとうございますが、これは私に予想される出力を与えていません...私は 'cumsum.na < - function(z){Reduce (関数x、y、na.rm = T)、z、accumulate = T)} 'となり、列" c "の2行目と3行目にはNAsの代わりに0が得られます。 –

+0

true、それは正しく動作しません - 次に、2番目のループを追加するのではなく、最初の非NA値またはsmthを追跡するためのインジケータを追加します(2番目のループは 'all(is.na (x [1:i])) ') – eddi