如何對大量列進行重新排序?

如何對大量列進行重新排序?

我正在尋找一種可管道化的單行程式碼來對大量列進行重新排序(在例如命令中手動輸入列號awkawk '{print $3,$2,$1}'不可行的)。順序可以透過排序方案給出(字母、數字 - 就像“排序”,但作用於列而不是行。)或在文字檔案中任意給出。

答案1

使用 Perl 的簡單解決方案。

首先填入您的值數組。

➜ ~ x="$(cat << END
22      79      83      16      25      1       4       82      34      68
48      43      2       26      39      2       71      43      57      41
77      70      73      18      76      33      21      54      67      50
6       65      46      92      25      70      53      28      3       40
32      60      76      39      26      44      34      91      24      39
59      75      96      85      52      98      69      28      72      94
48      0       88      55      6       78      1       54      83      81
3       43      48      24      23      87      28      98      38      67
97      73      74      24      92      67      1       27      90      85
32      55      52      44      26      37      87      37      100     92
END
)"
➜  ~ perl -lane '@i=sort({ @F[$a] <=> @F[$b] } 0..$#F) if $.==1; 
                 print join("\t", @F[@i])' <<< "$x"      

1       4       16      22      25      34      68      79      82      83
2       71      26      48      39      57      41      43      43      2
33      21      18      77      76      67      50      70      54      73
70      53      92      6       25      3       40      65      28      46
44      34      39      32      26      24      39      60      91      76
98      69      85      59      52      72      94      75      28      96
78      1       55      48      6       83      81      0       54      88
87      28      24      3       23      38      67      43      98      48
67      1       24      97      92      90      85      73      27      74
37      87      44      32      26      100     92      55      37      52

  • -a:啟用自動拆分,自動填入@F數組
  • -n:在 while 迴圈中讀取每一行
  • $#F:傳回數組中最大的從 0 開始的索引
  • <=>:排序函數的比較運算子(僅限數字輸入,用於字串比較時使用cmp
  • sort:從陣列傳回排序後的索引0..$#F(使用內建$a$b變數)
  • @i@F:包含(在本例中,@i = 5 6 3 0 4 8 9 1 7 2)的排序索引數組
  • $. == 1: 並且只在第一行執行
  • @F[@i]:根據排序後的索引對每一行進行排序

來源:https://learnbyexample.gitbooks.io/command-line-text-processing/content/perl_the_swiss_knife.html

答案2

這是一個可串流的解決方案。

我假設您想根據列的第一行進行排序,否則適應從其他地方獲取排序鍵。

產生排序鍵(重複使用Rush的陣列):

echo -e  "b a c\n5 4 6\n8 7 9" > data

key=$(head -n1 data | sed 's/ \+/\n/g' | nl -n ln | sort -k2 | cut -f1)
    

$key現在成立:

2
1
3

現在使用鍵對列進行排序:

awk -v key="$key" '
BEGIN { split(key, order, "\n") }

{ 
  for(i=1; i<=length(order); i++) { 
    printf("%s ", $order[i])
  }
  printf("\n");
}' data

輸出:

a b c 
4 5 6 
7 8 9

答案3

我不確定它是最好的解決方案,也不確定它是否能在巨大的表上快速運行,但它應該可以:

echo -e  "2 1 3\n5 4 6\n8 7 9"  | \
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}}' \ 
     | sort -n  | \
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}}'

它是如何工作的:它轉置表格,然後對表格進行排序並將其轉置回去。

順便說一句echo -e "2 1 3\n5 4 6\n8 7 9"將導致

2 1 3
5 4 6
8 7 9

腳本工作後,結果將是

1 2 3
4 5 6
7 8 9

附:我認為可以在 awk 中對數組進行排序,不幸的是我沒有足夠的時間來做到這一點。

答案4

假設您的檔案是 xy.dat,並以空格分隔:

cat xy.dat | while read line ; do  
   echo $line | tr ' ' '\n' | sort -nr | tr '\n' ' '
   echo
done

由於我的測試資料是數位升序,所以我在心中使用 sort -nr ,使其降序,並看到一些效果。

現在,為了使其可配置,只需將排序標誌作為參數傳遞,它允許升序(無)和降序 -r (反向),還可以 -n (數字)等等(請參閱:sort --help)。您可能喜歡配置的另一件事是分隔符號。空白/製表符/分號/逗號?也許正規表示式組喜歡"[ \t]"表示空白或製表符?那麼輸出用什麼呢?您不想對檔案名稱進行硬編碼,而是使用您的程式作為過濾器。這是一個快速方法:

#!/bin/bash
flags=$1
delim=$2 
while read line ; do  
    echo $line | tr "$delim" '\n' | sort $flags | tr '\n' "$delim"
    echo
done

調用:

cat num.dat | bash colsort.sh "-nr" ' ' 
4 3 2 1 
8 7 6 5 
11 10 9 

cat num.dat | bash colsort.sh "-r" ' ' 
4 3 2 1 
8 7 6 5 
9 11 10 

cat num.dat | bash colsort.sh "--" ' ' 
1 2 3 4 
5 6 7 8 
10 11 9 

查看預設如何排序 - (按字母順序:10 11 9)、反向(9 10 11)或數字(11 10 9)。

如果有記錄的話,主要是如何屏蔽空白、製表符等會很有幫助。

相關內容