我有两个文件。
文件1
NC_000001.11_NM_001005484.2 69270 234 69037
NC_000001.11_NM_001005484.2 69511 475 69037
NC_000001.11_NM_001005484.2 69761 725 69037
NC_000001.11_NM_001385640.1 942155 20 942136
文件2
NC_000001.11_NM_001005484.2 65565 9
NC_000001.11_NM_001005484.2 69037 969
NC_000001.11_NM_001385640.1 924432 517
NC_000001.11_NM_001385640.1 925922 92
NC_000001.11_NM_001385640.1 930155 182
NC_000001.11_NM_001385640.1 931039 51
NC_000001.11_NM_001385640.1 935772 125
NC_000001.11_NM_001385640.1 939040 90
NC_000001.11_NM_001385640.1 939272 141
NC_000001.11_NM_001385640.1 941144 163
NC_000001.11_NM_001385640.1 942136 116
NC_000001.11_NM_001385640.1 942410 79
NC_000001.11_NM_001385640.1 942559 500
NC_000001.11_NM_001385640.1 943253 125
NC_000001.11_NM_001385640.1 943698 111
NC_000001.11_NM_001385640.1 943908 243
如果文件 1 中的列 1 与文件 2 中的列 1 匹配,并且文件 2 的第 2 列中的值小于文件 1 中的第 4 列的值,我想将文件 2 的第 3 列与匹配的键相加。然后我想打印 file1 中的每一行以及 file2 中相应的总和。
预期产出
NC_000001.11_NM_001005484.2 69270 234 69037 9
NC_000001.11_NM_001005484.2 69511 475 69037 9
NC_000001.11_NM_001005484.2 69761 725 69037 9
NC_000001.11_NM_001385640.1 942155 20 942136 1361
我对 awk 或 python 的经验还不够,无法做到这一点,但现在已经修修补补了几天了。我将不胜感激任何帮助。
答案1
$4
对于此任务,您需要存储第 1 行的每个键 ( )的值(在下面的脚本中,我将使用为此$1
调用的数组,作为键和值)。keys
$1
$4
您还需要将每个实际行存储在另一个数组中(我将lines
为此使用行号作为键,整行作为值)。请注意,如果很大的话,这可能会消耗大量内存file1
......但是,除非它很大,否则在任何具有许多 GB RAM 的现代系统上可能不是问题。如果碰巧太大而无法放入 RAM,则必须修改脚本以再次迭代第一个文件,而不是将其存储在数组中lines
。
最后,您还需要存储与每个行号对应的键 ($1)(我将使用linekeys
为此调用的数组,以行号作为索引,键$1
作为值)。顺便说一句,如果第一个文件太大,您必须第二次处理它,那么就不需要这个数组,因为您可以$1
在再次处理每一行时获取它。从技术上讲,这个数组并不是真正需要的,因为您可以在需要时split()
从块lines[l]
中END{}
获取它,但这样做更容易 - 用更多的内存使用来换取更简单的代码和可能更快的运行时间。
awk '# process the first file
NR==FNR {
keys[$1] = $4; # remember the value of $4 for the key ($1)
lines[FNR] = $0; # store the entire line
linekeys[FNR] = $1; # remember the key for that line
next
};
# process any remaining file(s)
$1 in keys {
if ($2 < keys[$1]) {
sum[$1]+=$3
};
};
# All files have been processed, so print the output
END {
for (l in lines) {
print lines[l], sum[linekeys[l]]
}
}' file1 file2
NC_000001.11_NM_001005484.2 69270 234 69037 9
NC_000001.11_NM_001005484.2 69511 475 69037 9
NC_000001.11_NM_001005484.2 69761 725 69037 9
NC_000001.11_NM_001385640.1 942155 20 942136 1361
顺便说一句,我建议将其保存在任一sh
脚本中(除了用作"$@"
参数而不是awk
这样file1 file2
,您可以在运行它时在命令行上指定输入行(例如bash scriptname.sh file1 file2
,或者将其保存为 awk 脚本(删除命令awk
、单引号和文件名),以便您可以将其作为awk -f scriptname.awk file1 file2
.用适当的#!
线作为第一的行,您还可以使其可执行,这样您就可以直接运行它,而无需在运行时在命令行上键入解释器名称。
或者,如果您真的坚持,您可以将整个脚本压缩到一行中 - 在语句之间需要的地方保留分号以实现这一点。不过,我不推荐它,因为 shell 命令行是编辑脚本的糟糕地方,即使是这么短的脚本,甚至具有方便的功能(如Ctrl-XCtrl-Ebash)来编辑当前行vi
或您喜欢的编辑器。
答案2
使用 GNU awk 处理数组的数组:
$ cat tst.awk
NR==FNR {
addends[$1][$2][$3]
next
}
$1 in addends {
sum = 0
for ( val in addends[$1] ) {
if ( val < $4 ) {
for ( addend in addends[$1][val] ) {
sum += addend
}
}
}
print $0, sum
}
$ awk -f tst.awk file2 file1
NC_000001.11_NM_001005484.2 69270 234 69037 9
NC_000001.11_NM_001005484.2 69511 475 69037 9
NC_000001.11_NM_001005484.2 69761 725 69037 9
NC_000001.11_NM_001385640.1 942155 20 942136 1361
请注意,上面的内容将简单地以与 file1 中出现的顺序相同的顺序输出 file1 中的行,其他读取file1
而不是file2
写入内存的解决方案可能不会这样做,例如,如果它们使用for (i in array)
之后打印它们,则会将它们洗牌成“随机” " 顺序由您使用的 awk 版本的内部结构决定,请参阅https://www.gnu.org/software/gawk/manual/gawk.html#Scanning-an-Array,因此即使您碰巧获得了某些特定示例输入的预期输出,也不要依赖于所有输入总是发生的情况。