23

与えられたdata.tableデータ:

dat <- data.table(x_one=1:10, x_two=1:10, y_one=1:10, y_two=1:10) 

「ルート」名を付けて、2つの同じような行の間に式を作成する関数が必要x_one - x_twoです。

myfun <- function(name) {
  one <- paste0(name, '_one')
  two <- paste0(name, '_two')

  parse(text=paste(one, '-', two))
}

これで、ルート名を1つだけ使用すると期待どおりに機能し、結果としてベクトルになります。

dat[, eval(myfun('x')),]

[1] 0 0 0 0 0 0 0 0 0 0

listただし、この手法を使用してその出力に名前を割り当てようとすると失敗します。

dat[, list(x_out = eval(myfun('x'))),]

Error in eval(expr, envir, enclos) : object 'x_one' not found

これを追加することで「解決」できますwith(dat, ...)が、data.tableっぽいようには見えません。

dat[, list(x_out = with(dat, eval(myfun('x'))),
           y_out = with(dat, eval(myfun('y')))),]

    x_out y_out
 1:     0     0
 2:     0     0
 3:     0     0
 4:     0     0
 5:     0     0
 6:     0     0
 7:     0     0
 8:     0     0
 9:     0     0
10:     0     0

上記のような出力が必要な場合、これらの式を生成して評価する適切な方法は何ですか?

それが役立つ場合、sessionInfo()出力は以下のとおりです。私はこれ、またはそれに近い何かを行うことができたことを思い出しますが、それはしばらくの間であり、それdata.table以来更新されています...

R version 2.15.1 (2012-06-22)

Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=C                 LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] graphics  grDevices utils     datasets  stats     grid      methods   base     

other attached packages:
 [1] Cairo_1.5-1      zoo_1.7-7        stringr_0.6.1    doMC_1.2.5       multicore_0.1-7  iterators_1.0.6  foreach_1.4.0   
 [8] data.table_1.8.2 circular_0.4-3   boot_1.3-5       ggplot2_0.9.1    reshape2_1.2.1   plyr_1.7.1      

loaded via a namespace (and not attached):
 [1] codetools_0.2-8    colorspace_1.1-1   dichromat_1.2-4    digest_0.5.2       labeling_0.1       lattice_0.20-6    
 [7] MASS_7.3-20        memoise_0.1        munsell_0.3        proto_0.3-9.2      RColorBrewer_1.0-5 scales_0.2.1      
[13] tools_2.15.1      
4

2 に答える 2

19

1つの解決策はlist(...)、関数出力内に配置することです。

as.quoted私は、@hadleyがパッケージに実装する方法から盗んで.()使用する傾向がありplyrます。

library(data.table)
library(plyr)
dat <- data.table(x_one=1:10, x_two=1:10, y_one=1:10, y_two=1:10) 
myfun <- function(name) {
  one <- paste0(name, '_one')
  two <- paste0(name, '_two')
  out <- paste0(name,'_out')
 as.quoted(paste('list(',out, '=',one, '-', two,')'))[[1]]
}


dat[, eval(myfun('x')),]

#    x_out
# 1:     0
# 2:     0
# 3:     0
# 4:     0
# 5:     0
# 6:     0
# 7:     0
# 8:     0
# 9:     0
#10:     0

一度に2つの列を実行するには、通話を調整できます

myfun <- function(name) {
  one <- paste0(name, '_one')
  two <- paste0(name, '_two')
  out <- paste0(name,'_out')
  calls <- paste(paste(out, '=', one, '-',two), collapse = ',')


 as.quoted(paste('list(', calls,')'))[[1]]
}


dat[, eval(myfun(c('x','y'))),]

#   x_out y_out
# 1:     0     0
# 2:     0     0
# 3:     0     0
# 4:     0     0
# 5:     0     0
# 6:     0     0
# 7:     0     0
# 8:     0     0
# 9:     0     0
# 0:     0     0

理由は……。

このソリューションでは、'への呼び出し全体list(..)がdata.tableであるparent.frame内で評価されます。

内の関連するコード[.data.table

if (missing(j)) stop("logical error, j missing")
jsub = substitute(j)
if (is.null(jsub)) return(NULL)
jsubl = as.list.default(jsub)
if (identical(jsubl[[1L]],quote(eval))) {
    jsub = eval(jsubl[[2L]],parent.frame())
    if (is.expression(jsub)) jsub = jsub[[1L]]
}

if(あなたの場合)

j = list(xout = eval(myfun('x'))) 

##then

jsub <- substitute(j) 

 #  list(xout = eval(myfun("x")))

as.list.default(jsub)
## [[1]]
## list
## 
## $xout
## eval(myfun("x"))

そうjsubl[[1L]]ですlistjsubl[[2L]]ですeval(myfun("x"))

そのためdata.table、への呼び出しが見つからずeval、適切に処理されません。

これは機能し、正しいdata.table内で2番目の評価を強制します

# using OP myfun
dat[,list(xout =eval(myfun('x'), dat))]

同じ方法

eval(parse(text = 'x_one'),dat)
# [1]  1  2  3  4  5  6  7  8  9 10

動作しますが

 eval(eval(parse(text = 'x_one')), dat)

ではない

2013年10月4日編集

.SD環境として使用する方がおそらく安全ですが(ただし低速です)、その場合は堅牢であるか、iまたはby同様に堅牢になります。

dat[,list(xout =eval(myfun('x'), .SD))]

マシューから編集:

+10以上。私はそれを自分でもっとよく説明することはできませんでした。さらに一歩進んで、私が時々行うことは、 data.tableクエリ全体evalを構築してからそれを構築することです。そうすれば、場合によってはもう少し堅牢になることがあります。SQLのように思います。つまり、実行するSQLサーバーに送信される動的SQLステートメントを作成することがよくあります。デバッグしているときも、構築されたクエリを確認して、ブラウザのプロンプトで実行する方が簡単な場合があります。ただし、このようなクエリは非常に長い場合があるため、に渡すか、他のコンポーネントを再計算evalしないことでより効率的になります。いつものように、猫の皮を剥ぐ方法はたくさんあります。ijby

クエリ全体を検討する微妙な理由evalは次のとおりです。

  1. グループ化が高速である理由の1つは、j最初に式を検査することです。の場合list、名前は削除されますが、記憶されます。次に、各グループeval名前のないリストを作成し、名前を1回復元して、最後に最終結果を表示します。他の方法が遅くなる可能性がある理由の1つは、グループごとに同じ列名ベクトルを何度も繰り返し作成することです。ただし、定義が複雑jになるほど(たとえば、式が正確にで始まらない場合list)、検査ロジックを内部でコーディングするのが難しくなります。この分野にはたくさんのテストがあります。たとえば、と組み合わせてeval、ネームドロッピングが機能しない場合は冗長性レポートを作成します。ただし、「単純な」クエリ(完全なクエリ)を作成し、evalこのため、より高速で堅牢になる可能性があります。

  2. v1.8.2では、j:の最適化が行われoptions(datatable.optimize=Inf)ました。これは、これまでのところ、最適化とイディオムに合わせて検査jおよび変更します。これにより、桁違いの違いが生じ、ユーザーが知る必要が少なくなります(たとえば、いくつかのwikiポイントがなくなりました)。これ以上のことができます。たとえば、[2014年9月の更新-v1.9.3で実装された]場合、自動的に最適化できます。しかし、繰り返しになりますが、内部最適化は、たとえばへの呼び出しが含まれている場所ではなく、内部でコーディングするのが難しくなります。したがって、クエリ全体を実行すると、より適切に機能する可能性があります。meanlapply(.SD,...)DT[a==10]DT[J(10)]key(DT)[1]=="a"DT[,mean(a),by=b]DT[,list(x=eval(expr)),by=b]exprmeanevaldatatable.optimize。詳細度をオンにすると、実行内容がレポートされ、必要に応じて最適化をオフにできます。たとえば、速度差をテストします。

コメントによると、FR#2183が追加されました:「DTのスコープ内でj = list(xout = eval(...))のevalをevalに変更してください」。ハイライトしていただきありがとうございます。これは、式の中でがネストされているj場所を意味する一種の複雑なものです。ただし、で始まるeval場合は、はるかに単純で、すでにコード化され(上記のように)テストされているため、適切に最適化する必要があります。j eval

これから1つのポイントがある場合は、次のようになります。を含む動的クエリに使用した場合でも、使用DT[...,verbose=TRUE]またはoptions(datatable.verbose=TRUE)チェックが効率的に機能していることを確認します。data.tableeval

于 2012-08-08T23:02:06.030 に答える
2

これは理想的ではありませんが、私が思いついた中で最高です。それがより良い反応を引き出すのに役立つかどうかを確認するために、それをそこに投げます...

vars <- c("x", "y")
res <- do.call(data.table, (lapply(vars, function(X) dat[,eval(myfun(X)),])))
setnames(res, names(res), paste0(vars, "_out"))

## Check the results
head(res, 3)
#    x_out y_out
# 1:     0     0
# 2:     0     0
# 3:     0     0

私が気に入らないのはlapply()、出力のコピーをリスト形式で1つ作成し、data.table()(私が理解している限り)それらのデータを別の場所にコピーする必要があることです。これは、使用した場合よりも悪い結果です。list()内の構成[.data.frame()

于 2012-08-08T22:32:51.773 に答える