超过某个临界值的连续值数

时间:2018-06-20 14:29:30

标签: bash awk

我是bash和linux编程的新手。我有一个小问题。

对于特定的截止点(c),我想转储一个文件,如果两个连续的值都在c之上,则该文件将打印出c之上的值。例如

x y
1 0.34
2 0.3432
3 0.32
4 0.35
5 0.323
6 0.3623
7 0.345

如果c = 0.33,它将打印出第2列

0.34
0.3432
0.3623
0.345

尽管它超出了截止值0.33,但它不会打印出0.35,因为0.35之后的下一个值为0.323,这使参数“两个连续的值都高于c”失败。

4 个答案:

答案 0 :(得分:1)

原始问题: 打印2个或多个连续值满足给定条件的所有序列

以下应该可以工作:

awk 'p || (prev>c && $2>c && NR>2){print prev}
     { p = (prev>c && $2>c); prev=$2 }
     END{if(p) print $2 }' c=0.33 <file>

它具有以下逻辑:

  • p会跟踪是否已打印上一行。如果已打印,则也应打印当前行。
  • 如果未打印前一行(p==0),则应检查是否应在(prev>c && $2>c)上打印前一行
  • 为下一行计算p,并将prev设置为当前值
  • 最后,如果p==1打印最后一个值。

您基本上总是落后一线。

解决此问题的另一种方法是检查该值是否满足条件并将其存储在数组中。如果遇到不满足条件的值,请处理该数组。这会占用更多的内存:

awk '(NR==1){next}
     ($2>c) { a[NR]=$2; next }
     (length(a) == 1) { delete a[NR-1]; next }
     { for(i=NR-length(a);i<NR;++i) {print a[i]; delete a[i]} }
     END { if (length(a)>1) for(i=NR+1-length(a);i<=NR;++i) {print a[i]} }
    ' c=0.33 <file>

第二个问题: 打印$ 2的连续值的子集,其中m或更多的值满足条件cond,并且最多n个连续值值不满足cond。序列的开始和结束的值满足cond

以下awk脚本将执行此操作。不要忘记根据自己的意愿调整值mnc并更新条件函数。

function cond(val) { return val > c }
BEGIN{c=0.33; m=2; n=1}
# skip the header
(NR==1){next}
# if no values satisfy cond ...
(M==0 && !cond($2)) { next }
# ... otherwise continue from here
{ a[NR]=$2 }
# set counters M and N (M satisfy cond, N not )
 cond($2) { M++; N=0 }
!cond($2) { N++ }
# This sequence failed, delete it
(N>n && M<m) { for(i in a) delete a[i]; M=0; N=0 }
# This sequence is OK, strip it and print it
(N>n) { j=NR; while (!cond(a[j])) delete a[j--]
        for (i=j+1-length(a);i<=j;++i) { print a[i]; delete a[i] }
        M=0; N=0 }
# Check if the final stored sequence is successful
END { if (M>=m) { 
         j=NR; while (!cond(a[j])) delete a[j--]
         for (i=j+1-length(a);i<=j;++i) print a[i]
      }
    }

答案 1 :(得分:0)

Perl解决方案:

c=.33 m=2 perl -lane '
if ($F[1] > $ENV{c}) { push @r, $F[1] }
else {
    if (@r >= $ENV{m}) { print for @r }
    @r = ();
}
END { if (@r >= $ENV{m}) { print for @r } }' -- file

它将连续值存储到数组@r中,如果当前值小于阈值,则在足够长的情况下打印该数组。

  • -l从输入中删除换行符并将其添加到输出中
  • -n逐行读取输入
  • -a将每一行自动拆分为@F数组
  • 用于数字上下文的数组会调整其大小
  • %ENV哈希包含环境变量

如果序列往往很长,则只能将前m个元素存储在数组中以节省一些内存。

if ($F[1] > $ENV{c}) {
    push @r, $F[1];
    print shift @r if @r > $ENV{m};
} else {
    if (@r >= $ENV{m}) { print for @r }
    @r = ();
}
END { if (@r >= $ENV{m}) { print for @r } }'

答案 2 :(得分:0)

您可以使用以下awk脚本:

awk -v cutoff="0.33" '
  $2>cutoff{
    if(prev) 
      {print prev ORS $2 } 
    else 
      {prev=$2;next}
  }
  {prev=""}' file

如果超出临界值,它将在prev变量中存储该值,并将其重置为下一个数字。

答案 3 :(得分:0)

在awk中使用Bash参数的方式如下:

$ c=2.3
$ awk -v c="$c" 'BEGIN{print c}'
2.3

然后您可以使用它来编写脚本,如下所示:

c=0.33
m=2
awk -v c="$c" -v m="$m" '($2+0!=$2) {next}
                   $2+0<c {cnt=0; split("",lst); next}
                   $2+0>=c && cnt<m {lst[++cnt]=$2}
                   $2+0>=c && cnt==m {for (i=1; i<=m; i++) print lst[i]
                                    cnt=0; split("",lst)}' file

这不会显示重叠范围,例如:

1 0.34
2 0.3432     # prints 0.34\n0.3432\n here
3 0.35       # unclear if it should print 0.3432\n0.34\n  here....

给出更新,将打印连续的行。

给出:

$ cat file
x y
1 0.34
2 0.3432
2a 0.35
3 0.32
4 0.35
5 0.323
6 0.3623
7 0.345

您可以这样做:

c=0.33
m=2
awk -v c="$c" -v m="$m" '($2+0!=$2) {next}
             $2+0>=c {lst[++cnt]=$2; next}
             $2+0<c { if (cnt>=m) for (i=1; i<=cnt; i++) print lst[i]
                      cnt=0; split("",lst); next}
             END{if (cnt>=m) for (i=1; i<=cnt; i++) print lst[i]}' file

打印:

0.34
0.3432
0.35
0.3623
0.345