【问题标题】:Remove duplicates in each individual column from a text file从文本文件中删除每个单独列中的重复项
【发布时间】:2021-11-07 07:13:07
【问题描述】:

我有一个包含 7 个制表符分隔列的文本文件。每列都有不同数量的行,其中的值可以重复。我想删除重复项,以便每列只有该特定列的唯一值。举个例子:

输入

C1  C2  C3  C4  C5  C6  C7
111 111 222 333 111 222 777 
222 111 333 333 222 333 666
222 111 444 111 333 555 555
333 444 555 222 444 666 444
444 666 555 777 555 666 333
444 777 777 555 666 888 333
777 888 999 666 888                 
999

输出

C1  C2  C3  C4  C5  C6  C7
111 111 222 333 111 222 777
222 444 333 111 222 333 666
333 666 444 222 333 555 555
444 777 555 777 444 666 444
777 888 777 555 555 888 333
999     999 666 666 
                888

我想我需要使用 awk 来打印每一列并单独使用 sort -u,然后将这些输出粘贴在一起。那么,有没有一种方法可以创建一个循环,对于文本文件中的 i 列,将打印每一列 | sort - u,然后将它们全部粘贴在一起?

提前致谢, 卡洛斯

【问题讨论】:

  • 转置,然后在行中制作唯一编号,然后再次转置。
  • paste <(awk '!seen[$1]++ && $0=$1' file) <(awk '!seen[$2]++ && $0=$2' file) <(awk '!seen[$3]++ && $0=$3' file) <(awk '!seen[$4]++ && $0=$4' file) <(awk '!seen[$5]++ && $0=$5' file) <(awk '!seen[$6]++ && $0=$6' file) <(awk '!seen[$7]++ && $0=$7' file) ;-)
  • 谢谢@Cyrus,它适用于这个特定的文件。但是,将来我将使用具有随机列数的文件来执行此操作。有没有办法做类似的事情: for i columns do paste
  • @Carlos Cyrus 是在开玩笑,请注意他们评论末尾的笑脸表情符号。
  • @EdMorton:是的,我已经看到灾难即将到来,它不会停留在 7 列。

标签: sorting awk uniq


【解决方案1】:

使用perl 代替它支持真正的多维数组:

perl -lane '
    for my $n (0..$#F) {
      if (!exists ${$vals[$n]}{$F[$n]}) {
        push @{$cols[$n]}, $F[$n];
        ${$vals[$n]}{$F[$n]} = 1;
      }
    }
    END {
      for (1..$.) {
        my @row;
        for my $n (0..$#cols) {
          push @row, shift @{$cols[$n]};
        }
        print join("\t", @row);
     }
}' input.txt

【讨论】:

    【解决方案2】:

    在每个 Unix 机器上的任何 shell 中使用任何 awk:

    $ cat tst.awk
    BEGIN { FS=OFS="\t" }
    {
        for (colNr=1; colNr<=NF; colNr++) {
            val = $colNr
            if ( !seen[colNr,val]++ ) {
                rowNr = ++colRowNrs[colNr]
                vals[rowNr,colNr] = val
                numRows = (rowNr > numRows ? rowNr : numRows)
            }
        }
        numCols = (NF > numCols ? NF : numCols)
    }
    END {
        for (rowNr=1; rowNr<=numRows; rowNr++) {
            for (colNr=1; colNr<=numCols; colNr++) {
                val = vals[rowNr,colNr]
                printf "%s%s", val, (colNr<numCols ? OFS : ORS)
            }
        }
    }
    

    $ awk -f tst.awk file
    C1      C2      C3      C4      C5      C6      C7
    111     111     222     333     111     222     777
    222     444     333     111     222     333     666
    333     666     444     222     333     555     555
    444     777     555     777     444     666     444
    777     888     777     555     555     888     333
    999             999     666     666
                                    888
    

    【讨论】:

      【解决方案3】:

      假设

      • 整个输出结果的 (awk) 数组将适合内存
      • 列数和行数可变

      一个想法由一个(稀疏的)二维值数组组成,其中数组结构如下所示:

      values[<column#>][<row#>]=<unique_cell_value>
      

      使用单个 awk 调用的一个想法 a) 需要单次通过输入文件,而 b) 不需要任何转置/粘贴(以防万一任何人都认真对待赛勒斯的评论/建议):

      awk '
      BEGIN  { FS=OFS="\t" }
             { maxNF = (NF > maxNF ? NF : maxNF)            # keep track of max number of columns
               for (i=1; i<=NF; i++) {
                   if ( $i == "" )                          # ignore empty cell
                      continue
                   for (j=1; j<=ndx[i]; j++) {              # loop through values already seen for this column
                       if ( $i == vals[i][j] ) {            # and if already seen then
                          $i = ""                           # clear the current cell and
                          break                             # break out of this for/testing loop
                       }
                   }
                   if ( $i != "" ) {                        # if we got this var and the cell is not empty then 
                      vals[i][++ndx[i]] = $i                # store the new value in our array
                   }
               }
             }
      END    { for (j=1; j<=NR; j++) {                      # loop through all possible rows
                   pfx = ""
                   for (i=1; i<=maxNF; i++) {               # loop through all possible columns
                       printf "%s%s", pfx, vals[i][j]       # non-existent array entries default to ""
                       pfx = OFS
                   }
                   printf "\n"
               }
             }
      ' input_file
      

      注意:数组结构(arr[i][j])需要GNU awk,否则我们可以转换为arr[i,j]的伪双索引数组结构

      这会生成:

      C1      C2      C3      C4      C5      C6      C7
      111     111     222     333     111     222     777
      222     444     333     111     222     333     666
      333     666     444     222     333     555     555
      444     777     555     777     444     666     444
      777     888     777     555     555     888     333
      999             999     666     666
                                      888
      

      【讨论】:

        猜你喜欢
        • 2013-03-27
        • 2013-12-13
        • 1970-01-01
        • 2019-03-28
        • 1970-01-01
        • 1970-01-01
        • 2015-08-29
        • 2014-04-19
        相关资源
        最近更新 更多