Wie sortiere ich Spalten anhand der ersten Zeile?


12

Ich muss die Spalten eines sehr großen Datensatzes sortieren (1000 Zeilen und 700000 Spalten). Zum Beispiel sind meine Spalten wie folgt zufällig angeordnet: col1 col4 col3 col2, und das muss ich sortieren.

Ich habe einige Befehle ausprobiert, aber keinen Erfolg.

Beispiel:

ID M2 M5 M8 M1 M3 M9 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln

In diesem Beispiel bedeuten Punkte, dass ich viele Spalten und Zeilen habe. Wieder muss ich die Spalten sortieren, um wie folgt zu sein:

ID M1 M2 M3 M4 M5 M6 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln

Vielen Dank


Können Sie ein Beispiel mit einigen Zeilen des Datensatzes hinzufügen?
jcbermu

Ihr erwartetes Ergebnis ist nur in der ersten Zeile sortiert, andere Werte bleiben gleich. Warum?
RomanPerekhrest

Eigentlich muss es den Spalten folgen, war ein Fehler des Beispiels. Entschuldigung
LLVerardo

Müssen die gesamte Spalte nach der ersten Zeile sortiert werden.
LLVerardo

2
Transponiere, sortiere nach erster Spalte, transponiere zurück.
Satō Katsura

Antworten:


10

Mit GNU datamashund GNU sort:

datamash transpose -t ' ' -H <file_in.csv | sort -V | datamash transpose -t ' ' -H >file_out.csv

Dies funktioniert gut für "relativ kleine" Daten. Es kann mit Ihrer Datei funktionieren oder nicht.

Bearbeiten: Die folgenden Lösungen ohne Transpositionen sollten weniger ressourcenintensiv sein.


1
Der Befehl rs könnte eine leichtere Alternative zu datamashzB sein rs -T < file_in.csv | sort | rs -T -C' '( rssollte als Paket auf Debian-basierten Systemen verfügbar sein)
steeldriver

2
FWIW rs("reshape a data array") ist in den Basissystemen einiger BSDs verfügbar.
Kusalananda

6
perl -pale '
   $. == 1 and
   @I = map  { $_->[1] }
        sort { $a->[0] <=> $b->[0] }
        map  { [ $F[$_] =~ /^M(\d+)$/, $_ ] } 1..$#F;
   $_ = "@F[0, @I]";
' yourlargefile

  1. In der ersten Zeile sortieren wir die zweitletzten Spalten numerisch nach den numerischen Teilen nach der Ziffer, Mdie am Anfang vorkommt Schwartzian maneuver. Dadurch werden die Indizes neu angeordnet, sodass die Spalten in numerisch sortierter Reihenfolge (M1, M2, M3, ...) ausgegeben werden.
  2. Alles, was bleibt, ist, diese Indizes zu verwenden, @Ium die @FElemente neu anzuordnen .
  3. Wenn Sie das Array in Anführungszeichen setzen, wird es in eine Zeichenfolge mit durch Leerzeichen getrennten Elementen konvertiert.
  4. -pOption zu Perl aktiviert den Autoprint von $_Inhalten, -lsoll das hinzufügen newline.

6

Verwendung des Perl-Moduls Sort :: Natürlich

Eingabedaten

ID M2 M5 M8 M1 M3 M9 M700000
A1 m1,2 m1,5 m1,8 m1,1 m1,3 m1,9 m1,7000000
A2 m2,2 m2,5 m2,8 m2,1 m2,3 m2,9 m2,7000000
A3 m3,2 m3,5 m3,8 m3,1 m3,3 m3,9 m3,7000000
A1000 m1000,2 m1000,5 m1000,8 m1000,1 m1000,3 m1000,9 m1000,7000000
perl -MSort::Naturally -lane '
  if ($. == 1) {
    @indices = (0, map  { $_->[0] }
                   sort { ncmp($a->[1], $b->[1]) }
                   map  { [$_, $F[$_]] }
                   1..$#F
               );
    $, = " ";
  }
  print @F[@indices]
' test.data

Ausgabe

ID M1 M2 M3 M5 M8 M9 M700000
A1 m1,1 m1,2 m1,3 m1,5 m1,8 m1,9 m1,7000000
A2 m2,1 m2,2 m2,3 m2,5 m2,8 m2,9 m2,7000000
A3 m3,1 m3,2 m3,3 m3,5 m3,8 m3,9 m3,7000000
A1000 m1000,1 m1000,2 m1000,3 m1000,5 m1000,8 m1000,9 m1000,7000000

+1 für die elegantesten, nimmt nicht zu spezifisches Präfix für Spaltennamen an, One-Pass-Lösung.
Arielf

4

Wenn Sie das rsDienstprogramm installiert haben, können Sie dies tun:

rs -c' ' -T | {
    stdbuf -i0 sed "1q"
    sort -V
} | rs -C' ' -T

Oder alles in einer Zeile:

rs -c' ' -T | { stdbuf -i0 sed "1q"; sort -V ; } | rs -C' ' -T
  • Die erste rstransponiert die Eingabedaten (mit durch Leerzeichen getrennten Feldern)
  • Die Befehlsgruppe:
    • sedLiest die erste Zeile, gibt sie aus, beendet sie dann und lässt den Rest der Pipe rsunberührt. stdbufist erforderlich, um sicherzustellen, dass sednur bis zur ersten Zeile und nicht weiter gelesen wird, indem die Eingabepufferung ausgeschaltet wird
    • sorts die restlichen Zeilen
  • Die Sekunde rstransponiert den resultierenden Strom zurück in sein ursprüngliches Format.

rsist standardmäßig unter MacOS installiert. Auf Linux-Systemen müssen Sie es möglicherweise installieren - z

sudo apt install rs

Einschränkung: stdbufund sorts -VOption sind GNU-spezifisch, funktionieren also nicht unter unverändertem MacOS.


0

Wenn Sie GNU haben awk, können Sie dies versuchen:

NR == 1 {
    for (i = 2; i <= NF; i++) {
        columns[substr($i, 2)] = i;
    }
    count = asorti(columns, sorted, "@ind_num_asc");
    printf("%s", $1);
    for (i = 1; i <= count; i++) {
        printf(" M%s", sorted[i]);
        indx[i] = columns[sorted[i]];
    }
    print "";
    next;
}
{
    printf("%s", $1);
    for (i = 1; i <= count; i++) {
        printf(" %s", $(indx[i]));
    }
    print "";
}

0

In Python:

from csv import DictReader, DictWriter
with open('in_file.csv') as infile, open('out_file.csv') as outfile:
  reader = DictReader(infile)
  writer = DictReader(outfile, fieldnames=sorted(reader.fieldnames))
  writer.writerows(reader)

0

Ich weiß nicht, ob Sie das für eine gute Antwort hielten, aber ...

Warum verwenden Sie keine Datenbank, um dieses Problem zu lösen? Sie können Ihr Dataset als temporäre Tabelle importieren und dann Folgendes ausführen: a

SELECT spalte1, spalte2, ... spalte-n FROM mein_temp_tabelle

Sie können bei Bedarf auch andere Filter oder Transformationen verwenden. Anschließend können Sie Ihre Ausgabe nach Bedarf neu formatieren.

Alle diese Aufgaben können als Bash-Skript programmiert und die Ausgaben mithilfe von Pipes verkettet werden.

Manchmal wurde der Befehl "pv" verwendet, um den Fortschritt der Ausgabe zwischen den Befehlen zu sehen.

Um den Datensatz zu importieren, können Sie eine ETL mit Pentaho Data Integration programmieren.


0

Vielleicht könnte dir das auch helfen.

  1. Zuerst können Sie Ihre Datei transponieren (eine von /programming/1729824/an-efficient-way-to-transpose-a-file-in-bash )
  2. Erste Spalte mit Sortierbefehl sortieren.
  3. Nochmal transponieren.

Ex:

$ echo "ID M2 M5 M8 M1 M3 M9 .....M7000000
Animal1 1 0 2 1 0 2 .....1
Animal2 0 1 2 0 1 1 .....0
Animal3 2 1 0 1 2 1 .....0
.
.
.
.
Animaln" | 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
    }
}'
ID M1 M2 M3 M5 .....M7000000 M8 M9
Animal1 1 1 0 0 .....1 2 2
Animal2 0 0 1 1 .....0 2 1
Animal3 1 2 2 1 .....0 0 1
.       
.       
.       
.       
Animaln    
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.