แยกข้อความในคอลัมน์ของกรอบข้อมูลตามส่วนของรูปแบบ

การใช้ R ฉันกำลังพยายามแยกข้อความในคอลัมน์โดยมีตัวคั่นที่ระบุเป็นสองคอลัมน์ใหม่เสมอ กรอบข้อมูลตัวอย่างจะเป็นด้านล่าง:

repdf <- data.frame(a=c("abc(100)","def(95)","ghi(100)","j_(klm)(100)"),b=c("abc(100)","def(95)","ghi(100)","j_(klm)(100)"))

พูดง่ายๆ ฉันต้องการแยกที่ทุกวงเล็บเปิด "(" แต่เฉพาะในกรณีที่ตามด้วยตัวเลข แต่ไม่สูญเสียตัวเลข นั่นคือ ผลลัพธ์ที่ต้องการควรมีลักษณะดังนี้:

a1      a2    b1      b2
abc     100)  abc     100)
def     95)   def     95)
ghi     100)  ghi     100)
j_(klm) 100)  j_(klm) 100)

ฉันได้ลองทำงานกับsplitstackshape::cSplit และ stringr::str_split_fixed แล้ว แต่ก็ไม่มีประโยชน์ cSplit(repdf,c("a","b"),"(") แยกที่ทุกๆ '('

   a_1  a_2  a_3 b_1  b_2  b_3
1: abc 100)   NA abc 100)   NA
2: def  95)   NA def  95)   NA
3: ghi 100)   NA ghi 100)   NA
4:  j_ klm) 100)  j_ klm) 100)

cSplit(repdf,c("a","b"),"\\(([0-9])",fixed=FALSE) ลบหมายเลขแรกออก หากเป็นไปได้ที่จะใช้ \1 เพื่อเพิ่มการจับภาพไปยังกลุ่มที่สองก็คงจะดี แต่น่าเสียดายที่ไม่ใช่

       a_1 a_2     b_1 b_2
1:     abc 00)     abc 00)
2:     def  5)     def  5)
3:     ghi 00)     ghi 00)
4: j_(klm) 00) j_(klm) 00)

as.data.frame(lapply(repdf,function(x)str_split_fixed(x,"\\(",n=2))) อนุญาตให้ฉันแบ่งได้เป็น 2 คอลัมน์เท่านั้น แต่แน่นอนว่าจะใช้เวลาเฉพาะรายการแรกเท่านั้น:

  a.1       a.2 b.1       b.2
1 abc      100) abc      100)
2 def       95) def       95)
3 ghi      100) ghi      100)
4  j_ klm)(100)  j_ klm)(100)

person FM Kerckhof    schedule 03.08.2017    source แหล่งที่มา
comment
ตามที่กล่าวไว้ที่นี่, extract จาก tidyr อาจให้ความยืดหยุ่นที่จำเป็น   -  person Konrad    schedule 03.08.2017
comment
@Konrad ในขณะที่เป็นไปได้จริง ๆ (เช่น tidyr::extract(data=repdf,a,into=c('tax','prob'),"(.*)\\((?=\\d)(.*)",perl=TRUE) มันไม่ง่ายเลยที่จะคาดการณ์ data.frame ที่มีหลายคอลัมน์   -  person FM Kerckhof    schedule 23.08.2017
comment
@คอนราด: คุณทำให้ฉันคิด แม้ว่าในตอนแรกฉันจะติดอยู่กับเวอร์ชันการประเมินที่ไม่ได้มาตรฐานของ tidyr::extract แต่ฟังก์ชันการประเมินมาตรฐานนั้นใช้งานได้ค่อนข้างดีจริงๆ ฉันได้เพิ่มคำตอบด้านล่างที่ใช้แล้ว   -  person FM Kerckhof    schedule 23.08.2017


คำตอบ (2)


นี่คือจุดที่ lookahead มีประโยชน์... โดยพื้นฐานแล้ว เรามองหา ( ที่ตามด้วยตัวเลข \\d แต่การมองไปข้างหน้าไม่ได้ใช้ตัวเลขในการแยก

do.call(cbind, lapply(repdf, function(x){
  do.call(rbind, strsplit(as.character(x), "\\((?=\\d)", perl = TRUE))
}))

# [,1]      [,2]   [,3]      [,4]  
# [1,] "abc"     "100)" "abc"     "100)"
# [2,] "def"     "95)"  "def"     "95)" 
# [3,] "ghi"     "100)" "ghi"     "100)"
# [4,] "j_(klm)" "100)" "j_(klm)" "100)"
person emilliman5    schedule 03.08.2017

ได้รับแรงบันดาลใจจากคำแนะนำของ @Konrad ฉันพบว่าสิ่งต่อไปนี้ใช้งานได้กับ tidyr::extract แต่ฉันจำเป็นต้องใช้เวอร์ชันการประเมินมาตรฐาน tidyr::extract_:

do.call(cbind, lapply(seq_along(repdf),
       function(df, i){
         tidyr::extract_(data=df[i], col = names(df[i]),
                  into=c(paste0("tax",i),paste0("prob",i)),
                  regex = "(.*)\\((?=\\d)(.*)",perl=TRUE)}, df=repdf))

ข้อดีคือ คุณสามารถตั้งชื่อที่กำหนดไว้ล่วงหน้าให้กับคอลัมน์ที่แยกแล้วได้ (เช่น ที่นี่ tax และ prob) ซึ่งอาจมีประโยชน์สำหรับการถกเถียงข้อมูลดาวน์สตรีม ในทางกลับกัน สิ่งที่น่าสนใจเกี่ยวกับคำตอบที่ยอมรับก็คือ เมื่อไม่ทราบจำนวนคอลัมน์ที่ข้อมูลถูกแบ่งออกเป็นล่วงหน้า (หรือตัวแปรขึ้นอยู่กับคอลัมน์กรอบข้อมูลเริ่มต้น) มันจะยังคงทำงานได้อย่างมีเสน่ห์

person FM Kerckhof    schedule 24.08.2017