比较两个文件的两列并计算差异
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 中所要求的。
我有两个文件,制表符分隔,我想逐行比较文件 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 passFile1
andFile2
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 thegetline
-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 中所要求的。