【问题标题】:removal of lines based on category in a column根据列中的类别删除行
【发布时间】:2014-08-07 01:56:51
【问题描述】:

我正在尝试删除 APPID 相同且“类别”列属于同一类别的逗号分隔文件中的行。输入:

1,APPID,ID2,ID3,5,6,7,8,9,Category,
5002 , APP-1 ,,,,,,,, Cell ,
5002 , APP-1 ,,,,,,,, Cell ,
5002 , APP-2 ,,,,,,,, Cell ,
5002 , APP-2 ,,,,,,,, Enzyme ,
5002 , APP-3 ,,,,,,,, Cell ,
5002 , APP-3 ,,,,,,,, Biochemical ,
5002 , APP-4 ,,,,,,,, Enzyme ,
5002 , APP-4 ,,,,,,,, Enzyme ,
5002 , APP-4 ,,,,,,,, Enzyme ,
5002 , APP-4 ,,,,,,,, Cell ,

理想输出:

1,APPID,3,4,5,6,7,8,9,Category ,
5002 , APP-2 ,,,,,,,, Cell ,
5002 , APP-2 ,,,,,,,, Enzyme ,
5002 , APP-3 ,,,,,,,, Cell ,
5002 , APP-3 ,,,,,,,, Biochemical ,
5002 , APP-4 ,,,,,,,, Enzyme ,
5002 , APP-4 ,,,,,,,, Enzyme ,
5002 , APP-4 ,,,,,,,, Enzyme ,
5002 , APP-4 ,,,,,,,, Cell ,

“APP-1”被删除,因为它们的第 2 列是相同的,并且它们的类别列都是“单元格”。

保留“APP-2”是因为它们的“类别”列中有一个“细胞”,另一个是“生化”。

“APP-3”中的类似场景,其“类别”列包含异构类别。

(更新)保留“APP-4”是因为它们的列包含异构类别。我们希望保留“5002,APP-4 ......”的重复,这将在下一个脚本中处理。这一步是快速删除“Category”列(如果它们的APPID相同)中同质的数万个数据点,这样下一个脚本中的数组就不会爆炸。

到目前为止的尝试似乎没有奏效(来自这里的参考:removal of redundant lines based on value in last column

  awk -F " ," '!a[$1,$2,$3,$4,$5,$6,$7,$8,$9]++' input

每个文件的处理文件大约有百万行,总共需要处理大约 400 个文件。执行速度在这里似乎至关重要。有哪位大师能开导吗?谢谢!

【问题讨论】:

  • 一个APP-ID和一个类别重复3次怎么办?如果出现 3 次相同的 APP-ID,而其中只有两个属于同一类别,会发生什么情况?

标签: python shell awk


【解决方案1】:
def killDups(infilepath, outfilepath):
    data = {}
    with open(infilepath) as infile:
        infile.readline()
        for i,line in enumerate(infile):
            line = line.strip()
            cols = [col.strip() for col in line.split(',')]
            appid, cat = cols[1], cols[-1]
            if appid not in data:
                data[appid] = {cat:i}
            elif cat in data[appid]:
                data[appid].pop(cat)

    whitelist = set()
    for k,v in data.items():
        whitelist.update(v.values())

    with open(infilepath) as infile, open(outfilepath, 'w') as outfile:
        outfile.write(infile.readline())
        for i,line in enumerate(infile):
            if i in whitelist:
                outfile.write(line)

【讨论】:

    【解决方案2】:
    $ awk -F, '
      { key=$2 FS $(NF-1); nr2key[NR]=key; key2val[key]=$0; cnt[key]++ }
      END {
          for (i=1;i<=NR;i++) {
              key=nr2key[i]
              if (cnt[key] == 1) {
                  print key2val[key]
              }
          }
      }
      ' file
    1,APPID,ID2,ID3,5,6,7,8,9,Category,
    5002 , APP-2 ,,,,,,,, Cell ,
    5002 , APP-2 ,,,,,,,, Enzyme ,
    5002 , APP-3 ,,,,,,,, Cell ,
    5002 , APP-3 ,,,,,,,, Biochemical ,
    

    【讨论】:

    • 谢谢埃德!该脚本完美运行。但是,如果我通过在底部包含“5002 , APP-4 ,,,,,,,, Cell ”来稍微修改输入文件,则脚本只会打印出“5002 , APP-4 ,,,,,, ,, Cell ” 用于 APP-4,而不是打印出所有 APP-4 ...Enzyme/Cell。我可以知道你有什么意见吗?谢谢。 (我已经更新了问题。)
    • 下次您有问题时,请仔细考虑样本输入和预期输出,以便它代表所有困难/有趣的组合,这样我们就不会浪费时间解决错误的问题。
    【解决方案3】:

    这是awk的另一种方式:

    awk -F, '
    !patt[$2,$(NF-1)]++ { lines[$2,$(NF-1)] = $0 } 
    END {
        for (line in lines)
          if (patt[line] == 1)
            print lines[line]
    }' file | sort -t, -nk1,2
    1,APPID,ID2,ID3,5,6,7,8,9,Category,
    5002 , APP-2 ,,,,,,,, Cell ,
    5002 , APP-2 ,,,,,,,, Enzyme ,
    5002 , APP-3 ,,,,,,,, Biochemical ,
    5002 , APP-3 ,,,,,,,, Cell ,
    
    • 如果两列不在patt 数组中,则将整行分配给同一键的行数组
    • END 块中遍历行数组。如果模式数组中的键计数为 1,则打印该行。
    • 对要排序的输出管道进行排序。

    注意:如需更优雅的方式使用原版awk,请参考Ed Morton's 解决方案。

    如果你有 GNU awk 那么(类似的逻辑,但使用内置的排序算法):

    gawk -F, '
    BEGIN { PROCINFO["sorted_in"] = "@ind_num_desc" }
    !patt[$2,$(NF-1)]++ {
        lines[$2,$(NF-1)] = $0
    }
    END {
        for (line in lines)
          if (patt[line] == 1)
            print lines[line]
    }' file
    

    如果你可以使用perl 那么:

    perl -F, -lane'                        
        print and next if $.==1;        # print the header
        $key = "@F[1,-1]";              # form the key using two columns
        $h{$key} or push @rec, $key;    # if key is not in hash push to array (for order)
        push @{$h{$key}}, $_            # create hash of arrays
    }{                                  # In the END block ...
        print @{$h{$_}} for grep { @{$h{$_}} == 1 } @rec   # print line whose array count is 1
    ' file 
    1,APPID,ID2,ID3,5,6,7,8,9,Category,
    5002 , APP-2 ,,,,,,,, Cell ,
    5002 , APP-2 ,,,,,,,, Enzyme ,
    5002 , APP-3 ,,,,,,,, Cell ,
    5002 , APP-3 ,,,,,,,, Biochemical ,
    

    更新:

    perl -F, -lane'                        
        print and next if $.==1;       
        $seen{$F[1],$F[-1]}++ or push @rec, [$F[1], $F[-1]];    
        push @{$h{$F[1]}{$F[-1]}}, $_           
    }{    
        for (@rec) {
            next if keys %{$h{$_->[0]}} == 1;
            print join "\n", @{$h{$_->[0]}{$_->[1]}};
        }
    ' file
    1,APPID,ID2,ID3,5,6,7,8,9,Category,
    5002 , APP-2 ,,,,,,,, Cell ,
    5002 , APP-2 ,,,,,,,, Enzyme ,
    5002 , APP-3 ,,,,,,,, Cell ,
    5002 , APP-3 ,,,,,,,, Biochemical ,
    5002 , APP-4 ,,,,,,,, Enzyme ,
    5002 , APP-4 ,,,,,,,, Enzyme ,
    5002 , APP-4 ,,,,,,,, Enzyme ,
    5002 , APP-4 ,,,,,,,, Cell ,
    

    【讨论】:

      【解决方案4】:

      这是一个 GNU Awk 解决方案,其中包括具有整体异构值的键,其中可能包含重复项,例如 APP-4 中的那些:

      BEGIN {
          FS=","
          OFS=","
      }
      {
          key[NR]=$2
          count[$2]++
          v=$(NF-1)
          val[NR]=v
          val_count[$2][v]++
          line[NR]=$0
      }
      END {
          for(i=1;i<=NR;i++) {
              k=key[i]
              j=val[i]
              if(count[k] > 1) {
                  if(val_count[k][j] == count[k]) {
                      continue
                  }else{
                      print line[i]
                  }
              }else{
                  print line[i]
              }
          }
      }
      

      您可以将其创建为 Awk 文件并将其命名为 hetero.awk 并从 shell 运行脚本,如下所示:

      gawk -f hetero.awk file
      

      输出:

      1,APPID,ID2,ID3,5,6,7,8,9,Category,
      5002 , APP-2 ,,,,,,,, Cell ,
      5002 , APP-2 ,,,,,,,, Enzyme ,
      5002 , APP-3 ,,,,,,,, Cell ,
      5002 , APP-3 ,,,,,,,, Biochemical ,
      5002 , APP-4 ,,,,,,,, Enzyme ,
      5002 , APP-4 ,,,,,,,, Enzyme ,
      5002 , APP-4 ,,,,,,,, Enzyme ,
      5002 , APP-4 ,,,,,,,, Cell ,
      

      或者,对于更脏的方法,您可以将以下内容放入 shell 脚本中:

      gawk -F, -v OFS=, '{
          key[NR]=$2
          count[$2]++
          v=$(NF-1)
          val[NR]=v
          val_count[$2][v]++
          line[NR]=$0
      }END{
          for(i=1;i<=NR;i++) {
              k=key[i]
              j=val[i]
              if(count[k] > 1) {
                  if(val_count[k][j] == count[k]) {
                      continue
                  }else{
                      print line[i]
                  }
              }else{
                  print line[i]
              }
          }
      }' file
      

      作为一般做法,我更喜欢在我的 bash 脚本中只使用 Awk 一个内衬。

      请注意,这使用了数组的数组,这在 awk 变体(如 mawk)中不可用。

      【讨论】:

      • 谢谢约翰。抱歉,我可能对 gawk 不熟悉。我可以知道如何执行吗?我尝试了“sh test.sh”和“gawk -f test.gawk”,它们都出现错误“awk: awk_test3.gawk:1: ^ 语法错误”等。
      • 主要错误返回是 gawk -f gawk.sh "gawk: gawk.sh:1: gawk -F, '{ (换行) gawk: gawk.sh:1: ^ invalid char ' '' 在表达式中"
      猜你喜欢
      • 2017-02-16
      • 2019-07-06
      • 1970-01-01
      • 1970-01-01
      • 2015-11-14
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      相关资源
      最近更新 更多