Summenpaar von Spalten basierend auf übereinstimmenden Feldern


11

Ich habe eine große Datei im folgenden Format:

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

Wenn die Werte in Spalte 2 übereinstimmen, möchte ich die Werte in Spalte 3 und 4 beider Zeilen summieren, ansonsten nur die Summe der Werte in der eindeutigen Zeile.

Die Ausgabe, auf die ich hoffe, würde also so aussehen:

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

Ich kann Dateien nach Spalte 2 mit awkoder sortieren sortund die letzten Spalten mit summieren awk, aber nur für einzelne Zeilen, nicht für zwei Zeilen, in denen Spalte 2 übereinstimmt.


1
Was ist mit Spalte 1?
Glenn Jackman

@glennjackman: Spalte 1 hat in jeder Datei den gleichen Wert. Es dient als Kennung für die Datei (ich habe 45 davon) und wird für einige nachgelagerte Prozesse verwendet. Für meine Frage könnte es genauso gut ignoriert (oder gelöscht) und später wieder hinzugefügt werden.
TomPio

oder machen Sie $1 $2als Schlüssel.
Glenn Jackman

Antworten:


12

Ich würde das in Perl machen:

$ 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

Oder awk:

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

Wenn Sie die Ausgabe nach der zweiten Spalte sortieren möchten, können Sie einfach weiterleiten an sort:

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

Beachten Sie, dass beide Lösungen auch die 1. Spalte enthalten. Die Idee ist, die erste und die zweite Spalte als Schlüssel für einen Hash (in Perl) oder ein assoziatives Array (in awk) zu verwenden. Der Schlüssel in jeder Lösung lautet: column1 column2Wenn zwei Zeilen dieselbe Spalte zwei, aber eine andere Spalte eins haben, werden sie separat gruppiert:

$ 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

7

Vielleicht könnte dies helfen, aber ist Spalte 1 immer 2 und hängen die Ergebnisse davon ab?

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

oder wie von glenn jackman in kommentaren zum sortieren erwähnt:

gawk '{ map[$2] += $3 + $4; } END { PROCINFO["sorted_in"] = "@ind_str_asc"; for (i in map) { print 2, i, map[i] } }' file

2
Wenn Sie GNU awk haben, verwenden Sie PROCINFO["sorted_in"] = "@ind_num_asc"anstelle von Rohrleitungen zu sort. ref gnu.org/software/gawk/manual/html_node/…
Glenn Jackman

@taliezin: Danke Taliezin und Terdon. Beide Ansätze wirkten wie ein Zauber. Ich schätze deine Hilfe sehr.
TomPio

1
@taliezin: Wie gesagt, beide haben für mich gearbeitet, ich habe die Terdon-Antworten als "richtig" markiert. Ich denke, das hast du beabsichtigt. Danke noch einmal.
TomPio

1
Wenn ich die Frage verstehe, dass Sie die gesamten eindeutigen Schlüssel wünschen, können wir einfach einen Zähler hinzufügen und ausdrucken: awk '{map [$ 2] + = $ 3 + $ 4; } END {for (i in map) {print "2", i, map [i] | "sort-t'n '"; cnt ++; } print "total unique:" cnt} 'file
taliezin

1
Es ist fast dasselbe: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {für (i in map) {print "2", i, map [i], oc [i] | "sort-t'n '"; }} ', jetzt sehen Sie eine weitere Spalte mit Vorkommen.
Taliezin

4

Sie können die Daten vorsortieren und awk die Details überlassen:

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

Möglicherweise möchten Sie den Akku zurücksetzen:

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

Ausgabe:

1019 15
1021 19
1022 28
1030 34

Wenn Sie wirklich die erste Spalte behalten möchten, gehen Sie wie folgt vor:

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

Ausgabe:

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

Erläuterung

Die pVariable enthält den $2Wert der vorherigen Zeile oder $1FS$2im zweiten Fall oben. Dies bedeutet, dass das {print p,s}ausgelöst wird, wenn $2die vorherige Zeile nicht mit der aktuellen Zeile übereinstimmt ( p!=$2).


Beachten Sie, dass selbst wenn die erste Spalte unterschiedliche Werte hatte, Sie sort -k2nach der zweiten Spalte sortieren können
gaoithe

2

Mit dem Schweizer Taschenmesser util mlr:

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

Ausgabe:

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

Anmerkungen:

  • --nidxweist mlran, numerische Feldnamen zu verwenden.

  • put '$5=$3+$4'macht ein neues 5. Feld, die Summe der Felder 3 und 4 .

  • Die stats1Funktion (oder „ Verb “) ist ein kleines Schweizeres Messer
    innerhalb des größeren Schweizeren Taschenmesser der mlrmit mehrere Akkumulator basierter Funktionen wie sum, count, mean, usw.

    stats1 -g 1,2gruppiert die Daten nach Spalten 1 und 2 und -f 5 -a sumaddiert dann das Feld 5 dieser Gruppen . stats1 druckt nur benannte Felder.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.