グループごとにdata.tableで:=を使用して複数の列を割り当てる


130

を使用して複数の列に割り当てる最良の方法は何data.tableですか?例えば:

f <- function(x) {c("hi", "hello")}
x <- data.table(id = 1:10)

私はこのようなことをしたいと思います(もちろんこの構文は間違っています):

x[ , (col1, col2) := f(), by = "id"]

そして、それを拡張するために、変数に名前が格納された列がたくさんある可能性があるので(たとえばcol_names)、次のようにしたいと思います。

x[ , col_names := another_f(), by = "id", with = FALSE]

このようなことをする正しい方法は何ですか?


1
:それは回答されているように、このルックス stackoverflow.com/questions/11308754/...を
アレックス・

アレックス、その答えは近いですがby、@ Christoph_Jが言うのが正しいので、と組み合わせて機能するようには見えません。FR#2120に追加された質問へのリンク:「==のLHSのドロップ= with = FALSEが必要なので、再訪するのを忘れないでください。
Matt Dowle、2012

明確に言うと、f()は列ごとに1つずつ、複数の値を返す関数です。
smci 2018年

回答:


161

これはR-Forgeのv1.8.3で機能するようになりました。それを強調してくれてありがとう!

x <- data.table(a = 1:3, b = 1:6) 
f <- function(x) {list("hi", "hello")} 
x[ , c("col1", "col2") := f(), by = a][]
#    a b col1  col2
# 1: 1 1   hi hello
# 2: 2 2   hi hello
# 3: 3 3   hi hello
# 4: 1 4   hi hello
# 5: 2 5   hi hello
# 6: 3 6   hi hello

x[ , c("mean", "sum") := list(mean(b), sum(b)), by = a][]
#    a b col1  col2 mean sum
# 1: 1 1   hi hello  2.5   5
# 2: 2 2   hi hello  3.5   7
# 3: 3 3   hi hello  4.5   9
# 4: 1 4   hi hello  2.5   5
# 5: 2 5   hi hello  3.5   7
# 6: 3 6   hi hello  4.5   9 

mynames = c("Name1", "Longer%")
x[ , (mynames) := list(mean(b) * 4, sum(b) * 3), by = a]
#     a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27


x[ , get("mynames") := list(mean(b) * 4, sum(b) * 3), by = a][]  # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

x[ , eval(mynames) := list(mean(b) * 4, sum(b) * 3), by = a][]   # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

with引数を使用する古いバージョン(可能な場合、この引数はお勧めしません):

x[ , mynames := list(mean(b) * 4, sum(b) * 3), by = a, with = FALSE][] # same
#    a b col1  col2 mean sum Name1 Longer%
# 1: 1 1   hi hello  2.5   5    10      15
# 2: 2 2   hi hello  3.5   7    14      21
# 3: 3 3   hi hello  4.5   9    18      27
# 4: 1 4   hi hello  2.5   5    10      15
# 5: 2 5   hi hello  3.5   7    14      21
# 6: 3 6   hi hello  4.5   9    18      27

この回答と例をありがとう。2つの行を持つ1つの列ではなく、dim出力から各objectNameの2つの列を取得するには、次の行をどのように変更すればよいですか?data.table(objectName=ls())[,c("rows","cols"):=dim(get(objectName)),by=objectName](私はdata.table1.8.11 を使用しています)
dnlbrky

@dnlbrky dimはベクトルを返すため、それをタイプに変換するとlist回転します。例えば[,c("rows","cols"):=as.list(dim(get(objectName))),by=objectNa‌​me]。問題は、as.list呼び出しのオーバーヘッドがあり、小さなベクトルもコピーすることです。グループの数が増えるにつれて効率が問題になる場合は、お知らせください。
Matt Dowle、2014年

1
こんにちはマット。2番目のコードブロックの最初の例(つまりx[,mynames:=list(mean(b)*4,sum(b)*3),by=a,with=FALSE][])は警告をスローするので、おそらく削除しますか?関連するノートで、誰かが、で実際に動作するはずのoptions(datatable.WhenJisSymbolThenCallingScope=TRUE)ような割り当てを提案しましたx[,mynames:=list(mean(b)*4,sum(b)*3),by=a]か?他の変更と整合しているようですが、既存のユーザーコード(?)が多すぎると思います。
Josh O'Brien

1
@PanFranciscoなしby=aでは機能しますが、別の答えを返します。mean(a)そしてsum(a)凝集体は場合各グループ内でリサイクルされますby=aby=aそれがなければ、meansumを列全体に対して各セルに貼り付けます(つまり、異なる数値)。
Matt Dowle、2018

1
@MattDowle私の関数が名前付きリストを既に返している場合はどうですか、とにかく列に再度名前を付けなくてもdtに列を追加できますか?たとえば、f <-function(x){list( "c" = "hi"、 "d" = "hello")}は、名前付きcolsの結果をx [、f()、by = a] []で出力します。結果をdtに追加する方法がわかりません。
Jfly

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.