Como reordenar um grande número de colunas?

Como reordenar um grande número de colunas?

Estou procurando uma linha única canalizável para reordenar um grande número de colunas (onde inserir manualmente os números das colunas, por exemplo, um awkcomando como awk '{print $3,$2,$1}'não é viável). A ordem pode ser dada por um esquema de classificação (alfabética, numérica - semelhante a 'classificar', mas agindo em colunas em vez de linhas.) ou ser dada arbitrariamente em um arquivo de texto.

Responder1

Solução simples com Perl.

Primeiro preencha sua matriz de valores.

➜ ~ 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: ativa a divisão automática que preenche automaticamente o @Farray
  • -n: lê cada linha em um loop while
  • $#F: retorna o maior índice baseado em 0 na matriz
  • <=>: operador de comparação para funções de classificação (somente entrada numérica, para comparações de strings use cmp)
  • sort: retorna os índices classificados do array 0..$#F(usando as variáveis ​​internas $ae $b)
  • @i: contém a matriz de índices classificados para @F(neste exemplo, @i = 5 6 3 0 4 8 9 1 7 2)
  • $. == 1: e faça isso apenas na primeira linha
  • @F[@i]: classifica cada linha com base nos índices classificados

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

Responder2

Aqui está uma solução streamable.

Presumo que você queira classificar com base na primeira linha das colunas; caso contrário, adapte-se para obter a chave de classificação de outro lugar.

Gere a chave de classificação (reutilizando o array do 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)
    

$keyagora detém:

2
1
3

Agora use a chave para classificar colunas:

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

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

Saída:

a b c 
4 5 6 
7 8 9

Responder3

Não tenho certeza se é a melhor solução e não tenho certeza se funcionará rápido em tabelas enormes, mas deve funcionar:

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}}'

Como funciona: transpõe a tabela, depois classifica a tabela e transpõe de volta.

aliás, echo -e "2 1 3\n5 4 6\n8 7 9"resultará em

2 1 3
5 4 6
8 7 9

Após o trabalho do script, resultará em

1 2 3
4 5 6
7 8 9

obs. Acho que é possível ordenar array no awk, infelizmente não tenho tempo para fazer isso.

Responder4

Supondo que seu arquivo seja xy.dat e separado por espaço em branco:

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

Como meus dados de teste eram numéricos crescentes, eu uso sort -nr no coração, para torná-los decrescentes e ver algum efeito.

Agora, para torná-lo configurável, bastaria passar como parâmetros os flags para sort, o que permite -r ascendente (nenhum) e decrescente (reverso), mas também -n (numérico) e muito mais (veja:) sort --help. Outra coisa que você pode querer configurável é o delimitador. Em branco/guia/ponto e vírgula/vírgula? Talvez um grupo regex queira "[ \t]"significar espaço em branco ou tabulação? Mas o que usar para saída então? E você não gostaria de codificar o nome do arquivo, mas usar seu programa como filtro. Aqui está uma abordagem rápida:

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

invocação:

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 

Veja como é classificado por padrão com -- (alfabético: 10 11 9), reverso (9 10 11) ou numérico (11 10 9).

Principalmente como mascarar espaços em branco, tabulações e assim por diante seria útil, se documentado.

informação relacionada