如何对大量列进行重新排序?

如何对大量列进行重新排序?

我正在寻找一种可管道化的单行代码来对大量列进行重新排序(其中手动输入列号,例如在awk诸如此类的命令中awk '{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)。

如果有记录的话,主要是如何屏蔽空白、制表符等会很有帮助。

相关内容