Thao tác một số dữ liệu được phân tách kém thành một CSV hữu ích


13

Tôi có một số đầu ra ở dạng:

count  id     type
588    10 |    3
 10    12 |    3
883    14 |    3
 98    17 |    3
 17    18 |    1
77598    18 |    3
10000    21 |    3
17892     2 |    3
20000    23 |    3
 63    27 |    3
  6     3 |    3
 2446    35 |    3
 14    4 |    3
 15     4 |    1
253     4 |    2
19857     4 |    3
 1000     5 |    3
...

Điều này khá lộn xộn và cần được dọn sạch thành CSV để tôi có thể tặng nó cho Trình quản lý dự án cho họ bảng tính.

Cốt lõi của vấn đề là thế này: Tôi cần đầu ra của điều này là:

id, sum_of_type_1, sum_of_type_2, sum_of_type_3

Một ví dụ về điều này là id "4":

14    4 |    3
 15     4 |    1
253     4 |    2
19857     4 |    3

Điều này thay vào đó nên là:

4,15,253,19871

Thật không may, tôi khá là rác rưởi về vấn đề này, tôi đã xoay sở để dọn sạch tất cả các dòng và vào CSV nhưng tôi không thể sao chép và nhóm các hàng. Ngay bây giờ tôi có cái này:

awk 'BEGIN{OFS=",";} {split($line, part, " "); print part[1],part[2],part[4]}' | awk '{ gsub (" ", "", $0); print}'

Nhưng tất cả những gì làm là dọn sạch các ký tự rác và in lại các hàng.

Cách tốt nhất để có được mát xa các hàng vào đầu ra được đề cập ở trên là gì?


Bạn thậm chí có muốn tổng hợp số đếm với nhau?
hjk

Câu trả lời:


12

Một cách để làm điều đó là đặt mọi thứ trong một hàm băm.

# put values into a hash based on the id and tag
awk 'NR>1{n[$2","$4]+=$1}
END{
    # merge the same ids on the one line
    for(i in n){
        id=i;
        sub(/,.*/,"",id);
        a[id]=a[id]","n[i];
    }
    # print everyhing
    for(i in a){
        print i""a[i];
    }
}'

chỉnh sửa: câu trả lời đầu tiên của tôi không trả lời đúng câu hỏi


Đúng, điều này đã làm thủ thuật rất độc đáo. Cảm ơn! Chỉ có điều là tôi đã không giải thích được một số loại từ ID bị trống và do đó làm rối tung CSV, nhưng tôi có thể giải quyết chi tiết nhỏ đó
Paul

@Paul Có thể thêm NF<4{$4="no_type";}vào lúc bắt đầu
DarkHeart

11

Perl để giải cứu:

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

<>;  # Skip the header.

my %sum;
my %types;
while (<>) {
    my ($count, $id, $type) = grep length, split '[\s|]+';
    $sum{$id}{$type} += $count;
    $types{$type} = 1;
}

say join ',', 'id', sort keys %types;
for my $id (sort { $a <=> $b } keys %sum) {
    say join ',', $id, map $_ // q(), @{ $sum{$id} }{ sort keys %types };
}

Nó giữ hai bảng, bảng các loại và bảng id. Đối với mỗi id, nó lưu tổng số trên mỗi loại.


5

Nếu GNU datamash là một tùy chọn cho bạn, thì

awk 'NR>1 {print $1, $2, $4}' OFS=, file | datamash -t, -s --filler=0 crosstab 2,3 sum 1
,1,2,3
10,0,0,588
12,0,0,10
14,0,0,883
17,0,0,98
18,17,0,77598
2,0,0,17892
21,0,0,10000
23,0,0,20000
27,0,0,63
3,0,0,6
35,0,0,2446
4,15,253,19871
5,0,0,1000

4

Python (và pandasđặc biệt là thư viện rất phù hợp với loại công việc này

data = """count  id     type
588    10 |    3
 10    12 |    3
883    14 |    3
 98    17 |    3
 17    18 |    1
77598    18 |    3
10000    21 |    3
17892     2 |    3
20000    23 |    3
 63    27 |    3
  6     3 |    3
 2446    35 |    3
 14    4 |    3
 15     4 |    1
253     4 |    2
19857     4 |    3
 1000     5 |    3"""

import pandas as pd
from io import StringIO # to read from string, not needed to read from file

df = pd.read_csv(StringIO(data), sep=sep='\s+\|?\s*', index_col=None, engine='python')

Điều này đọc dữ liệu csv đến một pandas DataFrame

    count  id  type
0     588  10     3
1      10  12     3
2     883  14     3
3      98  17     3
4      17  18     1
5   77598  18     3
6   10000  21     3
7   17892   2     3
8   20000  23     3
9      63  27     3
10      6   3     3
11   2446  35     3
12     14   4     3
13     15   4     1
14    253   4     2
15  19857   4     3
16   1000   5     3

Sau đó, chúng tôi nhóm dữ liệu này theo idvà lấy tổng số cộtcount

df_sum = df.groupby(('type', 'id'))['count'].sum().unstack('type').fillna(0)

Việc unstack định hình lại điều này để di chuyển id đến các cột vàfillna điền vào các trường trống bằng 0

df_sum.to_csv()

Điều này trở lại

id,1,2,3
2,0.0,0.0,17892.0
3,0.0,0.0,6.0
4,15.0,253.0,19871.0
5,0.0,0.0,1000.0
10,0.0,0.0,588.0
12,0.0,0.0,10.0
14,0.0,0.0,883.0
17,0.0,0.0,98.0
18,17.0,0.0,77598.0
21,0.0,0.0,10000.0
23,0.0,0.0,20000.0
27,0.0,0.0,63.0
35,0.0,0.0,2446.0

Vì khung dữ liệu chứa dữ liệu bị thiếu (kết hợp kiểu id trống), gấu trúc biến đổi ints thành float(giới hạn của hoạt động bên trong) Nếu bạn biết các đầu vào sẽ chỉ là int, bạn có thể thay đổi dòng tiếp theo thành dòng cuối cùng thànhdf_sum = df.groupby(('type', 'id'))['count'].sum().unstack('type').fillna(0).astype(int)


1
Bạn nên giải thích những gì mã bạn đã cung cấp, vì vậy nó hữu ích cho mọi người nhìn thấy bài đăng này, thay vì một người cụ thể này.
Vụ kiện của Quỹ Monica

Điều này rõ ràng hơn? Tôi cũng đã sửa lỗi regex cho người
tách biệt

Co vẻ tôt vơi tôi. Cảm ơn đã thêm một lời giải thích!
Vụ kiện của Quỹ Monica ngày

3

Bạn có thể sử dụng Perl để lặp qua tệp CSV và tích lũy tổng các loại thích hợp trong một hàm băm khi đang trên đường. Và cuối cùng, hiển thị thông tin được thu thập cho mỗi ID.

Cấu trúc dữ liệu

%h = (
   ID1    =>  [ sum_of_type1, sum_of_type2, sum_of_type3 ],
   ...
)

Điều này giúp hiểu ý nghĩa của mã dưới đây:

Perl

perl -wMstrict -Mvars='*h' -F'\s+|\|' -lane '
   $, = chr 44, next if $. == 1;

   my($count, $id, $type) = grep /./, @F;
   $h{ $id }[ $type-1 ] += $count}{
   print $_, map { $_ || 0 } @{ $h{$_} } for sort { $a <=> $b } keys %h
' yourcsvfile

Đầu ra

2,0,0,17892
3,0,0,6
4,15,253,19871
5,0,0,1000
...

1

của tôi, không quá khác biệt với những người khác. Sử dụng GNU awk có mảng mảng

gawk '
    NR == 1 {next}
    {count[$2][$4] += $1}
    END {
        for (id in count) {
            printf "%d", id
            for (type=1; type<=3; type++) {
                # add zero to coerce possible empty string into a number 
                printf ",%d", 0 + count[id][type]
            }
            print ""        # adds the newline for this line
        }
    }
' file

đầu ra

2,0,0,17892
3,0,0,6
4,15,253,19871
5,0,0,1000
10,0,0,588
12,0,0,10
14,0,0,883
17,0,0,98
18,17,0,77598
21,0,0,10000
23,0,0,20000
27,0,0,63
35,0,0,2446

0

Bạn có thể sử dụng mã này để tổng hợp các giá trị dựa trên cột id của bạn,

Tôi đã thêm một tuyên bố awk sau mã của bạn

awk 'BEGIN{OFS=",";} {split($line, part, " "); print part[1],part[2],part[4]}' abcd | awk '{ gsub (" ", "", $0); print}' | awk 'BEGIN{FS=OFS=SUBSEP=","}{arr[$2,$3]+=$1;}END{for ( i in arr ) print i,arr[i];}'

Hãy tiếp tục với điều này ...

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.