如果前两列相等,select 前 3 基于第 3 列的降序

If first two columns are equal, select top 3 based on descending order of 3rd column

我想 select 前两列相同的每一行的前 3 个结果。

例如,数据看起来像

cat data.txt
A    A    10
A    A    1
A    A    2
A    A    5
A    A    8
A    B    1
A    B    2
A    C    6
A    C    5
A    C    10
A    C    1
B    A    1
B    A    1
B    A    2
B    A    8

而我想要的结果

A    A    10
A    A    8
A    A    5
A    B    2
A    B    1
A    C    10
A    C    6
A    C    5
B    A    1
B    A    1
B    A    2

请注意,某些 "groups" 不包含 3 行。

我试过了

sort -k1,1 -k2,2 -k3,3nr data.txt | sort -u -k1,1 -k2,2 > 1.txt 
comm -23 <(sort data.txt) <(sort 1.txt)| sort -k1,1 -k2,2 -k3,3nr| sort -u -k1,1 -k2,2 > 2.txt 
comm -23 <(sort data.txt) <(cat 1.txt 2.txt | sort)| sort -k1,1 -k2,2 -k3,3nr| sort -u -k1,1 -k2,2 > 3.txt 

它似乎有效,但由于我正在学习更好地编写代码,所以我想知道是否有更好的方法来解决这个问题。另外,我的代码会生成许多我必须删除的文件。

你可以这样做:

$ sort -k1,1 -k2,2 -k3,3nr file | awk 'a[,]++<3'
A    A    10
A    A    8
A    A    5
A    B    2
A    B    1
A    C    10
A    C    6
A    C    5
B    A    8
B    A    2
B    A    1

解释:

理解awk程序有两个关键点; associative arrays and fields.

如果您引用一个空的 awk 数组元素,它就是一个空容器 -- 可以容纳您放入其中的任何内容。您可以将其用作计数器。

你声明如果前两列相等...

排序将文件按所需顺序排列。语句 a[,] 使用前两个字段的值作为关联数组的唯一条目。

然后根据第 3 列的降序陈述 ...select 前 3...

再次,sort 将文件放入所需的顺序,语句 a[,]++ 对它们进行计数。现在数到三。

awk 被组织成 blockscondition {action} 语句 a[,]++<3 为真,直到看到超过 3 个相同的模式。

该程序的更冗长版本是:

awk 'a[,]++<3 {print [=11=]}'

但如果条件为真,默认操作是 print [=17=],因此不需要。

如果你在 Unix 中处理文本,你应该了解 awk。它是 POSIX 保证您将拥有的最强大的工具,通常用于这些任务。

最好的起点是 Arnold D. Robbins 的在线书籍 Effective AWK Programming

您可以主要按前两列对文件进行排序,然后按第三列的数字对文件进行排序,然后读取输出并只打印前两列的每种组合的前三行。

sort -k1,2 -k3,3rn data.txt \
| while read c1 c2 n ; do
    if [[ $c1 == $l1 && $c2 == $l2 ]] ; then
        ((c++))
    else
        c=0
    fi
    if (( c < 3 )) ; then
        echo $c1 $c2 $n
        l1=$c1
        l2=$c2
    fi
done

@Dawg 给出了最佳答案。这一个 little 内存更轻,这可能不会影响您的数据:

sort -k1,2 -k3,3nr file | 
awk '
    {key =  FS } 
    prev != key {prev = key; count = 1} 
    count <= 3 {print; count++}
'