Conte o número de ocorrências de um valor de coluna em um arquivo TSV com o AWK

3

Eu tenho um arquivo separado por tabulação TSV com 3 cols:

ID\tTEXT\tTYPE

Para imprimir a coluna TYPE que faço

cat /dataset.csv | awk -F $'\t' '{print $3}'

Esses valores são uma enumeração de valores como {CLASS_A,CLASS_B,CLASS_C} , etc.

Eu preciso de uma maneira inline com o AWK para contar o número de ocorrências ( NF ?) da coluna TYPE ao corresponder cada valor na enumeração para obter:

CLASS_A 1300
CLASS_B 450
CLASS_C 988

[ATUALIZAÇÃO]

De acordo com as soluções abaixo, estou colocando aqui minha última versão deste script

#!/bin/bash

COL=$1
FILE=$2

awk -v col="$COL" -F $'\t' '   {c[$col]++}
                 END{
                     for (i in c) printf("%s\t%s\n",i,c[i])
                 }' $FILE

e o uso para contar ocorrências de linhas na col 3 é

$ ./count_cols.sh 3 /myfile.csv
    
por loretoparisi 20.04.2017 / 12:51

3 respostas

4

Não há necessidade de usar cat para ler o arquivo. O AWK é perfeitamente capaz de lê-lo.

Uma instrução c[$3]++ principal deve obter a contagem de linhas de cada tipo.
Então, no final, basta imprimir (como valores separados por tabulações) todas as contagens:

#!/bin/bash

awk -F '\t' '   {c[$3]++}
                 END{
                     for (i in c) printf("%s\t%s\n",i,c[i])
                 }' dataset.csv 

Anexado

Dado o comentário do OP que:

I get some issues for colums that have quotes like that doesn\'t mean that you\'re not worth remembering think of the people who need to know they need to know so you need ​to show.... In this case the parsing on \t will fail.

Eu tenho que revisar a resposta. Eu criei este arquivo:

$ cat dataset.csv 
1233    that doesn\'t mean that you\'re not worth remembering think of the people who need to know they need to know so you need to show...    CLASS_0
1234    here    CLASS_A
1235    goes the values CLASS_B
1236    "that need counting"    CLASS_B
1237    "\like \this"   CLASS_B
1238    \or \this       CLASS_C
1239    including spaces        CLASS_B
1240    but not tabs    CLASS_A
1241    which could not work    CLASS_B
1242    finally CLASS_C
1243    this is CLASS_A
1244    over    CLASS_B
1245    988     CLASS_C

Esse arquivo, quando usado com o script, fornece o resultado correto:

$ ./script
CLASS_A 3
CLASS_B 6
CLASS_C 3
CLASS_0 1

Qual é o resultado correto.

Claro, o arquivo

  1. tem a quantidade correta de tabs para 3 campos e
  2. As variáveis
  3. são citadas corretamente quando expandidas e não estão em maiúsculas.

Para testar se um arquivo está em conformidade com o primeiro requisito, você pode usar este script:

#!/bin/bash

filetoread="$2"

<"$filetoread" tr -dc '\t\n' |
    awk '(length!=2){printf("Error in line: %s, has %s tabs\n",NR,length)}'

awk -F '\t' '(NF!=3){printf("Error in line: %s, has %s fields\n",NR,NF)}' "$filetoread"

Que verifica se há exatamente duas guias por linha e como Que o número de campos (como visto pelo awk) é na verdade três.

Adicionando algumas linhas de teste:

… …
1239    including spaces        CLASS_B
1       but not     tabs    CLASS_A
2       but not \ttabs  CLASS_A
1240    but not tabs    CLASS_A
… …

E executando o script acima:

$ ./script 3 dataset.csv
Error in line: 8, has 4 tabs
Error in line: 8, has 5 fields

detecta o ID da linha 1 que tem quatro guias (duas adicionadas) e não é enganado pela ID da linha 2 com \t .

Quanto à citação e uso de variáveis, isso é algo que você deve melhorar sozinho.

    
por 20.04.2017 / 13:19
3

Algo parecido com isso pode fazer o trabalho:

awk -F'\t' ' 
            $3=="CLASS_A" {a+=1} 
            $3=="CLASS_B" {b+=1} 
            $3=="CLASS_C" {c+=1} 
            END {
                printf "%s %d\n%s %d\n%s %d", CLASS_A,a,CLASS_B,b,CLASS_C,c
            }' /dataset.csv
    
por 20.04.2017 / 13:17
3

Espero ter entendido corretamente que a coluna 3 pode conter "CLASS_A" "CLASS_B" ou "CLASS_C"?

Então

awk -F'\t'  '
 { seen[$3]++ ;}
 END { for (i in seen) {
         printf "%s : %s\n",i,seen[i]
       }
      } 
 ' /dataset.csv

Deve fazer o truque?

Note que "para (i em visto)" não garante que eles são lidos na "ordem certa", mas você pode adicionar um | sort (depois de todo o awk) para classificá-los, ou usar mais truques convocados (dentro do awk).

Se você precisar também superar a primeira linha (se contiver cabeçalhos, por exemplo) adicione antes da primeira linha do script awk:

( NR==1 ) { next ;}

ou altere a primeira linha da seguinte forma:

( NR > 1 ) { seen[$3]++ ;}
    
por 20.04.2017 / 13:11