当特定列中的值更改时打印行

Print lines when value change in specific column

我有一个文件:

A 48
B 24
C 1
D 7
E 25
F 47
G 14
H 2
I 1

我想在第二列获得最低值时打印行,然后是最大值等(包括第一行和最后一行)。它看起来像一个变体: 第一个值 -> 最低的一个值 -> 最大的 -> 最低的.. -> 最后的:

A 48
C 1
F 47
I 1

我想用awk。我可以打印最大(和最小)值

awk 'NR == 1 ||  > max {number = [=12=]; max = } END {if (NR) print number, max}' file.txt

但不是与变体对应的所有行。

有什么帮助吗?

假设:

  • 如果第二个字段打印在图表上,那么 objective 将打印对应于图表上的波峰和波谷的输入行
  • 从寻找低谷开始(即,第二个字段值呈下降趋势)
  • 当第2个字段值的趋势改变方向时确定波峰和波谷

一个awk想法:

awk '
BEGIN  { dir=1 }                          # set trend direction: dir==1 => looking for trough; dir== -1 looking for peak
FNR==1 { print; prev2=; next }          # always print the 1st line
       { if ( (*dir > prev2*dir) ) {    # if we just switched the trend direction then ...
            if (FNR>2) print prevline     # print the previous line (as long as FNR>2) and ...
            dir*=-1                       # toggle the trend direction
         }
         prev2=                         # update our "previous" variables
         prevline=[=10=]
       }
END    { print }                          # always print the last line
' file.txt

备注:

  • 从测试的角度来看,如果我们的 > 测试结果为真,那么趋势已经改变,我们只是看到了谷底(如果 dir==1)或峰值(如果 dir== -1
  • 我们可以在查找波峰和波谷时使用相同的测试 (>),只需反转我们正在比较的值的符号即可; dir 区间变量 flip-flops 在 1-1 之间随着趋势方向的变化有效地 flip-flops 我们在 > 和 [=24= 之间的测试]

这会生成:

A 48
C 1
F 47
I 1

对输入文件的一些修改:

$ cat file.txt
A 48
AA 49
B 24
C 1
D 7
E 25
F 47
G 14
H 27
I 1

awk 脚本生成:

A 48
AA 49
C 1
F 47
G 14
H 27
I 1

更详细的版本:

awk '
BEGIN         { trend="down" }
FNR==1        { print; prev2=; next }
trend=="down" { if ( ( > prev2) ) {             # just found a trough?
                   if (FNR>2) print prevline
                   trend="up"
                }
              }
trend=="up"   { if ( ( < prev2) ) {             # just found a peak?
                   if (FNR>2) print prevline
                   trend="down"
                }
              }
              { prev2=; prevline=[=14=] }
END           { print }
' file.txt

首先,感谢您的好问题(继续)。

使用您展示的示例,请尝试按照 awk 编写一个 通用 程序,它将遍历整个 Input_file 并检查最小值,并将根据它们在上一次出现最小数字到下一次出现最小数字之后的出现获得最小值。

awk -v min="" '
FNR==NR{
  min=(min<?(min==""?:min):)
  next
}
==min{
  print arr[max] ORS [=10=]
  prevMax=max=""
}
{
  max=(max>?max:)
  if(prevMax!=max){
    arr[max]=[=10=]
  }
  prevMax=max
}
'  Input_file  Input_file

使用您展示的示例,输出将如下所示:

A 48
C 1
F 47
I 1

说明:为以上代码添加详细说明。

awk -v min="" '                     ##Starting awk program from here, setting min value to NULL here.
FNR==NR{                            ##Checking condition FNR==NR which will be TRUE when first time Input_file is being read.
  min=(min<?(min==""?:min):)  ##Getting minimum value among all the lines of Input_file.
  next                              ##next will skip all further statements from here.
}
==min{                            ##Checking condition if 2nd field is equal to min then do following.
  print arr[max] ORS [=12=]             ##printing array arr with index of max ORS and current line.
  prevMax=max=""                    ##Nullifying prevMax and max here.
}
{
  max=(max>?max:)               ##Checking max value if current max is greater than  and assign  to it else keep max.
  if(prevMax!=max){                 ##If prevMax is NOT equal to max then do following.
    arr[max]=[=12=]                     ##Setting current line to arr with index of max and value of current line.
  }
  prevMax=max                       ##Setting max to preMax here.
}
'  Input_file  Input_file           ##Mentioning Input_file names here.

假设您不想打印具有相同 high/low 值的连续行,那么在每个 Unix 机器上使用任何 shell 中的任何 awk,您可以这样做:

$ cat tst.awk
{
    prev2 = curr2
    prev0 = curr0
    curr2 = 
    curr0 = [=10=]
}

NR == 1 {
    print curr0
}

NR > 1 {
    if ( curr2 > prev2 ) {
        if ( dir == "dn" ) {
            print prev0
        }
        dir = "up"
    }

    if ( curr2 < prev2 ) {
        if ( dir == "up" ) {
            print prev0
        }
        dir = "dn"
    }
}

END {
    print curr0
}

$ awk -f tst.awk file
A 48
C 1
F 47
I 1