比较字段1中每个记录的值,以查找最小值和最大值AWK

时间:2016-06-17 11:26:43

标签: bash csv awk text-processing gawk

我是文本预处理和AWK语言的新手。

我试图遍历给定字段(field1)中的每条记录,找到值的最大值和最小值,并将其存储在变量中。

算法:

1)设置Min = 0和Max = 0

2)循环$ 1(字段1)

3)比较字段1的FNR并设置Max和Min

4)最后打印Max和Min

这是我试过的:

BEGIN{max = 0; min = 0; NF = 58}
{
     for(i = 0; i < NF-57; i++)
     {

           for(j =0; j < NR; j++)
           {
             min = (min < $j) ? min : $j
             max = (max > $j) ? max : $j
           }
     }
}
END{print max, min}

#Dataset
f1  f2  f3  f4 .... f58
0.3 3.3 0.5 3.6
0.9 4.7 2.5 1.6 
0.2 2.7 6.3 9.3
0.5 3.6 0.9 2.7
0.7 1.6 8.9 4.7

此处,f1,f2,..,f58是数据集中的字段或列。

我需要遍历第一列(f1)并找到Min-Max。

需要输出: 最小= 0.2 最大= 0.9

我得到的结果是: Min =''(我没有得到任何结果) Max = 9.3(我得到所有字段的最大值而不是field1)

这是出于学习目的,所以我要求一列,以便我可以自己尝试多列

这就是我所拥有的:

这个for循环只循环4次,因为只有4个字段。 for循环中的代码是否会为每个记录执行5次?

for(i = 0; i < NF; i++)
{
    if (min[i]=="") min[i]=$i
    if (max[i]=="") max[i]=$i
    if ($i<min[i]) min[i]=$i
    if ($i>max[i]) max[i]=$i
}

END
{
    OFS="\t"; 
    print "min","max";
    #If I am not wrong, I saved the data in an array and I guess this would be the right way to print all min and max?
    for(i=0; i < NF; i++;)
    {
            print min[i], max[i]
    }
}

2 个答案:

答案 0 :(得分:4)

这是一个工作解决方案,它比你正在做的更容易:

/^-?[0-9]*(\.[0-9]*)?$/检查$1确实是有效数字,否则将被丢弃。

sort -n | awk '$1 ~ /^-?[0-9]*(\.[0-9]*)?$/ {a[c++]=$1} END {OFS="\t"; print "min","max";print a[0],a[c-1]}'

如果你不使用它,那么需要初始化min和max,例如使用第一个值:

awk '$1 ~ /^-?[0-9]*(\.[0-9]*)?$/ {if (min=="") min=$1; if (max=="") max=$1; if ($1<min) min=$1; if ($1>max) max=$1} END {OFS="\t"; print "min","max";print min, max}'

可读版本:

sort -n | awk '
$1 ~ /^-?[0-9]*(\.[0-9]*)?$/ {
  a[c++]=$1
}
END {
  OFS="\t"
  print "min","max"
  print a[0],a[c-1]
}'

awk '
  $1 ~ /^-?[0-9]*(\.[0-9]*)?$/ {
    if (min=="") min=$1
    if (max=="") max=$1
    if ($1<min) min=$1
    if ($1>max) max=$1
  }
  END {
    OFS="\t"
    print "min","max"
    print min, max
  }'

在您的输入上,输出:

min     max
0.2     0.9

编辑(回复评论,要求提供有关awk工作原理的更多信息)

Awk循环遍历lines(名为records),并且每行都有可用的列(名为fields)。每个awk次迭代都会读取一行,并提供NRNF个变量。在您的情况下,您只对第一列感兴趣,因此您只能使用$1这是第一列field。对于record匹配$1的每个/^-?[0-9]*(\.[0-9]*)?$/,它是匹配正负整数或浮点数的正则表达式,我们要么将值存储在数组a中(在第一个版本)或根据需要设置min / max变量(在第二个版本中)。

以下是条件$1 ~ /^-?[0-9]*(\.[0-9]*)?$/的解释:

  • $1 ~表示我们正在检查第一个字段$1是否与斜杠之间的正则表达式匹配
  • ^表示我们从$1字段
  • 的开头开始匹配
  • -?表示可选的minus sign
  • [0-9]*是任意位数(包括零,因此.1-.1可以匹配)
  • ()?表示可以存在或不存在的可选块
  • \.[0-9]*如果存在可选块,则应该以{{1​​}}开头并包含零个或多个数字(因此dot-.可以匹配!正确的,如果你有不确定的输入)
  • .表示我们匹配到$字段
  • 中的最后一个字符

如果您想循环浏览$1,则必须使用从fields1(包含)的for循环,如下所示:

NF

(请注意,为了简单起见,我没有在这里检查输入)

哪个输出:

echo "1 2 3 4" | awk '{for (i=1; i<=NF; i++) {if (min=="") min=$(i); if (max=="") max=$(i); if ($(i)<min) min=$(i); if ($(i)>max) max=$(i)}} END {OFS="\t"; print "min","max";print min, max}'

如果您有更多行作为输入,min max 1 4 也会在阅读第一个awk之后处理它们,例如此输入:

record

输出:

1 2 3 4
5 6 7 8

要防止这种情况并且仅在第一行上运行,您可以添加min max 1 8 之类的条件来仅处理第一行或在NR == 1循环后添加exit语句以停止在第一行之后处理输入。

答案 1 :(得分:2)

如果您只查看第1列,可以尝试:

awk '/^[[:digit:]].*/{if($1<min||!min){min=$1};if($1>max){max=$1}}END{print min,max}' dataset

脚本查找以数字开头的行,如果之前没有找到,则设置最小值或最大值。