опора командной строки - PullRequest
       22

опора командной строки

3 голосов
/ 28 февраля 2012

Последние несколько дней я охотился за набором инструментов командной строки, Perl или awk-скриптом, который позволяет мне очень быстро транспонировать следующие данные:

Row|Col|Val
1|A|foo
1|B|bar
1|C|I have a real
2|A|bad
2|C|hangover

в это:

A|B|C
foo|bar|I have a real
bad||hangover

Обратите внимание, что в наборе данных есть только одно значение для каждой "ячейки" (т. Е., Как и в электронной таблице, дубликатов строки "1", столбца "A") не существует *

Я пробовал различные реализации оболочки awk для транспонирования данных, но не могу заставить их работать. У меня была одна идея - вырезать каждое значение «Col» в отдельный файл, а затем использовать командную строку «join», чтобы соединить их обратно «Row» - но ОБЯЗАТЕЛЬНО должен быть более простой способ. Я уверен, что это невероятно просто сделать, но я немного борюсь.

Мои входные файлы имеют Cols от A до G (в основном включая строки переменной длины) и 10000 строк. Если бы я мог избежать загрузки всего в память, это было бы огромным плюсом.

Пиво по почте для всех, кто получил ответ!

Как всегда - большое спасибо заранее за вашу помощь.

Приветствия

Джош

p.s. - Я немного удивлен, что нет встроенной утилиты командной строки для выполнения этого базового типа операции поворота / транспонирования. Я посмотрел на http://code.google.com/p/openpivot/ и http://code.google.com/p/crush-tools/, оба из которых, кажется, требуют совокупных вычислений.

Ответы [ 3 ]

3 голосов
/ 28 февраля 2012

Я могу сделать это на gawk, но не на nawk.

#!/usr/local/bin/gawk -f

BEGIN {
  FS="|";
}

{
  rows[$1]=1; cols[$2]=1; values[$1][$2]=$3;
}

END {
  for (col in cols) {
    output=output sprintf("|%s", col);
  }
  print substr(output, 2);
  for (row in rows) {
    output="";
    for (col in cols) {
      output=output sprintf("|%s", values[row][col]);
    }
    print substr(output, 2);
  }
}

И это даже работает:

ghoti@pc $ cat data
1|A|foo
1|B|bar
1|C|I have a real
2|A|bad
2|C|hangover
ghoti@pc $ ./doit.gawk data
A|B|C
foo|bar|I have a real
bad||hangover
ghoti@pc $ 

Я не уверен, насколько хорошо это будет работать с 10000 строками, но я подозреваю, что если у вас есть память для этого, у вас все будет хорошо. Я не понимаю, как можно избежать загрузки вещей в память , кроме , храня вещи в отдельных файлах, к которым вы позже присоединитесь. Что в значительной степени представляет собой ручную реализацию виртуальной памяти.

UPDATE:

За комментарии:

#!/usr/local/bin/gawk -f

BEGIN {
  FS="|";
}

{
  rows[$1]=1; cols[$2]=1; values[$1,$2]=$3;
}

END {
  for (col in cols) {
    output=output sprintf("|%s", col);
  }
  print output;
  for (row in rows) {
    output="";
    for (col in cols) {
      output=output "|" values[row,col];
    }
    print row output;
  }
}

И вывод:

ghoti@pc $ ./doit.awk data
|A|B|C
1|foo|bar|I have a real
2|bad||hangover
ghoti@pc $ 
1 голос
/ 28 февраля 2012

Если вы знаете Awk, я бы порекомендовал вам взглянуть на Perl.Perl просто намного мощнее, чем Awk.Преимущество состоит в том, что если вы знаете оболочки BASH / Bourne и Awk, большая часть синтаксиса в Perl будет знакома.

Еще одна приятная вещь о Perl - это полный репозиторий CPAN, который позволяет загружать уже написанные модули Perl виспользовать в вашей программе.Быстрый поиск в CPAN вызвал Data :: Pivot , который выглядит (очень быстро), что он может делать то, что вы хотите.

Если нет, взгляните на Acme :: Tools команда поворота.Или попробуйте один из многих других .

Другие уже предоставили несколько решений, но я рекомендую вам взглянуть на то, что имеет Perl-архив CPAN.Это очень мощный инструмент для таких вещей.

1 голос
/ 28 февраля 2012

Просто используйте хеш. Если вы не хотите загружать их в память, вам могут потребоваться такие модули, как DBM::Deep и серверная часть DBM.

my %table;

my $maxa = 'A';
my $maxr = 0;

<>;

while (<>) {
    chomp;
    my ($a,$b,$c) = split /\|/;
    $table{$a}->{$b} = $c;

    $maxr = $a if ($a > $maxr);
    $maxa = $b if ($b gt $maxa);
}

for (my $c = 'A' ; $c lt $maxa ; $c++) {
    print $c . '|';
}
print "$maxa\n";

for (my $r = 1 ; $r <= $maxr ; $r++) {
    for (my $c = 'A' ; $c lt $maxa ; $c++) {
        print $table{$r}->{$c} . '|';
    }
    print $table{$r}->{$maxa} . "\n";
}
...