एक उपयोगी 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 तक साफ़ करने की आवश्यकता है, ताकि मैं इसे एक प्रोजेक्ट मैनेजर को गिफ्ट कर सकूँ कि इसके लिए स्प्रेडशीट नर्क से बाहर है।

समस्या का मूल यह है: मुझे इसके उत्पादन की आवश्यकता है:

आईडी, sum_of_type_1, sum_of_type_2, sum_of_type_3

इसका एक उदाहरण आईडी "4" है:

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

इसके बजाय यह होना चाहिए:

4,15,253,19871

दुर्भाग्य से मैं इस तरह की चीज़ों पर बहुत बकवास हूं, मैंने सभी लाइनों को साफ करने और सीएसवी में लाने में कामयाबी हासिल की है लेकिन मैं पंक्तियों को कम करने और समूह बनाने में सक्षम नहीं हूं। अभी मेरे पास यह है:

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

संपादित करें: मेरा पहला जवाब सवाल का ठीक से जवाब नहीं था


हाँ, यह चाल बहुत अच्छी तरह से किया था। धन्यवाद! केवल एक चीज यह है कि मैंने आईडी से कुछ प्रकारों के खाली होने और इस तरह सीएसवी को गड़बड़ाने के लिए खाता नहीं बनाया था, लेकिन मैं उस छोटे से विस्तार को काम कर सकता हूं
पॉल

@Paul शायद NF<4{$4="no_type";}शुरुआत में जोड़ते हैं
DarkHeart

11

बचाव के लिए पर्ल:

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

यह दो टेबल, प्रकार की तालिका और आईडी की तालिका रखता है। प्रत्येक आईडी के लिए, यह प्रति प्रकार योग को संग्रहीत करता है।


5

यदि GNU डेटामैश आपके लिए एक विकल्प है, तो

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

अजगर (और 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 नया स्वरूप दे इस आईडी कॉलम करने के लिए ले जाने के लिए, और fillna0 के साथ रिक्त फ़ील्ड भरता है

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

क्योंकि डेटाफ़्रेम में अनुपलब्ध डेटा (खाली आईडी-प्रकार के संयोजन) होते हैं, पांडा intएस को float(आंतरिक कामकाज की सीमा) में बदल देते हैं।df_sum = df.groupby(('type', 'id'))['count'].sum().unstack('type').fillna(0).astype(int)


1
आपको यह बताना चाहिए कि आपके द्वारा प्रदान किया गया कोड क्या करता है, इसलिए यह इस विशिष्ट व्यक्ति के बजाय इस पोस्ट को देखने वाले सभी के लिए उपयोगी है।
निधि मोनिका का मुकदमा

क्या यह स्पष्ट है? मैंने
सेपरेटर के

मुझे ठीक लगता है। एक स्पष्टीकरण जोड़ने के लिए धन्यवाद!
निधि मोनिका का मुकदमा

3

आप CSV फ़ाइल पर लूप का उपयोग कर सकते हैं और रास्ते में उचित प्रकार के योग को हैश में जमा कर सकते हैं। और अंत में, प्रत्येक आईडी के लिए एकत्रित जानकारी प्रदर्शित करें।

डेटा संरचना

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

यह नीचे दिए गए कोड की समझ बनाने में मदद करता है:

पर्ल

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

इसके साथ आगे बढ़ें ...

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.