【问题标题】:split character columns and get names of field in string拆分字符列并获取字符串中的字段名称
【发布时间】:2020-03-24 23:27:37
【问题描述】:

我需要将包含信息的列拆分为几列。
我会使用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 方式

【问题讨论】:

    标签: r data.table reshape


    【解决方案1】:

    使用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 ...”
    • 此处的 type 和 end 也是字符,不确定是否符合预期
    • @Moody_Mudskipper 感谢您的评论。 (1)(这个警告(我认为)是由structure()创建的data.table引起的,我已经更新了答案以避免这个问题(2)它们是故意的字符......我觉得正确解析它们会是一个很难和单独的问题。看来你在你的回答中解决了它,我会看看我是否能学到新的东西。
    【解决方案2】:

    我猜您的数据来自VCF file,如果是这样的话,有专门的工具可以解决此类问题 - 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
    

    有关更多query 选项,请参阅手册。

    【讨论】:

    • 如果您有一个多样本 vcf 并希望将样本放在左侧列中,然后每一列都是 GT 的不同变体(即转置您上面的建议)可以在单独使用 bcftools?
    • @tacrolimus 作为新问题提出,链接到这篇文章,带有bioinformatics 标签。
    【解决方案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")]
    

    【讨论】:

      【解决方案4】:

      现在,我设法通过以下代码得到了我想要的:

      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(他们在 cmets 中提供)的两个选项:

      。在dcast之前有一个双cSplit

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

      【讨论】:

      • 我会做一个双重cSplit,像这样:cSplit(cSplit(myDT, "info", ";", "long"), "info", "=")[, dcast(.SD, chr + pos ~ info_1, value.var = "info_2")]
      • 或者,同样的概念:cSplit 后接tstrsplit,后接dcastcSplit(myDT, "info", ";", "long")[, c("t1", "t2") := tstrsplit(info, "=", fixed = TRUE)][, dcast(.SD, chr + pos ~ t1, value.var = "t2")]
      • @A5C1D2H2I1M1N2O1R2T1 非常感谢!两者都很棒,特别是双 cSplit 选项:-)
      【解决方案5】:

      我会这样做:

      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 package (v0.3.0) 于 2019 年 11 月 29 日创建

      【讨论】:

      • 我不需要更改“;”进入“,”并且不喜欢eval(parse(text=...))...但仍然感谢您的回答
      • 我无法与个人品味争论,但parse 的名声不好,因为它经常被错误地使用,这正是它的适当用例,从字符串到代码。您已经格式化了文本,但没有为 R 格式化,并且您已经命名了列表,所以我的第一行通过将“a;b”更改为“list(a,b)”来使其成为 R 列表的代码。然后我们评估它并用它制作一个表格。
      【解决方案6】:

      您可以为每个所需的提取字段单独调用sub,例如对于type

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

      【讨论】:

      • 我不知道会发生的所有文件,它们可能很多,所以这不是一个选项
      • 还不错;当我发布这个答案时我不知道这一点。
      • 我会添加它(顺便说一句,你没有给出想要的输出,你的答案错过了一些行......)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      • 2020-12-01
      • 2021-05-10
      相关资源
      最近更新 更多