문자 열을 나누고 문자열에서 필드 이름을 가져옵니다.


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

사용 regexstringi패키지 :

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))]

"잘못된 .internal.selfref가 data.table의 (얕은) 사본을 가져 와서 감지되고 수정되었습니다."라는 매우 긴 경고가 표시됩니다.
Moody_Mudskipper

또한 type과 end가 여기에있는 문자입니다. 그것이 예상되는지 확실하지 않습니다
Moody_Mudskipper

1
@Moody_Mudskipper 의견을 보내 주셔서 감사합니다. (1) (이 경고는 (이것은 생각합니다) 생성 된 data.table에 의해 발생합니다. structure()이 문제를 피하기 위해 답변을 업데이트했습니다. (2) 의도적 인 문자입니다 ... 정확하게 파싱하는 것이 어렵다고 느꼈습니다. 그리고 당신은 당신의 대답을 통해 그것을 해결 한 것 같습니다 그리고 내가 뭔가 새로운 것을 배울 수 있는지 살펴볼 것입니다
sindri_baldur

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 를 사용할 수 있습니다 . 다음 은 INFO 열 에서 AFDP 를 서브 셋팅하는 예입니다 .

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/ trstrplitdcast대신 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 '

정말 고마워요! 두 가지 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

reprex 패키지 (v0.3.0)로 2019-11-29에 작성


";"을 변경할 필요가 없습니다. ","로 마음에 들지 eval(parse(text=...))않지만 ...하지만 답장을 보내 주셔서 감사합니다
Cath

1
개인적 취향으로 논쟁 할 수는 없지만 parse종종 잘못된 이유로 사용되기 때문에 나쁜 담당자가 있습니다. 여기서는 문자열에서 코드로 이동하는 적절한 유스 케이스입니다. 텍스트의 형식을 지정했지만 R의 형식은 지정하지 않았고 목록의 이름을 지정 했으므로 첫 번째 줄은 "a; b"를 "list (a, b)"로 변경하여 R 목록을 코딩합니다. 그런 다음 평가하고 테이블을 만듭니다.
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.