Wie kann ich eine große Anzahl Spalten neu anordnen?

Wie kann ich eine große Anzahl Spalten neu anordnen?

Ich suche nach einem pipeable One-Liner, um eine große Anzahl von Spalten neu zu sortieren (wobei die manuelle Eingabe der Spaltennummern beispielsweise in einem awkBefehl wie awk '{print $3,$2,$1}'nicht möglich ist). Die Reihenfolge könnte durch ein Sortierschema vorgegeben werden (alphabetisch, numerisch – also wie „Sortieren“, aber auf Spalten statt auf Zeilen ausgerichtet) oder willkürlich in einer Textdatei angegeben werden.

Antwort1

Einfache Lösung mit Perl.

Füllen Sie zuerst Ihr Werte-Array auf.

➜ ~ 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: aktiviert Autosplit, das das @FArray automatisch füllt
  • -n: liest jede Zeile in einer While-Schleife
  • $#F: gibt den größten 0-basierten Index im Array zurück
  • <=>: Vergleichsoperator für Sortierfunktionen (nur numerische Eingabe, für Stringvergleiche verwenden cmp)
  • sort: gibt die sortierten Indizes aus dem Array zurück 0..$#F(unter Verwendung der integrierten $aund $bVariablen)
  • @i: enthält das Array der sortierten Indizes für @F(in diesem Beispiel @i = 5 6 3 0 4 8 9 1 7 2)
  • $. == 1: und zwar nur in der ersten Zeile
  • @F[@i]: sortiert jede Zeile basierend auf den sortierten Indizes

Quelle:https://learnbyexample.gitbooks.io/command-line-text-processing/content/perl_das_schweizermesser.html

Antwort2

Hier ist eine streambare Lösung.

Ich gehe davon aus, dass Sie basierend auf der ersten Spaltenzeile sortieren möchten, andernfalls passen Sie es so an, dass der Sortierschlüssel von woanders herkommt.

Sortierschlüssel generieren (unter Wiederverwendung des Arrays von 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)
    

$keygilt nun:

2
1
3

Verwenden Sie nun die Taste zum Sortieren der Spalten:

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

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

Ausgabe:

a b c 
4 5 6 
7 8 9

Antwort3

Ich bin nicht sicher, ob das die beste Lösung ist und ob es bei großen Tabellen schnell funktioniert, aber es sollte funktionieren:

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

Und so funktioniert es: Es transponiert die Tabelle, sortiert sie dann und transponiert sie wieder zurück.

btw echo -e "2 1 3\n5 4 6\n8 7 9"wird dazu führen,

2 1 3
5 4 6
8 7 9

Nach der Arbeit am Skript ergibt sich

1 2 3
4 5 6
7 8 9

PS: Ich glaube, es ist möglich, Arrays in AWK zu sortieren, leider habe ich nicht genug Zeit dafür.

Antwort4

Angenommen, Ihre Datei heißt xy.dat und ist durch Leerzeichen getrennt:

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

Da meine Testdaten numerisch aufsteigend waren, verwende ich im Herzen „sort -nr“, um sie absteigend zu machen und sehe einen Effekt.

Um es nun konfigurierbar zu machen, würde man einfach die Flags für die Sortierung als Parameter übergeben, was aufsteigend (keine) und absteigend -r (umgekehrt), aber auch -n (numerisch) und vieles mehr erlaubt (siehe: sort --help). Eine andere Sache, die Sie vielleicht konfigurierbar haben möchten, ist das Trennzeichen. Leerzeichen/Tabulator/Semikolon/Komma? Vielleicht eine Regex-Gruppe, die "[ \t]"Leerzeichen oder Tabulator bedeuten soll? Aber was soll dann für die Ausgabe verwendet werden? Und Sie möchten den Dateinamen nicht fest codieren, sondern Ihr Programm als Filter verwenden. Hier ist ein schneller Ansatz:

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

Aufruf:

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 

Sehen Sie, wie die Standardsortierung mit -- (alphabetisch: 10 11 9), umgekehrt (9 10 11) oder numerisch (11 10 9) erfolgt.

Vor allem Informationen zum Maskieren von Leerzeichen, Tabulatoren usw. wären hilfreich, wenn sie dokumentiert wären.

verwandte Informationen