根据一个字段对唯一行进行排序,并根据另一个字段决定输出哪些行

根据一个字段对唯一行进行排序,并根据另一个字段决定输出哪些行

这更多的是寻找一个优雅的问题解决方案,我想我有一个可行的解决方案。我在 Ubuntu 机器上有以下输入文件格式(制表符分隔):

AC003665.1  17  47813266    AGCAGGCGCA  83
RIOK3   18  23453502    GCAAGGCCCC  52
UBE2Z   17  48910880    CTAAGGATCC  48
CSNK1D  17  82251379    AATTTAGCCA  68
CSNK1D  17  82251379    AATTTCTTGT  38
SMURF1  7   99143726    GACAGATTGG  74
SMURF1  7   99143726    GACAGATTGG  61
RIOK3   18  23453502    GCAAGACTTT  69

我只想在字段 3 的每次出现时获取一行,即字段 5 中具有最高值的一行。因此输出应该是:

AC003665.1  17  47813266    AGCAGGCGCA  83
CSNK1D  17  82251379    AATTTAGCCA  68
UBE2Z   17  48910880    CTAAGGATCC  48
SMURF1  7   99143726    GACAGATTGG  74
RIOK3   18  23453502    GCAAGACTTT  69

顺序与我的目的无关。我找到了一个解决方案,首先在字段 5 上排序,然后在字段 3 上排序,我认为该解决方案有效:

sort -k 5,5nr input | sort -u -k 3,3n > output

它适用于我的所有测试文件,我认为在任何情况下都应该适用,因为这应该确保对于字段 3 的每个值,排序将首先看到(并因此保留)字段 5 具有最高值的行。

然而,我觉得应该有一个更优雅(也许更万无一失)的解决方案来解决这个问题?任何帮助表示赞赏。

答案1

如果要输出的数据足够小以适合内存,那么

awk '
    biggest[$3] < $5 { biggest[$3]=$5 ; saved[$3]=$0 }
    END { for (i in saved) { print saved[i] }}' 

一般来说,这会更快,在决定是否需要存储时,需要查看每一行一次。内存要求是针对输出的数据的,因此非常重复的输入可能会非常巨大。

这与每行需要多次比较的排序解决方案形成对比。基于排序的解决方案会比较慢,但会处理太大而无法放入内存的输出。

答案2

我将直接按第二个和第三个字段排序并通过awk以保留最大值:

$ sort -k 3,3nr -k 5,5rn input | awk '!a[$3]++' 
SMURF1  7   99143726    GACAGATTGG  74
CSNK1D  17  82251379    AATTTAGCCA  68
UBE2Z   17  48910880    CTAAGGATCC  48
AC003665.1  17  47813266    AGCAGGCGCA  83
RIOK3   18  23453502    GCAAGACTTT  69

这样做的优点是只对文件进行一次排序,并且不需要将整个文件保留在内存中。然而,我希望伊卡洛斯的 awk 方法更快,因为它只需要读取文件一次。

相关内容