Мне нужна помощь в написании скрипта AWK для группировки по строкам и поиска значений min / max / avg - PullRequest
1 голос
/ 31 января 2020

Я работаю в Bash и пытаюсь написать скрипт Awk, который берет данные из CSV-файла, группирует данные по строкам и затем получает значения min, max и avg значений.

Вот полный файл CSV:

Student,Catehory,Assignment,Score,Possible
Chelsey,Homework,H01,90,100
Chelsey,Homework,H02,89,100
Chelsey,Homework,H03,77,100
Chelsey,Homework,H04,80,100
Chelsey,Homework,H05,82,100
Chelsey,Homework,H06,84,100
Chelsey,Homework,H07,86,100
Chelsey,Lab,L01,91,100
Chelsey,Lab,L02,100,100
Chelsey,Lab,L03,100,100
Chelsey,Lab,L04,100,100
Chelsey,Lab,L05,96,100
Chelsey,Lab,L06,80,100
Chelsey,Lab,L07,81,100
Chelsey,Quiz,Q01,100,100
Chelsey,Quiz,Q02,100,100
Chelsey,Quiz,Q03,98,100
Chelsey,Quiz,Q04,93,100
Chelsey,Quiz,Q05,99,100
Chelsey,Quiz,Q06,88,100
Chelsey,Quiz,Q07,100,100
Chelsey,Final,FINAL,82,100
Chelsey,Survey,WS,5,5
Sam,Homework,H01,19,100
Sam,Homework,H02,82,100
Sam,Homework,H03,95,100
Sam,Homework,H04,46,100
Sam,Homework,H05,82,100
Sam,Homework,H06,97,100
Sam,Homework,H07,52,100
Sam,Lab,L01,41,100
Sam,Lab,L02,85,100
Sam,Lab,L03,99,100
Sam,Lab,L04,99,100
Sam,Lab,L05,0,100
Sam,Lab,L06,0,100
Sam,Lab,L07,0,100
Sam,Quiz,Q01,91,100
Sam,Quiz,Q02,85,100
Sam,Quiz,Q03,33,100
Sam,Quiz,Q04,64,100
Sam,Quiz,Q05,54,100
Sam,Quiz,Q06,95,100
Sam,Quiz,Q07,68,100
Sam,Final,FINAL,58,100
Sam,Survey,WS,5,5
Andrew,Homework,H01,25,100
Andrew,Homework,H02,47,100
Andrew,Homework,H03,85,100
Andrew,Homework,H04,65,100
Andrew,Homework,H05,54,100
Andrew,Homework,H06,58,100
Andrew,Homework,H07,52,100
Andrew,Lab,L01,87,100
Andrew,Lab,L02,45,100
Andrew,Lab,L03,92,100
Andrew,Lab,L04,48,100
Andrew,Lab,L05,42,100
Andrew,Lab,L06,99,100
Andrew,Lab,L07,86,100
Andrew,Quiz,Q01,25,100
Andrew,Quiz,Q02,84,100
Andrew,Quiz,Q03,59,100
Andrew,Quiz,Q04,93,100
Andrew,Quiz,Q05,85,100
Andrew,Quiz,Q06,94,100
Andrew,Quiz,Q07,58,100
Andrew,Final,FINAL,99,100
Andrew,Survey,WS,5,5
Ava,Homework,H01,55,100
Ava,Homework,H02,95,100
Ava,Homework,H03,84,100
Ava,Homework,H04,74,100
Ava,Homework,H05,95,100
Ava,Homework,H06,84,100
Ava,Homework,H07,55,100
Ava,Lab,L01,66,100
Ava,Lab,L02,77,100
Ava,Lab,L03,88,100
Ava,Lab,L04,99,100
Ava,Lab,L05,55,100
Ava,Lab,L06,66,100
Ava,Lab,L07,77,100
Ava,Quiz,Q01,88,100
Ava,Quiz,Q02,99,100
Ava,Quiz,Q03,44,100
Ava,Quiz,Q04,55,100
Ava,Quiz,Q05,66,100
Ava,Quiz,Q06,77,100
Ava,Quiz,Q07,88,100
Ava,Final,FINAL,99,100
Ava,Survey,WS,5,5
Shane,Homework,H01,50,100
Shane,Homework,H02,60,100
Shane,Homework,H03,70,100
Shane,Homework,H04,60,100
Shane,Homework,H05,70,100
Shane,Homework,H06,80,100
Shane,Homework,H07,90,100
Shane,Lab,L01,90,100
Shane,Lab,L02,0,100
Shane,Lab,L03,100,100
Shane,Lab,L04,50,100
Shane,Lab,L05,40,100
Shane,Lab,L06,60,100
Shane,Lab,L07,80,100
Shane,Quiz,Q01,70,100
Shane,Quiz,Q02,90,100
Shane,Quiz,Q03,100,100
Shane,Quiz,Q04,100,100
Shane,Quiz,Q05,80,100
Shane,Quiz,Q06,80,100
Shane,Quiz,Q07,80,100
Shane,Final,FINAL,90,100
Shane,Survey,WS,5,5

По сути, у меня есть 5 имен учеников, и каждый ученик выполнил викторину, лабораторную работу, домашнюю работу для каждого названия урока, плюс опрос и итоговый экзамен ...

То, что я пытаюсь сделать, это сгруппировать это по имени назначения и сгенерировать отчет, который показывает наименьшую оценку, достигнутую для этого назначения, наивысшую оценку и среднюю оценку ...

Вывод должен быть:

Name     Low     High  Avg
H02      66       99   74.22
L07      47       88   66.30

и включать каждое имя отдельного назначения из столбца 3 (3 доллара США). отформатирован с использованием tab (/ t)

Код, который я вставил, уже выводит заголовки и 2 десятичных знака в столбце avg, но фактические значения не верны.

У меня только две проблемы :

  1. Я не могу при жизни получить минимальное или максимальное значение для отдельных групп. Я знаю, как получить min / max и даже синтаксис basi c для него, но как мне получить его для отдельных групп?

  2. Сценарии этого. У меня очень ограниченный опыт использования bash, или что-то еще Linux в этом отношении, и я не знаком с awk (хотя сейчас я немного учусь).

Итак, чтобы получить Я, по крайней мере, начал, я написал один вкладыш для достижения нужной группировки и выходного форматирования, но это всего лишь суммирование баллов для каждой группы, и среднее значение все перепутано, потому что я до сих пор не понял, как получить количество баллов для использования в качестве делителя.

В любом случае, это то, что у меня есть:

awk -F "," 'BEGIN{printf "Name\tLow\tHigh\tAvg\n"}
            NR>=2{a[$3]+=$4; b[$3]+=$4;c[$3]+=$4/FNR }
            END {for (i in b) printf "%-7s\t%d\t%d\t%.02f\n", i,a[i],b[i],c[i]}'  \
    score-data.csv

Вывод идеален в том смысле, что он группируется по именам присвоений, 2 десятичных знака в колонка avg и табуляция .... но низкие и высокие значения не верны, а среднее, как вы видите, испорчено ... попытался разделить сумму на FNR. Также пробовал NF и NR оба ... не повезло. Опять же, я знаю, как получить счет, но не знаю, как его получить здесь.

Итак, если кто-нибудь может помочь мне позаботиться о min / max / avg, а также о синтаксисе для этого было бы полезно, если бы это был сценарий

Я не могу комментировать по какой-то причине, но я искал в Google и прочитал материал man awk, и у меня в браузере открыты две разные вкладки для просмотра документов на awk. Никто из них не решает эту проблему в моей ситуации.

Что касается именования массивов, то это все тот же массив, который используется; ассоциативный массив, который использует столбец 3 в качестве индекса / ключа и значения из столбца 4 в качестве значений ключа. все предлагаемые поиски и ссылки включают столбцы; Мне нужны строки.

Ответы [ 3 ]

2 голосов
/ 31 января 2020

Если вы хотите сохранить выходные данные в порядке, вы можете сделать что-то похожее на:

awk -F, '
BEGIN { printf "Name\tLow\tHigh\tAvg\n" }
NR > 1 {
    if ($3 in low) {            # if assignment already initialized
        if ($4 < low[$3])       # check new low score
            low[$3] = $4
        if ($4 > hi[$3])        # check new high score
            hi[$3] = $4
        sum[$3] += $4           # add to assignment sum
        grades[$3]++            # add to assignment score count
    }
    else {                      # new assignment name
        name[n++] = $3          # keep indexed array of names (for order)
        low[$3] = $4            # initialize low for assignment
        hi[$3]  = $4            # initialize high for assignment
        sum[$3] = $4            # initialize sum for assignment
        grades[$3] = 1          # initialize score count for assignment
    }
}
END {
    for (i=0; i<n; i++)         # output informaton in order
        printf "%s\t%d\t%d\t%.2f\n", name[i], low[name[i]], hi[name[i]], sum[name[i]]/grades[name[i]]
}' score-data.csv

Индексированный массив names выше используется для сохранения имен назначений в указанном порядке, а затем для перебрать назначения для вывода в следующем порядке:

Пример использования / Вывод

Name    Low     High    Avg
H01     19      90      47.80
H02     47      95      74.60
H03     70      95      82.20
H04     46      80      65.00
H05     54      95      76.60
H06     58      97      80.60
H07     52      90      67.00
L01     41      91      75.00
L02     0       100     61.40
L03     88      100     95.80
L04     48      100     79.20
L05     0       96      46.60
L06     0       99      61.00
L07     0       86      64.80
Q01     25      100     74.80
Q02     84      100     91.60
Q03     33      100     66.80
Q04     55      100     81.00
Q05     54      99      76.80
Q06     77      95      86.80
Q07     58      100     78.80
FINAL   58      99      85.60
WS      5       5       5.00
2 голосов
/ 31 января 2020

Это не awk, но GNU Datama sh - это удобный инструмент, разработанный специально для такого рода расчетов:

$ datamash -t, --header-in -g3 -s min 4 max 4 mean 4 < grades.csv \
  | awk 'BEGIN { FS=","; OFS="\t"; print "Name\tLow\tHigh\tAvg" } { $1=$1 } 1'
Name    Low     High    Avg
FINAL   58      99      85.6
H01     19      90      47.8
H02     47      95      74.6
H03     70      95      82.2
H04     46      80      65
H05     54      95      76.6
H06     58      97      80.6
H07     52      90      67
L01     41      91      75
L02     0       100     61.4
L03     88      100     95.8
L04     48      100     79.2
L05     0       96      46.6
L06     0       99      61
L07     0       86      64.8
Q01     25      100     74.8
Q02     84      100     91.6
Q03     33      100     66.8
Q04     55      100     81
Q05     54      99      76.8
Q06     77      95      86.8
Q07     58      100     78.8
WS      5       5       5

Хорошо, так что есть бит awk для печати желаемый заголовок и преобразовать из CSV в TSV.

Этот вызов говорит, что запятая является разделителем полей (-t,), что у входного файла есть строка заголовка, что он должен быть сгруппирован и отсортирован по третьему столбцу (-g3 -s; datamash требует сортировки групп), и для каждой группы должны быть рассчитаны минимальное, максимальное и среднее значения четвертого столбца.

2 голосов
/ 31 января 2020

Ваша проблема в том, что ваш скрипт Awk не проверяет результаты на ключ .

Попробуйте вместо этого.

awk -F , 'NR>1 { if(!($3 in course)) { low[$3] = high[$3] = $4 }
        if ($4 < low[$3]) low[$3] = $4;
        if ($4 > high[$3]) high[$3] = $4;
        sum[$3] += $4;
        ++course[$3] }
    END { OFS="\t"; print "Name", "Low", "High", "Avg";
        for (k in course)
          print k, low[k], high[k], sum[k]/course[k] }' file.csv

Результат для данных образца:

Name    Low High    Avg
FINAL   58  99  85.6
L01 41  91  75
L02 0   100 61.4
L03 88  100 95.8
L04 48  100 79.2
L05 0   96  46.6
Q01 25  100 74.8
L06 0   99  61
Q02 84  100 91.6
L07 0   86  64.8
H01 19  90  47.8
WS  5   5   5
Q03 33  100 66.8
H02 47  95  74.6
Q04 55  100 81
H03 70  95  82.2
Q05 54  99  76.8
H04 46  80  65
Q06 77  95  86.8
H05 54  95  76.6
Q07 58  100 78.8
H06 58  97  80.6
H07 52  90  67

Вычисление среднего значения путем деления на номер строки работает только тогда, когда вы хотите получить среднее значение для всего файла (и даже если, конечно, если вы пропускаете несколько строк в начале, они должны быть вычтены из делителя тоже).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...