Скрипт awk для суммирования чисел в столбце по циклу, не работающий для некоторых итераций цикла - PullRequest
3 голосов
/ 21 июня 2019

Пример ввода

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, сумма + = 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, не соответствующие одной строке.

Сценарий 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. Я изменил его, чтобы суммировать их отдельно. </p>

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 ]

2 голосов
/ 21 июня 2019

Это то, что вы пытаетесь сделать?

$ 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 вместо того, чтобы делать это после того, как мы прочитали весь ввод, если он либо слишком медленный, либо ваш входной файл слишком большой, чтобы хранить все в памяти, например:

$ 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
0 голосов
/ 22 июня 2019

Мне нравится твоя проблема.Это адекватный вызов.

Мой подход - включить все возможное в сценарий awk.И сканировать входной файл только один раз.Потому что манипулирование вводом / выводом медленнее, чем вычисления (в наши дни).

Выполните столько же вычислений (на самом деле 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...