如何在每行和每列中添加值并在Perl中最后打印?

时间:2009-11-16 13:37:43

标签: perl csv

以下是示例csv文件

date,type1,type2,.....
2009-07-01,n1,n2,.....
2009-07-02,n21,n22,....
and so on...

我想在每行和每列中添加值,并在每行的结尾处打印。即。

date,type1,type2
2009-07-01,n1,n2,.....row_total1
2009-07-02,n21,n22,....row_total2
Total,col_total1,col_total1,......total

请建议。

6 个答案:

答案 0 :(得分:3)

不那么优雅,更短:

$ perl -plaF, -e '$r=0;$r+=$F[$_],$c[$_]+=$F[$_]for 1..$#F;$_.=",$r";END{$c[0]="Total";print join",",@c}'

快速而肮脏,但应该在基本情况下做到这一点。对于任何更复杂的事情,请使用Text :: CSV和实际脚本。

扩展版本,因为它有点毛茸茸:

#! perl -plaF,
$r=0;
$r+=$F[$_], $c[$_]+=$F[$_] for 1..$#F;
$_.=",$r";
END { $c[0]="Total"; print join ",", @c }'

答案 1 :(得分:2)

这是一种直接的方式,您可以根据自己的要求轻松构建:

use strict;
use warnings;
use 5.010;
use List::Util qw(sum);
use List::MoreUtils qw(pairwise);
use Text::ParseWords;

our ($a, $b);
my @header = parse_csv( scalar <DATA> );
my @total  = (0) x @header;
output_csv( @header, 'row_total' );

for my $line (<DATA>) {
    my @cols  = parse_csv( $line );
    my $label = shift @cols;
    push @cols, sum @cols;
    output_csv( $label, @cols );
    @total = pairwise { $a + $b } @total, @cols;
}

output_csv( 'Total', @total );

sub parse_csv { 
    chomp( my $data = shift );
    quotewords ',', 0, $data; 
}

sub output_csv { say join ',' => @_ }

__DATA__
date,type1,type2
2009-07-01,1,2
2009-07-02,21,22

输出预期的:

date,type1,type2,row_total
2009-07-01,1,2,3
2009-07-02,21,22,43
Total,22,24,46

使用List::UtilList::MoreUtils时需要考虑的一些事项:

# using List::Util::sum
my $sum_of_all_values_in_list = sum @list;

# using List::MoreUtils::pairwise
my @two_arrays_added_together = pairwise { $a + $b } @array1, @array2;

虽然我在我的示例中使用了Text::ParseWords,但您应该考虑使用Text::CSV。这个模块涵盖了更奇怪的CSV边缘情况,并提供了正确的CSV组合(我的output_csv()sub非常天真!)。

/ I3az /

答案 2 :(得分:1)

这是否需要在Perl脚本中完成?在Perl中没有“快速而肮脏”的方法。您需要读取文件,累计总计,然后将文件写回(逐行处理输入和输出将是最干净的)。

如果这是一次性报告,或者您正在与合格的用户群合作,则可以使用Excel等电子表格程序轻松生成所需的数据。

答案 3 :(得分:1)

与JB的perlgolf候选人一样,除了打印终点行总数和标签。

#!/usr/bin/perl -alnF,
use List::Util qw(sum);
chomp;
push @F, $. == 1 ? "total" : sum(@F[1..$#F]);
print "$_,$F[-1]";
for (my $i=1;$i<@F;$i++) {
    $totals[$i] += $F[$i];
}
END {
    $totals[0] = "Total";
    print join(",",@totals);
};

答案 4 :(得分:0)

Perl中的以下内容做了你想要的,它不优雅但它的工作原理:-) 使用inputfile作为参数调用脚本,结果为stdout。

chop($_ = <>);

print "$_,Total\n";

while (<>) {

    chop;
    split(/,/);
    shift(@_);

    $sum = 0;

    for ($n = 0; 0 < scalar(@_); $n++) {
        $c = shift(@_);
        $sum += $c;
        $sums[$n] += $c;
    }

    $total += $sum;

    print "$_,$sum\n";
}

print "Total";

for ($n = 0; $n <= $#sums; $n++) {

    print "," . $sums[$n];
}

print ",$total\n";

修改:固定为0值。

输出如下:

date,type1,type2,type3,Total
2009-07-01,1, 2, 3,6
2009-07-02,4, 5, 6,15
Total,5,7,9,21

答案 5 :(得分:0)

每当我使用CSV时,我都会使用AnyData模块。它可能会增加一些开销,但它使我不会犯错误(“哦,废话,引用日期列并在其中加入逗号!?”)。 你的过程看起来像这样:

use AnyData;
my @columns = qw/date type1 type2 type3/;  ## Define your input columns.
my $input = adTie( 'CSV', 'input_file.csv', 'r', {col_names => join(',', @columns)} );
push @columns, 'total';  ## Add the total columns.
my $output = adTie( 'CSV', 'output_file.csv', 'o', {col_names => join(',', @columns)} );
my %totals;
while ( my $row = each %$input ) {
    next if ($. == 1);  ## Skip the header row.  AnyData will add it to the output.
    my $sum = 0;
    foreach my $col (@columns[1..3]) {
        $totals{$col} += $row->{$col};
        $sum += $row->{$col};
    }
    $totals{total} += $sum;
    $row->{total} = $sum;
    $output->{$row->{date}} = $row;
}
$output->{Total} = \%totals;
print adDump( $output ); ## Prints a little table to see the data.  Not required.
undef $input; ## Close the file.
undef $output;

输入:

date,type1,type2,type3
2009-07-01,1,2,3
2009-07-03,31,32,33
2009-07-06,61,62,63
"Dec 31, 1969",81,82,83

输出:

date,type1,type2,type3,total
2009-07-01,1,2,3,6
2009-07-03,31,32,33,96
2009-07-06,61,62,63,186
"Dec 31, 1969",81,82,83,246
Total,174,178,182,534