data.table列のテキスト文字列を分割します


87

CSVファイルからにデータを読み込みdata.table、1つの列のテキストをいくつかの新しい列に分割するスクリプトがあります。私は現在、lapplyandstrsplit関数を使用してこれを行っています。次に例を示します。

library("data.table")
df = data.table(PREFIX = c("A_B","A_C","A_D","B_A","B_C","B_D"),
                VALUE  = 1:6)
dt = as.data.table(df)

# split PREFIX into new columns
dt$PX = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 1))
dt$PY = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 2))

dt 
#    PREFIX VALUE PX PY
# 1:    A_B     1  A  B
# 2:    A_C     2  A  C
# 3:    A_D     3  A  D
# 4:    B_A     4  B  A
# 5:    B_C     5  B  C
# 6:    B_D     6  B  D 

上記の例では、列PREFIXは2つの新しい列PXPY「_」文字に分割されています。

これは問題なく機能しますが、を使用してこれを行うためのより良い(より効率的な)方法があるかどうか疑問に思いましたdata.table。私の実際のデータセットには1,000万以上の行があるため、時間/メモリの効率が非常に重要になります。


更新:

@Frankの提案に従って、より大きなテストケースを作成し、提案されたコマンドを使用しstringr::str_split_fixedましたが、元の方法よりもはるかに時間がかかります。

library("data.table")
library("stringr")
system.time ({
    df = data.table(PREFIX = rep(c("A_B","A_C","A_D","B_A","B_C","B_D"), 1000000),
                    VALUE  = rep(1:6, 1000000))
    dt = data.table(df)
})
#   user  system elapsed 
#  0.682   0.075   0.758 

system.time({ dt[, c("PX","PY") := data.table(str_split_fixed(PREFIX,"_",2))] })
#    user  system elapsed 
# 738.283   3.103 741.674 

rm(dt)
system.time ( {
    df = data.table(PREFIX = rep(c("A_B","A_C","A_D","B_A","B_C","B_D"), 1000000),
                     VALUE = rep(1:6, 1000000) )
    dt = as.data.table(df)
})
#    user  system elapsed 
#   0.123   0.000   0.123 

# split PREFIX into new columns
system.time ({
    dt$PX = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 1))
    dt$PY = as.character(lapply(strsplit(as.character(dt$PREFIX), split="_"), "[", 2))
})
#    user  system elapsed 
#  33.185   0.000  33.191 

したがって、このstr_split_fixed方法には約20倍の時間がかかります。


最初にdata.tableの外部で操作を行う方が良いと思います。stringrパッケージを使用する場合、これは次のコマンドですstr_split_fixed(PREFIX,"_",2)。スピードアップをテストしていないので答えていません...または、1つのステップで:dt[,c("PX","PY"):=data.table(str_split_fixed(PREFIX,"_",2))]
フランク

回答:


123

更新:バージョン1.9.6(2015年9月現在のCRAN)から、関数tstrsplit()を使用して結果を直接(そしてはるかに効率的な方法で)取得できます。

require(data.table) ## v1.9.6+
dt[, c("PX", "PY") := tstrsplit(PREFIX, "_", fixed=TRUE)]
#    PREFIX VALUE PX PY
# 1:    A_B     1  A  B
# 2:    A_C     2  A  C
# 3:    A_D     3  A  D
# 4:    B_A     4  B  A
# 5:    B_C     5  B  C
# 6:    B_D     6  B  D

tstrsplit()基本的にはのラッパーでありtranspose(strsplit())transpose()最近実装された関数がリストを転置します。?tstrsplit()?transpose()例を参照してください。

古い答えについては履歴を参照してください。


ありがとうアルン。「a_spl」で説明されているように、最初にリストを作成し、次にインデックスを作成し、次に列を作成する方法については考えていませんでした。私はいつも、すべてを1行で行うことが最善の方法だと思っていました。好奇心から、なぜインデックスウェイがそれほど速く機能するのですか?
デリックルイス

@Arun、この質問に関連して、私がここに書いたような関数で見られる落とし穴のいくつかは何ですかgist.github.com/mrdwab/6873058基本的に、私はを利用しましたfreadが、そうするために、引数に相当するtempfileものfreadがないように見えるので、(ボトルネックのように見える)を使用する必要がありましたtext。このサンプルデータを使用してテストすると、そのパフォーマンスはあなたa_spla_subアプローチの間にあります。
A5C1D2H2I1M1N2O1R2T1 2014年

4
:=のLHSの列数を推測し、grep tstrsplitの発生に基づいて新しい列の名前を動的に作成する方法を考えていました
amonk 2017

このアプローチを使用して、元のPREFIX列を一度に削除する効率的な方法はありますか?つまり、チェーンしたり、個別の操作として実行したりするよりも、処理中のメモリの使用量が少ないか、速い可能性があります。
マークE.

15

data.table v1.9.5を使用しておらず、1行のソリューションも必要な人のために回答を追加します。

dt[, c('PX','PY') := do.call(Map, c(f = c, strsplit(PREFIX, '-'))) ]

7

splitstackshapeパッケージの使用:

library(splitstackshape)
cSplit(df, splitCols = "PREFIX", sep = "_", direction = "wide", drop = FALSE)
#    PREFIX VALUE PREFIX_1 PREFIX_2
# 1:    A_B     1        A        B
# 2:    A_C     2        A        C
# 3:    A_D     3        A        D
# 4:    B_A     4        B        A
# 5:    B_C     5        B        C
# 6:    B_D     6        B        D

4

私たちは試すことができます:

library(data.table)  
cbind(dt, fread(text = dt$PREFIX, sep = "_", header = FALSE))
    #    PREFIX VALUE V1 V2
    # 1:    A_B     1  A  B
    # 2:    A_C     2  A  C
    # 3:    A_D     3  A  D
    # 4:    B_A     4  B  A
    # 5:    B_C     5  B  C
    # 6:    B_D     6  B  D

1

tidyrを使用した場合の解決策は次のとおりです。

separate(df,col = "PREFIX",into = c("PX", "PY"), sep = "_")

質問は特にdata.tableソリューションを求めました。このドメインで働く人々は、彼らの課題に関連する正当な理由から、tidyrソリューションよりもdata.tableソリューションをすでに選択しています。
MichaelTuchman20年

他のユーザーも他のライブラリでソリューションを提供しています。私は有効な代替手段を簡単かつ迅速に提供しました。
SKAN
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.