Разделить текстовую строку в столбцах data.table
У меня есть script, который считывает данные из файла CSV в data.table
, а затем разбивает текст в одном столбце на несколько новых столбцов. В настоящее время я использую функции lapply
и strsplit
для этого. Вот пример:
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
разбивается на два новых столбца PX
и PY
на символ "_".
Несмотря на то, что это работает отлично, мне было интересно, есть ли лучший (более эффективный) способ сделать это, используя data.table
. Мои реальные наборы данных имеют >= 10M + строк, поэтому эффективность времени/памяти становится очень важной.
UPDATE:
Следуя предложению @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
занимает примерно 20X раз.
Ответы
Ответ 1
Обновление: Из версии 1.9.6 (по CRAN с сентября 15) мы можем использовать функцию 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()
.
См. историю старых ответов.
Ответ 2
Я добавляю ответ для тех, кто не использует data.table
v1.9.5, а также хочет однострочное решение.
dt[, c('PX','PY') := do.call(Map, c(f = c, strsplit(PREFIX, '-'))) ]
Ответ 3
Использование пакета 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
С тидиром решение:
separate(df,col = "PREFIX",into = c("PX", "PY"), sep = "_")