使用Awk按列值范围合并制表符分隔数据集的问题
修正Awk脚本实现数据集匹配需求
我有两个制表符分隔的数据集dataset1和dataset2,需求为:遍历dataset2的每一行,若dataset2第2列与dataset1第1列匹配,且dataset2第3列的值处于dataset1第2、3列的范围之间,则输出dataset2该行加dataset1匹配行;否则输出dataset2该行加全为"."的列。但我使用的Awk脚本执行后输出不符合预期,请求修正脚本实现预期功能。
dataset1数据
NC_044998.1 14582 80739 LOC100221041 NC_044998.1 31388 68748 DCBLD2 NC_044998.1 80874 299341 CMSS1 NC_044998.1 112495 297570 FILIP1L NC_044998.1 287349 289742 LOC116808959 NC_044998.1 300404 343805 TBC1D23 NC_044998.1 333622 344667 NIT2 NC_044998.1 346168 368957 TOMM70 NC_044998.1 371654 380427 LNP1 NC_044998.1 387231 413422 TMEM45A
dataset2数据
1 NC_044998.1 15001 6.040368 2.038993e-04 1 NC_044998.1 25002 0.000000 3.333334e-01 1 NC_044998.1 35003 2.309260 4.638924e-03 1 NC_044998.1 45004 3.438428 5.053365e-03 1 NC_044998.1 55005 1.086369 9.663565e-02 1 NC_044998.1 65006 3.250019 8.298793e-04 1 NC_044998.1 75007 1.081163 8.039542e-03 1 NC_044998.1 85008 0.186722 8.158607e-02 1 NC_044998.1 95009 2.236803 3.256445e-03 1 NC_044998.1 105010 0.089978 2.846438e-01
使用的原Awk脚本
NR==FNR { q[++n] = $0 f1[n] = $2 f2[n] = $3 next } # process file2 { for (i = 1; i <= n; i++) { if ($1 == f1[i] && (f2[i] > $2 && f2[i] < $3)) print q[i]"\t"$0 else print q[i]"\t"."\t"."\t"."\t"."." } }
当前输出
1 NC_044998.1 15001 6.040368 2.038993e-04 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 25002 0.000000 3.333334e-01 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 35003 2.309260 4.638924e-03 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 45004 3.438428 5.053365e-03 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 55005 1.086369 9.663565e-02 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 65006 3.250019 8.298793e-04 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 75007 1.081163 8.039542e-03 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 85008 0.186722 8.158607e-02 . . . . . 1 NC_044998.1 95009 2.236803 3.256445e-03 . . . . . 1 NC_044998.1 105010 0.089978 2.846438e-01 . . . . .
预期输出
1 NC_044998.1 15001 6.040368 2.038993e-04 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 25002 0.000000 3.333334e-01 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 35003 2.309260 4.638924e-03 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 35003 2.309260 4.638924e-03 NC_044998.1 31388 68748 DCBLD2 1 NC_044998.1 45004 3.438428 5.053365e-03 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 45004 3.438428 5.053365e-03 NC_044998.1 31388 68748 DCBLD2 1 NC_044998.1 55005 1.086369 9.663565e-02 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 55005 1.086369 9.663565e-02 NC_044998.1 31388 68748 DCBLD2 1 NC_044998.1 65006 3.250019 8.298793e-04 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 65006 3.250019 8.298793e-04 NC_044998.1 31388 68748 DCBLD2 1 NC_044998.1 75007 1.081163 8.039542e-03 NC_044998.1 14582 80739 LOC100221041 1 NC_044998.1 85008 0.186722 8.158607e-02 NC_044998.1 80874 299341 CMSS1 1 NC_044998.1 95009 2.236803 3.256445e-03 NC_044998.1 80874 299341 CMSS1 1 NC_044998.1 105010 0.089978 2.846438e-01 NC_044998.1 80874 299341 CMSS1
修正后的Awk脚本
NR==FNR { # 存储dataset1的每一行、匹配键、起始和结束位置 lines[++n] = $0 chr[n] = $1 start[n] = $2 end[n] = $3 next } { matched = 0 # 遍历所有dataset1条目,查找所有匹配项 for (i=1; i<=n; i++) { # 修正匹配条件:dataset2第2列匹配dataset1第1列,且dataset2第3列在dataset1的起止区间内 if ($2 == chr[i] && start[i] <= $3 && $3 <= end[i]) { # 按需求输出dataset2行在前,匹配的dataset1行在后 print $0 "\t" lines[i] matched = 1 } } # 无匹配时输出dataset2行加全为"."的列 if (!matched) { print $0 "\t.\t.\t.\t." } }
脚本修正说明
- 字段存储修正:正确记录dataset1的匹配键(第1列)、起始位置(第2列)、结束位置(第3列)和整行内容,解决原脚本字段对应错误的问题。
- 匹配条件修正:将原脚本颠倒的字段对应关系和范围判断逻辑纠正,确保判断逻辑符合需求。
- 输出顺序调整:按照需求输出
dataset2行 + dataset1匹配行的顺序,而非原脚本的反向输出。 - 多匹配处理:遍历所有dataset1条目,找到所有符合条件的匹配项并逐一输出,实现预期输出中一行dataset2对应多行匹配结果的场景。
- 无匹配场景处理:当没有找到任何匹配时,输出dataset2行加四个"."的列,符合需求要求。
内容的提问来源于stack exchange,提问作者user5719890
相关产品推荐
相关产品推荐

