假设我有这个输入文件:
A 5
B 6
C 3
A 4
B 2
C 1
是否可以按第2列中的值对第1列中的组进行排序?所需输出如下:
B 6 <-- B group at the top, because 6 is larger than 5 and 3
B 2 <-- 2 less than 6
A 5 <-- A group in the middle, because 5 is smaller than 6 and larger than 3
A 4 <-- 4 less than 5
C 3 <-- C group at the bottom, because 3 is smaller than 6 and 5
C 1 <-- 1 less than 3
以下是我的解决方案:
join -t$'\t' -1 2 -2 1 \
<(cat 49142202.txt | sort -k2nr,2 | sort --stable -k1,1 -u | sort -k2nr,2 \
| cut -f1 | nl | tr -d " " | sort -k2,2) \
<(cat 49142202.txt | sort -k1,1 -k2nr,2) \
| sort --stable -k2n,2 | cut -f1,3
按第2列排序的
49142202.txt
的第一个输入是:2 A
1 B
3 C
按列1排序的
join
的第二个输入是:A 5
A 4
B 6
B 2
C 3
C 1
join
的输出是:A 2 5
A 2 4
B 1 6
B 1 2
C 3 3
C 3 1
然后按第2列中的
join
行号排序,然后用nl
保留原始输入列1和3。我知道用Python的pandas的
cut
可以容易得多,但是在坚持使用GNU Coreutils如groupby
、sort
、join
、cut
和tr
时,有没有一种更优雅的方法呢?最好我想避免一个内存效率低下的解决方案,但请分享这些。谢谢! 最佳答案
如注释所述,我的解决方案试图减少pipes
的数量、不必要的cat
命令,尤其是管道sort
操作的数量,因为排序是一个复杂/耗时的操作:
我得到了以下解决方案,其中f_grp_sort
是输入文件:
for elem in $(sort -k2nr f_grp_sort | awk '!seen[$1]++{print $1}')
do
grep $elem <(sort -k2nr f_grp_sort)
done
输出:
B 6
B 2
A 5
A 4
C 3
C 1
说明:
sort -k2nr f_grp_sort
将生成以下输出:B 6
A 5
A 4
C 3
B 2
C 1
sort -k2nr f_grp_sort | awk '!seen[$1]++{print $1}'
将生成输出:B
A
C
awk
将以相同的顺序生成临时输出第一列的1个唯一元素。然后
for elem in $(...)do grep $elem <(sort -k2nr f_grp_sort); done
将
grep
用于包含B
然后A
的行,然后C
将提供所需输出的行。现在作为增强功能,您可以使用临时文件避免执行两次
sort -k2nr f_grp_sort
操作:$ sort -k2nr f_grp_sort > tmp_sorted_file && for elem in $(awk '!seen[$1]++{print $1}' tmp_sorted_file); do grep $elem tmp_sorted_file; done && rm tmp_sorted_file