tổng các cặp cột dựa trên các trường khớp


11

Tôi có một tệp lớn ở định dạng sau:

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

Nếu các giá trị trong cột 2 khớp nhau, tôi muốn tính tổng các giá trị trong cột 34 của cả hai dòng, ngoài ra chỉ là tổng của các giá trị trong dòng duy nhất.

Vì vậy, đầu ra tôi hy vọng sẽ trông như thế này:

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

Tôi có thể sắp xếp các tệp theo cột 2 bằng awkhoặc sorttính tổng các cột cuối cùng awk, nhưng chỉ cho các dòng riêng lẻ không dành cho hai dòng có cột 2 khớp.


1
Còn cột 1 thì sao?
glenn jackman

@glennjackman: Cột 1 có cùng giá trị trong mỗi tệp. Nó phục vụ như một định danh cho tệp (tôi có 45 trong số đó) và sẽ được sử dụng cho một số quy trình tiếp theo. Đối với câu hỏi của tôi, nó cũng có thể bị bỏ qua (hoặc bị xóa) và sau đó được thêm lại.
TomPio 18/03/2015

hoặc, làm $1 $2chìa khóa.
Glenn jackman

Câu trả lời:


12

Tôi sẽ làm điều này trong 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

Hoặc awk:

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

Nếu bạn muốn đầu ra được sắp xếp theo cột thứ hai, bạn có thể chuyển sang sort:

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

Lưu ý rằng cả hai giải pháp đều bao gồm cột 1. Ý tưởng là sử dụng cột thứ nhất và thứ hai làm khóa cho hàm băm (tính bằng perl) hoặc mảng kết hợp (tính bằng awk). Chìa khóa trong mỗi giải pháp là column1 column2như vậy nếu hai dòng có cùng một cột hai nhưng một cột khác nhau, chúng sẽ được nhóm riêng:

$ 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

Có lẽ điều này có thể giúp ích, nhưng cột 1 luôn là 2 và kết quả có phụ thuộc vào nó không?

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

hoặc như được đề cập bởi glenn jackman trong các bình luận về sắp xếp:

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

2
Nếu bạn có GNU awk, hãy sử dụng PROCINFO["sorted_in"] = "@ind_num_asc"thay vì đường ống đến sort. ref gnu.org/software/gawk/manual/html_node/...
glenn jackman

@taliezin: Cảm ơn Taliezin và terdon. Cả hai cách tiếp cận làm việc như một lá bùa. Tôi thực sự đánh giá cao sự giúp đỡ của bạn.
TomPio 18/03/2015

1
@taliezin: Như tôi đã nói cả hai đều có tác dụng với tôi, tôi đã đánh dấu các câu trả lời terdon là câu trả lời "đúng". Tôi đoán đó là những gì bạn dự định. Cảm ơn một lần nữa.
TomPio 20/03/2015

1
Nếu tôi hiểu câu hỏi bạn muốn có tổng số khóa duy nhất, chúng ta có thể chỉ cần thêm một bộ đếm và in nó: awk '{map [$ 2] + = $ 3 + $ 4; } END {for (i trong bản đồ) {in "2", i, map [i] | "sắp xếp -t'n '"; cnt ++; } in tập tin "tổng số duy nhất:" cnt} '
Taliezin

1
Nó gần giống nhau: awk '{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {for (i trong bản đồ) {in "2", i, map [i], oc [i] | "sắp xếp -t'n '"; }} ', bây giờ bạn sẽ thấy một cột khác có lần xuất hiện.
Taliezin

4

Bạn có thể sắp xếp trước dữ liệu và để awk xử lý các chi tiết:

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

Bạn có thể muốn đặt lại bộ tích lũy:

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

Đầu ra:

1019 15
1021 19
1022 28
1030 34

Nếu bạn thực sự muốn giữ cột đầu tiên, hãy làm một cái gì đó như thế này:

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

Đầu ra:

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

Giải trình

Các pbiến giữ $2giá trị của dòng trước đó, hoặc $1FS$2trong trường hợp thứ hai ở trên. Điều này có nghĩa {print p,s}là được kích hoạt khi $2dòng trước đó không giống với dòng trên dòng hiện tại ( p!=$2).


lưu ý rằng ngay cả khi cột đầu tiên có các giá trị khác nhau, bạn có thể sử dụng sort -k2để sắp xếp theo cột thứ hai
gaoithe

2

Sử dụng dao quân đội Thụy Sĩ sử dụng mlr:

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

Đầu ra:

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

Ghi chú:

  • --nidxnói mlrđể sử dụng tên trường số.

  • put '$5=$3+$4'tạo trường thứ 5 mới , tổng của trường 34 .

  • Các stats1chức năng (hoặc " động từ ") là một con dao quân đội swiss nhỏ
    trong lớn hơn con dao quân đội swiss của mlr, với một số chức năng dựa ắc như sum, count, mean, , vv

    stats1 -g 1,2nhóm dữ liệu theo cột 12 , -f 5 -a sumsau đó thêm trường 5 của các nhóm đó . stats1 chỉ in tên các lĩnh vực.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.