1. ホーム
  2. r

[解決済み] data.tableのカラムで文字列を分割する

2023-02-28 13:19:07

質問

CSVファイルからデータを読み込むスクリプトがあります。 data.table に読み込んで、1つのカラムのテキストを複数の新しいカラムに分割するスクリプトがあります。現在、私は lapplystrsplit という関数で行います。以下はその例です。

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 は二つの新しいカラムに分割されます。 PXPY という文字があります。

これはうまくいくのですが、もっと良い(効率的な)方法はないものかと考えていました。 data.table . 私の実際のデータセットは10M以上の行があるので、時間やメモリの効率は本当に重要になります。


アップデートを行いました。

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倍の時間がかかります。

どのように解決するのですか?

更新しました。 バージョン 1.9.6 (15年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() を例にとります。

古い回答は履歴を参照してください。