2

この質問はこれと非常によく似ています。キーごとにグループ化された平均偏差と標準偏差を取得するにはどうすればよいですか。しかし、私は自分の問題に合うようにそれを変更することができません。

7列のファイル(.csv)がたくさんありますが、最後の3列は次のようになります。

col5,col6,col7
1408,1,123
1408,2,234
1408,3,345
1408,4,456
1408,5,567
1408,6,678
1409,0,123
1409,1,234
1409,2,345
1409,3,456
1409,4,567
1409,5,678
1409,6,789
...
N,0,123
N,1,234
N,2,345
N,3,456
N,4,567
N,5,678
N,6,789

私がやりたいのは、列5(col5)で同じ値を持つすべての値について、最後の列(col7)の平均を計算することです。したがって、1408、1409、1410、... Nになるまで、私は計算しません。 Nを知っています。6列目(col6)に3が含まれている行(col8)の横にこの平均値を出力したいと思います。列6(col6)の値は0から6になりますが、ファイルの最初の番号は常に0であるとは限らないことに注意してください。したがって、必要なのは次のとおりです。

col1,col2,col3,col4,col5,col6,col7,col8
bla,bla,bla,bla,1408,3,345,400.5
bla,bla,bla,bla,1409,3,456,456
...
bla,bla,bla,bla,N,3,456,456

平均を計算するために使用できるスクリプトがいくつかありますが、そのために値を配列に入れることができなければなりません。以下は私がやろうとしたことですが、うまくいきません。また、私は自分でPerlを学ぼうとしているだけなので、それががらくたのように見える場合は、私はただ試みています!

    open (FILE, "<", $dir.$file) or die;
    my @lines = <FILE>;
    foreach my $line(@lines) {
        my ($col1,$col2,$col3,$col4,$col5,$col6,$col7) = split(/\,/, $line);
        push @arrays5, $col5;
    }

    foreach my $array5(@arrays5) {            
        foreach my $line(@lines) {
            my ($col1,$col2,$col3,$col4,$col5,$col6,$col7) = split(/\,/, $line);
            if ($array5 == $col5) {
                push @arrays7, $col7;
            }
        }
    }
close(FILE);
4

3 に答える 3

2

Text::CSV_XSモジュールを使用する一方向。組み込みのものではないため、CPANまたは同様のツールからインストールする必要があります。

内容script.pl

use warnings;
use strict;
use Text::CSV_XS;

my ($offset, $col_total, $row3, $rows_processed);

## Check arguments to the script.
die qq[Usage: perl $0 <input-file>\n] unless @ARGV == 1;

## Open input file.
open my $fh, q[<], shift or die qq[Open error: $!\n];

## Create the CSV object.
my $csv = Text::CSV_XS->new or  
        die qq[ERROR: ] . Text::CSV_XS->error_diag();

## Read file content seven lines each time.
while ( my $rows = $csv->getline_all( $fh, $offset, 7 ) ) { 

        ## End when there is no more rows.
        last unless @$rows;

        ## For each row in the group of seven...
        for my $row ( 0 .. $#{$rows} ) { 

                ## Get value of last column.
                my $last_col_value = $rows->[ $row ][ $#{$rows->[$row]} ];

                ## If last column is not a number it is the header, so print it
                ## appending the eigth column and read next one.
                unless ( $last_col_value =~ m/\A\d+\Z/ ) { 
                        $csv->print( \*STDOUT, $rows->[ $row ] );
                        printf qq[,%s\n], q[col8];
                        next;
                }   

                ## Acumulate total amount for last column.
                $col_total += $last_col_value;

                ## Get third row. The output will be this row with the
                ## average appended.
                if ( $rows->[ $row ][-2] == 3 ) { 
                        $row3 = [ @{ $rows->[ $row ] } ];
                }   

                ## Count processed rows.
                ++$rows_processed;
        }   

        ## Print row with its average.
        if ( $rows_processed > 0  && ref $row3 ) { 
                $csv->print( \*STDOUT, $row3 );
                printf qq[,%g\n], $col_total / $rows_processed;
        }   

        ## Initialize variables.
        $col_total = $rows_processed = 0;
        undef $row3;
}

内容infile

col1,col2,col3,col4,col5,col6,col7
bla,bla,bla,bla,1408,1,123
bla,bla,bla,bla,1408,2,234
bla,bla,bla,bla,1408,3,345
bla,bla,bla,bla,1408,4,456
bla,bla,bla,bla,1408,5,567
bla,bla,bla,bla,1408,6,678
bla,bla,bla,bla,1409,0,123
bla,bla,bla,bla,1409,1,234
bla,bla,bla,bla,1409,2,345
bla,bla,bla,bla,1409,3,456
bla,bla,bla,bla,1409,4,567
bla,bla,bla,bla,1409,5,678
bla,bla,bla,bla,1409,6,789

次のように実行します。

perl script.pl infile

次の出力で:

col1,col2,col3,col4,col5,col6,col7,col8
bla,bla,bla,bla,1408,3,345,400.5
bla,bla,bla,bla,1409,3,456,456
于 2012-04-26T14:07:05.377 に答える
0

これでうまくいくはずです。Cols[index]を適切に置き換えます。

    use Data::Dumper ;
    open (FILE, "<", '/tmp/myfile') or die;
    my @lines ;
    my (%Sum,%Count);

    chomp(@lines = <FILE>);
    foreach my $line(@lines) {
        next if $line =~ /col/;
        my @Cols = split /,/, $line;
        $Sum{$Cols[0]} +=  $Cols[2] ;
        $Count{$Cols[0]}++;
    }

    foreach my $line(@lines) {
        if($line=~/col/) {
            print "$line,colX\n" ;
            next;
        }

        my @Cols = split /,/, $line;
        if($Cols[1]==3) {
            print "$line,",$Sum{$Cols[0]}/$Count{$Cols[0]},"\n" ;
        } else {
            print "$line,-1\n";
        }
    }

サンプル入力/tmp/ myfile

col5,col6,col7
1408,1,123
1408,2,234
1408,3,345
1408,4,456
1408,5,567
1408,6,678
1409,0,123
1409,1,234

サンプル出力

col5,col6,col7,colX
1408,1,123,-1
1408,2,234,-1
1408,3,345,400.5
1408,4,456,-1
1408,5,567,-1
1408,6,678,-1
1409,0,123,-1
1409,1,234,-1
于 2012-04-26T12:20:57.823 に答える
0

答えを完成させる前に、これを試して、あなたが望むものにどれだけ近いか教えていただけますか?

#!/usr/bin/perl
use warnings;
use strict;

my $target = 3;

my %summary;

while(<>) {
    chomp;
    my ($col1,$col2,$col3,$col4,$col5,$col6,$col7) = split /\,/;
    $summary{$col5}{total} += $col7;
    ++$summary{$col5}{count};
    $summary{$col5}{line} = $_ if $col6 == $target;
}

$summary{$_}{average} = $summary{$_}{total} / $summary{$_}{count}
    for keys %summary;

print "${summary{$_}{line}},${summary{$_}{average}}\n"
    for sort keys %summary;

十分に近い場合は、自分で終了することをお勧めします。そうでない場合は、問題についてさらに話し合うことができます。

標準入力からではなくデータファイルから読み取る場合は、<>をに置き換えることができることに注意してください。<FILE>

実装上の注意

このコードは、Perlの自動生存機能に依存しています。++$summary{$col5}{count};たとえば、最初は存在しないカウンターをインクリメントするように見える行を観察します。ただし、これは実際には標準のPerlイディオムです。存在しないオブジェクトに対して算術的(インクリメントなど)を実行しようとすると、Perlはオブジェクトを暗黙的に作成し、ゼロに初期化してから、必要な処理(インクリメントなど)を実行します。

C ++のようなより地味なプログラミング言語が自動活性化するのはおそらく賢明ではありませんが、長年の経験から、Perlのような少し地味な言語では自動活性化が秩序と利便性の適切なバランスをとることが示唆されています。

より基本的なレベルでは、コードはおそらくPerlのハッシュに慣れているものにのみ意味があります。ただし、これまでPerlのハッシュを使用したことがない場合は、これを学ぶチャンスは他にありません。ハッシュは言語の中心的な柱であり、上記はその使用法のかなり典型的な例です。

この場合、ハッシュのハッシュがありますが、これもかなり一般的です。

于 2012-04-26T12:46:26.310 に答える