¿Cómo ordenar las columnas según la primera línea?

12

Necesito ordenar las columnas de un conjunto de datos muy grande (1000 líneas y 700000 columnas). Como ejemplo, mis columnas están ordenadas aleatoriamente como: col1 col4 col3 col2, y necesito ordenar eso.

He estado intentando algunos comandos, pero no he tenido éxito.

ejemplo:

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

En este ejemplo, los puntos significan que tengo muchas columnas y líneas. Nuevamente, necesito ordenar las columnas para que sean como:

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

Gracias

LLVerardo
fuente
¿Puedes agregar un ejemplo con unas pocas líneas del conjunto de datos?
jcbermu
su resultado esperado solo tiene ordenada la primera línea, otros valores permanecen iguales, ¿por qué?
RomanPerekhrest
En realidad, necesita seguir las columnas, fue un error del ejemplo. lo siento
LLVerardo
Necesita que toda la columna se ordene según la primera línea.
LLVerardo
2
Transponer, ordenar por primera columna, volver a transponer.
Satō Katsura

Respuestas:

10

Con GNU datamashy GNU sort:

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

Esto funciona bien para datos "razonablemente pequeños". Puede o no funcionar con su archivo.

Editar: Las soluciones a continuación sin transposiciones deberían consumir menos recursos.

Satō Katsura
fuente
1
El comando de RS podría ser una alternativa más ligera a datamashpor ejemplo rs -T < file_in.csv | sort | rs -T -C' '( rsdebe estar disponible como un paquete en sistemas basados en Debian)
steeldriver
2
FWIW, rs("remodelar una matriz de datos") está disponible en los sistemas base de algunos 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. Para la primera línea, clasificamos numéricamente su 2da ... última columna usando sus porciones numéricas después del dígito que Maparece al principio, usando el bien conocido Schwartzian maneuver. Esto nos proporciona los índices reordenados para que las columnas salgan ordenadas numéricamente (M1, M2, M3, ...)
  2. Todo lo que queda es usar estos índices que provienen @Ipara reorganizar los @Felementos.
  3. La asignación de la matriz en forma de comillas dobles la convierte en una cadena con elementos separados por espacios.
  4. -pLa opción de Perl habilita la impresión automática de $_contenidos, -lagregará el newline.

fuente
6

Usando el módulo perl Ordenar :: Naturalmente

los datos de entrada

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

salida

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
fuente
+1 para los más elegantes, no supone un prefijo demasiado específico para los nombres de columna, una solución de paso
arielf
4

Si tiene instalada la rsutilidad , puede hacer esto:

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

O todo en una línea:

rs -c' ' -T | { stdbuf -i0 sed "1q"; sort -V ; } | rs -C' ' -T
  • El primero rstranspone los datos de entrada (con campos espaciados)
  • El grupo de comando:
    • sedlee la primera línea, la emite y luego se cierra, dejando rsintacto el resto de la tubería . stdbufes necesario para garantizar que sedsolo se lee hasta la primera línea nueva y no más, al desactivar el almacenamiento en búfer de entrada
    • sorts las líneas restantes
  • El segundo rstranspone la secuencia resultante a su formato original.

rsse instala por defecto en MacOS. En los sistemas Linux puede que tenga que instalarlo, p. Ej.

sudo apt install rs

Advertencia: stdbufy sorts -Vopción son específicas de GNU así que no funcionará en MacOS no modificados.

Trauma digital
fuente
0

Si tienes GNU awk, puedes probar esto:

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
fuente
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)
Whereswalden
fuente
0

No sé si consideraste esto como una buena respuesta, pero ...

¿Por qué no usa una base de datos para resolver este problema? puede importar su conjunto de datos como una tabla temporal y luego hacer un

SELECCIONE column1, column2, ... column-n FROM my_temp_table

Puede usar otros filtros o transformaciones según lo necesite. Luego, puede formatear su salida según lo necesite.

Todas estas tareas podrían programarse como un script bash y encadenar salidas utilizando tuberías.

A veces se me ha utilizado el comando "pv" para ver el progreso de salida entre comandos.

Para importar el conjunto de datos, puede programar un ETL utilizando Pentaho Data Integration.

Gonzalo
fuente
0

Quizás esto también te pueda ayudar.

  1. Primero puede usar transponer su archivo (uno de /programming/1729824/an-efficient-way-to-transpose-a-file-in-bash )
  2. Ordenar la primera columna con el comando ordenar.
  3. Transponer de nuevo.

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    
Mustafa DOGRU
fuente