jumlah pasangan kolom berdasarkan bidang yang cocok


11

Saya memiliki file besar dalam format berikut:

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

Jika nilai-nilai di kolom 2 cocok, saya ingin menjumlahkan nilai-nilai di kolom 3 dan 4 dari kedua baris, kalau tidak hanya jumlah nilai-nilai di baris unik.

Jadi output yang saya harapkan akan terlihat seperti ini:

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

Saya dapat mengurutkan file menurut kolom 2 dengan awkatau sortdan menjumlahkan kolom terakhir dengan awk, tetapi hanya untuk setiap baris bukan untuk dua baris di mana kolom 2 cocok.


1
Bagaimana dengan kolom 1?
glenn jackman

@glennjackman: Kolom 1 memiliki nilai yang sama di setiap file. Ini berfungsi sebagai pengidentifikasi untuk file (saya punya 45 dari mereka) dan akan digunakan untuk beberapa proses hilir. Untuk pertanyaan saya, mungkin saja diabaikan (atau dihapus) dan kemudian ditambahkan lagi.
TomPio

atau, jadikan $1 $2sebagai kuncinya.
glenn jackman

Jawaban:


12

Saya akan melakukan ini di 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

Atau awk:

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

Jika Anda ingin output diurutkan sesuai dengan kolom kedua, Anda bisa mengirimkannya ke sort:

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

Perhatikan bahwa kedua solusi termasuk kolom 1 juga. Idenya adalah menggunakan kolom pertama dan kedua sebagai kunci hash (dalam perl) atau array asosiatif (dalam awk). Kunci dari setiap solusi adalah column1 column2jika dua baris memiliki kolom yang sama dua tetapi kolom yang berbeda, mereka akan dikelompokkan secara terpisah:

$ 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

Mungkin ini bisa membantu, tetapi apakah kolom 1 selalu 2 dan apakah hasilnya bergantung padanya?

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

atau seperti yang disebutkan oleh glenn jackman dalam komentar tentang penyortiran:

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

2
Jika Anda memiliki GNU awk, gunakan PROCINFO["sorted_in"] = "@ind_num_asc"sebagai ganti perpipaan sort. ref gnu.org/software/gawk/manual/html_node/…
glenn jackman

@taliezin: Terima kasih taliezin dan terdon. Kedua pendekatan itu bekerja seperti pesona. Saya sangat menghargai bantuan Anda.
TomPio

1
@aliezin: Seperti yang saya katakan, keduanya bekerja untuk saya, saya menandai jawaban terdon sebagai jawaban yang "benar". Saya kira itulah yang Anda maksudkan. Terima kasih lagi.
TomPio

1
Jika saya mengerti pertanyaan Anda ingin total kunci unik, kita bisa menambahkan penghitung dan mencetaknya: awk '{map [$ 2] + = $ 3 + $ 4; } END {for (i in map) {print "2", i, map [i] | "sort -t'n '"; cnt ++; } cetak "total unik:" cnt} 'file
taliezin

1
Hampir sama: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {for (i in map) {print "2", i, map [i], oc [i] | "sort -t'n '"; }} ', sekarang Anda akan melihat kolom lain dengan kejadian.
taliezin

4

Anda dapat melakukan pra-sortir data dan membiarkan awk menangani detail:

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

Anda mungkin ingin mengatur ulang akumulator:

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

Keluaran:

1019 15
1021 19
1022 28
1030 34

Jika Anda benar-benar ingin menyimpan kolom pertama, lakukan sesuatu seperti ini:

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

Keluaran:

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

Penjelasan

The pvariabel memegang $2nilai dari baris sebelumnya, atau $1FS$2dalam kasus kedua di atas. Ini berarti bahwa {print p,s}dipicu ketika $2dari baris sebelumnya tidak sama dengan yang ada di baris saat ini ( p!=$2).


perhatikan bahwa meskipun kolom pertama memiliki nilai yang berbeda yang dapat Anda gunakan sort -k2untuk mengurutkan berdasarkan kolom kedua
gaoithe

2

Menggunakan pisau swiss tentara util mlr:

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

Keluaran:

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

Catatan:

  • --nidxmemberitahu mlruntuk menggunakan nama bidang numerik.

  • put '$5=$3+$4'membuat bidang 5 baru , jumlah bidang 3 dan 4 .

  • The stats1fungsi (atau " kata kerja ") adalah lebih kecil swiss army knife
    dalam lebih besar swiss pisau tentara mlr, dengan beberapa fungsi berdasarkan akumulator seperti sum, count, mean, dll

    stats1 -g 1,2mengelompokkan data berdasarkan kolom 1 dan 2 , dan -f 5 -a sumkemudian menambahkan bidang kelompok-kelompok tersebut 5 . stats1 hanya mencetak bidang bernama.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.