拆分字符列并获取字符串中的字段名称


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的方式

Answers:


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

我收到很长的警告“通过获取(浅)data.table副本检测到并修复了无效的.internal.selfref ...”
Moody_Mudskipper

此处的类型和结尾也是字符,不确定是否可以预期
Moody_Mudskipper

1
@Moody_Mudskipper感谢您的评论。(1)(此警告(我认为)是由structure()我创建的data.table导致的,我已更新了答案以避免此问题(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/

1
或相同的概念:cSplit其次是tstrsplit,然后是dcastcSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")]
A5C1D2H2I1M1N2O1R2T1'1/

@ 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.