【问题标题】:Split file with multiple delimited entries in some columns into separate lines将某些列中具有多个分隔条目的文件拆分为单独的行
【发布时间】:2017-06-20 04:52:57
【问题描述】:

我有一个非常大的文件,具有以下基本格式,并带有许多附加字段:

 posA,id1,id2,posB,id3,name,(n additional fields)
 1,ENST7,ENSP93,1,ENSG92,Gene1
 2,ENST25;ENST76;ENST35,ENSP91;ENSP77;ENSP78,515;544;544,ENSG765,Gene2
 3,ENST25;ENST76;ENST35,ENSP91;ENSP77;ENSP78,515;544;544,ENSG765,Gene2
 4,ENST54;ENST93,ENSP83;ENSP36,1864;722,ENSG48,Gene3
 5,ENST54;ENST93,ENSP83;ENSP36,1864;722,ENSG48,Gene3
 6,ENST54;ENST93,ENSP83;ENSP36,1864;722,ENSG48,Gene3

第一行 (posA=1) 的每一列都有一个条目,不需要修改。对于某些列的多个条目数量可变的行,对于第三行 (posA=2),“id1”的第一个条目 (ENST25) 与“id2”的第一个条目 (ENSP91) 和第一个条目配对对于“posB”(515)等,但具有单个条目的列(例如,“posA”、“id3”、“name”)适用于第 2-4 列中的所有配对条目。除了第 2-4 列之外,一些字段也很少包含多个条目。

我想将包含多个条目的列拆分为单独的行,同时保留其他列中的数据,如下所示:

 posA,id1,id2,posB,id3,name,(n additional fields)
 1,ENST7,ENSP93,1,ENSG92,Gene1
 2,ENST25,ENSP91,515,ENSG765,Gene2
 2,ENST76,ENSP77,544,ENSG765,Gene2
 2,ENST35,ENSP78,544,ENSG765,Gene2
 3,ENST25,ENSP91,515,ENSG765,Gene2
 3,ENST76,ENSP77,544,ENSG765,Gene2
 3,ENST35,ENSP78,544,ENSG765,Gene2
 4,ENST54,ENSP83,1864,ENSG48,Gene3
 4,ENST93,ENSP36,722,ENSG48,Gene3
 ...

解决这个问题的最佳方法是什么?

谢谢!

【问题讨论】:

  • 欢迎来到 StackOverflow。你需要解释更多关于配对的信息。请提供更多示例输入。对我来说,现有的解释和单个示例不足以理解语法。我发现了; 而不是,,但我预计会分成2,c,+2,d,e,f,+。还请说明您使用的语言并展示您自己的编码尝试,以免给人留下只是在寻找免费编码服务的印象。强烈推荐使用tour,您绝对应该阅读How to Ask

标签: linux bash


【解决方案1】:

以您的示例为例,最多会有两个复合属性,然后使用简单的带有子字符串删除的参数扩展,您可以相当容易地完成您想要的,例如

#!/bin/bash

while IFS=, read -r p a1 a2 a3; do 
    [[ $a1 =~ ';' ]] && { 
        printf "%s,%s,%s,%s\n" "$p" "${a1%;*}" "${a2%;*}" "$a3"
        printf "%s,%s,%s,%s\n" "$p" "${a1#*;}" "${a2#*;}" "$a3"
    } || printf "%s,%s,%s,%s\n" "$p" "$a1" "$a2" "$a3"
done < "$1"

[[ $a1 =~ ';' ]]$a1 中检查';',如果找到,则使用${a1%;*}${a2%;*} 挑选$a1$a2 中的第一个属性。然后对于每个中的第二个属性,使用${a1#*;}${a2#*;}

如果$a1 中不包含';',则打印属性不变。 IFS=, 确保参数在 ',' 上被分词。

注意:您应该在最终脚本中添加文件名有效等验证。如果您愿意,也可以使用echo

使用/输出示例

$ splitattrib.sh file
Pos,Attribute1,Attribute2,Attribute3
1,a,b,-
2,c,e,+
2,d,f,+

【讨论】:

    【解决方案2】:

    最好的是将它分成三个部分。

    您有 3 种线条图案。一个有 6 列。另一个有 12 个,最后一个是 9 个。

    6 列 => 1 行
    12 列 => 3 行
    9 列 => 2 行

    不应修改您的 6 列。所以提醒 12 和 9。您可以在 ifelse ifelse 中将它们分开。喜欢:

    if( column == 6 ){...}  
    else if( column == 12 ){...}  
    else {...}   
    

    这是一个 Perl 单行 解决方案:

    perl -a -F",|;" -lne '$s=scalar @F;if($s==6){print join ",",@F}elsif($s==12){print join",",@F[0,1,4,7,-2,-1];print join",",@F[0,1,5,8,-2,-1];print join",",@F[0,1,6,9,-2,-1];}else{print join",",@F[0,1,3,5,-2,-1];print join",",@F[0,1,4,6,-2,-1]} ' file
    

    对于你的输入,输出是:

     1,ENST7,ENSP93,1,ENSG92,Gene1
     2,ENST25,ENSP91,515,ENSG765,Gene2
     2,ENST25,ENSP77,544,ENSG765,Gene2
     2,ENST25,ENSP78,544,ENSG765,Gene2
     3,ENST25,ENSP91,515,ENSG765,Gene2
     3,ENST25,ENSP77,544,ENSG765,Gene2
     3,ENST25,ENSP78,544,ENSG765,Gene2
     4,ENST54,ENSP83,1864,ENSG48,Gene3
     4,ENST54,ENSP36,722,ENSG48,Gene3
     5,ENST54,ENSP83,1864,ENSG48,Gene3
     5,ENST54,ENSP36,722,ENSG48,Gene3
     6,ENST54,ENSP83,1864,ENSG48,Gene3
     6,ENST54,ENSP36,722,ENSG48,Gene3
    

    【讨论】:

      【解决方案3】:

      假设您的多个条目用分号 ; 分隔,这是要执行的 awk 版本。

      BEGIN {
          FS="[,]"
      }
      {
          if ($0 ~ /^[0-9].*/) {
              end_split_field = 0
              for (f=2;f<=NF;f++) {
                  if ($f ~ /.*;.*/) {
                      end_split_field=f
                  }
              }
      
              if (end_split_field == 0) {
                  print $0
              } else {
                  for (f=2;f<=end_split_field;f++) {
                      n = split($f, a, ";") #split and return the number
                      for (i=1;i<=n;i++) {
                          b[f, i] = a[i]
                      }
                  }
      
                  for (i=1;i<=n;i++) {
                      printf $1","
                      for (j=2;j<=end_split_field;j++) {
                          printf b[j, i]","
                      }
                      for (k=end_split_field;k<NF;k++) {
                          printf $k","
                      }
                      printf $NF"\n"
                  }
              }
          } else {
              print $0
          }
      }
      

      将上面的内容保存为input.awk,示例输入输出

      $ cat input
      Pos,Attribute1,Attribute2,Attribute3
      1,a,b,-
      2,c;d,e;f,+
      3,g;h;i,j;k;l,-
      

      我们可以得到分割输出

      $ awk -f input.awk input
      Pos,Attribute1,Attribute2,Attribute3
      1,a,b,-
      2,c,e,+
      2,d,f,+
      3,g,j,-
      3,h,k,-
      3,i,l,-
      

      【讨论】:

      • 感谢您的浏览!这似乎拆分了行,但在我的真实数据中,只有每个属性的第一个条目打印在新行中,即:Pos,Attribute1,Attribute2,Attribute3 1,a,b,- 2,c,e,+ 3,g,j,-
      • 你能提供一段你的真实数据吗?这可能是格式的原因。
      • 嗨,Alijandro,再次感谢!我用更具代表性的数据编辑了我的问题。
      猜你喜欢
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-13
      相关资源
      最近更新 更多