จัดการข้อมูลที่คั่นด้วยคุณภาพต่ำลงใน CSV ที่มีประโยชน์


13

ฉันมีผลลัพธ์บางอย่างในรูปแบบของ:

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

ซึ่งค่อนข้างยุ่งและต้องทำความสะอาดได้ถึง CSV ดังนั้นฉันจึงสามารถมอบให้กับผู้จัดการโครงการสำหรับพวกเขาในสเปรดชีตนรกจากนั้น

แก่นของปัญหาคือ: ฉันต้องการผลลัพธ์ของสิ่งนี้เพื่อ:

id, sum_of_type_1, sum_of_type_2, sum_of_type_3

ตัวอย่างนี้คือ id "4":

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

สิ่งนี้ควรเป็น:

4,15,253,19871

โชคไม่ดีที่ฉันเป็นขยะในเรื่องแบบนี้ฉันจัดการเพื่อให้ทุกบรรทัดถูกกำจัดและเป็น CSV แต่ฉันไม่สามารถทำซ้ำและจัดกลุ่มแถวได้ ตอนนี้ฉันมีสิ่งนี้:

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

แต่สิ่งที่จะทำความสะอาดตัวอักษรขยะและพิมพ์แถวอีกครั้ง

วิธีที่ดีที่สุดในการนวดแถวลงในเอาต์พุตที่กล่าวถึงข้างต้นคืออะไร?


คุณต้องการรวมการนับเข้าด้วยกันหรือไม่?
hjk

คำตอบ:


12

วิธีทำคือใส่ทุกอย่างไว้ในแฮช

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

แก้ไข: คำตอบแรกของฉันไม่ตอบคำถามอย่างถูกต้อง


ใช่นี่เป็นกลอุบายอย่างมาก ขอบคุณ! มีเพียงสิ่งเดียวที่ฉันไม่ได้พิจารณาว่ามีบางประเภทจาก ID ที่ว่างเปล่าและทำให้ CSV ยุ่งเหยิง แต่ฉันสามารถทำงานรายละเอียดเล็ก ๆ น้อย ๆ ได้
Paul

@Paul อาจจะเพิ่มNF<4{$4="no_type";}ในช่วงเริ่มต้น
DarkHeart

11

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

มันเก็บสองตารางตารางประเภทและตารางรหัส สำหรับแต่ละ id จะเก็บผลรวมต่อประเภท


5

หากGNU datamashเป็นตัวเลือกสำหรับคุณ

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 (และpandasไลบรารี่โดยเฉพาะนั้นเหมาะมากสำหรับงานประเภทนี้

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

สิ่งนี้อ่านข้อมูล csv ไปที่ 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

จากนั้นเราจัดกลุ่มข้อมูลนี้โดยidและนำผลรวมของคอลัมน์count

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

การunstack ปรับรูปแบบนี้เพื่อย้าย id ของคอลัมน์และfillnaเติมฟิลด์ว่างด้วย 0

df_sum.to_csv()

ผลตอบแทนนี้

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

เนื่องจาก dataframe มีข้อมูลที่ขาดหายไป (ชุดค่าผสม id ที่ว่างเปล่า), pandas แปลงints เป็นfloat(ข้อ จำกัด ของการทำงานภายใน) ถ้าคุณรู้ว่าอินพุตจะเป็น int เท่านั้นคุณสามารถเปลี่ยนบรรทัดถัดไปเป็นบรรทัดสุดท้ายเป็นdf_sum = df.groupby(('type', 'id'))['count'].sum().unstack('type').fillna(0).astype(int)


1
คุณควรอธิบายว่ารหัสที่คุณให้ไว้มีประโยชน์อย่างไรสำหรับทุกคนที่เห็นโพสต์นี้มีประโยชน์มากกว่าคนนี้
คดีของกองทุนโมนิกา

ชัดเจนกว่านี้ไหม ฉันยังแก้ไข regex สำหรับ seperator
Maarten Fabré

ดูดีกับผม. ขอบคุณที่เพิ่มคำอธิบาย!
คดีฟ้องร้องกองทุนโมนิก้า

3

คุณสามารถใช้ Perl เพื่อวนลูปไฟล์ CSV และสะสมผลรวมของประเภทที่เหมาะสมในแฮชระหว่างทาง และในท้ายที่สุดแสดงข้อมูลที่รวบรวมสำหรับทุก ID

โครงสร้างข้อมูล

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

ซึ่งช่วยในการทำความเข้าใจโค้ดด้านล่าง:

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

เอาท์พุต

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

1

ของฉันไม่แตกต่างจากคนอื่นมากเกินไป ใช้ GNU awk ซึ่งมีอาร์เรย์ของอาร์เรย์

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

เอาท์พุท

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

คุณสามารถใช้รหัสนี้เพื่อสรุปค่าตามคอลัมน์รหัสของคุณ

ฉันได้เพิ่มหนึ่งคำสั่ง awk หลังจากรหัสของคุณ

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

ไปข้างหน้ากับสิ่งนี้ ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.