แยกสตริงข้อความในคอลัมน์ data.table


87

ฉันมีสคริปต์ที่อ่านข้อมูลจากไฟล์ CSV เป็นไฟล์data.tableแล้วแยกข้อความในคอลัมน์หนึ่งออกเป็นคอลัมน์ใหม่หลายคอลัมน์ ฉันกำลังใช้ฟังก์ชันlapplyand 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 + แถวดังนั้นประสิทธิภาพของเวลา / หน่วยความจำจึงมีความสำคัญมาก


อัพเดท:

ตามคำแนะนำของ @ 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)ฉันไม่ตอบเพราะยังไม่ได้ทดสอบ speedup ... หรือในขั้นตอนเดียว:dt[,c("PX","PY"):=data.table(str_split_fixed(PREFIX,"_",2))]
Frank

คำตอบ:


123

อัปเดต:จากเวอร์ชัน 1.9.6 (บน CRAN ณ ก.ย. 58) เราสามารถใช้ฟังก์ชัน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" ฉันคิดเสมอว่าการทำทุกอย่างในบรรทัดเดียวเป็นวิธีที่ดีที่สุด ด้วยความอยากรู้ว่าทำไมวิธีดัชนีจึงทำงานได้เร็วขึ้นมาก?
Derric Lewis

@Arun เกี่ยวข้องกับคำถามนี้อะไรคือข้อผิดพลาดที่คุณจะเห็นในฟังก์ชั่นที่ฉันเขียนไว้ที่นี่: gist.github.com/mrdwab/6873058โดยทั่วไปฉันได้ใช้ประโยชน์freadแต่เพื่อทำเช่นนั้น ฉันต้องใช้ a tempfile(ซึ่งดูเหมือนว่ามันจะเป็นคอขวด) เนื่องจากดูเหมือนว่าจะไม่freadเทียบเท่ากับการtextโต้แย้ง การทดสอบกับข้อมูลตัวอย่างนี้ประสิทธิภาพจะอยู่ระหว่างคุณa_splและa_subแนวทาง
A5C1D2H2I1M1N2O1R2T1

4
ฉันสงสัยว่าเราสามารถเดาจำนวนคอลัมน์ใน LHS ของ: = ได้อย่างไรและสร้างชื่อของคอลัมน์ใหม่แบบไดนามิกตามการเกิด tstrsplit grep
amonk

มีวิธีที่มีประสิทธิภาพในการวางคอลัมน์ PREFIX เดิมทั้งหมดพร้อมกันโดยใช้วิธีนี้หรือไม่? ฉันหมายความว่าอาจเร็วกว่าหรือใช้หน่วยความจำในกระบวนการน้อยกว่าการต่อโซ่หรือทำเป็นการดำเนินการแยกกัน
Mark E.

15

ฉันเพิ่มคำตอบสำหรับผู้ที่ไม่ได้ใช้data.table v1.9.5และต้องการโซลูชันแบบบรรทัดเดียว

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

ด้วยความเป็นระเบียบเรียบร้อยการแก้ปัญหาคือ:

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

คำถามที่ถามโดยเฉพาะสำหรับโซลูชัน data.table ผู้ที่ทำงานในโดเมนนี้ได้เลือกโซลูชัน data.table ตามความต้องการของโซลูชันที่เป็นระเบียบเรียบร้อยแล้วด้วยเหตุผลที่ดีเมื่อเทียบกับความท้าทายของพวกเขา
Michael Tuchman

ผู้ใช้รายอื่นได้ให้โซลูชันกับไลบรารีอื่นเช่นกันฉันเพิ่งให้ทางเลือกที่ถูกต้องง่ายและรวดเร็ว
กัน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.