【问题标题】:Uniq in awk; removing duplicate values in a column using awkawk 中的 uniq;使用 awk 删除列中的重复值
【发布时间】:2010-06-04 23:18:42
【问题描述】:

我有一个大数据文件,格式如下:

ENST00000371026 WDR78,WDR78,WDR78,  WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 2,
ENST00000371023 WDR32   WD repeat domain 32 isoform 2
ENST00000400908 RERE,KIAA0458,  atrophin-1 like protein isoform a,Homo sapiens mRNA for KIAA0458 protein, partial cds.,

这些列是制表符分隔的。列中的多个值以逗号分隔。我想删除第二列中的重复值以产生如下结果:

ENST00000371026 WDR78   WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 1,WD repeat domain 78 isoform 2,
ENST00000371023 WDR32   WD repeat domain 32 isoform 2
ENST00000400908 RERE,KIAA0458   atrophin-1 like protein isoform a,Homo sapiens mRNA for KIAA0458 protein, partial cds.,

我尝试了下面的代码,但它似乎没有删除重复的值。

awk ' 
BEGIN { FS="\t" } ;
{
  split($2, valueArray,",");
  j=0;
  for (i in valueArray) 
  { 
    if (!( valueArray[i] in duplicateArray))
    {
      duplicateArray[j] = valueArray[i];
      j++;
    }
  };
  printf $1 "\t";
  for (j in duplicateArray) 
  {
    if (duplicateArray[j]) {
      printf duplicateArray[j] ",";
    }
  }
  printf "\t";
  print $3

}' knownGeneFromUCSC.txt

如何正确删除第 2 列中的重复项?

【问题讨论】:

    标签: bash awk unique


    【解决方案1】:

    由于NR==2,您的脚本仅作用于文件中的第二条记录(行)。我把它拿出来了,但它可能是你想要的。如果是这样,你应该把它放回去。

    in 运算符检查 index 的存在,而不是值,因此我将 duplicateArray 设为使用来自 @ 的值的关联数组* 987654324@ 作为其索引。这样就不必在循环内循环遍历两个数组。

    split 语句将“WDR78,WDR78,WDR78”视为四个字段而不是三个字段,因此我添加了一个if 以防止它打印空值,这将导致在以下情况下打印“,WDR78” if 不在那里。

    * 实际上,AWK 中的所有数组都是关联的。

    awk '
    BEGIN { FS="\t" } ;
    {
      split($2, valueArray,",");
      j=0;
      for (i in valueArray)
      { 
        if (!(valueArray[i] in duplicateArray))
        { 
          duplicateArray[valueArray[i]] = 1
        }
      };
      printf $1 "\t";
      for (j in duplicateArray)
      {
        if (j)    # prevents printing an extra comma
        {
          printf j ",";
        }
      }
      printf "\t";
      print $3
      delete duplicateArray    # for non-gawk, use split("", duplicateArray)
    }'
    

    【讨论】:

      【解决方案2】:

      抱歉,我知道你问过关于 awk 的问题……但是 Perl 让这变得更简单了:

      $ perl -n -e ' @t = split(/\t/);
        %t2 = map { $_ => 1 } split(/,/,$t[1]);
        $t[1] = join(",",keys %t2);
        print join("\t",@t); ' knownGeneFromUCSC.txt
      

      【讨论】:

      • +1 非常感谢您的回答。这个解决方案比我的好。但是我也很好奇为什么我的解决方案不起作用。出于这个原因,我将暂时设置一个接受的答案。也许有人会知道如何在 awk 中做到这一点。
      【解决方案3】:

      Perl:

      perl -F'\t' -lane'
        $F[1] = join ",", grep !$_{$_}++, split ",", $F[1]; 
        print join "\t", @F; %_ = ();
        ' infile  
      

      awk:

      awk -F'\t' '{
        n = split($2, t, ","); _2 = x
        split(x, _) # use delete _ if supported
        for (i = 0; ++i <= n;)
          _[t[i]]++ || _2 = _2 ? _2 "," t[i] : t[i]
        $2 = _2 
        }-3' OFS='\t' infile
      

      awk 脚本中的第 4 行用于在过滤唯一值后保留第二个字段中值的原始顺序。

      【讨论】:

        【解决方案4】:

        Pure Bash 4.0(一个关联数组):

        declare -a part                            # parts of a line
        declare -a part2                           # parts 2. column
        declare -A check                           # used to remember items in part2
        
        while read  line ; do
          part=( $line )                           # split line using whitespaces
          IFS=','                                  # separator is comma
          part2=( ${part[1]} )                     # split 2. column using comma
          if [ ${#part2[@]} -gt 1 ] ; then         # more than 1 field in 2. column?
            check=()                               # empty check array
            new2=''                                # empty new 2. column
            for item in ${part2[@]} ; do 
              (( check[$item]++ ))                 # remember items in 2. column
              if [ ${check[$item]} -eq 1 ] ; then  # not yet seen?
                new2=$new2,$item                   # add to new 2. column
              fi 
            done
            part[1]=${new2#,}                      # remove leading comma
          fi 
          IFS=$'\t'                                # separator for the output
          echo "${part[*]}"                        # rebuild line
        done < "$infile"
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-30
          • 2017-07-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多