Jak sortować kolumny na podstawie pierwszego wiersza?

12

Muszę posortować kolumny bardzo dużego zestawu danych (1000 wierszy i 700000 kolumn). Na przykład moje kolumny są losowo ułożone w następujący sposób: col1 col4 col3 col2, i muszę to posortować.

Próbowałem kilka poleceń, ale bez powodzenia.

przykład:

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

W tym przykładzie kropki oznaczają, że mam dużo kolumn i linii. Ponownie muszę posortować kolumny, aby wyglądały następująco:

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

Dziękuję Ci

LLVerardo
źródło
Czy możesz dodać przykład z kilkoma wierszami zestawu danych?
jcbermu
oczekiwany wynik został posortowany tylko w pierwszym wierszu, inne wartości pozostają takie same, dlaczego?
RomanPerekhrest
W rzeczywistości musi podążać za kolumnami, to był błąd w przykładzie. przepraszam
LLVerardo
Potrzebujesz posortować całą kolumnę na podstawie pierwszego wiersza.
LLVerardo
2
Transponuj, sortuj według pierwszej kolumny, transponuj z powrotem.
Satō Katsura,

Odpowiedzi:

10

Z GNU datamashi GNU sort:

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

Działa to dobrze w przypadku „stosunkowo małych” danych. Może, ale nie musi działać z twoim plikiem.

Edycja: Poniższe rozwiązania bez transpozycji powinny mniej obciążać zasoby.

Satō Katsura
źródło
1
Polecenie rs może być lżejszą alternatywą dla datamashnp. rs -T < file_in.csv | sort | rs -T -C' '( rsPowinno być dostępne jako pakiet w systemach opartych na Debianie)
steeldriver
2
FWIW rs(„przekształć tablicę danych”) jest dostępny w systemach podstawowych niektórych BSD.
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. W pierwszym wierszu sortujemy numerycznie to drugie ... ostatnie kolumny, używając ich liczbowych części po cyfrze Mwystępującej na początku, przy użyciu dobrze znanego Schwartzian maneuver. To daje nam uporządkowane indeksy, dzięki czemu kolumny wychodzą w porządku numerycznym (M1, M2, M3, ...)
  2. Wszystko, co pozostaje, to użyć tych indeksów pochodzących z, @Iaby zmienić rozmieszczenie @Felementów.
  3. Przypisanie tablicy w formie podwójnego cudzysłowu przekształca ją w ciąg z oddzielonymi spacjami elementów.
  4. -popcja Perla włącza autodruk $_zawartości, -ldodaje newline.

źródło
6

Korzystanie z modułu perla Sort :: Naturalnie

dane wejściowe

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

wynik

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
Glenn Jackman
źródło
+1 dla najbardziej eleganckiego, nie zakłada zbyt konkretnego prefiksu dla nazw kolumn, rozwiązanie jednoprzebiegowe.
arielf
4

Jeśli masz zainstalowane rsnarzędzie , możesz to zrobić:

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

Lub wszystko w jednej linii:

rs -c' ' -T | { stdbuf -i0 sed "1q"; sort -V ; } | rs -C' ' -T
  • Pierwszy rstransponuje dane wejściowe (z polami o odstępach spacji)
  • Grupa poleceń:
    • sedczyta pierwszy wiersz, wysyła go, a następnie kończy pracę, pozostawiając resztę potoku rsnietkniętą. stdbufjest wymagane, aby upewnić się, że sedtylko czyta do pierwszej nowej linii, a nie dalej, poprzez wyłączenie buforowania danych wejściowych
    • sorts pozostałe linie
  • Drugi rstransponuje wynikowy strumień z powrotem do oryginalnego formatu.

rsjest instalowany domyślnie na MacOS. W systemach Linux może być konieczne jego zainstalowanie - np

sudo apt install rs

Zastrzeżenie: stdbufi sorts -Vsą specyficzne dla GNU, więc nie będą działać na niezmodyfikowanym MacOS.

Cyfrowa trauma
źródło
0

Jeśli masz GNU awk, możesz spróbować:

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 "";
}
Michael Vehrs
źródło
0

W Pythonie:

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)
whereswalden
źródło
0

Nie wiem, czy uważałeś to za dobrą odpowiedź, ale ...

Dlaczego nie używasz bazy danych do rozwiązania tego problemu? możesz zaimportować zestaw danych jako tabelę tymczasową, a następnie wykonać

WYBIERZ kolumna 1, kolumna 2, ... kolumna-n Z mojej_temp_table

Możesz użyć innych filtrów lub transformacji, jeśli potrzebujesz. Następnie możesz sformatować dane wyjściowe według potrzeb.

Wszystkie te zadania można zaprogramować jako skrypt bash i łączenie danych wyjściowych za pomocą potoków.

Czasami używano polecenia „pv”, aby zobaczyć postęp wyjściowy między poleceniami.

Aby zaimportować zestaw danych, możesz zaprogramować ETL za pomocą integracji danych Pentaho.

Gonzalo
źródło
0

Może to też może ci pomóc.

  1. Najpierw możesz użyć transpozycji pliku (jeden z /programming/1729824/an-efficient-way-to-transpose-a-file-in-bash )
  2. Sortuj pierwszą kolumnę za pomocą polecenia sortowania.
  3. Transponuj ponownie.

Dawny:

$ 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    
Mustafa DOGRU
źródło