Comment trier les colonnes en fonction de la première ligne?


12

J'ai besoin de trier les colonnes d'un très grand ensemble de données (1000 lignes et 700000 colonnes). Par exemple, mes colonnes sont disposées au hasard comme: col1 col4 col3 col2, et je dois trier cela.

J'ai essayé quelques commandes, mais sans succès.

exemple:

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

Dans cet exemple, les points signifient que j'ai beaucoup de colonnes et de lignes. Encore une fois, je dois trier les colonnes comme suit:

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

Je vous remercie


Pouvez-vous ajouter un exemple avec quelques lignes de l'ensemble de données?
jcbermu

votre résultat attendu n'a trié que la première ligne, les autres valeurs restent les mêmes, pourquoi?
RomanPerekhrest

En fait, il faut suivre les colonnes, c'était une erreur de l'exemple. désolé
LLVerardo

Besoin de trier la colonne entière en fonction de la première ligne.
LLVerardo

2
Transposer, trier par première colonne, transposer en arrière.
Satō Katsura

Réponses:


10

Avec GNU datamashet GNU sort:

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

Cela fonctionne très bien pour les données "raisonnablement petites". Il peut ou non fonctionner avec votre fichier.

Edit: Les solutions ci-dessous sans transpositions devraient être moins gourmandes en ressources.


1
La commande rs pourrait être une alternative plus légère, par datamashexemple rs -T < file_in.csv | sort | rs -T -C' '( rsdevrait être disponible sous forme de package sur les systèmes basés sur Debian)
steeldriver

2
FWIW, rs("remodeler un tableau de données") est disponible dans les systèmes de base de certains 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. Pour la première ligne, nous trions numériquement sa 2e ... dernière colonnes en utilisant leurs portions numériques après le chiffre Mapparaissant au début, en utilisant le bien connu Schwartzian maneuver. Cela nous donne les indices réorganisés pour que les colonnes sortent dans l'ordre trié numériquement (M1, M2, M3, ...)
  2. Il ne reste plus qu'à utiliser ces indices provenant de @Ipour réorganiser les @Féléments.
  3. L'affectation du tableau sous une forme entre guillemets le convertit en une chaîne avec des éléments séparés par un espace.
  4. -pL'option Perl permet l'impression automatique du $_contenu, -lajoutera le newline.

6

Utilisation du module Perl Sort :: Naturally

des données d'entrée

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

production

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 pour les plus élégants, ne suppose pas de préfixe trop spécifique pour les noms de colonne, solution à un passage.
arielf

4

Si l' rsutilitaire est installé, vous pouvez le faire:

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

Ou tout sur une seule ligne:

rs -c' ' -T | { stdbuf -i0 sed "1q"; sort -V ; } | rs -C' ' -T
  • Le premier rstranspose les données d'entrée (avec des champs séparés par l'espace)
  • Le groupe de commande:
    • sedlit la première ligne, la sort, puis quitte, laissant le reste du tuyau rsintact. stdbufest nécessaire pour garantir que sedne lit que jusqu'à la première nouvelle ligne et pas plus loin, en désactivant la mise en mémoire tampon d'entrée
    • sorts les lignes restantes
  • La seconde rstranspose le flux résultant dans son format d'origine.

rsest installé par défaut sur MacOS. Sur les systèmes Linux, vous devrez peut-être l'installer - par exemple

sudo apt install rs

Attention: stdbufet l' option sorts -Vest spécifique à GNU et ne fonctionnera donc pas sur MacOS non modifié.


0

Si vous avez GNU awk, vous pouvez essayer ceci:

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

En 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

Je ne sais pas si vous avez considéré cela comme une bonne réponse, mais ...

Pourquoi vous n'utilisez pas de base de données pour résoudre ce problème? vous pouvez importer votre jeu de données en tant que table temporaire, puis faire un

SELECT colonne1, colonne2, ... colonne-n FROM my_temp_table

Vous pouvez utiliser d'autres filtres ou transformations selon vos besoins. Ensuite, vous pouvez reformater votre sortie selon vos besoins.

Toutes ces tâches peuvent être programmées en tant que script bash et chaîner les sorties à l'aide de canaux.

Parfois, j'ai utilisé la commande "pv" pour voir la progression de la sortie entre les commandes.

Pour importer l'ensemble de données, vous pouvez programmer un ETL à l'aide de Pentaho Data Integration.


0

Cela pourrait peut-être aussi vous aider.

  1. Vous pouvez d'abord utiliser la transposition de votre fichier (un des /programming/1729824/an-efficient-way-to-transpose-a-file-in-bash )
  2. Trier la première colonne avec la commande sort.
  3. Transposer à nouveau.

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    
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.