Script awk exibindo saída incorreta


8

Estou enfrentando um problema no awkscript - preciso gerar um relatório contendo as pontuações mais baixa, mais alta e média para cada atribuição no arquivo de dados. O nome da tarefa está localizado column 3.

Os dados de entrada são:

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

script awk :

BEGIN {
  FS=" *\\, *"
}

FNR>1 {
  min[$3]=(!($3 in min) || min[$3]> $4 )? $4 : min[$3]
  max[$3]=(max[$3]> $4)? max[$3] : $4
  cnt[$3]++
  sum[$3]+=$4
}
END {
  print "Name\tLow\tHigh\tAverage"
  for (i in cnt)
    printf("%s\t%d\t%d\t%.1f\n", i, min[i], max[i], sum[i]/cnt[i])

}

Resultado esperado da amostra:

Name    Low     High    Average
Q06     77      95      86.80
L05     40      96      46.60
WS      5       5       5
Q07     58      100     78.80
L06     60      99      61
L07     77      86      64.80

Ao executar o script, recebo um "Baixo" de 0 para todas as atribuições que não estão corretas. Onde eu estou errando? Por favor, guie.


mostre seus dados de amostra e script aqui.
Karakfa # 8/19

Evite postar imagens ou links para amostras de entrada e saída esperada, solicite que você os publique como texto com tags de código na sua pergunta e informe-nos.
RavinderSingh13

Editado por outro usuário já.
pikaraider

Usando o GNU awk, obtenho os mesmos resultados do seu código postado que da minha resposta de datamash, btw (depois de imprimir as atribuições na ordem de classificação em vez de aleatoriamente). O seu está funcionando bem.
Shawn

@ Shawn, você quer dizer que o script awk que publiquei acima está funcionando conforme o esperado para você? Ao executá-lo, vejo um "Baixo" de 0 para todas as atribuições. Você pode compartilhar o código awk que produz a saída correta para que eu possa corrigir meu erro?
Pikaraider # 8/19

Respostas:


1

Certamente você pode fazer isso com o awk, mas desde que você marcou esse script também, presumo que outras ferramentas sejam uma opção. Para esse tipo de coleta de estatísticas sobre grupos presentes nos dados, o GNU datamash geralmente reduz o trabalho a uma linha única. Por exemplo:

$ (echo Name,Low,High,Average; datamash --header-in -s -t, -g3 min 4 max 4 mean 4  < input.csv) | tr , '\t'
Name    Low     High    Average
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

Isso indica que, para cada grupo com o mesmo valor para a terceira coluna ( -g3, mais -spara classificar a entrada (requisito A da ferramenta)) da entrada CSV simples ( -t,) com um cabeçalho ( --header-in), exiba o mínimo, o máximo e a média de a quarta coluna. É tudo dado um novo cabeçalho e canalizado trpara transformar as vírgulas em abas.


1

Seu código funciona como está com o GNU awk. No entanto, executá-lo com a -topção de avisar sobre construções não portáteis fornece:

awk: foo.awk:6: warning: old awk does not support the keyword `in' except after `for'
awk: foo.awk:2: warning: old awk does not support regexps as value of `FS'

E executar o script com uma implementação diferente do awk ( mawkno meu caso) fornece 0 para a coluna Baixa. Então, alguns ajustes no script:

BEGIN {
  FS=","
}

FNR>1 {
  min[$3]=(cnt[$3] == 0 || min[$3]> $4 )? $4 : min[$3]
  max[$3]=(max[$3]> $4)? max[$3] : $4
  cnt[$3]++
  sum[$3]+=$4
}
END {
  print "Name\tLow\tHigh\tAverage"
  PROCINFO["sorted_in"] = "@ind_str_asc" # gawk-ism for pretty output; ignored on other awks
  for (i in cnt)
    printf("%s\t%d\t%d\t%.1f\n", i, min[i], max[i], sum[i]/cnt[i])

}

e funciona como esperado nesse outro awk também.

As mudanças:

  • Usando uma vírgula simples como separador de campos em vez de uma regex.
  • Alterando o mínimo condicional para definir o valor atual na primeira vez em que essa atribuição foi vista, verificando se cnt[$3]é igual a 0 (que será a primeira vez porque esse valor é incrementado em uma linha posterior) ou se o valor min atual é maior que esse valor.

Obrigado @ Shawn. Funciona perfeitamente!
Pikaraider # 8/19

Ou, supondo que esteja instalado no seu computador, basta executar gawka versão GNU.
Shawn

1

outra abordagem semelhante

$ awk -F, 'NR==1 {print "name","low","high","average"; next} 
                 {k=$3; sum[k]+=$4; count[k]++}
     !(k in min) {min[k]=max[k]=$4} 
       min[k]>$4 {min[k]=$4} 
       max[k]<$4 {max[k]=$4}                    
       END       {for(k in min) print k,min[k],max[k],sum[k]/count[k]}' file | 
 column -t

name   low  high  average
Q06    77   95    86.8
L05    0    96    46.6
WS     5    5     5
Q07    58   100   78.8
L06    0    99    61
L07    0    86    64.8
H01    19   90    47.8
H02    47   95    74.6
H03    70   95    82.2
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.