awk脚本在循环中某列中的数字求和,不适用于该循环中的某些迭代

时间:2019-06-21 15:57:59

标签: awk

样本输入

12.0000 0.6000000 0.05
13.0000 1.6000000 0.05
14.0000 2.6000000 0.05
15.0000 3.0000000 0.05
15.0000 3.2000000 0.05
15.0000 3.4000000 0.05
15.0000 3.6000000 0.10
15.0000 3.8000000 0.10
15.0000 4.0000000 0.10
15.0000 4.2000000 0.11
15.0000 4.4000000 0.12
15.0000 4.6000000 0.13
15.0000 4.8000000 0.14
15.0000 5.0000000 0.15
15.0000 5.2000000 0.14
15.0000 5.4000000 0.13
15.0000 5.6000000 0.12
15.0000 5.8000000 0.11
15.0000 6.0000000 0.10
15.0000 6.2000000 0.10
15.0000 6.4000000 0.10
15.0000 6.6000000 0.05
15.0000 6.8000000 0.05
15.0000 7.0000000 0.05

目标

  1. 将输出的第1行打印为0 0
  2. 对于$ 2 = 5.000000,$ 3 = 0.15。
    • 将输出的第2行打印为1 0.15
  3. 对于$ 2 = 4.800000至$ 2 = 5.200000,每行的sum + = $ 3(即0.14 + 0.15 + 0.14 = 0.43)。
    • 将输出中的第3行打印为2 0.43。
  4. 对于$ 2 = 4.600000至$ 2 = 5.400000,每行总和+ = $ 3(即0.13 + 0.14 + 0.15 + 0.14 + 0.13 = 0.69)。
    • 在输出中将第4行打印为3 0.69
  5. 继续此模式,直到$ 2 = 5.000000 +-1.6(总共9行,加上第1行为0 0 =输出中总共有10行)

所需的输出

0 0
1 0.15
2 0.43
3 0.69
4 0.93
5 1.15
6 1.35
7 1.55
8 1.75
9 1.85

尝试

脚本1

#!/bin/bash

for (( i=0; i<=8; i++ )); do

awk '$2 >= 5.0000000-'$i'*0.2 {sum+=$3}
     $2 == 5.0000000+'$i'*0.2 {print '$i', sum; exit
     }' test.dat
     done > test.out

产生

0 0.15
1 0.43
2 0.69
3 0.93
4 1.15
5 1.35
6 1.55
7 1.75
8 1.85

这非常接近。但是,输出缺少第1行的0 0,因此,第2行到第10行的$ 1和$ 2不匹配1行。

脚本2

#!/bin/bash

for (( i=0; i<=8; i++ )); do

awk ''$i'==0 {sum=0}
     '$i'>0 && $2 > 5.0000000-'$i'*0.2 {sum+=$3}
     $2 == 5.0000000+'$i'*0.2 - ('$i' ? 0.2 : 0) {print '$i', sum; exit
     }' test.dat
     done > test.out

产生

0 0
1 0.15
2 0.43
4 0.93
5 1.15
6 1.35
7 1.55

$ 1和$ 2现在正确匹配了。但是,我完全缺少$ 1 = 3,$ 1 = 8和$ 1 = 9的行。添加三元运算符会使我的代码以某种方式跳过循环中的这些迭代。

问题

谁能解释脚本2的问题或如何在一行代码中实现所需的输出?谢谢。

解决方案

我用埃德·莫顿的解决方案解决了这个问题。他们两个都为不同的目标而努力。我没有使用模数来节省数组空间,而是将数组限制为$ 1 = 15.0000。我这样做是为了代替模数,以便将我也想在输入的不同部分求和的另外两个“关键”变量合并到单独的输出文件中。

据我所知,脚本只对$ 2> = 5.0000000的行求和,然后将总和乘以2,以包括$ 2 <= 5.0000000的行。这适用于此处的示例输入,因为我使$ 3对称于0.15。不过,我对其进行了修改以将它们分别求和。

awk 'BEGIN { key=5; range=9}
$1 == 15.0000 {     
      a[NR] = $3
}
$2 == key { keyIdx = NR}
END {
    print (0, 0) > "test.out"
    sum = a[keyIdx]
    for (delta=1; delta<=range; delta++) {
        print (delta, sum) > "test.out"
        plusIdx = (keyIdx + delta) 
        minusIdx = (keyIdx - delta)
        sum += a[plusIdx] + a[minusIdx]
    }
    exit
}' test.dat

2 个答案:

答案 0 :(得分:2)

这是您要做什么吗?

$ cat tst.awk
$2 == 5 { keyNr = NR }
{ nr2val[NR] = $3 }
END {
    print 0, 0
    sum = nr2val[keyNr]
    for (delta=1; delta<=9; delta++) {
        print delta, sum
        sum += nr2val[keyNr+delta] + nr2val[keyNr-delta]
    }
}

$ awk -f tst.awk file
0 0
1 0.15
2 0.43
3 0.69
4 0.93
5 1.15
6 1.35
7 1.55
8 1.75
9 1.85

我们可以对其进行优化,以便仅将2*(range=9)的值存储在vals[]中(使用模数运算符NR%(2*range)作为索引),并在遇到以下情况的NR时进行计算: range行会超过$2 == key行,而不是在行速太慢或您的输入文件太大而无法将所有内容存储在内存中后,在读取全部输入之后执行此操作,例如:< / p>

$ cat tst.awk
BEGIN { key=5; range=9 }
{
    idx = NR % (2*range)
    nr2val[idx] = $3
}
$2 == key { keyIdx = idx; endNr = NR+range }
NR == endNr { exit }
END {
    print 0, 0
    sum = nr2val[keyIdx]
    for (delta=1; delta<=range; delta++) {
        print delta, sum
        idx = (keyIdx + delta) % (2*range)
        sum += nr2val[idx] + nr2val[idx]
    }
    exit
}

$ awk -f tst.awk file
0 0
1 0.15
2 0.43
3 0.69
4 0.93
5 1.15
6 1.35
7 1.55
8 1.75
9 1.85

答案 1 :(得分:0)

我喜欢你的问题。这是一个足够的挑战。

我的方法是将所有可能的内容放入awk脚本中。并且仅扫描输入文件一次。因为这些天来I / O操作比计算要慢。

在相关输入行上进行尽可能多的计算(实际上是9)。

所需的输入是变量F1和文本文件input.txt

执行命令是:

awk -v F1=95 -f script.awk input.txt

因此逻辑是:

1. Initialize: Compute the 9 range markers and store their values in an array.

2. Store the 3rd input value in an order array `field3`. We use this array to compute the sum.

3. On each line that has 1st field equals 15.0000. 

3.1 If found begin marker then mark it.

3.2 If found end marker then compute the sum, and mark it.

4. Finalize: Output all the computed results

script.awk,其中包含很少的调试打印输出以协助调试

BEGIN {
    itrtns = 8; # iterations count consistent all over the program.
    for (i = 0; i <= itrtns; i++) { # compute range markers per iteration
        F1start[i] = (F1 - 2 - i)/5 - 14; # print "F1start["i"]="F1start[i];
        F1stop[i] = (F1 - 2 + i)/5 - 14; # print  "F1stop["i"]="F1stop[i];
        b[i] = F1start[i] + (i ? 0.2 : 0); # print "b["i"]="b[i];
    }
}
{    field3[NR] = $3;}  # store 3rd input field in ordered array.
$1==15.0000 { # for each input line that has 1st input field 15.0000
    currVal = $2 + 0; # convert 2nd input field to numeric value
    for (i = 0; i <= itrtns; i++) { # on each line scan for range markers
        # print "i="i, "currVal="currVal, "b["i"]="b[i], "F1stop["i"]="F1stop[i], isZero(currVal-b[i]), isZero(currVal-F1stop[i]);
        if (isZero(currVal - b[i])) { # if there is a begin marker
            F1idx[i] = NR; # store the marker index postion
            # print "F1idx["i"] =", F1idx[i];
        }
        if (isZero(currVal - F1stop[i])) { # if there is an end marker
            for (s = F1idx[i]; s <= NR; s++) {sum[i] += field3[s];} # calculate its sum
            F2idx[i] = NR; # store its end marker postion (for debug report)
            # print "field3["NR"]=", field3[NR];
        }
    }
}
END { # output the computed results
    for (i = 0; i <= itrtns; i++) {print i, sum[i], "rows("F1idx[i]"-"F2idx[i]")"}
}
function isZero(floatArg) { # floating point number pecision comparison
    tolerance = 0.00000000001;
    if (floatArg < tolerance && floatArg > -1 * tolerance )
        return 1;
    return 0;
}

从问题中提供了input.txt

12.0000 0.6000000 0.05
13.0000 1.6000000 0.05
14.0000 2.6000000 0.05
15.0000 3.0000000 0.05
15.0000 3.2000000 0.05
15.0000 3.4000000 0.05
15.0000 3.6000000 0.10
15.0000 3.8000000 0.10
15.0000 4.0000000 0.10
15.0000 4.2000000 0.11
15.0000 4.4000000 0.12
15.0000 4.6000000 0.13
15.0000 4.8000000 0.14
15.0000 5.0000000 0.15
15.0000 5.2000000 0.14
15.0000 5.4000000 0.13
15.0000 5.6000000 0.12
15.0000 5.8000000 0.11
15.0000 6.0000000 0.10
15.0000 6.2000000 0.10
15.0000 6.4000000 0.10
15.0000 6.6000000 0.05
15.0000 6.8000000 0.05
15.0000 7.0000000 0.05

awk -v F1=95 -f script.awk input.txt

的输出
0 0.13 rows(12-12)
1 0.27 rows(12-13)
2 0.54 rows(11-14)
3 0.79 rows(10-15)
4 1.02 rows(9-16)
5 1.24 rows(8-17)
6 1.45 rows(7-18)
7 1.6 rows(6-19)
8 1.75 rows(5-20)

awk -v F1=97 -f script.awk input.txt

的输出
0 0.15 rows(14-14)
1 0.29 rows(14-15)
2 0.56 rows(13-16)
3 0.81 rows(12-17)
4 1.04 rows(11-18)
5 1.25 rows(10-19)
6 1.45 rows(9-20)
7 1.65 rows(8-21)
8 1.8 rows(7-22)