我正在使用 Ubuntu,我有一个像这样的输入文件
ifile.dat
1 10 15
3 34 20
1 4 22
3 32 33
5 3 46
2 2 98
4 20 100
3 13 23
4 50 65
1 40 76
2 20 22
我该如何实现这一目标?
ofile.dat
1 40 76
2 20 98
3 34 33
4 50 100
5 3 46
我的意思是通过比较第一列来获得每列的最大值。谢谢。
这是我尝试过的(在具有 13 列的示例文件上)。但最高价值并不是这样出现的。
cat input.txt | sort -k1,1 -k2,2nr -k3,3nr -k4,4nr -k5,5nr -k6,6nr -k7,7nr -k8,8nr -k9,9nr -k10,10nr -nrk11,11 -nrk12,12 -nrk13,13 | sort -k1,1 -u
它不起作用。所以一个乐于助人的人试图在下面帮助我解决这个问题。但是无论在mac上还是在ubuntu上用gawk,我都无法运行它并看到下面的错误
awk 'BEGIN{PROCINFO["sorted_in"] = "@val_num_asc"} {for(i=2;i<=NF;++i) if (a[$1][i]<$i){a[$1][i]=$i}} END{n=asorti(a, asorted); for(col1 in asorted){print col1, a[col1][2], a[col1][3]}}' input.txt
错误是
awk: syntax error at source line 1
context is
BEGIN{PROCINFO["sorted_in"] = "@val_num_asc"} {for(i=2;i<=NF;++i) if >>> (a[$1][ <<<
awk: illegal statement at source line 1
awk: illegal statement at source line 1
我确实尝试删除 BEGIN 语句并使用 for 循环,但找不到运气。谢谢。
PS:我从 stackoverflow 得到了这个答案。所以我在这里发帖是因为这是一个unix/linux专题论坛。
答案1
GNU 数据混合对于这样的事情很好:
$ datamash -sW groupby 1 max 2,3 < ifile.dat
1 40 76
2 20 98
3 34 33
4 50 100
5 3 46
要处理更多数量的列,您可以指定范围例如
datamash -sW groupby 1 max 2-13 < ifile.dat
答案2
awk解决方案任何列数(您已经提到过包含 13 列的示例文件):
假设我们有扩展的示例文件:
1 10 15 10 99
3 34 20 20 111
1 4 22 22 33
3 32 33 12 5
5 3 46 44 9
2 2 98 55 55
4 20 100 11 33
3 13 23 77 23
4 50 65 33 66
1 40 76 78 16
2 20 22 98 93
awk '{ for(i=2;i<=NF;i++) { if (!($1 in a) || $i > a[$1][i]) a[$1][i]=$i }}
END{ r=""; for(i in a) { r=i; for(j in a[i]) r=r OFS a[i][j]; print r }
}' OFS='\t' file
输出:
1 40 76 78 99
2 20 98 98 93
3 34 33 77 111
4 50 100 33 66
5 3 46 44 9
答案3
这是 awk 中的一种方法:
$ awk '{
if($2 > a[$1][2]){
a[$1][2] = $2
}
if($3 > a[$1][3]){
a[$1][3] = $3
}
}
END{
for(i in a){
printf "%s ", i;
for(c=1; c<=maxFields; c++){
if(c in a[i]){
printf "%s ",a[i][c]
}
}
print ""
}' ifile.dat
1 40 76
2 20 98
3 34 33
4 50 100
5 3 46
该脚本仅使用二维数组 a
来存储 2 列中每一列的最大值。对于i
第一列的每个值, 将保存在第二列中a[i][2]
找到的最大值和第三列中的最大值。处理完整个文件后,我们将打印每个 值的最大值。i
a[i][3]
i
如果您有超过 3 列,您可以使用:
awk '{
for(c=2; c<=NF; c++){
if($c > a[$1][c]){
a[$1][c] = $c;
}
}
}
END{
for(i in a){
printf "%s: ", i;
for(c in a[i]){
printf "%s ",a[i][c]
}
print ""
}
}' ifile.dat
请注意,上述解决方案对于负值将无法正常工作,或者如果您可以使用0
etc,它可能会导致字段的顺序错误,因为awk
不一定按顺序遍历数组。更稳健的方法是:
awk '{
for(c=2; c<=NF; c++){
if(!(c in a) || $c > a[$1][c]){
a[$1][c] = $c;
}
}
}
END{
for(i in a){
printf "%s ", i;
for(c in a[i]){
printf "%s ",a[i][c]
}
print ""
}
}' ifile.dat
答案4
Python 3 脚本
#!/usr/bin/env python3
import sys
from collections import OrderedDict as od
# read data in the file first, create data dictionary of column lists
data = od()
with open(sys.argv[1]) as f:
for line in f:
columns = line.strip().split()
how_many = len(columns)-1
if columns[0] not in data.keys():
data[ columns[0] ] = [ [] for i in range(how_many) ]
for index in range(how_many):
data[ columns[0] ][index].append( int(columns[index+1]) )
# post process all the created lists of lists by applying max() on each
for item in sorted(data.keys()):
print(item,end=" ")
for array in data[item]:
print(max(array),end=" ")
print("")
测试运行
OP提供的输入示例:
$ ./columns_max.py input.txt
1 40 76
2 20 98
3 34 33
4 50 100
5 3 46
Roman Perekhrest 的回答中有扩展的例子:
$ ./columns_max.py input.txt
1 40 76 78 99
2 20 98 98 93
3 34 33 77 111
4 50 100 33 66
5 3 46 44 9
这是如何运作的:
基本思想是我们创建一个第一列项目的字典。因此,在字典中,我们将有键 1、2、3、4 和 5。字典项的每个对应值都是列表的列表,其中每个子列表对应于一列。因此,对于键 1,我们将有一个包含两个列表的列表,其中第一个列表用于所有第 2 列项目,第二个列表用于所有第 3 列项目。基本上是这样的:
('1', [ ['10', '4', '40'], ['15', '22', '76']] )
现在,有一个非常好的函数叫做max()
,它允许我们获取一个数字列表并从中提取最大的项目。我们所要做的就是迭代每个键,取出所有列表,并对max()
它们应用函数。