แยกคอลัมน์อักขระและรับชื่อฟิลด์ในสตริง


11

ฉันต้องการแยกคอลัมน์ที่มีข้อมูลออกเป็นหลายคอลัมน์
ฉันต้องการใช้tstrsplitแต่ข้อมูลชนิดเดียวกันนั้นไม่ได้อยู่ในลำดับเดียวกันในแถวต่างๆและฉันต้องการแยกชื่อของคอลัมน์ใหม่ภายในตัวแปร สิ่งสำคัญที่ควรทราบ: อาจมีข้อมูลจำนวนมาก (ฟิลด์เป็นตัวแปรใหม่) และฉันไม่รู้จักทั้งหมดดังนั้นฉันไม่ต้องการโซลูชัน "ฟิลด์ต่อฟิลด์"

ด้านล่างเป็นตัวอย่างของสิ่งที่ฉันมี:

library(data.table)

myDT <- structure(list(chr = c("chr1", "chr2", "chr4"), pos = c(123L,
                  435L, 120L), info = c("type=3;end=4", "end=6", "end=5;pos=TRUE;type=2"
                  )), class = c("data.table", "data.frame"), row.names = c(NA,-3L))

#    chr pos                  info
#1: chr1 123          type=3;end=4
#2: chr2 435                 end=6
#3: chr4 120 end=5;pos=TRUE;type=2

และฉันต้องการได้รับ:

#    chr pos end  pos type
#1: chr1 123   4 <NA>    3
#2: chr2 435   6 <NA> <NA>
#3: chr4 120   5 TRUE    2

วิธีที่ตรงไปตรงมาที่สุดที่จะได้รับการชื่นชมมาก ( หมายเหตุ: ฉันไม่เต็มใจที่จะไปด้วยวิธี dplyr / tidyr )

คำตอบ:


5

การใช้regexและstringiแพ็คเกจ:

setDT(myDT) # After creating data.table from structure()

library(stringi)

fields <- unique(unlist(stri_extract_all(regex = "[a-z]+(?==)", myDT$info)))
patterns <- sprintf("(?<=%s=)[^;]+", fields)
myDT[, (fields) := lapply(patterns, function(x) stri_extract(regex = x, info))]
myDT[, !"info"]

    chr  pos type end
1: chr1 <NA>    3   4
2: chr2 <NA> <NA>   6
3: chr4 TRUE    2   5

แก้ไข: เพื่อให้ได้ประเภทที่ถูกต้องดูเหมือนว่า (?) type.convert()สามารถใช้ได้:

myDT[, (fields) := lapply(patterns, function(x) type.convert(stri_extract(regex = x, info), as.is = TRUE))]

ฉันได้รับคำเตือนที่ยาวมาก "ตรวจไม่ถูกต้อง. ภายในตัวเองตรวจพบและแก้ไขโดยการคัดลอก (ตื้น) ของ data.table ... "
Moody_Mudskipper

ยังพิมพ์และสิ้นสุดเป็นตัวละครที่นี่ไม่แน่ใจว่าที่คาด
Moody_Mudskipper

1
@Moody_Mudskipper ขอบคุณสำหรับการแสดงความคิดเห็น (1) (คำเตือนนี้คือ (ฉันคิดว่า) ที่เกิดจาก data.table ที่ถูกสร้างขึ้นโดยstructure()ฉันได้อัปเดตคำตอบเพื่อหลีกเลี่ยงปัญหานี้ (2) พวกเขาเป็นตัวละครที่มีวัตถุประสงค์ ... ฉันรู้สึกว่าการแยกวิเคราะห์อย่างถูกต้องจะยาก และคำถามที่แยกต่างหากดูเหมือนว่าคุณจะแก้ไขได้ แต่ในคำตอบของคุณและฉันจะดูและดูว่าฉันสามารถเรียนรู้สิ่งใหม่ ๆ ได้หรือไม่
5252

4

ฉันเดาข้อมูลของคุณจะมาจากไฟล์ VCFถ้าเป็นเช่นนั้นมีเครื่องมือเฉพาะสำหรับปัญหาดังกล่าว - bcftools

ลองสร้างตัวอย่างไฟล์VCFสำหรับการทดสอบ:

# subset some data from 1000genomes data
tabix -h ftp://ftp-trace.ncbi.nih.gov/1000genomes/ftp/release/20100804/ALL.2of4intersection.20100804.genotypes.vcf.gz 17:1471000-1472000 > myFile.vcf
# zip it and index:
bgzip -c myFile.vcf > myFile.vcf.gz
tabix -p vcf myFile.vcf.gz

ตอนนี้เราสามารถใช้bcftools ต่อไปนี้เป็นตัวอย่างเรากำลังเซตย่อยAFและDPจากคอลัมน์INFO :

bcftools query -f '%CHROM %POS %INFO/AF %INFO/DP \n' myFile.vcf.gz 
17  1471199  1916 0.088
17  1471538  2445 0.016
17  1471611  2733 0.239
17  1471623  2815 0.003
17  1471946  1608 0.007
17  1471959  1612 0.014
17  1471975  1610 0.179

ดูคู่มือสำหรับตัวเลือกแบบสอบถามเพิ่มเติม


3

เราสามารถแยก";"จากนั้นปรับรูปร่างกว้างต่อยาวจากนั้นแยกอีกครั้ง"="แล้วปรับรูปร่างกลับเป็นยาวกว้าง:

dcast(
  melt(dt[,  paste0("col", 1:3) := tstrsplit(info, split = ";") ],
       id.vars = c("chr", "pos", "info"))[, -c("info", "variable")][
         ,c("x1", "x2") := tstrsplit(value, split = "=")][
           ,value := NULL][ !is.na(x1), ],
  chr + pos ~ x1, value.var = "x2")

#     chr pos end  pos type
# 1: chr1 123   4 <NA>    3
# 2: chr2 435   6 <NA> <NA>
# 3: chr4 120   5 TRUE    2

รุ่นที่ปรับปรุง / อ่านง่ายขึ้น:

dt[, paste0("col", 1:3) := tstrsplit(info, split = ";")
   ][, melt(.SD, id.vars = c("chr", "pos", "info"), na.rm = TRUE)
     ][, -c("info", "variable")
       ][, c("x1", "x2") := tstrsplit(value, split = "=")
         ][, dcast(.SD, chr + pos ~ x1, value.var = "x2")]

@ Jaap ขอบคุณฉันรู้ว่ามีวิธี DT ที่ดีกว่าของการผูกมัดสิ่ง
zx8754

3

ตอนนี้ฉันจัดการเพื่อรับสิ่งที่ฉันต้องการด้วยรหัสต่อไปนี้:

newDT <- reshape(splitstackshape::cSplit(myDT, "info", sep=";", "long")[, 
                  c(.SD, tstrsplit(info, "="))], 
                 idvar=c("chr", "pos"), direction="wide", timevar="V4", drop="info")
setnames(newDT, sub("V5\\.", "", names(newDT)))

newDT
#    chr pos type end  pos
#1: chr1 123    3   4 <NA>
#2: chr2 435 <NA>   6 <NA>
#3: chr4 120    2   5 TRUE

สองตัวเลือกในการปรับปรุงบรรทัดด้านบนด้วย @ A5C1D2H2I1M1N2O1R2T1 (ผู้ที่ให้ความคิดเห็น):

. ด้วยสองครั้งcSplitก่อนdcast:

cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")]

. ด้วยcSplit/ trstrplitและdcastแทนreshape:

cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")]

1
ฉันต้องการทำคู่เช่นนี้cSplit cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")]
A5C1D2H2I1M1N2O1R2T1

1
หรือแนวคิดเดียวกัน: cSplitตามด้วยtstrsplitตามด้วย:dcast cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")]
A5C1D2H2I1M1N2O1R2T1

@ A5C1D2H2I1M1N2O1R2T1 ขอบคุณมาก! ทั้งสองมีความดีพิเศษสำหรับคู่cSplitตัวเลือก :-)
Cath

2

นี่คือวิธีที่ฉันจะทำ:

library(data.table)

myDT <- structure(list(chr = c("chr1", "chr2", "chr4"), pos = c(123L,
                                                                435L, 120L), info = c("type=3;end=4", "end=6", "end=5;pos=TRUE;type=2"
                                                                )), class = c("data.table", "data.frame"), row.names = c(NA,-3L))

R_strings <- paste0("list(", chartr(";", ",", myDT$info),")")
lists <- lapply(parse(text=R_strings),eval)
myDT[,info:=NULL]
myDT <- cbind(myDT,rbindlist(lists, fill = TRUE))
myDT
#>     chr pos type end  pos
#> 1: chr1 123    3   4   NA
#> 2: chr2 435   NA   6   NA
#> 3: chr4 120    2   5 TRUE

สร้างเมื่อ 2019-11-29 โดยแพ็คเกจ reprex (v0.3.0)


ฉันไม่ต้องการเปลี่ยน ";" เป็น "," และไม่ชอบeval(parse(text=...))... แต่ขอบคุณสำหรับคำตอบของคุณ
Cath

1
ฉันไม่สามารถโต้เถียงกับรสนิยมส่วนตัว แต่parseมีตัวแทนที่ไม่ดีเพราะมักใช้ด้วยเหตุผลที่ไม่ถูกต้องนี่คือกรณีการใช้งานที่เหมาะสมอย่างแม่นยำตั้งแต่สตริงไปจนถึงรหัส คุณมีข้อความที่จัดรูปแบบแล้ว แต่ไม่ได้จัดรูปแบบสำหรับ R และคุณมีชื่อรายการดังนั้นบรรทัดแรกของฉันจึงทำให้โค้ดสำหรับรายการ R โดยเปลี่ยน "a; b" เป็น "list (a, b)" จากนั้นเราประเมินมันและสร้างตารางจากมัน
Moody_Mudskipper

1

คุณสามารถใช้การโทรแยกกันsubสำหรับแต่ละเขตข้อมูลที่ต้องการแยกเช่นสำหรับtype:

myDT$type <- sub("^.*\\btype=([^;]+)\\b.*$", "\\1", myDT$info)

ฉันไม่รู้ว่าจะเกิดอะไรขึ้นและพวกมันก็มีเยอะมากดังนั้นนี่จึงไม่ใช่ตัวเลือก
Cath

1
ยุติธรรมเพียงพอ ฉันไม่รู้สิ่งนี้เมื่อฉันโพสต์คำตอบนี้
Tim Biegeleisen

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