前(INPUT.txt):
Foo#1 Foo#2 Foo#3 Foo#4 Foo#4 Foo#5 SUM
Bar#1 0 0 0 0 3 0 3
Bar#2 2 0 1 0 0 0 3
Bar#3 0 0 0 2 2 0 4
Bar#4 0 0 1 1 2 0 4
Bar#5 1 0 1 0 0 0 2
Bar#6 3 20 0 0 1 0 24
Bar#7 1 0 2 0 0 0 3
SUM 7 20 5 3 8 0 43
以降(OUTPUT.txt):
Foo#2 Foo#4 Foo#1 Foo#3 Foo#4 Foo#5 SUM
Bar#6 20 1 3 0 0 0 24
Bar#3 0 2 0 0 2 0 4
Bar#4 0 2 0 1 1 0 4
Bar#1 0 3 0 0 0 0 3
Bar#2 0 0 2 1 0 0 3
Bar#7 0 0 1 2 0 0 3
Bar#5 0 0 1 1 0 0 2
SUM 20 8 7 5 3 0 43
難しい質問:BashまたはPerlでSUM列と行に基づいて垂直方向と水平方向にソートする方法は?
スクリーンショット:
今後:
後ろに:
答え1
問題は2つあります。まず、ほとんどの行ベースのコマンドラインツールで、かなり単純なソート操作である列値に基づいて#bar
行をソートしようとします。H
sort -nr -k8,1 input.txt |column -t > intermediate1.txt
テーブルのヘッダーと合計行にはいくつかの手動ミックスが必要ですが、それ以降の中間結果は次のとおりです。
- Foo#1 Foo#2 Foo#3 Foo#4 Foo#4 Foo#5 SUM
Bar#6 3 20 0 0 1 0 24
Bar#4 0 0 1 1 2 0 4
Bar#3 0 0 0 2 2 0 4
Bar#7 1 0 2 0 0 0 3
Bar#2 2 0 1 0 0 0 3
Bar#1 0 0 0 0 3 0 3
Bar#5 1 0 1 0 0 0 2
SUM 7 20 5 3 8 0 43
2番目はもう少し複雑で、一番下の行の列の合計値に基づいて列を混在させます。
最初の問題を解決すると、簡単に解決できます。変えるあなたの行列は列を行にまたはその逆に切り替えます。なぜなら、あなたの仕事は再び単純な列ソートだからです。これを使うGNU awkコードスタックオーバーフローから:
awk '
{
for (i=1; i<=NF; i++) {
a[NR,i] = $i
}
}
NF>p { p = NF }
END {
for(j=1; j<=p; j++) {
str=a[1,j]
for(i=2; i<=NR; i++){
str=str" "a[i,j];
}
print str
}
}' intermediate1.txt | column -t > intermediate2.txt
次の中間結果を取得します。
- Bar#6 Bar#4 Bar#3 Bar#7 Bar#2 Bar#1 Bar#5 SUM
Foo#1 3 0 0 1 2 0 1 7
Foo#2 20 0 0 0 0 0 0 20
Foo#3 0 1 0 2 1 0 1 5
Foo#4 0 1 2 0 0 0 0 3
Foo#4 1 2 2 0 0 3 0 8
Foo#5 0 0 0 0 0 0 0 0
SUM 24 4 4 3 3 3 2 43
この行列は合計列の値に基づいて並べ替えることができ、sort -k9,1 -nr intermediate2.txt > intermediate3.txt
ヘッダーと合計行の順序を手動で変更した後、行列は次のようになります。
- Bar#6 Bar#4 Bar#3 Bar#7 Bar#2 Bar#1 Bar#5 SUM
Foo#2 20 0 0 0 0 0 0 20
Foo#4 1 2 2 0 0 3 0 8
Foo#1 3 0 0 1 2 0 1 7
Foo#3 0 1 0 2 1 0 1 5
Foo#4 0 1 2 0 0 0 0 3
Foo#5 0 0 0 0 0 0 0 0
SUM 24 4 4 3 3 3 2 43
次に、以前と同じ awk コードを使用して、上記の中間結果を元の列と行のレイアウトに戻します。
awk '
{
for (i=1; i<=NF; i++) {
a[NR,i] = $i
}
}
NF>p { p = NF }
END {
for(j=1; j<=p; j++) {
str=a[1,j]
for(i=2; i<=NR; i++){
str=str" "a[i,j];
}
print str
}
}' intermediate3.txt | column -t > output.txt
フォーマットされた結果:
- Foo#2 Foo#4 Foo#1 Foo#3 Foo#4 Foo#5 SUM
Bar#6 20 1 3 0 0 0 24
Bar#4 0 2 0 1 1 0 4
Bar#3 0 2 0 0 2 0 4
Bar#7 0 0 1 2 0 0 3
Bar#2 0 0 2 1 0 0 3
Bar#1 0 3 0 0 0 0 3
Bar#5 0 0 1 1 0 0 2
SUM 20 8 7 5 3 0 43
Bar#4とBar#3の順序は合計値が同じであるため、例の結果と反対ですが、A列もBar#7、Bar#2、Bar#と同様に降順に従います。 1
答え2
これは比較的簡単ですperl
。
perl -F'\s+' -lane '
push @row, [@F];
END{
@sum = @{pop @row};
@col = (0, (sort {$sum[$b] <=> $sum[$a]} (1..$#sum-1)), $#sum);
for $i ($row[0], (sort {$b->[$#sum] <=> $a->[$#sum]} @row[1..$#row]), \@sum) {
print join "\t", @{$i}[@col]
}
}'
答え3
参考までにpython
解決策です。
from pprint import pprint
x = [(0,0,0,0,3,0),
(2,0,1,0,0,0),
(0,0,0,2,2,0),
(0,0,1,1,2,0),
(1,0,1,0,0,0),
(3,20,0,0,1,0),
(1,0,2,0,0,0)
]
y = sorted(x, key=sum, reverse=True)
pprint(zip(*sorted(zip(*y), key=sum, reverse=True)))
[(20, 1, 3, 0, 0, 0),
(0, 2, 0, 0, 2, 0),
(0, 2, 0, 1, 1, 0),
(0, 3, 0, 0, 0, 0),
(0, 0, 2, 1, 0, 0),
(0, 0, 1, 2, 0, 0),
(0, 0, 1, 1, 0, 0)]