somme des colonnes en fonction des champs correspondants

11

J'ai un gros fichier au format suivant:

2 1019 0 12 
2 1019 3 0 
2 1021 0 2 
2 1021 2 0 
2 1022 4 5
2 1030 0 1 
2 1030 5 0 
2 1031 4 4

Si les valeurs de la colonne 2 correspondent, je veux additionner les valeurs des colonnes 3 et 4 des deux lignes, sinon juste la somme des valeurs de la ligne unique.

Ainsi, la sortie que j'espère ressemblerait à ceci:

2 1019 15 
2 1021 4 
2 1022 9 
2 1030 6 
2 1031 8

Je peux trier les fichiers selon la colonne 2 avec awkou sortet additionner les dernières colonnes avec awk, mais uniquement pour les lignes individuelles et non pour deux lignes où la colonne 2 correspond.

TomPio
la source
1
Et la colonne 1?
glenn jackman
@glennjackman: la colonne 1 a la même valeur dans chaque fichier. Il sert d'identifiant pour le fichier (j'en ai 45) et sera utilisé pour certains processus en aval. Pour ma question, il pourrait tout aussi bien être ignoré (ou supprimé) et ajouté plus tard.
TomPio
ou, faites $1 $2comme la clé.
glenn jackman

Réponses:

12

Je le ferais en Perl:

$ perl -lane '$k{"$F[0] $F[1]"}+=$F[2]+$F[3]; 
              END{print "$_ $k{$_}" for keys(%k) }' file 
2 1019 15
2 1021 4
2 1030 6
2 1031 8
2 1022 9

Ou awk:

awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file 

Si vous souhaitez que la sortie soit triée selon la deuxième colonne, vous pouvez simplement diriger vers sort:

awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file | sort -k2

Notez que les deux solutions incluent également la 1ère colonne. L'idée est d'utiliser les première et deuxième colonnes comme clés d'un hachage (en perl) ou d'un tableau associatif (en awk). La clé de chaque solution est column1 column2que si deux lignes ont la même colonne deux mais une colonne différente, elles seront regroupées séparément:

$ cat file
2 1019 2 3
2 1019 4 1
3 1019 2 2

$ awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file
3 1019 4
2 1019 10
terdon
la source
7

Cela pourrait peut-être aider, mais la colonne 1 est-elle toujours 2 et les résultats en dépendent-ils?

awk '{ map[$2] += $3 + $4; } END { for (i in map) { print "2", i, map[i] | "sort -t't'" } }' file

ou comme l'a mentionné glenn jackman dans les commentaires sur le tri:

gawk '{ map[$2] += $3 + $4; } END { PROCINFO["sorted_in"] = "@ind_str_asc"; for (i in map) { print 2, i, map[i] } }' file
taliezin
la source
2
Si vous avez GNU awk, utilisez PROCINFO["sorted_in"] = "@ind_num_asc"plutôt que piping sort. ref gnu.org/software/gawk/manual/html_node/…
glenn jackman
@taliezin: Merci taliezin et terdon. Les deux approches ont fonctionné comme un charme. J'apprécie vraiment votre aide.
TomPio
1
@taliezin: Comme je l'ai dit, les deux fonctionnaient pour moi, j'ai marqué les réponses de terdon comme étant "correctes". Je suppose que c'est ce que vous vouliez. Merci encore.
TomPio
1
Si je comprends la question que vous voulez le total des clés uniques, nous pouvons simplement ajouter un compteur et l'imprimer: awk '{map [$ 2] + = $ 3 + $ 4; } FIN {pour (i sur la carte) {imprimer "2", i, carte [i] | "sort -t'n '"; cnt ++; } print "total unique:" cnt} 'file
taliezin
1
C'est presque la même chose: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {for (i in map) {print "2", i, map [i], oc [i] | "sort -t'n '"; }} ', vous verrez maintenant une autre colonne avec des occurrences.
taliezin
4

Vous pouvez pré-trier les données et laisser awk gérer les détails:

sort -n infile | awk 'NR>1 && p!=$2 {print p,s} {s+=$3+$4} {p=$2}'

Vous voudrez peut-être réinitialiser l'accumulateur:

sort -n infile | awk 'NR>1 && p!=$2 {print p,s;s=0} {s+=$3+$4} {p=$2}'

Production:

1019 15
1021 19
1022 28
1030 34

Si vous voulez vraiment garder la première colonne, faites quelque chose comme ceci:

sort -n infile | awk 'NR>1 && p!=$1FS$2 {print p,s} {s+=$3+$4} {p=$1FS$2}'

Production:

2 1019 15
2 1021 19
2 1022 28
2 1030 34

Explication

La pvariable contient la $2valeur de la ligne précédente, ou $1FS$2dans le deuxième cas ci-dessus. Cela signifie que le {print p,s}est déclenché lorsque $2la ligne précédente n'est pas la même que celle de la ligne actuelle ( p!=$2).

Thor
la source
notez que même si la première colonne avait des valeurs différentes, vous pouvez utiliser sort -k2pour trier par la deuxième colonne
gaoithe
2

Utilisation du couteau suisse mlr:

mlr --nidx   put '$5=$3+$4'   then   stats1 -g 1,2 -f 5 -a sum   infile

Production:

2   1019    15
2   1021    4
2   1022    9
2   1030    6
2   1031    8

Remarques:

  • --nidxindique mlrd'utiliser des noms de champs numériques.

  • put '$5=$3+$4'crée un nouveau 5ème champ, la somme des champs 3 et 4 .

  • La stats1fonction (ou « verbe ») est un couteau plus petit de l' armée suisse
    dans le plus grand suisse couteau de mlr, avec plusieurs fonctions à base d'accumulateurs tels que sum, count, mean, etc.

    stats1 -g 1,2regroupe les données par les colonnes 1 et 2 , -f 5 -a sumpuis additionne le champ 5 de ces groupes . stats1 imprime uniquement les champs nommés.

agc
la source