Разделение текстового столбца на рваные несколько новых столбцов в таблице данных в R

У меня есть таблица данных, содержащая 20000+ строк и один столбец. Строка в каждом столбце имеет разное количество слов. Я хочу разделить слова и поместить каждое из них в новый столбец. Я знаю, как я могу сделать это слово в слово:

Data [ , Word1 := as.character(lapply(strsplit(as.character(Data$complaint), split=" "), "[", 1))]

(Data моя таблица данных и complaint это название колонки)

Очевидно, что это неэффективно, потому что каждая ячейка в каждой строке имеет разное количество слов.

Не могли бы вы рассказать мне о более эффективном способе сделать это?

5 ответов

Решение

Проверять, выписываться cSplit из моего пакета "splitstackshape". Это работает на любом data.frameс или data.tables (но всегда возвращает data.table).

Предполагая, что выборочные данные KFB хотя бы немного отражают ваши фактические данные, вы можете попробовать:

library(splitstackshape)
cSplit(df, "x", " ")
#     x_1      x_2         x_3 x_4
# 1: This       is interesting  NA
# 2: This actually          is not

Другой (блестящий) вариант заключается в использовании stri_split_fixed с simplify = TRUE (из "stringi") (который, очевидно, скоро будет введен в код "splitstackshape"):

library(stringi)
stri_split_fixed(df$x, " ", simplify = TRUE)
#      [,1]   [,2]       [,3]          [,4] 
# [1,] "This" "is"       "interesting" NA   
# [2,] "This" "actually" "is"          "not"

Две функции, transpose() а также tstrsplit(), доступны с версии 1.9.6 на CRAN.

С этим мы можем сделать:

require(data.table)
setDT(tstrsplit(as.character(df$x), " ", fixed=TRUE))[]
#      V1       V2          V3  V4
# 1: This       is interesting  NA
# 2: This actually          is not

tstrsplit это обертка для transpose(strsplit(...)),

Вот решение, основанное на rbind.fill.matrix(...) в plyr пакет. В наборе данных с 20000 строк он выполняется примерно за 3,6 с.

# create an sample dataset - you have this already
library(data.table)
words <- LETTERS[1:10]     # "words" are just letters in this example
set.seed(1)                # for reproducible example
w  <- sapply(1:2e4,function(i)paste(words[sample(1:10,sample(1:10,1))],collapse=" "))
dt <- data.table(words=w)
head(dt)
#          complaint
# 1:           D F H
# 2:           I J F
# 3:   A B I E C D H
# 4: J D G H B I A E
# 5:         A D G C
# 6:       F E B J I

# you start here...
library(plyr)
result <- rbind.fill.matrix(lapply(strsplit(dt$words, split=" "),matrix,nr=1))
result <- as.data.table(result)
head(result)
#    1 2 3  4  5  6  7  8  9 10
# 1: D F H NA NA NA NA NA NA NA
# 2: I J F NA NA NA NA NA NA NA
# 3: A B I  E  C  D  H NA NA NA
# 4: J D G  H  B  I  A  E NA NA
# 5: A D G  C NA NA NA NA NA NA
# 6: F E B  J  I NA NA NA NA NA

РЕДАКТИРОВАТЬ: Добавлен некоторый сравнительный анализ на основе комментария @Ananda ниже.

f.rfm    <- function() as.data.table(rbind.fill.matrix(lapply(strsplit(dt$complaint, split=" "),matrix,nr=1)))
library(splitstackshape)
f.csplit <- function() cSplit(dt, "complaint", " ",type.convert=FALSE)
library(stringi)
f.sl2m   <- function() as.data.table(stri_list2matrix(strsplit(dt$complaint, split=" "), byrow = TRUE))
f.ssf    <- function() as.data.table(stri_split_fixed(dt$complaint, " ", simplify = TRUE))

all.equal(f.rfm(),f.csplit(),check.names=FALSE)
# [1] TRUE
all.equal(f.rfm(),f.sl2m(),check.names=FALSE)
# [1] TRUE
all.equal(f.rfm(),f.ssf(),check.names=FALSE)
# [1] TRUE
library(microbenchmark)
microbenchmark(f.rfm(),f.csplit(),f.sl2m(),f.ssf(),times=10)
# Unit: milliseconds
#        expr        min         lq     median        uq        max neval
#     f.rfm() 3566.17724 3589.31203 3606.93303 3665.4087 3719.32299    10
#  f.csplit()   98.05709  102.46456  104.51046  107.9588  117.26945    10
#    f.sl2m()   55.45527   55.58852   56.75406   58.9347   67.44523    10
#     f.ssf()   17.77499   17.98879   18.30831   18.4537   21.62161    10

Так выглядит stri_split_fixed(...) это победитель.

Пример данных был бы хорош, но если я понимаю, что вы хотите, это невозможно сделать правильно во фрейме данных. Поскольку в каждом ряду есть разное количество слов, вам понадобится список. Несмотря на то, что это очень просто разделить слова на весь объект.

Если вы бежите strsplit(as.character(Data[,1]), " ") вы получите список с каждым элементом, соответствующим строке в вашем кадре данных. Исходя из этого, существует несколько различных вариантов перестановки этого объекта, но наилучший подход будет зависеть от вашей цели.

ОК для data.table и data.frame

# toy data
df <- structure(list(x = structure(c(2L, 1L), .Label = c("This actually is not", 
"This is interesting"), class = "factor")), .Names = "x", row.names = c(NA, 
-2L), class = "data.frame")

#                      x
# 1  This is interesting
# 2 This actually is not

# the code
split_result <- strsplit(as.character(df$x), " ")
length_n <- sapply(split_result, length)
length_max <- seq_len(max(length_n))
as.data.frame(t(sapply(split_result, "[", i = length_max))) # Or as.data.table(...)

#     V1       V2          V3   V4
# 1 This       is interesting <NA>
# 2 This actually          is  not
Другие вопросы по тегам