我有一个制表符分隔的文本文件,其中包含类似数据
Col 1 Col 2
1 a
1 b
1 c
1 d
1 d
2 a
2 b
3 a
等等等等。
我想把这个结构改造成
a b c d
1 1 1 1 2
2 1 1 1 1
3 1 0 0 0
这样,a、b、c 和 d 就变成列;1、2 和 3 变成一行;数字代表计数。例如,1 有一个“a”和两个“d”。
如何使用 awk 或类似工具来实现这一点?
答案1
awk 'NR>1 {
count[$1,$2]++;
rows[$1]++;
cols[$2]++;
}
END {
printf("%3s", "");
for (col in cols) {
printf("%4s", col);
}
printf("\n");
for (row in rows) {
printf("%3d", row);
for (col in cols) {
printf(" %3d", count[row,col]);
}
printf("\n");
}
}' data
不一定高效或优雅,但它应该相当容易阅读并能完成工作。此外,行和列不一定按排序顺序打印。关键是使用来count[row,col]
模拟多维数组,而 awk 并不直接支持该数组。在 Google 上搜索“awk 多维数组”会出现几篇文章,包括这个。
答案2
这是一个 PERL 解决方案:
perl -e '
my (%col1, %col2);
while(<>){
chomp;
@a=split(/\s+/); ## split line on whitespace
$col2{$a[1]}++; ## Collect unique values from the 2nd column
$col1{$a[0]}{$a[1]}++;## Count values per column/line
}
my @l=sort keys %col2;
$"="\t"; ## Array record separator, using tabs to deal with variable size input
print "\t@l\n";
foreach my $c1 (sort keys(%col1)) {## For each column1 value
print "$c1\t";
my $str;
for (my $i=0; $i<=$#l; $i++) {
## Collect the values for each position or 0 if there is none
$col1{$c1}{$l[$i]}="0" unless defined($col1{$c1}{$l[$i]});
$str.="$col1{$c1}{$l[$i]}\t";
}
chop($str); ## remove extra \t
print "$str\n";
}' data >ll