Как изменить порядок большого количества столбцов?

Как изменить порядок большого количества столбцов?

Я ищу однострочный конвейер для переупорядочивания большого количества столбцов (где ручной ввод номеров столбцов, например, в awkкоманде, такой как , awk '{print $3,$2,$1}'невозможен). Порядок может быть задан схемой сортировки (алфавитной, числовой - то есть как 'sort', но действующей на столбцы, а не на строки.) или может быть произвольно задан в текстовом файле.

решение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

Вот потоковое решение.

Я предполагаю, что вы хотите выполнить сортировку по первой строке столбцов, в противном случае адаптируйтесь так, чтобы получить ключ сортировки откуда-то еще.

Сгенерировать ключ сортировки (повторно используя массив Раша):

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

ps. Я думаю, что можно отсортировать массив в 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).

В основном было бы полезно, если бы было задокументировано, как маскировать пробелы, табуляции и т. д.

Связанный контент