2016-08-17 9 views
6

data.tableの構文を使って、元のdata.tableの列と次の列の違いが各列に含まれているdata.tableを作成するにはどうすればよいですか?data.tableの列方向の違いを取る

例:等、私は、各行が基data.tableを有し、各列は1年後、年間0後の集団を生存され、2のような:

pop <- data.table(group_id = c(1, 2, 3), 
        N = c(4588L, 4589L, 4589L), 
        N_surv_1 = c(4213, 4243, 4264), 
        N_surv_2 = c(3703, 3766, 3820), 
        N_surv_3 = c(2953, 3054, 3159)) 
# group_id N N_surv_1 N_surv_2 N_surv_3 
#  1 4588  4213  3703  2953 
#  2 4589  4243  3766  3054 
#  3 4589  4264  3820  3159 

( Nが真の整数カウントとN_surv_1などは分数の可能性が予測されているため、データの種類が異なります)

私が行っているもの:。は、ベースdiffと転置行列を使用して、私たちすることができます:

diff <- data.table(t(diff(t(as.matrix(pop[,-1,with=FALSE]))))) 
setnames(diff, paste0("deaths_",1:ncol(diff))) 
cbind(group_id = pop[,group_id],diff) 
# produces desired output: 
# group_id deaths_1 deaths_2 deaths_3 
#   1  -375  -510  -750 
#   2  -346  -477  -712 
#   3  -325  -444  -661 

は私がmelt.data.tableによって作製した単列のグループによってベースdiffを使用することができることを知っているので、これは動作しますが、かなりではありません。

melt(pop, 
    id.vars = "group_id" 
    )[order(group_id)][, setNames(as.list(diff(value)), 
            paste0("deaths_",1:(ncol(pop)-2))), 
          keyby = group_id] 

であることを最もdata.table-riffic道へこれを行うか、それをdata.tableの複数列演算として行う方法がありますか?

答えて

6

さて、あなたはサブセットを引くことができます:

ncols = grep("^N(_surv_[0-9]+)?", names(pop), value=TRUE) 
pop[, Map(
    `-`, 
    utils:::tail.default(.SD, -1), 
    utils:::head.default(.SD, -1) 
), .SDcols=ncols] 

# N_surv_1 N_surv_2 N_surv_3 
# 1:  -375  -510  -750 
# 2:  -346  -477  -712 
# 3:  -325  -444  -661 

:=という新しい列にこれらの値を割り当てることができます。 tailheadがより簡単に利用できない理由がわかりません... @akrunが指摘したように、pop[, .SD[, -1, with=FALSE] - .SD[, -ncol(.SD), with=FALSE], .SDcols=ncols]のようにwith=FALSEを代わりに使用できます。

とにかく、これは単に整形に比べてかなり複雑です:

melt(pop, id="group_id")[, tail(value, -1) - head(value, -1), by=group_id] 
# group_id V1 
# 1:  1 -375 
# 2:  1 -510 
# 3:  1 -750 
# 4:  2 -346 
# 5:  2 -477 
# 6:  2 -712 
# 7:  3 -325 
# 8:  3 -444 
# 9:  3 -661 
2

一意のIDとデータおよび各行を整形しないと、あなたは、id列によってグループが、その後、すなわちunlist(.SD)、各行にdiffとの差を計算することができます

基本的に
pop[, setNames(as.list(diff(unlist(.SD))), paste0("deaths_", 1:(ncol(pop)-2))), group_id] 

# group_id deaths_1 deaths_2 deaths_3 
# 1:  1  -375  -510  -750 
# 2:  2  -346  -477  -712 
# 3:  3  -325  -444  -661 

、このような何かあなたの場合カラム名を設定無視:

pop[, as.list(diff(unlist(.SD))), group_id] 
2

はここより速くそれを作る可能性のある再成形またはグループ化せずにそれを行うための別の方法です。行数が少ない場合は、目立つ違いではないでしょう。

cols<-names(pop)[-1] 
combs<-list() 
for(i in 2:length(cols)) { 
    combs[[length(combs)+1]]<-c(cols[i-1], cols[i]) 
} 
newnames<-sapply(combs,function(x) gsub('N_surv','death',x[2])) 
deathpop<-copy(pop) 
deathpop[,(newnames):=lapply(combs,function(x) get(x[2])-get(x[1]))] 
deathpop[,(cols):=NULL] 

私はいくつかのベンチマークに

rows<-10000000 
pop <- data.table(group_id = 1:rows, 
        N = runif(rows,3000,4000), 
        N_surv_1 = runif(rows,3000,4000), 
        N_surv_2 = runif(rows,3000,4000), 
        N_surv_3 = runif(rows,3000,4000)) 
system.time({ 
    cols<-names(pop)[-1] 
    combs<-list() 
    for(i in 2:length(cols)) { 
     combs[[length(combs)+1]]<-c(cols[i-1], cols[i]) 
    } 
    newnames<-sapply(combs,function(x) gsub('N_surv','death',x[2])) 
    deathpop<-copy(pop) 
    deathpop[,(newnames):=lapply(combs,function(x) get(x[2])-get(x[1]))] 
    deathpop[,(cols):=NULL]}) 

を行なったし、それは私が

system.time(pop[, as.list(diff(unlist(.SD))), group_id]) 

を行なったし、それが

0を返さこれとは対照的に

user system elapsed 
0.192 0.808 1.003 

を返さ

user system elapsed 
169.836 0.428 170.469 

私も

system.time(melt(pop, id="group_id")[, tail(value, -1) - head(value, -1), by=group_id]) 

戻り

user system elapsed 
223.360 1.736 225.315 
をやって、最後に

user system elapsed 
0.044 0.044 0.089 

を返さ

system.time({ 
    ncols = grep("^N(_surv_[0-9]+)?", names(pop), value=TRUE) 
    pop[, Map(
    `-`, 
    utils:::tail.default(.SD, -1), 
    utils:::head.default(.SD, -1) 
), .SDcols=ncols] 
}) 

をしました

フランクのMapのソリューションが最速です。あなたが私のコピーを取るなら、それはフランクの時間にかなり近くなりますが、彼はまだこのテストケースのために勝ちます。

+0

興味深いことに、溶融は遅いですが、あまりにも驚くことではないと思います。私はまだデータを格納する "正しい"方法としてそれを主張しています。つまり、各グループの集団測定のシーケンスをキャプチャする「時間」列を持つ長い形式でなければなりません。私はakrunの解を推測しています(私の答えで見つけた: 'pop [、.SD [、-1、= FALSE] - .SD [、-ncol(.SD)、= FALSE]、.SDcols = ncols)' )もかなり速いです。 – Frank