【问题标题】:How can I split comma separated values into multiple rows?如何将逗号分隔值拆分为多行?
【发布时间】:2019-08-30 17:41:19
【问题描述】:

我正在尝试将多个以逗号分隔的值拆分为行。

我已经在少量使用逗号分隔值的列中实现了它(使用awk),但在我的真实表中,我必须在 80 列中完成。所以,我正在寻找一种迭代方式。

我需要拆分的输入示例:

CHROM  POS REF  ALT   GT_00  C_00  D_OO  E_00 F_00  GT_11 
 chr1  10   T    A     2,2   1,1   0,1   1,2   1,0   2
 chr1  10   T    G     3      2     1     2     0    0

预期输出:

chr1  10    T    A     2      1     0     1     1   2
chr1  10    T    A     2      1     1     2     0   2
chr1  10    T    G     3      2     1     2     0   0

我已经用下面的代码做到了:

 awk 'BEGIN{FS=OFS="\t"}
  {
    j=split($5,a,",");split($6,b,",");
    split($7,c,",");split($8,d,",");split($9,e,",");
    for(i=1;i<=j;++i)
    {
      $5=a[i];$6=b[i];$7=c[i];$8=d[i];$9=e[i];print
    }}'

但是,正如我之前所说,在我的真实数据中有 80 列(或更多)具有逗号分隔的值。

有没有办法使用迭代?

注意:我需要在 bash 中完成(不是 MySQL、SQL、python...)

【问题讨论】:

  • 第 2 行第 5 列应该是 2?
  • @dibery 是的,我弄错了,我刚刚编辑过。谢谢!
  • 还能有更多这样的2,3,42,34
  • @Jotne 两种情况,但考虑到一行,单元格中逗号分隔值的数量将相同。在同一行中不可能有一个带有1,2,3 的列和另一个带有1,2 的列
  • @kvantour 在 OP 的输入中只需要拆分一系列字段,他/她说这些字段中都有相同数量的逗号

标签: bash awk


【解决方案1】:

这个awk 可能会:

文件:

chr1    10      T       A       2,2     1,1     0,1     1,2     1,0     2
chr1    10      T       G       3       2       1       2       0       0
chr1    10      T       C       5       1,2,3   4,2,1   7       1,8,3   3
chr1    10      T       D       1,2,3,5 4,2,1,8 1,8,3,2 3       5       7

解决方案:

awk '{
    n=0;
    for(i=5;i<=NF;i++) {
        t=split($i,a,",");if(t>n) n=t};
    for(j=1;j<=n;j++) {
        printf "%s\t%s\t%s\t%s",$1,$2,$3,$4;
        for(i=5;i<=NF;i++) {
            split($i,a,",");printf "\t%s",(a[j]?a[j]:a[1])
            };
        print ""
        }
    }' file
chr1    10      T       A       2       1       0       1       1       2
chr1    10      T       A       2       1       1       2       1       2
chr1    10      T       G       3       2       1       2       0       0
chr1    10      T       C       5       1       4       7       1       3
chr1    10      T       C       5       2       2       7       8       3
chr1    10      T       C       5       3       1       7       3       3
chr1    10      T       D       1       4       1       3       5       7
chr1    10      T       D       2       2       8       3       5       7
chr1    10      T       D       3       1       3       3       5       7
chr1    10      T       D       5       8       2       3       5       7

您的测试输入给出:

chr1    10      T       A       2       1       0       1       1       2
chr1    10      T       A       2       1       1       2       1       2
chr1    10      T       G       3       2       1       2       0       0

逗号分隔的值是否连续无关紧要,只要您不在同一行中混合使用 2 个或 3 个逗号即可。

【讨论】:

【解决方案2】:

这是另一个 awk。与之前将字段拆分为数组的解决方案相比,我们使用替换以不同方式解决问题。没有进行字段迭代:

awk '
BEGIN { OFS="\t" }
     { $1=$1;t=$0; }
     { while(index($0,",")) {
         gsub(/,[[:alnum:],]*/,""); print;
         $0=t; gsub(OFS "[[:alnum:]]*,",OFS); t=$0;
       }
       print t
     }' file

它是如何工作的: 这个想法基于两种类型的替换:

  1. gsub(/,[[:alnum:],]*/,""): 这将删除所有由字母数字字符和以逗号开头的逗号组成的子字符串:1,2,3,4 -&gt; 1。这不会更改没有逗号的字段。
  2. gsub(OFS "[[:alnum:]]*,",OFS): 这将删除字母数字字符后跟一个逗号并位于字段开头:1,2,3,4 -&gt; 2,3,4

所以使用这两个替换,我们迭代直到没有逗号。有关[[:alnum:]] 的详细信息,请参阅How can you tell which characters are in which character classes?

输入:

chr1    10      T       A       2,2     1,1     0,1     1,2     1,0     2
chr1    10      T       G       3       2       1       2       0       0
chr1    10      T       C       5       1,2,3   4,2,1   7       1,8,3   3
chr1    10      T       D       1,2,3,5 4,2,1,8 1,8,3,2 3       5       7

输出:

chr1    10  T   A   2   1   0   1   1   2
chr1    10  T   A   2   1   1   2   0   2
chr1    10  T   G   3   2   1   2   0   0
chr1    10  T   C   5   1   4   7   1   3
chr1    10  T   C   5   2   2   7   8   3
chr1    10  T   C   5   3   1   7   3   3
chr1    10  T   D   1   4   1   3   5   7
chr1    10  T   D   2   2   8   3   5   7
chr1    10  T   D   3   1   3   3   5   7
chr1    10  T   D   5   8   2   3   5   7

【讨论】:

  • 感谢这些不同的方法! @kvantour 这三种不同的解决方案中哪一种更快?在我的真实数据集中,我有数亿行和 130 列
  • @Marta_ma 这里是您所有问题的综合答案:(A) 是的,脚本考虑大于 9 的数字,但不考虑浮点数。 (B) 以逗号分隔的带有字母或数字的字符串被同等处理。所以是的,第三列可以有列 (C) 这种方法将比拆分方法更快,因为单个 gsub 击败 NF-5split
  • 它似乎无法正常工作。我想是因为我在第三列中没有一个字母,在某些情况下,我在那个字段中有 None这个词
  • 当我有这样的一行时会发生这种情况:chr1 11 None,G None,T 2 1 0 3 2 2
  • 问题解决了!我习惯写像awk -F"\t"这样的FS。我已经改了但是忘了在最后加上OFS="\t"
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-10
  • 2019-09-11
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
相关资源
最近更新 更多