2012-03-23 21 views
11

data.tableの行を「ループスルー」して各行の平均を計算します。平均値は、以下のメカニズムに基づいて計算されるべきである:私は(ID(i))を一列に識別子IDをルックアップ条件付き平均を計算するための "ループスルー" data.table

  1. 行におけるT2の値I(T2(I))をルックアップ
  2. これら2つの基準を満たすすべての行jData1値の平均を計算する:ID(j) = ID(i)
  3. T1(j) = T2(i)行の列データ2で算出した平均を入力I

    DF = data.frame(ID=rep(c("a","b"),each=6), 
          T1=rep(1:2,each=3), T2=c(1,2,3), Data1=c(1:12)) 
    DT = data.table(DF) 
    DT[ , Data2:=NA_real_] 
        ID T1 T2 Data1 Data2 
    [1,] a 1 1  1 NA 
    [2,] a 1 2  2 NA 
    [3,] a 1 3  3 NA 
    [4,] a 2 1  4 NA 
    [5,] a 2 2  5 NA 
    [6,] a 2 3  6 NA 
    [7,] b 1 1  7 NA 
    [8,] b 1 2  8 NA 
    [9,] b 1 3  9 NA 
    [10,] b 2 1 10 NA 
    [11,] b 2 2 11 NA 
    [12,] b 2 3 12 NA 
    

この単純な例の場合、結果は次のようになります。

 ID T1 T2 Data1 Data2 
[1,] a 1 1  1 2 
[2,] a 1 2  2 5 
[3,] a 1 3  3 NA 
[4,] a 2 1  4 2 
[5,] a 2 2  5 5 
[6,] a 2 3  6 NA 
[7,] b 1 1  7 8 
[8,] b 1 2  8 11 
[9,] b 1 3  9 NA 
[10,] b 2 1 10 8 
[11,] b 2 2 11 11 
[12,] b 2 3 12 NA 

私はこれを行う方法の1つは、行をループすることだと思うが、私はそれは非効率的だと思います。私はapply()の機能を見てきましたが、私の問題を解決できるかどうかは確信しています。これがはるかに効率的またははるかに容易になるならばdata.tableの代わりにdata.frameを使用することもできます。実際のデータセットには約100万行が含まれています。

+2

書かれた仕様では操作が難しいように見えますが、あなたの例では、各IDグループ内で、T2がT1の値の範囲内にある値のグループの手段が必要であることが示唆されています。しかし、2行目のData2がなぜ5であるべきかを理解しようとすると、その解釈さえもばらばらになります。 –

+0

@DWinそれは平均化が 'Data1'列で行われるからです。 '5は(4、5、6)の平均値なので、' Data2 [2] 'は5になります。 – ulidtko

答えて

10

経験則は、最初に集計してから結合することです。

agg = DT[,mean(Data1),by=list(ID,T1)] 
setkey(agg,ID,T1) 
DT[,Data2:={JT=J(ID,T2);agg[JT,V1][[3]]}] 
     ID T1 T2 Data1 Data2 
[1,] a 1 1  1  2 
[2,] a 1 2  2  5 
[3,] a 1 3  3 NA 
[4,] a 2 1  4  2 
[5,] a 2 2  5  5 
[6,] a 2 3  6 NA 
[7,] b 1 1  7  8 
[8,] b 1 2  8 11 
[9,] b 1 3  9 NA 
[10,] b 2 1 10  8 
[11,] b 2 2 11 11 
[12,] b 2 3 12 NA 

ご覧のとおり、この場合は少し醜いですが(速いでしょう)。 [[3]]ビットを回避しますdropを追加する計画だと多分私達はIDは両方aggであるため、ここでは必要とされているJT=ビットを避けることになる(つまり、何も自己が参加していない)範囲を呼び出すにiを評価するために[.data.tableを指示する方法を提供することができおよびDT

keybyがR-Forgeのv1.8.0に追加されましたので、setkeyの必要はありません。

+0

ありがとう、マシュー。これは信じられないほど高速です。カラム名に関する混乱を避けるために、 'agg'の' V1'カラムを作成するときにカスタム名を与える権利がありますか? – Cake

+1

'DT [、list(myname = mean(Data1))、by = list(ID、T1)]'を試してください。この場合のさらなる高速化のために、[data.table wiki](http://rwiki.sciviews.org/doku.php?id=packages:cran:data.table)ポイント3を参照してください。 –

+0

私はあなたの3行目を 'DT [、Data2:= {agg [J(ID、T2)] [[3]]}]'に置き換え、同じ結果を得ました。すなわち、I **は 'JT ='ビット( '、V1'でも同様)を避けました。どちらか悪い練習ですか? –

2

ローを反復する代わりに、やや高速な方法が、ベクトル化を使用するソリューションになります。

R> d <- data.frame(ID=rep(c("a","b"),each=6), T1=rep(1:2,each=3), T2=c(1,2,3), Data1=c(1:12)) 
R> d 
    ID T1 T2 Data1 
1 a 1 1  1 
2 a 1 2  2 
3 a 1 3  3 
4 a 2 1  4 
5 a 2 2  5 
6 a 2 3  6 
7 b 1 1  7 
8 b 1 2  8 
9 b 1 3  9 
10 b 2 1 10 
11 b 2 2 11 
12 b 2 3 12 

R> rowfunction <- function(i) with(d, mean(Data1[which(T1==T2[i] & ID==ID[i])])) 
R> d$Data2 <- sapply(1:nrow(d), rowfunction) 
R> d 
    ID T1 T2 Data1 Data2 
1 a 1 1  1  2 
2 a 1 2  2  5 
3 a 1 3  3 NaN 
4 a 2 1  4  2 
5 a 2 2  5  5 
6 a 2 3  6 NaN 
7 b 1 1  7  8 
8 b 1 2  8 11 
9 b 1 3  9 NaN 
10 b 2 1 10  8 
11 b 2 2 11 11 
12 b 2 3 12 NaN 

また、私はR.すなわちにそれを取得する前に、データの前処理にを好みますSQLサーバーからデータを取得している場合は、サーバーで平均を計算させる方が良いでしょう。

Rは、いくつかの理由から、実際にはナンバークランチングであまり良くありません。しかし、すでに処理されたデータに対して統計を行うときは優れています。 tapply及び他の最近の記事の一部を使用して

1

DF = data.frame(ID=rep(c("a","b"),each=6), T1=rep(1:2,each=3), T2=c(1,2,3), Data1=c(1:12)) 

EDIT:実際には、元の関数のほとんどは冗長であり、他の何かのために意図されていました。ここでは簡略化されました:

ansMat <- tapply(DF$Data1, DF[, c("ID", "T1")], mean) 

i <- cbind(match(DF$ID, rownames(ansMat)), match(DF$T2, colnames(ansMat))) 

DF<-cbind(DF,Data2 = ansMat[i]) 


# ansMat<-tapply(seq_len(nrow(DF)), DF[, c("ID", "T1")], function(x) { 
# curSub <- DF[x, ] 
# myIndex <- which(DF$T2 == curSub$T1 & DF$ID == curSub$ID) 
# meanData1 <- mean(curSub$Data1) 
# return(meanData1 = meanData1) 
# }) 

IDとT2の代わりにIDとT1にトリックが行われました。スピード感は何ですか?