从文本文件中提取行,其中一列等于最大值

从文本文件中提取行,其中一列等于最大值

我有一个名为 file.txt 的文本文件(带有标题行)。我试图提取等于特定列最大值的行(我不知道最大值是多少):

ID t1 q1 t2 q2 q3 
1 f 45 ex 1 45
2 r 47 tr 1 33
3 r 33 ex 2 44
4 f 44 s 0 55
5 e 32 ex 0 54
6 f 34 tr 2 46

我需要找到 $5 列的最大值,然后仅打印第 5 列等于该数字的行:

3 r 33 ex 2 44
6 f 34 tr 2 46

我认为以下代码有效,但我的文件很大并且需要很长的时间,所以我正在寻找更快的解决方案(也许使用排序?):

这就是我现在所拥有的:

首先找到最大值:

max=`awk '{print $5}' file.txt | sort -nr | sed -n 2p`

然后选择第 5 列等于该值的行:

awk 'NR>1' file.txt|while read LINE; do value=`echo $LINE|awk '{print $5}'`; if [ $value -eq $max ]; then echo $LINE >> test.txt; fi; done

答案1

一种方法是读取文件一次以获取max然后再次打印相关行:

max=$(awk 'NR>1 && $5>max {max=$5}; END{print max}' file.txt) && 
 awk -v max="$max" '$5==max' file.txt 

或者,更简洁地说:

awk -v m="$(awk '(NR>1 && $5>m){m=$5};END{print m}' file.txt)"  '$5==m' file.txt 

这里的技巧是 awk 的-v标志,它允许您将变量传递给awk.在这种情况下,我首先计算最大值,然后将其awk作为变量给出max

答案2

这是一个相当典型的问题,存在一个惯用的 awk 解决方案,涉及对文件进行两次传递。在第一遍中,确定 的最大值$5,在第二遍中,提取 中包含该最大值的记录$5。这是一个简单的例子。

awk 'NR == FNR && NR > 1{max = max < $5? $5: max; next}; $5 == max{print}' file.txt file.txt

答案3

如果内存使用不是真正的问题,perl 中的一次性版本可能是:

perl -ane 'END { $"=""; print "@res"; } if($F[4] =~ /^\d+/ and $F[4] > $max) { 
    $max = $F[4]; @res = (); } push @res, $_ if($F[4] =~ /^\d+$/ and $max == $F[4]);' infile

-ninfile告诉 perl一次处理一行,将每一行传递给 中指定的命令-e。告诉-aperl 展开字段分隔符周围的每一行(默认为空格),并将其分配给一个名为 的数组@F。结果是我们可以处理每一行并使用它$F[n]来引用该行的第 n 个元素。

Perl 本身:

END { $"=""; print "@res"; } # at the end of execution set the field separator to
                             # empty and print the contents of @res, which includes
                             # newlines when the matching rows were stored

if($F[4] =~ /^\d+/ and $F[4] > $max) {  # if the 5th element of the line is solely a
    $max = $F[4]; @res = ();            # number and it's greater than $max (which
}                                       # starts as undefined), set $max to this number
                                        # and empty the @res results array.

push @res, $_                                  # push this line to @res ...
     if($F[4] =~ /^\d+$/ and $max == $F[4]);   # IF the 5th element is solely a 
                                               # number and equal to $max

逻辑是$max从未定义开始且@res数组为空。当我们第一次在第 5 列中找到一个数字时,我们将其存储在$max并为空@res@res如果我们在后续行的第 5 列中找到新的最大值,这也会产生清空的副作用。作为单独的检查,如果第 5 列等于,则$max将此行添加到@res(存储具有当前最大值的行)。对所有行重复此操作,然后END { }执行该块,该块打印结果数组的内容,@res而无需通常在 中设置的前导空格字段分隔符$"

这也可能在 中实现awk,但我的awk-fu并不是那么好!

答案4

按降序对文件中的值进行排序,然后从顶部打印行,直到值发生变化。

sort -k 5n | awk 'NR==1 {max=$5} $5!=max {quit} {print}'

相关内容