GUIDA AD AWK, 07 – raggruppare dati

Raggruppare dati in un file csv o txt

$ cat file4
 primo,100
 secondo,200
 terzo,300
 quarto,400
 quinto,500
 sesto,600
 settimo,700
 ottavo,800
 terzo,400

Calcolare la somma dei valori di una data colonna:

$ awk -F"," '{x=x+$2} END{print x}' file4
 4000

Il comando è abbastanza autoesplicativo. NB “x=x+$2″ si può indicare anche in modo più sintetico “x+=$2;”:

$ awk -F"," '{x+=$2} END{print x}' file4
 4000
# Variante del precedente:
 $ VAR="terzo"
 $ awk -F, -v inp=$VAR '$1==inp{x+=$2;}END{print x}' file4
 700

Per comprendere come awk gestisce gli array, vedi qui <link a awk array -> awk_9>

# Cercare valori unici nell prima colonna
 $ awk -F, '{a[$1];}END{for (i in a)print i;}' file4

a[$1] -> imposta l’array “a” con $1 come indice. L’array del nostro esempio quindi conterrà: “primo” nella prima ricorrenza; “secondo” nella seconda ricorrenza; terzo nella terza ricorrenza e così via.

END{for (i in a)print i;} -> alla fine della lettura del documento il ciclo for legge tutti gli indici dell’array assegnandoli alla variabile “i” e li stampa a scxhermo ad uno ad uno.
Siccome gli indici devono essere unici, nell’array “a” del nostro esempio il campo “terzo” sarà presente una sola volta. Di fatto il comando cancella i “doppioni”.
Nota: generalmente l’array ha un altro elemento, cioè il valore di quel dato indice. Ad esempio array1[“Dic”]=25 significa che l’array “array” ha un indice “Dic” con valore “25”. Per approfondimento sull’array: <link>

# Calcolare la somma di valori di particolari gruppi di record
 $ awk -F, '{a[$1]=a[$1]+$2;}END{for(i in a)print i", "a[i];}' file4
 sesto, 600
 quarto, 400
 settimo, 700
 primo, 100
 ottavo, 800
 quinto, 500
 terzo, 700
 secondo, 200
# "a[$1]=a[$1]+$2" può essere indicato nella forma contratta "a[$1]+=$2":
 $ awk -F, '{a[$1]+=$2;}END{for(i in a)print i", "a[i];}' file4
 sesto, 600
 quarto, 400
 settimo, 700
 primo, 100
 ottavo, 800
 quinto, 500
 terzo, 700
 secondo, 200
Sviluppo di "{a[$1]+=$2;}":
 a[primo]=100
 a[secondo]=200
 a[terzo]=300
 ...
 a[ottavo]=800
 a[terzo]=700 # in quanto al preesistente 300 viene aggiunto il nuovo valore 400

Essendo gli indici UNICI, otterremo la somma dei valori appartenenti allo stesso indice. Awk, nel nostro esempio, imposta la prima volta l’indice “terzo” con valore 300, poi alla seconda ricorrenza dello stesso indice “terzo” aggiungerà semplicemente il valore di 400.
END{for(i in a)print i”, “a[i];} -> per ogni indice nell’array “a” stampa l’indice (primo, secondo etc) seguito da virgola e spazio, e poi il valore assegnato al relativo indice.
In questo esempio l’array è “completo”: nome array (“a”), indice (valori prima colonna) e relativo valore (valori seconda colonna).

# Appendere all'output la somma di una colonna:
 $ awk -F"," '{x+=$2;print}END{print "Totale, "x}' file4
 primo,100
 secondo,200
 terzo,300
 quarto,400
 quinto,500
 sesto,600
 settimo,700
 ottavo,800
 terzo,400
 Totale, 4000
# Stampare il valore massimo di ogni gruppo di dati:
 awk -F, '{if(a[$1]<$2)a[$1]=$2;}END{for(i in a){print i,a[i];}}' file4

Prima di storare nell’array il valore “$2″ awk compara tale valore del record corrente con il valore già storato. Quest’ultimo viene sostituito se inferiore al primo. Cambiando l’operatore < in > si ottiene ovviamente l’effetto contrario.

# Conta le entries di ciascun gruppo:
 $ awk -F, '{a[$1]++;}END{for(i in a)print i, a[i];}' file4
 sesto 1
 quarto 1
 settimo 1
 primo 1
 ottavo 1
 quinto 1
 terzo 2
 secondo 1

Ogni volta che a[$1] viene matchato, incrementa il valore di quell’indice di 1. Quindi ogni volta che un indice viene matchato, incrementa il relativo valore di 1. Una volta elaborate tutte le righe, stampa l’indice e il relativo valore.

# Stampare solo il primo record di ogni gruppo:
 $ awk -F, '!a[$1]++ {print}' file4
 primo,100
 secondo,200
 terzo,300
 quarto,400
 quinto,500
 sesto,600
 settimo,700
 ottavo,800

Interessante: quando la prima riga di un gruppo viene elaborata a[$1] resta a 0, visto che ++ è conseguente alla variabile, ma il not (!) di 0 è 1 che è vero e quindi la prima ricorrenza viene stampata. Quando la seconda ricorrenza dello stesso gruppo viene elaborata a[$1] va a 1, ma il not (!) di 1 è 0, che è falso e quindi la riga non viene stampata. In tal modo solo la prima ricorrenza viene stampata.
Ovviamente rimuovendo il not (!) vengono stampate tutte le ricorrenze di ogni gruppo, tranne la prima.

# Concatenare i valori di ogni gruppo:
 $ awk -F, '{if(a[$1])a[$1]=a[$1]":"$2; else a[$1]=$2;} END{for (i in a)print i, a[i];}' file4
 sesto 600
 quarto 400
 settimo 700
 primo 100
 ottavo 800
 quinto 500
 terzo 300:400
 secondo 200

Se “a[$1]” contiene un valore concatena il valore corrente utilizzando i “:”, altrimenti assegna semplicemente ad a[$1] il valore del relativo 2, visto che evidentemente è il primo valore della ricorrenza.
Lo stesso comando, in forma di script awk sarà:

 #!/usr/bin/awk -f
 
{
 FS=","
 if (a[$1]){
 a[$1]=a[$1]":"$2;
 }
 else {
 a[$1]=$2;
 }
 }
 END{
 OFS=","
 for (i in a){
 print i, a[i];
 }
 }

Lo stesso risultato si può ottenere con l’operatore ternario di awk, uguale a quello del linguaggio C:

 $ awk -F, '{a[$1]=a[$1]?a[$1]":"$2:$2;}END{for (i in a)print i, a[i];}' OFS=, file4

————————————

INDICE

Lascia una risposta

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>