Tách chuỗi văn bản trong một cột data.table


86

Tôi có một tập lệnh đọc dữ liệu từ tệp CSV thành một data.tablevà sau đó chia văn bản trong một cột thành nhiều cột mới. Tôi hiện đang sử dụng các hàm lapplystrsplitđể làm việc này. Đây là một ví dụ:

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 

Trong ví dụ trên, cột PREFIXđược chia thành hai cột mới PXPYtrên ký tự "_".

Mặc dù điều này hoạt động tốt, tôi đã tự hỏi liệu có cách nào tốt hơn (hiệu quả hơn) để làm điều này bằng cách sử dụng không data.table. Bộ dữ liệu thực của tôi có> = 10 triệu + hàng, vì vậy hiệu quả về thời gian / bộ nhớ trở nên thực sự quan trọng.


CẬP NHẬT:

Theo gợi ý của @ Frank, tôi đã tạo một trường hợp thử nghiệm lớn hơn và sử dụng các lệnh được đề xuất, nhưng stringr::str_split_fixedmất nhiều thời gian hơn so với phương pháp ban đầu.

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 

Vì vậy, str_split_fixedphương pháp này mất khoảng 20 lần lâu hơn.


Tôi nghĩ rằng thực hiện thao tác bên ngoài data.table trước tiên có thể tốt hơn. Nếu bạn sử dụng stringrgói, đây là lệnh: str_split_fixed(PREFIX,"_",2). Tôi không trả lời vì tôi chưa kiểm tra tốc độ ... Hoặc, trong một bước:dt[,c("PX","PY"):=data.table(str_split_fixed(PREFIX,"_",2))]
Frank

Câu trả lời:


122

Cập nhật: Từ phiên bản 1.9.6 (trên CRAN kể từ ngày 15 tháng 9), chúng tôi có thể sử dụng chức năng tstrsplit()để nhận kết quả trực tiếp (và theo cách hiệu quả hơn nhiều):

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()về cơ bản là một trình bao bọc transpose(strsplit()), nơi transpose()chức năng, cũng được triển khai gần đây, chuyển vị một danh sách. Vui lòng xem ?tstrsplit()?transpose()cho ví dụ.

Xem lịch sử để biết câu trả lời cũ.


Cảm ơn Arun. Tôi đã không nghĩ đến phương pháp đầu tiên tạo danh sách, sau đó là chỉ mục và sau đó là các cột như được mô tả trong "a_spl". Tôi luôn nghĩ rằng làm mọi thứ trong một dòng duy nhất là cách tốt nhất. Chỉ vì tò mò tại sao cách lập chỉ mục hoạt động nhanh hơn rất nhiều?
Derric Lewis

@Arun, liên quan đến câu hỏi này, một số cạm bẫy bạn sẽ thấy trong một hàm như tôi đã viết ở đây là gì: gist.github.com/mrdwab/6873058 Về cơ bản, tôi đã sử dụng fread, nhưng để làm như vậy, Tôi đã phải sử dụng một tempfile(có vẻ như nó sẽ là một nút thắt cổ chai) vì nó dường như không freadtương đương với một textđối số. Thử nghiệm với dữ liệu mẫu này, hiệu suất của nó là giữa bạn a_spla_subcách tiếp cận.
A5C1D2H2I1M1N2O1R2T1

4
Tôi đã tự hỏi như thế nào người ta có thể đoán số lượng cột trên LHS của: = và tự động tạo ra các tên của các mới cột dựa trên grep tstrsplit lần xuất hiện
amonk

15

Tôi thêm câu trả lời cho một người không sử dụng data.table v1.9.5 và cũng muốn có một giải pháp một dòng.

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

7

Sử dụng splitstackshapegói:

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

Chúng ta có thể thử:

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

Với slimr, giải pháp là:

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

Câu hỏi dành riêng cho các giải pháp data.table. Những người làm việc trong miền này đã chọn các giải pháp data.table thay vì các giải pháp gọn gàng hơn vì một lý do chính đáng liên quan đến thách thức của họ.
Michael Tuchman

Những người dùng khác cũng đã cung cấp giải pháp với các thư viện khác, tôi vừa đưa ra một giải pháp thay thế hợp lệ, dễ dàng và nhanh chóng.
skan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.