2014-12-21 2 views
-4

с из Excel является то, что можно делать промежуточные итоги для нескольких столбцов, используя AWK для CSV-файлаИтого с использованием AWK для нескольких столбцов

FILE1.TXT

Group,Host,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14 
GroupA,HostA,10,20,30,40,50,60 
GroupB,HostB,10,20,30,40,50,60 
GroupA,HostC,10,20,30,40,50,60 
GroupC,HostD,10,20,30,40,50,60 
GroupB,HostE,10,20,30,40,50,60 
GroupC,HostF,10,20,30,40,50,60 

Файл с отделами:

Group,Host,Dept,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14 
GroupA,HostA,Finance,10,20,30,40,50,60 
GroupB,HostB,HR,10,20,30,40,50,60 
GroupA,HostC,Finance,10,20,30,40,50,60 
GroupC,HostD,HR,10,20,30,40,50,60 
GroupB,HostE,Finance,10,20,30,40,50,60 
GroupC,HostF,HR,10,20,30,40,50,60 

Результат

Group,Host ,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14 
GroupA Total,,20,40,60,80,100,120 
GroupB Total,,20,40,60,80,100,120 
GroupC Total,,20,40,60,80,100,120 
GrandTotal,,60,120,180,240,300,360 
+2

Конечно, это возможно. Используйте ассоциативный массив. – Barmar

+0

@ Barmar awk -F, '{a [$ 1] + = $ 3;} END {для (i in a) print i "," a [i];}' выше код выполняет для 3-го столбца, как сделать то же самое для нескольких столбцов, не говоря уже о упоминании столбца ref в массиве, а также исключить заголовок в файле –

+0

'NR> 1', чтобы пропустить первую строку. –

ответ

2

С GNU AWK для истинных 2D массивов:

$ cat tst.awk 
BEGIN{ FS=OFS="," } 
NR==1 { print; next } 
{ 
    for (i=3; i<=NF; i++) { 
     subtot[$1][i] += $i 
    } 
} 
END { 
    for (group in subtot) { 
     printf "%s%s", group, OFS 
     for (i=3; i<=NF; i++) { 
      printf "%s%s", OFS, subtot[group][i] 
     } 
     print "" 
    } 
} 
$ 
$ awk -f tst.awk file 
Group,Host,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14 
GroupA,,20,40,60,80,100,120 
GroupB,,20,40,60,80,100,120 
GroupC,,20,40,60,80,100,120 
+0

Получение ошибки awk: tst.awk: 5: subtot [$ 1] [i] + = $ i awk: tst.awk: 5:^синтаксическая ошибка awk: tst.awk: 12: printf "% s% s", OFS, subtot [группа] [i] awk: tst.awk: 12:^синтаксическая ошибка –

+0

. Тогда вы не используете GNU awk, как указано, требуется (или использует очень старую версию, которая не поддерживает 2D-массивы). –

+0

gawk-3.1.7-10.el6.x86_64 установлен на ПК, что поддерживает версия выше сценария –

0

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

use strict; 
use warnings; 

use Text::CSV; 

my $fn = 'File1.txt'; 
my $csv = Text::CSV->new(); 
open(my $fh, '<', $fn) or die "Could not open file '$fn': $!\n"; 
my $header = <$fh>; 
my %grp; 
my @tot; 
while (my $line = <$fh>) { 
    chomp $line; 
    if ($csv->parse($line)) { 
     my @fields = $csv->fields(); 
     my $key = $fields[0]; 
     $grp{$key} //= [(0) x (scalar(@fields) - 2)]; 
     for my $i (2..$#fields) { 
      $grp{$key}->[$i - 2] += $fields[$i]; 
      $tot[$i - 2] += $fields[$i]; 
     } 
    } else { 
     warn "Line could not be parsed: $line\n"; 
    } 
} 
close($fh); 

print $header; 
for (sort keys %grp) { 
    print $_ . " Total,," . join(",", @{$grp{$_}}) . "\n"; 
} 
print "GrandTotal,," . join(",", @tot) . "\n"; 
+1

. На самом деле нет необходимости в' chomp' или '$ grp {$ key} // = [(0) x (scalar (@fields) - 2)]'. Кроме того, 'Text :: CSV-> error_diag' возвращает * поле * из входного файла, который является ошибкой, что может быть намного более полезным, чем целая строка. – Borodin

+0

@Borodin Да, я узнаю, что от вашего ответа :) Спасибо! –

+1

В моем решении 'chomp' * * необходимо *, потому что я просто' разделяю 'каждую строку, и без 'chomp' последнее поле заканчивается новой строкой. В вашем случае 'Text :: CSV' удалит его для вас. И ты очень рад. – Borodin

2

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

В настоящее время он считывает ввод от DATA и отправляет вывод на STDOUT. Надеюсь, вы знаете, как открыть свои собственные входные и выходные файлы? Вывод может быть сохранен в любом файле, который вам нравится, используя перенаправление > в командной строке, но, пожалуйста, скажите, нужна ли вам дополнительная помощь.

use strict; 
use warnings; 

print scalar <>; # Copy and ignore the header 

my (%data, @groups, @grand); 

while (<>) { 
    chomp; 
    my ($group, $host, @vals) = split /,/; 
    push @groups, $group unless $data{$group}; 
    for my $i (0 .. $#vals) { 
    $data{$group}[$i] += $vals[$i]; 
    $grand[$i] += $vals[$i]; 
    } 
} 

for my $group (@groups) { 
    print join(',', $group, '', @{ $data{$group} }), "\n"; 
} 

print join(',', 'GrandTotal', '', @grand), "\n"; 

выход

Group,Host,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14 
GroupA,,20,40,60,80,100,120 
GroupB,,20,40,60,80,100,120 
GroupC,,20,40,60,80,100,120 
GrandTotal,,60,120,180,240,300,360 
+0

ah получил его, сохраненный выше кода в качестве подпрограммы и выполняемый как perl sub , но каждый раз нам нужно сохранять данные в одном файле с кодом? –

+0

Хорошо, я предположил, что вы знаете, как открыть файл в Perl. Я изменил свое решение так, что он ожидает путь к входному файлу в качестве параметра в командной строке, поэтому, если вы укажете программный файл 'group_totals.pl', вы можете запустить его с помощью' group_totals.pl input.csv> totalals .csv', который примет вход от 'input.csv' и напишет вывод на' totals.csv' – Borodin

+0

Спасибо! nice code –

0

Дублирование выход трубы с помощью тройника + mkfifo. Полезные для больших файлов

$>cat foo 
foo,10,foo_a 
bar,10,bar_a 
foo,20,foo_b 
bar,20,bar_b 
foo,69,foo_c 
bar,69,bar_c 

$>mkfifo fifotmp 

$>awk -F',' ' 
BEGIN {T="\t" } 

pass==1 { 
    sum_first_col[ $1 ] += $2; 
    sum_all += $2; 
} 

pass==2 { 
    percentage_itm=100 * $2/sum_first_col[ $1 ]; 
    percentage_all=100 * $2/sum_all; 

    print $1 T $2 T $3 T percentage_itm T percentage_all; 

}' pass=1 <(cat foo | tee -a fifotmp) pass=2 <(cat fifotmp) 

OR 

$>cat foo | tee -a fifotmp | awk -F',' ' 
    BEGIN {T="\t" } 

    pass==1 { 
     sum_first_col[ $1 ] += $2; 
     sum_all += $2; 
    } 

    pass==2 { 
     percentage_itm=100 * $2/sum_first_col[ $1 ]; 
     percentage_all=100 * $2/sum_all; 

     print $1 T $2 T $3 T percentage_itm T percentage_all; 

    }' pass=1 - pass=2 <(cat fifotmp) 

foo 10 foo_a 10.101 5.05051 
bar 10 bar_a 10.101 5.05051 
foo 20 foo_b 20.202 10.101 
bar 20 bar_b 20.202 10.101 
foo 69 foo_c 69.697 34.8485 
bar 69 bar_c 69.697 34.8485