AWK查找每个键的最大值和最小值

时间:2018-07-17 13:37:50

标签: awk

我的数据看起来像这样: A学校和B学校每个班级的最高和最低中期成绩(未显示班级)

#school   highest  lowest
schoolA   99       53
schoolA   95       66
schoolA   88       48
schoolB   94       55
schoolB   91       36

我想像这样合并它:

schoolA   99       48
schoolB   94       36

显示每所学校的最大和最小。 我已经尝试过类似的东西:

awk '
BEGIN{getline;min=$3;max=$2} 
{($3<min)?min=$3:"";($2>max)?max=$2:""} 
END{OFS="\t";print $1,max,min}
'

它奏效了;但是,有时它会自动在最小值处加一个点(第三列)

有人可以教我如何正确执行此操作,并解释一下上面的代码是什么意思吗? (尤其是“ getline”)此代码可以按第一列(学校)合并行吗?

4 个答案:

答案 0 :(得分:3)

如果datamash没问题:

$ datamash -W -g1 max 2 min 3 < ip.txt 
schoolA 99  48
schoolB 94  36
  • -W使用空格作为分隔符
  • -g1按第一字段分组
  • max 2 min 3最多第二个字段,最少三个字段
  • 如果输入文件包含标题行,请使用--header-in选项将其忽略

答案 1 :(得分:0)

首先,我认为您的(condition)?var=one:two不正确。例如:

awk 'BEGIN{(3>5)?a=1:2;print a}'

不输出任何内容。应该写成:

$ awk 'BEGIN{a=(3>5)?1:2;print a}' 
2
  • 您不需要getline
  • 您未选中school,因此您的代码将在所有学校值中找到最小值/最大值。这不是你想要的。

对于您的问题,您可以这样写:

awk -v OFS='\t' '$1 in min{min[$1]=$3<min[$1]?$3:min[$1]
               max[$1]=$2>max[$1]?$2:max[$1]
               next } {min[$1]=$3;max[$1]=$2}
            END{for(x in min)print x, max[x], min[x]}' file

答案 2 :(得分:0)

使用POSIX awk,您可以执行以下操作:

awk ' BEGIN{fmt="%-15s%-10s%-10s\n"; printf fmt,"School","max","min"}
      !($1 in sch) {idx[++i]=$1; sch[$1]; arr[$1,"min"]=100}
      $2>arr[$1,"max"]{arr[$1,"max"]=$2}
      $3<arr[$1,"min"]{arr[$1,"min"]=$3}
      END{for (e=1;e<=length(idx);e++) printf fmt,idx[e],arr[idx[e],"max"],arr[idx[e],"min"]}' file
School         max       min       
schoolA        99        48        
schoolB        94        36      

按照书面规定,这将维护印刷学校的文件顺序。如果您不关心输出顺序,则编写起来会更简单。

不清楚您的文件是否带有标题。

如果确实有标题,则将printf fmt,"School","max","min"替换为FNR==1{printf fmt,$1,$2,$3}以打印标题。 (如果要跳过标题,则为FNR==1{next}。)

答案 3 :(得分:0)

getline获取下一个输入行。不过,从BEGIN块开始执行此操作并不算什么,因为Awk仍会读取每一行。我会改为:

NR==1{min=$3;max=$2;next} 

确保在脚本启动时将maxmin初始化为第一行的值。

($3<min?min=$3:"")$2max的相应语句非常晦涩。构造x ? y : z被称为三元运算符,并且是if (x) y; else z的简写,因此其计算结果为

if($3<min)
  min=$3
else
  ""

在这种情况下,""基本上就像一条注释,即一种明确表示“不做任何事情”的方式。

您当前的脚本获取总的最大值和最小值。您显然希望分别获取每个键的最大值和最小值。假设每个学校的所有值都相邻,

NR>1 && $1!=prev { print prev, max, min }
NR==1 || $1!=prev { prev=$1; max=$2; min=$3 }
$2>max { max=$2 }
$3<min { min=$3 }
END { print prev, max, min }

如果对输入进行了排序,至少足以在相邻行上全部提及一个keye,Awk可以处理更大的输入文件,因为它不需要将所有键都保留在内存中。您的示例文件似乎具有此属性,因此,如果它具有代表性,则应该可以使用。如果不是,请使用sort file | awk(在丢弃任何标题行(如果存在)之后)。

如果输入文件具有标题,但已经进行了排序,则在顶部添加NR==1 { next }以跳过它,然后将其他NR提及的内容调整为2而不是1。