Lệnh uniq nhóm?


7

Tôi đang tìm kiếm một lệnh để lấy từ một tệp ở định dạng này:

hello 32
hello 67
hi    2
ho    1212
ho    1390
ho    3000

Theo định dạng này (lặp lại bằng cách lấy hàng cuối cùng của một "nhóm"):

hello 67
hi    2
ho    3000

Hiện tại tôi đang sử dụng đoạn mã Python và gấu trúc:

    df = pd.read_csv(self.input().path, sep='\t', names=('id', 'val'))

    # how to replace this logic with shell commands?
    surface = df.drop_duplicates(cols=('id'), take_last=True)

    with self.output().open('w') as output:
        surface.to_csv(output, sep='\t', cols=('id', 'val'))

Cập nhật: Cảm ơn câu trả lời tuyệt vời. Dưới đây là một số điểm chuẩn:

Tệp đầu vào là 246M và chứa 8583313 dòng. Đặt hàng không quan trọng. Cột đầu tiên có kích thước cố định là 9 ký tự.

Ví dụ về tệp đầu vào:

000000027       20131017023259.0        00
000000027       20131017023259.0        11
000000035       20130827104320.0        01
000000035       20130827104320.0        04
000000043       20120127083412.0        01
...

                              time        space complexity

tac .. | sort -k1,1 -u        27.43682s   O(log(n))
Python/Pandas                 11.76063s   O(n)
awk '{c[$1]=$0;} END{for(...  11.72060s   O(n)

Vì cột đầu tiên có độ dài cố định, uniq -wcũng có thể được sử dụng:

tac {input} | uniq -w 9        3.25484s   O(1)

tac {input} | uniq -w 9Công việc thế nào
cuonglm

@Gnouc, -w Nsẽ chỉ xem xét các Nký tự đầu tiên . Đối với tệp cục bộ của tôi, tôi đã có 9 char ID trong cột đầu tiên, vì vậy uniq -w 9.
miku

Tôi đã thử nghiệm với đầu vào của bạn và kết quả là không chính xác.
cuonglm

@Gnouc, Có cho đầu vào nhỏ, uniq -w 5nên hoạt động. Tôi nghĩ rằng câu trả lời của bạn và Mikels là tốt hơn, vì họ không đưa ra giả định về số lượng ký tự trong cột đầu tiên. Tuy nhiên, nếu đầu vào tuân theo một ràng buộc như vậy, thì uniq -wlà nhanh nhất.
miku

Câu trả lời:


5

Điều này có vẻ điên rồ, và hy vọng có một cách tốt hơn, nhưng:

tac foo | sort -k 1,1 -u

tac được sử dụng để đảo ngược tập tin, vì vậy bạn nhận được cuối cùng chứ không phải đầu tiên.

-k 1,1 nói chỉ sử dụng trường đầu tiên để so sánh.

-u làm cho nó độc đáo


ah lol, tac là giải pháp cho câu trả lời của tôi haha
polym

+1, đây là giải pháp dòng lệnh thanh lịch nhất mà tôi đã thấy cho đến nay. :)
Ramesh

Điều này thực sự tốt, và đó là điều đầu tiên tôi nghĩ đến, nhưng bạn không thể sort -ruk1,1 foosao? Có lẽ tôi không đọc đúng.
mikeerv

Cảm ơn, tôi thích điều đó. Nó thực sự chậm hơn một chút so với Python / Pandas, nhưng rõ ràng và súc tích. Và chi phí bộ nhớ tốt hơn nhiều so với Python và các giải pháp dựa trên hàm băm khác.
miku

4

Nếu bạn không quan tâm đến thứ tự đầu ra, đây là một awkgiải pháp:

$ awk '
    {a[$1] = !a[$1] ? $2 : a[$1] < $2 ? $2 : a[$1]}
    END {
        for (i in a) { print i,a[i] }
    }
' file
hi 2
hello 67
ho 3000

3

Một số tùy chọn khác:

  1. perl, nếu bạn không quan tâm đến thứ tự của các dòng.

    perl -lane '$k{$F[0]}=$F[1]; END{print "$_ $k{$_}" for keys(%k)}' file
  2. Đơn giản hơn awk

    awk '{c[$1]=$0;} END{for(i in c){print c[i]}}' file
  3. Một cái vỏ ngớ ngẩn

    while read a b; do grep -w ^"$a" file | tail -n1 ; done < file | uniq

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.