文字列を分割し、文字列内のフィールドの名前を取得します


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

ここでもタイプと終了は文字ですが、それが予期されているかどうかは
わかり

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(コメントでそれらを提供した)のおかげで、上記の行を改善する2つのオプション:

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ようにdoubleを実行します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

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

私はそれを追加します(あなたが望む出力を与えない、あなたの答えはいくつかの行を逃します...)
Cath
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.