如果前两列相等,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
被组织成 blocks 个 condition {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++}
'
我想 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
被组织成 blocks 个 condition {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++}
'