这个问题与How can I get the average and standard deviations grouped by key?非常相似,但我无法修改它以适应我的问题。
我有很多带有7列的文件(.csv),最后三列看起来像这样:
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和I不知道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);
答案 0 :(得分: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
答案 1 :(得分: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
答案 2 :(得分: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的 autovivification 功能。例如,观察行++$summary{$col5}{count};
,它似乎最初会增加一个不存在的计数器。但是,这实际上是标准的Perl习语。如果您尝试对不存在的对象执行算术(如递增),Perl会隐式创建对象,将其初始化为零,然后执行您想要的操作(如递增)。
对于像C ++这样更加清醒的编程语言来说,自动生成可能是不明智的,但多年的经验表明,自动更新在订单和便利之间取得了正确的平衡,而在像Perl这样稍微不那么清醒的语言中。
在更基础的层面上,代码可能只对那些用于Perl哈希的人有意义。但是,如果您以前没有使用过Perl的哈希值,那么这对于学习它们来说是一个很好的机会。哈希是语言的核心支柱,上面是一个相当典型的例子。
在这种情况下,我们有哈希哈希,这也是相当典型的。