tách các cột ký tự và lấy tên của trường trong chuỗi


11

Tôi cần chia một cột chứa thông tin thành nhiều cột.
Tôi sẽ sử dụng tstrsplitnhưng cùng loại thông tin không theo cùng một thứ tự giữa các hàng và tôi cần trích xuất tên của cột mới trong biến. Điều quan trọng cần biết: có thể có nhiều mẩu thông tin (các trường để trở thành biến mới) và tôi không biết tất cả chúng, vì vậy tôi không muốn có giải pháp "theo trường".

Dưới đây là một ví dụ về những gì tôi có:

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

Và tôi muốn nhận được:

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

Một cách đơn giản nhất để có được điều đó sẽ được nhiều đánh giá cao! ( Lưu ý: Tôi không sẵn sàng đi theo cách dplyr / tidyr )

Câu trả lời:


5

Sử dụng regexvà các stringigói:

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

Chỉnh sửa: Để có được loại chính xác, có vẻ như (?) type.convert()Có thể được sử dụng:

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

Tôi nhận được một cảnh báo rất dài ".internal.selfref không hợp lệ phát hiện và cố định bằng cách lấy một (cạn) sao chép của data.table ..."
Moody_Mudskipper

cũng nhập và kết thúc là ký tự ở đây, không chắc là điều đó có được mong đợi hay không
Moody_Mudskipper

1
@Moody_Mudskipper Cảm ơn bạn đã bình luận. (1) (Cảnh báo này là (tôi nghĩ) gây ra bởi dữ liệu. Có thể được tạo bởi structure()tôi đã cập nhật câu trả lời để tránh vấn đề này (2) Chúng là những ký tự có chủ đích ... Tôi cảm thấy việc phân tích chúng một cách chính xác sẽ khó khăn và một câu hỏi riêng biệt. Có vẻ như bạn đã giải quyết nó mặc dù trong câu trả lời của bạn và tôi sẽ xem và xem liệu tôi có thể học được điều gì mới không.
sindri_baldur

4

Tôi đoán dữ liệu của bạn đến từ tệp VCF , nếu vậy có một công cụ chuyên dụng cho các vấn đề như vậy - bcftools .

Hãy tạo tệp VCF mẫu để thử nghiệm:

# 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

Bây giờ chúng ta có thể sử dụng bcftools . Dưới đây là một ví dụ, chúng tôi đang đặt lại AFDP từ cột 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

Xem hướng dẫn để biết thêm các tùy chọn truy vấn .


3

Chúng ta có thể chia nhỏ ";"sau đó định hình lại từ rộng đến dài, sau đó phân chia lại "=", sau đó định hình lại thành dài:

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

Một phiên bản cải tiến / dễ đọc hơn:

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 Cảm ơn bạn, tôi biết có một cách xâu chuỗi DT tốt hơn.
zx8754

3

Hiện tại, tôi đã có được những gì tôi muốn với đoạn mã sau:

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

Hai tùy chọn để cải thiện các dòng trên, nhờ @ A5C1D2H2I1M1N2O1R2T1 (người đã cho chúng trong các nhận xét):

. với một gấp đôi cSplittrước dcast:

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

. với cSplit/ trstrplitdcastthay vì reshape:

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

1
Tôi sẽ làm một đôi cSplit, như thế này : cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")].
A5C1D2H2I1M1N2O1R2T1

1
Hoặc, cùng một khái niệm: cSplittheo sau tstrsplit, theo sau dcast: cSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")].
A5C1D2H2I1M1N2O1R2T1

@ A5C1D2H2I1M1N2O1R2T1 Cảm ơn rất nhiều! Cả hai đều tuyệt vời, với một cSplittùy chọn đặc biệt cho tùy chọn kép :-)
Cath

2

Đây là cách tôi làm:

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

Được tạo vào ngày 2019-11-29 bởi gói reprex (v0.3.0)


Tôi không có nhu cầu thay đổi ";" vào "," và không thích eval(parse(text=...))... nhưng dù sao cũng cảm ơn câu trả lời của bạn
Cath

1
Tôi không thể tranh luận với sở thích cá nhân nhưng parsecó một đại diện xấu vì nó thường được sử dụng vì lý do sai, đây chính xác là trường hợp sử dụng phù hợp của nó, đi từ chuỗi sang mã. Bạn đã định dạng văn bản, nhưng không được định dạng cho R và bạn đã đặt tên danh sách, vì vậy dòng đầu tiên của tôi tạo mã cho danh sách R, bằng cách thay đổi "a; b" thành "danh sách (a, b)". Sau đó, chúng tôi đánh giá nó và làm cho một bảng ra khỏi nó.
Moody_Mudskipper

1

Bạn có thể sử dụng các lệnh gọi riêng subcho từng trường được trích xuất mong muốn, ví dụ type:

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

Tôi không biết tất cả các hồ sơ sẽ xảy ra và chúng có thể rất nhiều vì vậy đây không phải là một lựa chọn
Cath

1
Đủ công bằng; Tôi đã không biết điều này khi tôi đăng câu trả lời này.
Tim Biegeleisen

Tôi sẽ thêm nó (btw bạn không đưa ra đầu ra mong muốn, câu trả lời của bạn bỏ lỡ một số dòng ...)
Cath
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.