2016-05-24 4 views
5

私が試していることを行うための標準的な方法を探しましたが、早くてエレガントな作業を少ししかしていないようです。要するに、私は複数の値の列を持つ大きなテーブルを持っており、それぞれにルックアップテーブルの対応する要素を乗算したい。ルックアップ値を乗算したい列を動的に渡す方法や、基本的な式の外でルックアップ値を参照する方法を理解できません。高速のdata.table検索でグループごとに複数の列を割り当てます

これは私の例ですが、10の値の列を持つ300万行を設定しましたが、これはあまり時間がかかりすぎず、データサイズを多少表しています(これはもっと大きなループの一部として実装されます)したがって、パフォーマンスに重点を置いています)。 6つのレベルのルックアップテーブルと、value_1のvalue_10カラムに対応するいくつかの揃えられた乗数もあります。

library(data.table) 

setsize <- 3000000 
value_num <- 10 
factors <- c("factor_a", "factor_b", "factor_c", "factor_d", "factor_e", "factor_f") 
random <- data.table(replicate(10, sample(factors, size = setsize, replace = T)) 
        , replicate(10, rnorm(setsize, mean = 700, sd = 50))) 
lookup <- data.table("V1" = factors, replicate(10, seq(.90, 1.5, length.out = length(factors)))) 
wps <- paste("value", c(1:10), sep = "_") 
names(random)[11:20] <- wps 
names(lookup)[2:11] <- wps 
setkeyv(random, "V1") 
setkeyv(lookup, "V1") 

解決方法1:それはかなり速いですが、私は一般的にi.value_1のようなI-列を参照する方法を見つけ出すことができないので、私はループにそれらを渡すことができますかいっそ一度にすべてを適用します。

f <- function() { 
    random[lookup, value_1 := value_1 * i.value_1, by = .EACHI] 
    random[lookup, value_2 := value_2 * i.value_2, by = .EACHI] 
    random[lookup, value_3 := value_3 * i.value_3, by = .EACHI] 
    random[lookup, value_4 := value_4 * i.value_4, by = .EACHI] 
    random[lookup, value_5 := value_5 * i.value_5, by = .EACHI] 
    random[lookup, value_6 := value_6 * i.value_6, by = .EACHI] 
    random[lookup, value_7 := value_7 * i.value_7, by = .EACHI] 
    random[lookup, value_8 := value_8 * i.value_8, by = .EACHI] 
    random[lookup, value_9 := value_9 * i.value_9, by = .EACHI] 
    random[lookup, value_10 := value_10 * i.value_10, by = .EACHI] 
} 

system.time(f()) 

    user system elapsed 
    0.184 0.000 0.181 

解決策2:私は解決策1は一般的なことを得ることができなかった後、私はset()ベースのアプローチを試してみました。しかし、私が文字列wpsの目標値列を指定できるようにしたにもかかわらず、それは実際には上記よりはるかに遅いです。私はそれを間違って使用していることを知っているが、すべての[.data.tableオーバーヘッドを削除するために改善する方法がわからない。

idx_groups <- random[,.(rowstart = min(.I), rowend = max(.I)), by = key(random)][lookup] 
system.time(
for (i in 1:nrow(idx_groups)){ 
    rows <- idx_groups[["rowstart"]][i]:idx_groups[["rowend"]][i] 
    for (j in wps) { 
    set(random, i=rows, j=j, value= random[rows][[j]] * idx_groups[[j]][i]) 
    } 
}) 

    user system elapsed 
    3.940 0.024 3.967 

これらの操作をよりうまく構成する方法についてのアドバイスをいただければ幸いです。

編集:私はこの質問を投稿する前に、この明白な解決策を試して失敗のために自分で非常にイライラしてる:

system.time(
for (col in wps){ 
    random[lookup, (col) := list(get(col) * get(paste0("i.", col))), by = .EACHI, with = F] 
}) 

    user system elapsed 
    1.600 0.048 1.652 

私は相対速度でやりたいようです。しかし、それはまだ上記の最初のソリューション(私は確信しているので、get()を繰り返している)よりも10倍遅いので、私はまだアドバイスを受けています。

編集2:get()eval(parse(text=col))に置き換えると、このトリックが完了したようです。

system.time(
for (col in wps){ 
    random[lookup, (col) := list(eval(parse(text=col)) * eval(parse(text=paste0("i.", col)))), by = .EACHI, with = F] 
}) 
    user system elapsed 
    0.184 0.000 0.185 

編集3:いくつかの良い回答が提供されています。ラファエルのソリューションはおそらく一般的なケースでは最高ですが、私はJangoreckiが推奨するコール構築から数ミリ秒も短く絞ったヘルパー機能と引き換えに絞ることができます。私はそれを返事としてマークしました、皆の助けをありがとう。

+0

私が代わりに ''はeval(パース(...)のmget'を使用して推測します) 'は同じ結果を達成するはずです(しかしテストしませんでした)。あなた自身があなたの質問に答えていれば、正しい解決策を「回答」(編集ではなく)として投稿してください。 THX :-) –

+1

この質問は役に立ちます:https://stackoverflow.com/questions/30468455/dynamically-build-call-for-lookup-multiple-columns-そこから最新のソリューションを試すことができます'get'の構文解析フィールドと実体化フィールドを避けるため、最も効率的です。現在提供されているより速く/より良い解決策を見つけたら、自問自答してください。 – jangorecki

+0

Rヨーダ、残念ながら 'mget'は' eval(parse(...)) 'のように私にとってはうまくいきません。ジャンゴレッキー、リンクありがとう!私はあなたの最後の解決策が私が見たことが最も速いと思います。そして、J表現を検査する能力は、何が起こっているかについてもう少し直観的になります。私はこの質問の答えとして私のバージョンを掲示します。 – etrippler

答えて

4

ます。またlapplyを使用することができます。

cols <- noquote(paste0("value_",1:10)) 

random[lookup, (cols) := lapply (cols, function(x) get(x) * get(paste0("i.", x))), by = .EACHI ] 

データセットが大きすぎると、あなたの操作のプログレスバーを表示したい場合は、あなたがpblapplyを使用することができますへ

library(pbapply) 

random[lookup, (cols) := pblapply(cols, function(x) get(x) * get(paste0("i.", x))), by = .EACHI ] 
+0

これはコールの構築より数ミリ秒遅く、ヘルパー機能の必要性を避けるため、答えに感謝します。なぜあなたは 'with = F'を含むのか不思議ですが、それは必要ではないように思えますし、タイミングにも影響しません。 – etrippler

+0

@etrippler、私は助けてうれしいです。あなたは正しいですが、 'with = F'を使う必要はありません。 [使用方法についての説明はこちら](https://rawgit.com/wiki/Rdatatable/data.table/vignettes/datatable-intro.html) –

+1

ここで 'Map'を使用する方がはるかに良いです:'(cols) := icols = paste0( "i。"、cols) '=マップ(\' * \ '、mget(cols)、mget(icols))' – Arun

2

感謝をjangoreckiは答えを指摘するためにhereです。これはヘルパー関数を使ってJ式を動的に構築し、すべてを一度に評価します。これは、解析/取得のオーバーヘッドを回避し、私が得ようとしている最速のソリューションと思われます。また、呼び出される関数を手動で指定する機能(ある場合は、*の代わりに/が必要な場合があります)を評価して評価する前にJ式を調べることができます。

batch.lookup = function(x) { 
    as.call(list(as.name(":="),x 
       ,as.call(c(
       list(as.name("list")), 
       sapply(x, function(x) call("*", as.name(x), as.name(paste0("i.",x))), simplify=FALSE) 
       )) 
)) 
} 

print(batch.lookup(wps)) 

`:=`(c("value_1", "value_2", "value_3", "value_4", "value_5", 
"value_6", "value_7", "value_8", "value_9", "value_10"), list(value_1 = value_1 * 
    i.value_1, value_2 = value_2 * i.value_2, value_3 = value_3 * 
    i.value_3, value_4 = value_4 * i.value_4, value_5 = value_5 * 
    i.value_5, value_6 = value_6 * i.value_6, value_7 = value_7 * 
    i.value_7, value_8 = value_8 * i.value_8, value_9 = value_9 * 
    i.value_9, value_10 = value_10 * i.value_10)) 

system.time(
    random[lookup, eval(batch.lookup(wps)), by = .EACHI]) 

    user system elapsed 
    0.14 0.04 0.18 
3

これは、テキストの構文解析に比べて約2倍遅い/建設を呼び出しますが、もう少し読みやすいです:

random[lookup, (wps) := Map('*', mget(wps), mget(paste0('i.', wps))), by = .EACHI] 
関連する問題