変数名が文字ベクトルに格納されている場合は、data.tableを選択/割り当てます


93

data.table変数名が文字ベクトルに格納されている場合、aの変数をどのように参照しますか?たとえば、これは次の場合に機能しますdata.frame

df <- data.frame(col1 = 1:3)
colname <- "col1"
df[colname] <- 4:6
df
#   col1
# 1    4
# 2    5
# 3    6

:=表記の有無にかかわらず、data.tableに対してこれと同じ操作を実行するにはどうすればよいですか?の明らかなことはdt[ , list(colname)]機能しません(私はそれを期待していませんでした)。

回答:


134

プログラムで変数を選択する2つの方法:

  1. with = FALSE

     DT = data.table(col1 = 1:3)
     colname = "col1"
     DT[, colname, with = FALSE] 
     #    col1
     # 1:    1
     # 2:    2
     # 3:    3
    
  2. 'ドットドット'(..)プレフィックス:

     DT[, ..colname]    
     #    col1
     # 1:    1
     # 2:    2
     # 3:    3
    

'dot dot'(..)表記の詳細については、1.10.2の新機能を参照してください(現在、ヘルプテキストには記載されていません)。

変数に割り当てるには、のLHSを:=括弧で囲みます。

DT[, (colname) := 4:6]    
#    col1
# 1:    4
# 2:    5
# 3:    6

後者は、列ベクトル全体を参照で置き換えるため、列プロンクと呼ばれます。サブセットiが存在する場合、参照によってサブ割り当てされます。周りの(colname)親は 2014年10月のCRANのバージョンv1.9.4で導入された速記です。ニュース項目は次のとおりです。

使うwith = FALSEとすると、:=今のLHS包むことを考えると、すべてのケースで廃止され:=、括弧ではいくつかの時間のために好まれています。

colVar = "col1"
DT[, (colVar) := 1]                             # please change to this
DT[, c("col1", "col2") := 1]                    # no change
DT[, 2:4 := 1]                                  # no change
DT[, c("col1","col2") := list(sum(a), mean(b))]  # no change
DT[, `:=`(...), by = ...]                       # no change

詳細セクションも参照してください?`:=`

DT[i, (colnamevector) := value]
# [...] The parens are enough to stop the LHS being a symbol

そして、コメントでさらに質問に答えるために、ここに1つの方法があります(いつものように多くの方法があります):

DT[, colname := cumsum(get(colname)), with = FALSE]
#    col1
# 1:    4
# 2:    9
# 3:   15 

または、あなたはそれが簡単に、ちょうどへの書き込みとデバッグ読むことを見つけるかもしれないサーバーに送信するための動的SQLステートメントを構築するための類似しました、:evalpaste

expr = paste0("DT[,",colname,":=cumsum(",colname,")]")
expr
# [1] "DT[,col1:=cumsum(col1)]"

eval(parse(text=expr))
#    col1
# 1:    4
# 2:   13
# 3:   28

それをたくさん行う場合は、ヘルパー関数を定義できますEVAL

EVAL = function(...)eval(parse(text=paste0(...)),envir=parent.frame(2))

EVAL("DT[,",colname,":=cumsum(",colname,")]")
#    col1
# 1:    4
# 2:   17
# 3:   45

今ことdata.table1.8.2を自動的に最適化j効率のために、使用することが好ましいeval方法。get()中には、j例えば、いくつかの最適化を防ぐことができます。

または、がありset()ます。オーバーヘッドが低く、機能的な形式の:=、ここでは問題ありません。を参照してください?set

set(DT, j = colname, value = cumsum(DT[[colname]]))
DT
#    col1
# 1:    4
# 2:   21
# 3:   66

1
マシューの返事をありがとう。with = FALSEは、私の問題の一部を確実に解決します。しかし実際には、列を列の累積に置き換えたいと思います。どういうわけか、割り当ての右側にある変数で列名を参照できますか?
frankc 2012

実際、私は、dt内に存在せず、正常に機能する別の名前でcumsumを外部に配置しました。
frankc 2012

1
しかし、それは完全に余分な行になります!あまりエレガントではありません:)しかし、時々それは便利です。そのような場合は、変数名を.、で始めるか、将来その記号が列名として含まれる..可能性がある場合DTはマスキングを回避するのが最善です(列名はで始まらないという規則に従ってください.)。追加.()やなど、そのようなスコープの問題に対してより堅牢にするための機能要求がいくつかあり..()ます。
Matt Dowle 2012

あなたがあなたの答えを編集したことに気付く前に私は答えました。私の最初の考えはeval(parse())でしたが、何らかの理由で、外部で実行することになったとき、それを機能させるのに問題がありました。これは私が考えていなかったことがたくさんある素晴らしい答えです。一般的にdata.tableをありがとう、それは素晴らしいパッケージです。
frankc 2012

2
fn$gsubfnパッケージからの準perlタイプの文字列補間を使用して、EVALソリューションの可読性を向上させることができることに注意してください library(gsubfn); fn$EVAL( "DT[,$colname:=cumsum($colname)]" )
G. Grothendieck 2013年

8

*これは実際には答えではありませんが、コメントを投稿するのに十分な通りの信用がありません:/

とにかく、変数に格納された名前でデータテーブルに新しい列を実際に作成しようとしている人のために、次のことが機能します。私はそれのパフォーマンスについての手がかりがありません。改善のための提案はありますか?名前のない新しい列には常にV1という名前が付けられると想定しても安全ですか?

colname <- as.name("users")
# Google Analytics query is run with chosen metric and resulting data is assigned to DT
DT2 <- DT[, sum(eval(colname, .SD)), by = country]
setnames(DT2, "V1", as.character(colname))

sum()で問題なく参照できますが、同じ手順で割り当てることができないようです。ところで、これを行う必要がある理由は、colnameがShinyアプリでのユーザー入力に基づいているためです。


ただ働くための+1:これがこれを行うための「方法」ではないことに同意しますが、このテーマに関するすべてのSO投稿に45分ほど注いでいるので、これが実際に到達できた唯一の解決策です仕事-それを指摘するために時間を割いてくれてありがとう!
神経心理学

お役に立てて嬉しいです!残念ながら、この3つのライナーはひどいものではありませんが、data.tablesを直接使用してより洗練されたソリューションを見つけることはできませんでした。私のシナリオでは、ユーザー入力に基づいて、セットから選択するのではなく、常に単一の列でフィルタリングできるため、tidyrを使用してデータを「ワイド」ではなく「ロング」にする方が簡単であることに気付きました。列の。
efh0888 2016年

2
V1新しい名前であると想定するのは安全ではありません。たとえば、でcsvを読み取りfread、名前のない列がある場合、その列にはV1名前が付けられます(そしてread.csv与えられますX)。したがって、テーブルにすでにが含まれている可能性がありますV1。たぶん、名前を取得するだけですnames(DT)[length(names(DT))]
dracodoc 2016

2

複数の列および列の値に適用される関数の場合。

関数から値を更新する場合、RHSはリストオブジェクトである必要があるため、.SDwithでループを使用するlapplyとうまくいきます。

以下の例では、整数列を数値列に変換します

a1 <- data.table(a=1:5, b=6:10, c1=letters[1:5])
sapply(a1, class)  # show classes of columns
#         a           b          c1 
# "integer"   "integer" "character" 

# column name character vector
nm <- c("a", "b")

# Convert columns a and b to numeric type
a1[, j = (nm) := lapply(.SD, as.numeric ), .SDcols = nm ]

sapply(a1, class)
#         a           b          c1 
# "numeric"   "numeric" "character" 

2

変数または関数を介してdata.tableから複数の列を取得します。

library(data.table)

x <- data.table(this=1:2,that=1:2,whatever=1:2)

# === explicit call
x[, .(that, whatever)]
x[, c('that', 'whatever')]

# === indirect via  variable
# ... direct assignment
mycols <- c('that','whatever')
# ... same as result of a function call
mycols <- grep('a', colnames(x), value=TRUE)

x[, ..mycols]
x[, .SD, .SDcols=mycols]

# === direct 1-liner usage
x[, .SD, .SDcols=c('that','whatever')]
x[, .SD, .SDcols=grep('a', colnames(x), value=TRUE)]

すべてがもたらす

   that whatever
1:    1        1
2:    2        2

私はその.SDcols方法が最もエレガントだと思います。


1

あなたはこれを試すことができます

colname <-as.name( "COL_NAME")

DT2 <-DT [、list(COL_SUM = sum(eval(colname、.SD)))、by = c(group)]


1
コードを投稿するだけでなく、コードに説明を追加することを常にお勧めします。
MBorg
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.