【问题标题】:Compare two columns of two files and count the differences比较两个文件的两列并计算差异
【发布时间】:2021-10-03 09:34:39
【问题描述】:

我有两个文件,用制表符分隔,我想逐行比较 file1 的第 1 列的值与 file2 的第 1 列的值,依此类推,直到 n 列。

比较是为了计算差异。

列中的值可以是 0、1 或 2,例如:

File1:

col1 col2 col3 col4
1 1 1 2
1 1 1 2
2 1 2 2
2 1 2 2

File2:
col1 col2 col3 col4
1 1 1 1
1 1 0 1
0 1 0 1
1 0 1 0

Results
2 1 3 4

因此,file1 和 file2 的 col1 有 2 个差异,col2 off file1 和 file2 有 1 个差异,依此类推... 我在 AWK 中看到了许多类似的问题,但其中大多数是比较列并在匹配或不匹配的情况下从任一文件追加列,但不计算差异。

我相信两列不匹配的比较会从这样开始,但从那里我完全迷失了......

awk 'NR==FNR { a[$1]!=$1; next}

谢谢

【问题讨论】:

  • col1 of file1 and file2 with 2 differences: 你确定吗?两个文件中相同,即1 1 2 2 值?
  • @anubhava 你是对的。我修复了这个例子。

标签: awk comparison multiple-columns


【解决方案1】:

你可以使用这个awk:

awk 'BEGIN{FS=OFS="\t"} FNR == NR {for (i=1; i<=NF; ++i) a[i,FNR] = $i; next} FNR > 1 {for (i=1; i<=NF; ++i) if ($i != a[i,FNR]) ++out[i]; ncol=NF} END {print "Results"; for (i=1; i <= ncol; ++i) printf "%s%s", out[i]+0, (i < ncol ? OFS : ORS)}' f2 f1

Results
2   1   3   4

更易读的形式:

awk 'BEGIN {FS=OFS="\t"}
FNR == NR {
   for (i=1; i<=NF; ++i)
      a[i,FNR] = $i
   next
}
FNR > 1 {
   for (i=1; i<=NF; ++i)
      if ($i != a[i,FNR])
         ++out[i]
}
END {
   print "Results"
   for (i=1; i <= NF; ++i)
      printf "%s%s", out[i]+0, (i < ncol ? OFS : ORS)
}' f2 f1

【讨论】:

    【解决方案2】:

    如果您有可用的粘贴,则无需在数组中存储除输出之外的任何内容

    paste File1 File2 |
    awk '
        NR > 1 {
            mid = NF/2
            for (i=1; i<=mid; i++) {
                count[i] += ( $i == $(mid+i) ? 0 : 1 )
            }
        }
        END {
            for (i=1; i<=mid; i++) {
                printf "%d%s", count[i], (i<mid ? OFS : ORS)
            }
        }
    '
    

    输出:

    2 1 3 4
    

    【讨论】:

      【解决方案3】:

      getline:

      $ cat foo.awk
      NR == 1 { n = NF; }
      {
        if(NF != n) { print "error"; exit 1; }
        for(i = 1; i <= n; i++) a[i] = $i;
        if(getline < f != 1 || NF != n) { print "error"; exit 1; }
        for(i = 1; i <= NF; i++) if($i && a[i] != $i) c[i] += 1;
      }
      END {
        for(i = 1; i <= n; i++) printf("%d%c", c[i], (i == n) ? "\n" : " ");
      }
      
      $ awk -v f=File1 -f foo.awk File2
      2 1 3 4
      

      解释:

      • 变量f 保存第一个文件的名称,我们使用-v f=File1 选项将其传递给awk,我们将第二个文件名(File2) 作为要处理的文件传递给awk。
      • 我们从第二个文件的第一行设置n(字段数)。稍后,如果我们在两个文件之一中遇到包含不同数量字段的行,我们将退出并显示错误消息。
      • 我们用当前行中的字段填充数组a。
      • 然后我们从带有getline 的第一个文件中读取下一行,它使用新值设置当前字段。如果getline 失败,我们将退出并显示错误消息。
      • 我们将字段与数组a 进行比较,如果发现差异,则增加数组c 的元素。
      • 最后我们打印数组c。

      注意:一些 awk 专家反对getline。如果您也喜欢避免它,那么更喜欢将 File1 和 File2 传递给 awk 的解决方案并将第一个的内容存储在数组中。但是,如果您的文件很大,请记住您可能会遇到内存问题,而基于 getline 的解决方案可以毫无问题地处理数十亿行数百个字段(但在这种情况下您会使用 awk 吗?)。

      【讨论】:

      • 在使用时提供有关getline 的警告很好,如果您引用解释问题的awk.freeshell.org/AllAboutGetline 会更有用。
      • 声明 getline &lt; f != 1 是未定义的行为。任何给定的 awk 都可以将其解释为 (getline &lt; f) != 1 或 getline &lt; (f != 1) 或其他内容。每当您在输入或输出重定向的右侧有一个表达式(例如f != 1)时,它必须用括号括起来以便可移植到所有 awk。
      • for(i = 1; i &lt;= n; i++) a[i] = $i = split($0,a)
      • 如果$i 是0,if($i &amp;&amp; 将失败,我认为没有必要,因为您已经检查了n NF。
      • 打印ORS 比打印""\n" 更好,因为后者是硬编码一个您希望/假设ORS 具有但如果您设置@987654352 会导致脚本失败的值@ 什么的。
      【解决方案4】:

      由于字段中的值是单个字符(0,1,2),我们排除标题并将字段值打包到字段编号索引字符串中,不带分隔符(例如a[1]="1122")并使用substr()提取字符用于比较 ($i!=substr(a[i],FNR-1,1)):

      awk '
      NR==FNR && NR>1 {                         # process first file, ignore header
          for(i=1;i<=NF;i++)                    # since column values are 1 digit only
              a[i]=a[i] $i                      # just catenate themem, no separators
          next
      }
      FNR>1 {                                   # process second file
          for(i=1;i<=NF;i++)
              r[i]+=($i!=substr(a[i],FNR-1,1))  # compare field data and count mismatches
      }
      END {                                     # in the end
          for(i=1;(i in r);i++)                 # loop and ...
              printf "%s%s",(i==1?"":OFS),r[i]  # output
          print ""
      }' file1 file2
      

      输出:

      2 1 3 4
      

      注意:这仅适用于 OP 中要求的单个字符值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-08
        • 2015-08-10
        • 2021-08-30
        • 1970-01-01
        相关资源
        最近更新 更多