2013-03-28 16 views
18

私はdata.tableの各行に効率的に関数を適用する方法を探しています。のは、次のデータテーブルを考えてみましょう: data.tableの各行に関数を適用する

library(data.table) 
library(stringr) 

x <- data.table(a = c(1:3, 1), b = c('12 13', '14 15', '16 17', '18 19')) 
> x 
    a  b 
1: 1 12 13 
2: 2 14 15 
3: 3 16 17 
4: 1 18 19 

だが、私はスペースで列 bの各要素を分割したいとしましょう(そのため、元のデータの行ごとに2つの行を与える)し、得られたデータテーブルを結合。上記の例では、私は次のような結果を必要とする:いくつかがある場合を除き

x[, list(str_split(b, ' ')[[1]]), by = a] 

ほとんど作品(:列aのみ一意の値を持っている場合

a V1 
1: 1 12 
2: 1 13 
3: 2 14 
4: 2 15 
5: 3 16 
6: 3 17 
7: 1 18 
8: 1 19 

次はに働くだろう元のデータテーブルでは同じ行)、しかし、xが多くのカラムを持ち、カラムbを結果にコピーすると醜いですが、これは避けたいものです。

>  x[, list(str_split(b, ' ')[[1]]), by = list(a,b)] 
    a  b V1 
1: 1 12 13 12 
2: 1 12 13 13 
3: 2 14 15 14 
4: 2 14 15 15 
5: 3 16 17 16 
6: 3 16 17 17 
7: 1 18 19 18 
8: 1 18 19 19 

この問題を解決する最も効率的かつ慣用的な方法はありますか?よりよい解決策がある場合は

x[, list(a, str_split(b, ' ')[[1]]), by = r] 

私は思ったんだけど:

答えて

14

方法について:

x 
    a  b 
1: 1 12 13 
2: 2 14 15 
3: 3 16 17 
4: 1 18 19 

x[,list(a=rep(a,each=2), V1=unlist(strsplit(b," ")))] 
    a V1 
1: 1 12 
2: 1 13 
3: 2 14 
4: 2 15 
5: 3 16 
6: 3 17 
7: 1 18 
8: 1 19 

一般化ソリューション与えられたコメント:

x[,{s=strsplit(b," ");list(a=rep(a,sapply(s,length)), V1=unlist(s))}] 
+0

Matthewありがとう各bの2つのコンポーネントはスペースで区切られていますが、より一般的なケースでは動作しません。それぞれのbは1〜10のコンポーネントを持つことができます)。それはあなたの質問を何度か正確に指定するのは難しいことを示しています:)。 –

+0

@ VictorK。そこに行く。 –

+0

Matt、これは、時間を大幅に節約し、効率的に実行する完璧なソリューションです。あなたのDTは本当にrベースでDFを置き換える必要があることを示しています。私はこれを私のビッグデータ分析クラスで引用します。 1つの疑問は、マルチコアで並列実行することで、どのように効率を上げることができるのでしょうか?私はhtopと1つのコアを確認しました。 –

2

1つのオプションは、行番号

x[, r := 1:nrow(x)] 

、その後rでグループを追加するだろうか?

+3

よりesteticおそらく多くの慣用的に見える、あなたがrownames' 'への呼び出しを含めることができますですkeyby = list(a、rownames(x))] 'を実行します。 –

+0

はい、私はそれが好きです。あなたが答えとして投稿すると、私はそれを喜んで受け入れます。私は 'keyby'を必要としているとは思えません。(私は一度だけデータテーブルを使いたいので)キーに' a'は必要ありません - 私の目的にはrownames(x)だけで十分です。 –

2

最も効果的で慣用的なアプローチは、ベクトル化された関数を持つことです。この場合

regexのいくつかの種類を使用すると、各分割要素を返すようにしたい、とあなたは2がそれぞれ1である知っている場合は、あなたが強制するMapを使用することができますが、

x[, V1 := gsub(" [[:alnum:]]*", "", b)] 

    a  b V1 
1: 1 12 13 12 
2: 2 14 15 14 
3: 3 16 17 16 
4: 1 18 19 18 

やりたいだろう入力と所望の出力を見て正しい形

x[, c('b1','b2') := do.call(Map, c(f = c, strsplit(b, ' ')))] 



x 
    a  b b1 b2 
1: 1 12 13 12 13 
2: 2 14 15 14 15 
3: 3 16 17 16 17 
4: 1 18 19 18 19 
+0

私はおそらく私が正しく欲しいものを説明していないでしょう。私が必要とするのは、私の質問の底にある結果ですが、列 'b'はありません。私の特定の例では、元のデータテーブルの各行は結果に2つの行を生成するはずです.bの各値は2つの部分文字列に分割されます。 –

+0

@VictorK私の編集を参照してください... – mnel

+0

@mnelおそらくもっと賢明なフォーマットですが、これはOPの望みの結果を達成しません。 –

0

strsplitの結果は、これが動作するはず -

x <- data.frame(a=c(1,2,3,1),b=c("12 13","14 15","16 17","18 19")) 
data.frame(a=rep(x$a,each=2), new_b=unlist(strsplit(as.character(x$b)," "))) 
+0

これは、要求された出力( 'a'の通知順序)を返しません。 –

+0

よろしいですか?それは簡単に修正する:) – Nishanth

+0

と結果を一般化するためには、各= 2を-と置き換えてください(それぞれの長さはunlist(strsplit as.character(x $ b))) – Nishanth

1

dplyr/tidyrアプローチはまた、データテーブルで動作します。標準的な評価フォームを使用して

library(dplyr) 
library(tidyr) 
x %>% 
    separate(b, into = c("b1", "b2")) %>% 
    gather(b, "V1", b1:b2) %>% 
    arrange(V1) %>% 
    select(a, V1) 

または、:

x %>% 
    separate_("b", into = c("b1", "b2")) %>% 
    gather_("b", "V1", c("b1", "b2")) %>% 
    arrange_(~ V1) %>% 
    select_(~ a, ~ V1) 

b列の値の異なる数字の場合は、もう少し複雑です。

library(stringr) 

x2 <- data.table(
    a = c(1:3, 1), 
    b = c('12 13', '14', '15 16 17', '18 19') 
) 

n <- max(str_count(x2$b, " ")) + 1 
b_cols <- paste0("b", seq_len(n)) 
x2 %>% 
    separate_("b", into = b_cols, extra = "drop") %>% 
    gather_("b", "V1", b_cols) %>% 
    arrange_(~ V1) %>% 
    select_(~ a, ~ V1) 
1
x[, .(a,strsplit(b,' ')), by=1:nrow(x)] 

by=nrow(x)により、グループごとに

2
x[, .(a,strsplit(b,' ')), by = .I] 

を1行を強制するための簡単な方法は、

関連する問題