3

过去几天我一直在寻找一组命令行工具、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 shell 实现来转置数据 - 但似乎无法让它们工作。我的一个想法是将每个“Col”值剪切到一个单独的文件中,然后使用“join”命令行通过“Row”将它们重新组合在一起——但必须有一种更简单的方法。我确信这非常简单 - 但我有点挣扎。

我的输入文件有列 A 到 G(主要包括可变长度字符串)和 10,000 行。如果我可以避免将所有内容加载到内存中,那将是一个巨大的优势。

给任何有答案的人邮寄啤酒!

一如既往 - 非常感谢您的帮助。

干杯,

乔什

ps - 我有点惊讶,没有一个开箱即用的命令行工具来执行这种非常基本的枢轴/转置操作。我查看了http://code.google.com/p/openpivot/http://code.google.com/p/crush-tools/两者似乎都需要聚合计算。

4

3 回答 3

3

我可以在 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 行有多好,但我怀疑如果你有它的记忆,你会没事的。我看不出如何避免将内容加载到内存中,除非将内容存储在稍后加入的单独文件中。这几乎是虚拟内存的手动实现。

更新:

根据评论:

#!/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 $ 
于 2012-02-28T04:17:15.763 回答
1

只需使用哈希。如果您不想将它们加载到内存中,则可能需要像DBM::DeepDBM 后端这样的模块。

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";
}
于 2012-02-28T03:26:02.650 回答
1

如果你了解 Awk,我建议你看看 Perl。Perl 比 Awk 强大得多。优点是如果您了解 BASH/Bourne shell 和 Awk,就会熟悉 Perl 中的大部分语法。

Perl 的另一个好处是整个 CPAN 存储库,它允许您下载已经编写的 Perl 模块以在您的程序中使用。在 CPAN 中快速搜索会显示Data::Pivot,它看起来(快速浏览)可能会执行您想要的操作。

如果没有,请查看Acme::Tools pivot 命令。或尝试众多其他方法之一。

其他人已经提供了一些解决方案,但我建议您查看 CPAN Perl 存档的内容。对于这样的事情,它是一个非常强大的工具。

于 2012-02-28T04:33:28.807 回答