Memanipulasi beberapa data yang dibatasi dengan buruk menjadi CSV yang bermanfaat

13

Saya memiliki beberapa output dalam bentuk:

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
...

Yang cukup berantakan dan perlu dibersihkan hingga CSV sehingga saya bisa memberikannya kepada Manajer Proyek untuk mereka spreadsheet sih.

Inti dari masalahnya adalah ini: Saya perlu hasil ini:

id, sum_of_type_1, sum_of_type_2, sum_of_type_3

Contoh dari ini adalah id "4":

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

Ini seharusnya menjadi:

4,15,253,19871

Sayangnya saya cukup sampah pada hal-hal semacam ini, saya sudah berhasil membersihkan semua baris dan masuk ke CSV tetapi saya belum dapat menduplikasi dan mengelompokkan baris. Sekarang saya punya ini:

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

Tetapi yang dilakukan hanyalah membersihkan karakter sampah dan mencetak baris lagi.

Apa cara terbaik untuk memijat baris ke dalam output yang disebutkan di atas?

Paul
sumber
Apakah Anda bahkan ingin menjumlahkan jumlah bersama?
hjk

Jawaban:

12

Cara untuk melakukannya adalah dengan meletakkan segala sesuatu di dalam hash.

# 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];
    }
}'

sunting: jawaban pertama saya tidak menjawab pertanyaan dengan benar

Hati gelap
sumber
Yap, ini berhasil dengan sangat baik. Terima kasih! Satu-satunya hal adalah saya tidak memperhitungkan beberapa jenis ID yang kosong dan dengan demikian mengacaukan CSV, tetapi saya dapat mengerjakan detail kecil itu
Paul
@ Paul Mungkin tambahkan NF<4{$4="no_type";}di awal
DarkHeart
11

Perl untuk penyelamatan:

#!/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 };
}

Itu membuat dua tabel, tabel jenis dan tabel id. Untuk setiap id, ia menyimpan jumlah per jenis.

choroba
sumber
5

Jika datamash GNU adalah opsi untuk Anda, maka

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
Steeldriver
sumber
4

Python (dan pandasperpustakaan khususnya sangat cocok untuk jenis pekerjaan ini

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')

Ini membaca data csv ke a 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

Lalu kami mengelompokkan data ini dengan id, dan mengambil jumlah kolomcount

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

The unstack membentuk ulang ini untuk memindahkan id ini ke kolom, dan fillnamengisi bidang kosong dengan 0 ini

df_sum.to_csv()

Ini kembali

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

Karena kerangka data berisi data yang hilang (kombinasi tipe-id kosong), panda mengubah ints menjadi float(batasan kerja internal) Jika Anda tahu input hanya akan menjadi int, Anda dapat mengubah baris berikutnya ke terakhir kedf_sum = df.groupby(('type', 'id'))['count'].sum().unstack('type').fillna(0).astype(int)

Maarten Fabré
sumber
1
Anda harus menjelaskan apa yang kode Anda berikan lakukan, jadi sangat membantu bagi semua orang yang melihat posting ini, daripada orang yang satu ini.
Dana Gugatan Monica
Apakah ini lebih jelas? Saya juga mengoreksi regex untuk pemisah
Maarten Fabré
Terlihat bagus untukku. Terima kasih telah menambahkan penjelasan!
Dana Gugatan Monica
3

Anda dapat menggunakan Perl untuk mengulangi file CSV dan mengumpulkan jumlah dari tipe yang sesuai dalam hash saat dalam perjalanan. Dan pada akhirnya, tampilkan informasi yang dikumpulkan untuk setiap ID.

Struktur data

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

Ini membantu dalam memahami kode di bawah ini:

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

Keluaran

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

sumber
1

pendapat saya, tidak terlalu berbeda dari yang lain. Menggunakan awk GNU yang memiliki array array

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

output

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
glenn jackman
sumber
0

Anda dapat menggunakan kode ini untuk meringkas nilai berdasarkan kolom id Anda,

Saya telah menambahkan satu pernyataan awk setelah kode Anda

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];}'

Silakan dengan ini ...

Prem Joshi
sumber