Sınırlı olmayan bazı verileri yararlı bir CSV'ye dönüştürün


13

Ben şeklinde bazı çıktı var:

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

Bu oldukça dağınık ve bir CSV kadar temizlenmesi gerekiyor, bu yüzden onlar için bir proje yöneticisi için cehennemden elektronik tablo hediye edebilirsiniz.

Sorunun özü şudur: Bunun çıktısına ihtiyacım var:

id, sum_of_type_1, sum_of_type_2, sum_of_type_3

Bunun bir örneği id "4" dir:

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

Bunun yerine şöyle olmalıdır:

4,15,253,19871

Ne yazık ki bu tür bir şey oldukça saçma, tüm hatları temiz ve CSV içine almayı başardım ama satırları tekilleştirip gruplayamadım. Şu anda bu var:

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

Ama tüm bu çöp karakterleri temizlemek ve satırları tekrar yazdırmak.

Satırları yukarıda belirtilen çıktıya masaj yapmanın en iyi yolu nedir?


Sayıları birlikte toplamak ister misiniz?
Ocak 17'de

Yanıtlar:


12

Bunu yapmanın bir yolu, her şeyi bir karmaya koymaktır.

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

edit: ilk cevabım soruyu düzgün cevaplamadı


Evet, bu çok iyi oldu. Teşekkürler! Tek şey, kimliğin bazı türlerinin boş olmasını ve böylece CSV'yi bozmasını açıklamamıştım, ancak bu küçük ayrıntıyı çözebilirim
Paul

@Paul Belki NF<4{$4="no_type";}başlangıçta ekleyin
DarkHeart

11

Kurtarmaya Perl:

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

İki tablo, tür tablosu ve kimlik tablosu tutar. Her kimlik için, tür başına toplamı saklar.


5

Eğer GNU datamash sizin için bir seçenek daha sonra ise

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 (ve pandasözellikle kütüphane bu tür çalışmalar için çok uygundur

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

Bu, csv verilerini bir 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

Sonra bu verileri gruplandırıpid sütun toplamını alıyoruzcount

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

unstack Yeniden şekillendirir bu kimliği sütunlara var taşımak ve fillna0 ile 'boş alanları doldurur

df_sum.to_csv()

Bu geri döner

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

Veri çerçevesi eksik veriler içerdiğinden (boş id-tipi kombinasyonlar) pandalar, ints'yi float(dahili çalışmaların sınırlandırılması ) olarak dönüştürür . Girişlerin yalnızca int olacağını biliyorsanız, bir sonraki son satırıdf_sum = df.groupby(('type', 'id'))['count'].sum().unstack('type').fillna(0).astype(int)


1
Sağladığınız kodun ne yaptığını açıklamalısınız, bu nedenle belirli bir kişi yerine bu yayını gören herkes için yararlıdır.
Monica'nın Davası

Bu daha net mi?
Ayırıcı

Bana iyi görünüyor. Bir açıklama eklediğiniz için teşekkürler!
Monica'nın Davası

3

Perl'i CSV dosyası üzerinde döngü yapmak ve yoldayken bir karma içinde uygun türlerin toplamını biriktirmek için kullanabilirsiniz. Sonunda, her kimlik için toplanan bilgileri görüntüleyin.

Veri yapısı

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

Bu, aşağıdaki kodu anlamamıza yardımcı olur:

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

Çıktı

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

1

benim almam, diğerlerinden çok farklı değil. Diziler içeren GNU awk kullanır

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

çıktılar

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

Kimlik sütununuza dayalı değerleri toplamak için bu kodu kullanabilirsiniz,

Kodundan sonra bir awk ifadesi ekledim

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

Bununla devam et ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.