7
Lets say I have a data table like this.

   customer_id time_stamp value
1:           1        223     4
2:           1        252     1
3:           1        456     3
4:           2        455     5
5:           2        632     2

そのため、customer_id と time_stamp が一緒になって一意のキーを形成します。「値」の前と最後の値を示す新しい列をいくつか追加したいと思います。つまり、このような出力が必要です。

  customer_id time_stamp value value_PREV value_NEXT
1:           1        223     4         NA          1
2:           1        252     1          4          3
3:           1        456     3          1         NA
4:           2        455     5         NA          2
5:           2        632     2          5         NA

これが高速で、まばらで不規則な時間で動作することを望みます。私は、data.table ローリング ジョインがそれをやってくれると思っていました。ただし、ローリング結合は、前回または同じ時間を検出するように見えます。そのため、(コピーの列名に _PREV を追加した後に) 同じテーブルの 2 つのコピーに対してローリング結合を行うと、うまくいきません。コピーの時間変数に小さな数字を追加することでそれをごまかすことができますが、これはちょっと厄介です。

ローリンジョインまたは他のdata.tableメソッドでこれを簡単に行う方法はありますか? 効率的な方法を見つけましたが、それでも約 40 行の R コードが必要です。ローリング ジョインに同じ時刻を含めずに最後の時刻を探すように指示できる場合、これはワンライナーになる可能性があるようです。あるいは、他に巧妙なトリックがあるかもしれません。

サンプルデータはこちら。

data=data.table(customer_id=c(1,2,1,1,2),time_stamp=c(252,632,456,223,455),value=c(1,2,3,4,5))
data_sorted=data[order(customer_id,time_stamp)]

これは私が書いたコードです。customer_id が異なる行に NA を入れる行は警告をスローし、おそらく変更する必要があることに注意してください。以下にコメントアウトしました。これらの2行を置き換えるための提案はありますか?

add_prev_next_cbind<-function(data,ident="customer_id",timecol="time_stamp",prev_tag="PREV",
                   next_tag="NEXT",sep="_"){
  o=order(data[[ident]],data[[timecol]])
  uo=order(o)
  data=data[o,]
  Nrow=nrow(data)
  Ncol=ncol(data)
  #shift it, put any junk in the first row
  data_prev=data[c(1,1:(Nrow-1)),]
  #shift it, put any junk in the last row
  data_next=data[c(2:(Nrow),Nrow),]
  #flag the rows where the identity changes, these get NA
  prev_diff=data[[ident]] != data_prev[[ident]]
  prev_diff[1]=T
  next_diff=data[[ident]] != data_next[[ident]]  
  next_diff[Nrow]=T
  #change names
  names=names(data)
  names_prev=paste(names,prev_tag,sep=sep)
  names_next=paste(names,next_tag,sep=sep)
  setnames(data_prev,names,names_prev)
  setnames(data_next,names,names_next)
  #put NA in rows where prev and next are from a different ident
  #replace the next two lines with something else
  #data_prev[prev_diff,]<-NA
  #data_next[next_diff,]<-NA
  data_all=cbind(data,data_prev,data_next)
  data_all=data_all[uo,]
  return(data_all)
}
4

2 に答える 2

9

更新:#965が1.9.5で実装されるようになりました。ニュースから:

  1. 新しい関数は、 vectorlistdata.frames、またはdata.tablesの高速をshift()実装します。「lag」(デフォルト)または「lead」のいずれかである引数を取り、常にリストを返すため、またはと一緒に使用すると非常に便利です。例:。詳細については、をご覧ください。lead/lagtype:=set()DT[, (cols) := shift(.SD, 1L), by=id]?shift

したがって、次のことができます。

dt[, c("value_PREV", "value_NEXT") := c(shift(value, 1L, type="lag"), 
                     shift(value, 1L, type="lead")), by=customer_id]

ここではロール参加はまったく必要ありません。headとでこれを行うことができますtaildata.tableあなたがDTであると仮定します:

setkey(DT, "customer_id")
DT[, list(time_stamp = time_stamp, 
          prev.val = c(NA, head(value, -1)), 
          next.val = c(tail(value, -1), NA)), 
by=customer_id]
#   customer_id time_stamp prev.val next.val
# 1:           1        223       NA        1
# 2:           1        252        4        3
# 3:           1        456        1       NA
# 4:           2        455       NA        2
# 5:           2        632        5       NA

編集:さらに良い:

DT[, `:=`(prev.val = c(NA, head(value, -1)), 
          next.val = c(tail(value, -1), NA)), 
          by=customer_id]
于 2013-03-20T20:24:14.013 に答える
6

はい、同等に一致させたくない場合はroll、double 型の場合は少しオフにするか、整数を使用して 1L を加算または減算します。

DT = data.table( customer_id=c(1,2,1,1,2), 
                 time_stamp=as.integer(c(252,632,456,223,455)),
                 value=c(1,2,3,4,5))
setkey(DT, customer_id, time_stamp)
DT[ DT[,list(customer_id,time_stamp+1L,value)], value_PREV:=i.value, roll=-Inf]
DT[ DT[,list(customer_id,time_stamp-1L,value)], value_NEXT:=i.value, roll=+Inf]
DT
   customer_id time_stamp value value_PREV value_NEXT
1:           1        223     4         NA          1
2:           1        252     1          4          3
3:           1        456     3          1         NA
4:           2        455     5         NA          2
5:           2        632     2          5         NA

DTそのように再び列のサブセットを取らなければならないのiは少し厄介です、私は同意します。

新しい引数を追加するためにFR#2628を提出しましたrollequal=TRUE|FALSE。次に、次のようになります。

setkey(DT, customer_id, time_stamp)
DT[ DT, value_PREV:=i.value, roll=-Inf, rollequal=FALSE]
DT[ DT, value_NEXT:=i.value, roll=+Inf, rollequal=FALSE]

列のコピーを回避し、 andiに割り当てる必要がないため、これも高速になります。time_stamp-1Ltime_stamp+1L

しかし、この場合、DTtoからの自己結合でDTあり、DTのキーは一意であるため、Arun が言うように、roll結合は必要ありません。c()速度のために、 andhead()またはのオーバーヘッドを回避するために、高速シフトまたはラグ関数が必要になる場合がありますtail()

強調してくれてありがとう!

于 2013-03-21T00:30:54.367 に答える