2012-04-26 152 views
2

這個問題與這個問題非常相似How can I get the average and standard deviations grouped by key?但我無法修改它以適應我的問題。計算列的小部分的平均值,按perl鍵分組?

我有很多的文件(.CSV)與7列,最後三列是這樣的:

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 

我想要做的是計算最後一列的平均值(COL7)對於在第5列(col5)中具有相同值的所有值,所以1408,1409,1410,...直到N和我不知道N.我想在該行旁邊打印該平均值(在col8中)在第6列(col6)中包含3。請注意,在第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); 
+0

請問$ tmp_line是什麼? – thb 2012-04-26 12:12:32

+0

另外,你的意思只是忽略第1至第4列? – thb 2012-04-26 12:14:20

+0

$ tmp_line是一個錯誤,現在糾正它。我不需要第1 - 4列來計算平均值,但我最終還是要打印它們,我會在我的問題中糾正它! – Nuttieke 2012-04-26 12:17:43

回答

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++這樣更加清醒的編程語言來說,自動化可能是不明智的,但多年的經驗表明,autovivification能夠在像Perl這樣溫和的語氣稍遜一些的語言中實現順序和便利之間的正確平衡。

在更基本的層面上,代碼可能僅適用於那些用於Perl散列的代碼。但是,如果你以前沒有使用過Perl的哈希,這將是學習它們的好機會。哈希是語言的核心支柱,上面提供了一個相當典型的例子。

在這種情況下,我們有散列哈希值,這也是相當典型的。

+0

謝謝。我試過了,但我得到一個空文件,但我認爲這是正確的方向。我以爲我需要使用哈希,但我真的不知道如何.. – Nuttieke 2012-04-26 13:04:56

+0

好。關於空文件,我的代碼從標準輸入中讀取,而不是從您的文件中讀取。爲了讓它從你的文件中讀取,你需要用''代替'<>'(當然,你必須首先打開'FILE',因爲你的原始代碼已經做到了)。 – thb 2012-04-26 13:10:11

+0

是的,我確實改變了它。我會再試一次! – Nuttieke 2012-04-26 13:25:08

0

這應該有所斬斷。適當替換列表[索引]

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 
+0

嗯,我試過了,但我得到一些錯誤:使用未初始化的值(+)在test.pl行10,行1886.它讓我感到困惑.. – Nuttieke 2012-04-26 12:49:49

+0

是它的**警告**不應該本身就是**錯誤**。其真正的第10行** $ Result {$ Cols [0]} + = $ Cols [2]; **,在添加之前不會初始化。你是否複製了代碼並運行 - 只修改@ Cols **的索引? – tuxuday 2012-04-26 13:00:58

+0

啊哈,它現在可行!它需要這些值的總和。 – Nuttieke 2012-04-26 13:24:06

2

使用Text::CSV_XS模塊的一種方法。它不是一個內置的,所以它必須從CPAN或類似的工具安裝。

內容script.pl

含量 infile
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; 
} 

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 
+0

我也會試試這個,但是Text :: CSV_XS沒有安裝。 – Nuttieke 2012-04-27 07:40:47

+0

我不明白這一點comepletely,得到一個錯誤:預期的字段是在script.pl線57的數組引用,<$fh>線7 – Nuttieke 2012-04-27 09:28:22

+0

@Nuttieke:我編輯的腳本,在'$ ROW3添加專項檢查'變量。我假設,因爲我不能重現你的錯誤。 – Birei 2012-04-27 12:27:22