2012-04-26 139 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