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

Compare two columns of two files and count the differences

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

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

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

谢谢

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

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) 作为要处理的文件。
  • 我们从第二个文件的第一行开始设置n(字段数)。稍后,如果我们在两个文件之一中遇到具有不同字段数的行,我们将退出并显示错误消息。
  • 我们用当前行中的字段填充数组 a
  • 然后我们使用 getline 读取第一个文件的下一行,它将当前字段设置为新值。如果 getline 失败,我们将退出并显示错误消息。
  • 我们将字段与数组 a 进行比较,如果发现差异,则递增数组 c 的元素。
  • 最后我们打印数组c

Note: some awk experts advocate against getline. If you prefer avoiding it too, prefer the solutions that pass File1 and File2 to awk and store the content of the first one in an array. But if your files are large remember that you could encounter memory issues, while the getline-based solution could process billions of lines of hundreds of fields without any problem (but would you use awk in this case?).

如果您有可用的粘贴,您可以在不将任何内容存储在数组中的情况下执行此操作,除了输出

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

由于字段中的值是单个字符 (0,1,2),我们排除 headers 并将字段值打包为不带分隔符的字段编号索引字符串(例如 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 中所要求的。