Как добавить значения в каждую строку и столбец и напечатать в конце в Perl? - PullRequest
0 голосов
/ 16 ноября 2009

Ниже приведен пример файла 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 ]

3 голосов
/ 16 ноября 2009

Менее элегантно и короче:

$ 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 }'
2 голосов
/ 16 ноября 2009

Вот простой способ, который вы можете легко построить в зависимости от ваших требований:

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::Util и List::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 () довольно наивна!).

/ I3az /

1 голос
/ 16 ноября 2009

Как и кандидат в Perlgolf JB, за исключением того, что печатает итоги и метки в конце строки.

#!/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);
};
1 голос
/ 16 ноября 2009

Это то, что должно быть обязательно сделано в скрипте Perl? В Perl нет «быстрого и грязного» метода для этого. Вам нужно будет прочитать файл, накопить итоги и записать файл обратно (обработка ввода и вывода построчно будет самой чистой).

Если это одноразовый отчет или вы работаете с компетентной базой пользователей, то необходимые данные легче всего получить с помощью программы для работы с электронными таблицами, такой как Excel.

0 голосов
/ 16 ноября 2009

Всякий раз, когда я работаю с 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
0 голосов
/ 16 ноября 2009

Следующее в Perl делает то, что вы хотите, это не элегантно, но работает :-) Вызов сценария с входным файлом в качестве аргумента приводит к выводу 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...