如果一列中的值包含连续数字并且所有其他列匹配,如何合并行
How to merge rows if values in one column contains consective numbers and all other columns match
我有一个非常大的文件(约 7 亿行),我想通过对大部分匹配的行进行分组来减小大小。具体来说,该文件按字段 1 和 2 排序,我想对字段 2 包含连续数字但所有其他字段匹配的行进行分组。如果字段 2 中存在空白,或者如果任何其他字段与前一行不匹配,那么我想开始一个新的间隔。理想情况下,我希望输出 return 分组行的间隔范围,并且更喜欢使用 awk and/or sed 在 bash 中工作的解决方案。我对其他解决方案持开放态度,只要它们不需要重新排序或其他可能因如此长的文件而崩溃的操作。
输入文件看起来像这样。
NW_005179401.1 100 1 0 0 0 0 0 0 0 0
NW_005179401.1 101 1 0 0 0 0 0 0 0 0
NW_005179401.1 102 1 0 0 0 0 0 0 0 0
NW_005179401.1 103 1 0 0 0 0 0 1 0 0
NW_005179401.1 104 1 0 0 0 0 0 1 0 0
NW_005179401.1 105 1 0 0 0 0 0 1 0 0
NW_005179401.1 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 1 0 0 0 0 0 1 0 0
NW_005179401.1 109 1 0 0 0 0 0 1 0 0
NW_005179401.1 110 1 0 0 0 0 0 1 0 0
NW_005179401.1 111 1 0 0 0 0 0 1 0 0
NW_005179401.1 112 1 0 0 0 0 0 1 0 0
NW_005179401.1 992 0 0 1 1 0 0 0 0 2
NW_005179401.1 993 0 0 1 1 0 0 0 0 2
NW_005179401.1 994 0 0 1 1 0 0 0 0 2
NW_005179401.1 995 0 0 1 1 0 0 0 0 2
NW_005179401.1 996 0 0 1 1 0 0 0 0 0
NW_005179401.1 997 0 0 1 1 0 0 0 0 0
NW_005179401.1 998 0 0 1 1 0 0 0 0 0
NW_005179401.1 999 0 0 1 1 0 0 0 0 0
实际上,该文件有更多字段,但都包含整数,例如示例中的字段 3 及以后的字段。理想的输出将如下所示,连续字段 2 间隔中的第一个和最后一个值打印在输出字段 2 和 3 中。
NW_005179401.1 100 102 1 0 0 0 0 0 0 0 0
NW_005179401.1 103 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 112 1 0 0 0 0 0 1 0 0
NW_005179401.1 992 995 0 0 1 1 0 0 0 0 2
NW_005179401.1 996 999 0 0 1 1 0 0 0 0 0
我发现解决方案将连续行与特定字段中的匹配项分组,但 none 也在一个字段中查找连续整数,而不是可以 return 范围的整数。一种想法是在跳过前 2 个字段的同时使用带有 -c 标志的 uniq,然后将计数添加到字段 2 中的值,但是考虑到在字段 2 中需要连续数字的附加条件我不太确定从哪里开始这个。提前致谢。
编辑:我为最初没有添加我尝试的代码而道歉,但我的管道使用了生物信息学程序 bedtools 并且它由于内存不足而不断被杀死,这不是我预期的由于缺乏预编程功能。我是 awk 新手,不知道从哪里开始寻找重新格式化此类文件的替代管道。
我怀疑是否有像 uniq -c
这样的标准工具。但是您可以使用这个自定义 awk
脚本:
awk '{=} [=10=]!=n {s=; printf "%s", g}
{=+1; n=[=10=]; =s" "-1; g=[=10=] ORS}
END {printf "%s", g}' yourFile
n
是下一个预期的记录,
例如如果当前行是 abc 100 x y z
那么 n
=abc 101 x y z
.
g
是要打印的 组 记录,以防下一个预期行 n
没有出现并且该组结束。
s
是start组数g
,即区间的下界。
{=}
只是为了确保当前行[=23=]
和生成的行n
中的字段分隔符是一致的,这样我们就可以使用[=25来检查相等性=],在这种情况下更确切地说是 !=
。
对于您的示例,这将打印
NW_005179401.1 100 102 1 0 0 0 0 0 0 0 0
NW_005179401.1 103 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 112 1 0 0 0 0 0 1 0 0
NW_005179401.1 992 995 0 0 1 1 0 0 0 0 2
NW_005179401.1 996 999 0 0 1 1 0 0 0 0 0
$ cat tst.awk
{
prevVals = currVals
origRec = [=10=]
= ""
currVals = [=10=]
[=10=] = origRec
}
( != endKey+1) || (currVals != prevVals) {
if ( NR>1 ) {
prt()
}
begKey =
}
{ endKey = }
END { prt() }
function prt( origRec) {
origRec = [=10=]
= begKey OFS endKey
print
[=10=] = origRec
}
$ awk -f tst.awk file
NW_005179401.1 100 102 1 0 0 0 0 0 1 0 0
NW_005179401.1 103 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 112 0 0 1 1 0 0 0 0 2
NW_005179401.1 992 995 0 0 1 1 0 0 0 0 0
NW_005179401.1 996 999 0 0 1 1 0 0 0 0 0
我有一个非常大的文件(约 7 亿行),我想通过对大部分匹配的行进行分组来减小大小。具体来说,该文件按字段 1 和 2 排序,我想对字段 2 包含连续数字但所有其他字段匹配的行进行分组。如果字段 2 中存在空白,或者如果任何其他字段与前一行不匹配,那么我想开始一个新的间隔。理想情况下,我希望输出 return 分组行的间隔范围,并且更喜欢使用 awk and/or sed 在 bash 中工作的解决方案。我对其他解决方案持开放态度,只要它们不需要重新排序或其他可能因如此长的文件而崩溃的操作。
输入文件看起来像这样。
NW_005179401.1 100 1 0 0 0 0 0 0 0 0
NW_005179401.1 101 1 0 0 0 0 0 0 0 0
NW_005179401.1 102 1 0 0 0 0 0 0 0 0
NW_005179401.1 103 1 0 0 0 0 0 1 0 0
NW_005179401.1 104 1 0 0 0 0 0 1 0 0
NW_005179401.1 105 1 0 0 0 0 0 1 0 0
NW_005179401.1 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 1 0 0 0 0 0 1 0 0
NW_005179401.1 109 1 0 0 0 0 0 1 0 0
NW_005179401.1 110 1 0 0 0 0 0 1 0 0
NW_005179401.1 111 1 0 0 0 0 0 1 0 0
NW_005179401.1 112 1 0 0 0 0 0 1 0 0
NW_005179401.1 992 0 0 1 1 0 0 0 0 2
NW_005179401.1 993 0 0 1 1 0 0 0 0 2
NW_005179401.1 994 0 0 1 1 0 0 0 0 2
NW_005179401.1 995 0 0 1 1 0 0 0 0 2
NW_005179401.1 996 0 0 1 1 0 0 0 0 0
NW_005179401.1 997 0 0 1 1 0 0 0 0 0
NW_005179401.1 998 0 0 1 1 0 0 0 0 0
NW_005179401.1 999 0 0 1 1 0 0 0 0 0
实际上,该文件有更多字段,但都包含整数,例如示例中的字段 3 及以后的字段。理想的输出将如下所示,连续字段 2 间隔中的第一个和最后一个值打印在输出字段 2 和 3 中。
NW_005179401.1 100 102 1 0 0 0 0 0 0 0 0
NW_005179401.1 103 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 112 1 0 0 0 0 0 1 0 0
NW_005179401.1 992 995 0 0 1 1 0 0 0 0 2
NW_005179401.1 996 999 0 0 1 1 0 0 0 0 0
我发现解决方案将连续行与特定字段中的匹配项分组,但 none 也在一个字段中查找连续整数,而不是可以 return 范围的整数。一种想法是在跳过前 2 个字段的同时使用带有 -c 标志的 uniq,然后将计数添加到字段 2 中的值,但是考虑到在字段 2 中需要连续数字的附加条件我不太确定从哪里开始这个。提前致谢。
编辑:我为最初没有添加我尝试的代码而道歉,但我的管道使用了生物信息学程序 bedtools 并且它由于内存不足而不断被杀死,这不是我预期的由于缺乏预编程功能。我是 awk 新手,不知道从哪里开始寻找重新格式化此类文件的替代管道。
我怀疑是否有像 uniq -c
这样的标准工具。但是您可以使用这个自定义 awk
脚本:
awk '{=} [=10=]!=n {s=; printf "%s", g}
{=+1; n=[=10=]; =s" "-1; g=[=10=] ORS}
END {printf "%s", g}' yourFile
n
是下一个预期的记录,
例如如果当前行是abc 100 x y z
那么n
=abc 101 x y z
.g
是要打印的 组 记录,以防下一个预期行n
没有出现并且该组结束。s
是start组数g
,即区间的下界。{=}
只是为了确保当前行[=23=]
和生成的行n
中的字段分隔符是一致的,这样我们就可以使用[=25来检查相等性=],在这种情况下更确切地说是!=
。
对于您的示例,这将打印
NW_005179401.1 100 102 1 0 0 0 0 0 0 0 0
NW_005179401.1 103 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 112 1 0 0 0 0 0 1 0 0
NW_005179401.1 992 995 0 0 1 1 0 0 0 0 2
NW_005179401.1 996 999 0 0 1 1 0 0 0 0 0
$ cat tst.awk
{
prevVals = currVals
origRec = [=10=]
= ""
currVals = [=10=]
[=10=] = origRec
}
( != endKey+1) || (currVals != prevVals) {
if ( NR>1 ) {
prt()
}
begKey =
}
{ endKey = }
END { prt() }
function prt( origRec) {
origRec = [=10=]
= begKey OFS endKey
print
[=10=] = origRec
}
$ awk -f tst.awk file
NW_005179401.1 100 102 1 0 0 0 0 0 1 0 0
NW_005179401.1 103 106 1 0 0 0 0 0 1 0 0
NW_005179401.1 108 112 0 0 1 1 0 0 0 0 2
NW_005179401.1 992 995 0 0 1 1 0 0 0 0 0
NW_005179401.1 996 999 0 0 1 1 0 0 0 0 0