比较两个文件的两列并计算差异

时间:2021-07-27 14:37:44

标签: awk comparison multiple-columns

我有两个文件,制表符分隔,我想逐行比较 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 个差异,file1 的 col2 和 file2 有 1 个差异,依此类推... 我在 AWK 中看到了许多类似的问题,但其中大多数是比较列并在匹配或不匹配的情况下从任一文件中附加一列,但不计算差异。

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

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

谢谢

4 个答案:

答案 0 :(得分:3)

您可以使用此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

答案 1 :(得分:3)

如果您有可用的粘贴,则无需在数组中存储任何内容(输出除外)即可执行此操作

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

答案 2 :(得分:1)

使用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。如果您也希望避免它,则更喜欢将 File1File2 传递给 awk 并将第一个内容存储在数组中的解决方案。但是,如果您的文件很大,请记住您可能会遇到内存问题,而基于 getline 的解决方案可以毫无问题地处理数十亿行数百个字段(但在这种情况下您会使用 awk 吗?)。

答案 3 :(得分:1)

由于字段中的值是单个字符 (0,1,2),我们排除标题并将字段值打包到没有分隔符的字段编号索引字符串(例如 a[1]="1122")并使用 {{1} } 用于提取用于比较的字符 (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

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