Como classificar colunas com base na primeira linha?

12

Preciso classificar as colunas de um conjunto de dados muito grande (1000 linhas e 700000 colunas). Como exemplo, minhas colunas são organizadas aleatoriamente como: col1 col4 col3 col2, e eu preciso classificar isso.

Eu tenho tentado alguns comandos, mas sem sucesso.

exemplo:

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

Neste exemplo, pontos significa que tenho muitas colunas e linhas. Novamente, eu preciso classificar as colunas para que sejam 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

Obrigado

LLVerardo
fonte
Você pode adicionar um exemplo com algumas linhas do conjunto de dados?
precisa saber é o seguinte
seu resultado esperado tem apenas a primeira linha classificada, outros valores permanecem os mesmos, por quê?
RomanPerekhrest
Na verdade, ele precisa seguir as colunas, foi um erro do exemplo. desculpe
LLVerardo
Precisa que a coluna inteira seja classificada com base na primeira linha.
LLVerardo 7/06
2
Transpor, classificar pela primeira coluna, transpor de volta.
Satō Katsura

Respostas:

10

Com GNU datamashe GNU sort:

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

Isso funciona bem para dados "razoavelmente pequenos". Pode ou não funcionar com seu arquivo.

Edit: As soluções abaixo sem transposições devem ser menos intensivas em recursos.

Satō Katsura
fonte
1
O comando rs pode ser uma alternativa mais leve para datamasheg rs -T < file_in.csv | sort | rs -T -C' '( rsdeve estar disponível como um pacote em sistemas baseados em Debian)
steeldriver
2
O FWIW rs("remodelar uma matriz de dados") está disponível nos sistemas básicos de alguns BSDs.
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 a primeira linha, classificamos numericamente a segunda ... última coluna usando suas porções numéricas após o dígito Mocorrer no início, usando o bem conhecido Schwartzian maneuver. Isso nos fornece os índices reordenados para que as colunas saiam em ordem numericamente classificada (M1, M2, M3, ...)
  2. Tudo o que resta é usar esses índices provenientes @Ipara reorganizar os @Felementos.
  3. Atribuir a matriz em um formulário entre aspas duplas converte-a em uma sequência com elementos separados por espaço.
  4. -popção para Perl habilita a impressão automática de $_conteúdo, -ldeve adicionar o newline.

fonte
6

Usando o módulo perl Sort :: Naturally

dados 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

resultado

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
fonte
+1 para os mais elegantes, não assume prefixo específico demais para nomes de colunas, uma solução de passagem.
Arielf
4

Se você tiver o rsutilitário instalado, poderá fazer o seguinte:

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

Ou tudo em uma linha:

rs -c' ' -T | { stdbuf -i0 sed "1q"; sort -V ; } | rs -C' ' -T
  • O primeiro rstranspõe os dados de entrada (com campos com economia de espaço)
  • O grupo de comandos:
    • sedlê a primeira linha, sai e sai, deixando o restante do tubo rsintocado. stdbufé necessário para garantir que sedapenas seja lida até a primeira nova linha e não mais, desativando o buffer de entrada
    • sorts as linhas restantes
  • O segundo rs transpõe o fluxo resultante de volta ao seu formato original.

rsé instalado por padrão no MacOS. Nos sistemas Linux, pode ser necessário instalá-lo - por exemplo,

sudo apt install rs

Advertência: stdbufe sorta -Vopção s são específicas do GNU, portanto não funcionarão no MacOS não modificado.

Trauma Digital
fonte
0

Se você possui o GNU awk, pode tentar o seguinte:

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
fonte
0

Em 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
fonte
0

Não sei se você considerou isso uma boa resposta, mas ...

Por que você não usa um banco de dados para resolver esse problema? você pode importar seu conjunto de dados como uma tabela temporária e fazer uma

SELECT coluna1, coluna2, ... coluna-n FROM minha_temp_tabela

Você pode usar outros filtros ou transformações conforme necessário. Em seguida, você pode reformatar sua saída conforme necessário.

Todas essas tarefas podem ser programadas como um script bash e encadear saídas usando pipes.

Às vezes, tenho sido usado o comando "pv" para ver o progresso da saída entre os comandos.

Para importar o conjunto de dados, você pode programar um ETL usando o Pentaho Data Integration.

Gonzalo
fonte
0

Talvez isso também possa ajudá-lo.

  1. Primeiro, você pode usar a transposição do seu arquivo (um dos /programming/1729824/an-efficient-way-to-transpose-a-file-in-bash )
  2. Classifique a primeira coluna com o comando classificar.
  3. Transponha novamente.

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
fonte