Substitua cada valor único em todas as colunas por um identificador exclusivo

3

Eu tenho um arquivo com 250k linhas e 10 colunas como:

img1 aa bb cc ...
img2 aa yy dd ...
img3 uu bb ee ...
img4 NA bb tt ...

Eu quero um script que converta este arquivo para:

img1 1 1 1 ...
img2 1 2 2 ...
img3 2 1 3 ...
img4 0 1 4 ...

Um valor único de cada coluna após o primeiro deve ser substituído por um identificador exclusivo, começando em 0, onde 0 é reservado para a string "NA".

Além disso, para cada coluna, quero gerar um arquivo que contenha o mapeamento. Por exemplo, o arquivo da segunda coluna deve ser:

NA 0
aa 1
uu 2

Alguém pode sugerir uma solução elegante para isso? Qualquer ajuda seria muito apreciada.

    
por Haseeb Shah 16.09.2017 / 23:19

3 respostas

2

Aqui está uma abordagem muito simples. Funciona bem para mim, usando o gawk 3.1.7.

#!/usr/bin/awk -f
{
    for(x=2;x<=NF;x++) {
        if(x$x in a) {
            $x=a[x$x]
        } else {
            if($x=="NA") {
                print $x,0 > "column"x
                a[x$x]=0
                $x="0"
            } else {
                m[x]++
                print $x,m[x] > "column"x
                a[x$x]=m[x]
                $x=m[x]
            }
        }
    }
    print $0 > "results"
}
    
por 17.09.2017 / 00:23
2
$ awk 'BEGIN { id["NA"] = ++n } { for (i=2; i<=NF; ++i) { id[$i] || id[$i] = ++n; $i = id[$i] - 1 } } { print } END { for (i in id) { print i, id[i] - 1 >"map" } }' file
img1 1 2 3
img2 1 4 5
img3 6 2 7
img4 0 2 8

Isso atribui uma ID exclusiva a cada valor em todas as colunas, exceto a primeira (a coluna img ). Optei por tornar os IDs globalmente exclusivos, em vez de exclusivos para uma coluna, pois isso reduziria o número de arquivos de mapeamento necessários a serem produzidos.

O script foi desvendado:

BEGIN   { id["NA"] = ++n }

        {
            for (i=2; i<=NF; ++i) {
                id[$i] || id[$i] = ++n;
                $i = id[$i] - 1
            }
        }

        { print }

END     {
            for (i in id) {
                print i, id[i] - 1 >"map"
            }
        }

Começa atribuindo a string NA o ID 1 (os IDs são sempre decrementados por 1 antes da saída) e também atualiza o contador n para 1. n será sempre o ID atribuído ao anterior string.

Para cada linha de entrada, nós iteramos pelos campos. Se nenhum ID foi atribuído à string no campo atual, nós atribuímos um e modificamos o campo no lugar.

A linha é impressa com seus campos modificados.

No final, todas as strings e seus IDs correspondentes são armazenados no arquivo chamado map .

Para a entrada dada, este arquivo pode ser parecido com

bb 2
ee 7
cc 3
NA 0
tt 8
dd 5
yy 4
aa 1
uu 6
    
por 17.09.2017 / 09:58
1

GNU awk solution (o suporte a matriz bidimensional é necessário).

awk '{
    printf "%s ", $1;

    for(i = 2; i <= NF; i++) {
        filename = "column_" i - 1 "_mapping"

        if(NR == 1) {
            arr[i]["NA"] = 0;
            print "NA 0" > filename;    
        }

        if(! ($i in arr[i]) ) {
            cnt[i]++;
            arr[i][$i] = cnt[i];
            print  $i, cnt[i] > filename;   
        }

        printf "%d ", arr[i][$i];
    }

    print "";
}' input.txt

Entrada

img1 aa bb cc
img2 aa yy dd
img3 uu bb ee
img4 NA bb tt

Resultado

img1 1 1 1 
img2 1 2 2 
img3 2 1 3 
img4 0 1 4 

Conteúdo dos arquivos de mapeamento

tail -n +1 -- *_mapping

==> column_1_mapping <==
NA 0
aa 1
uu 2

==> column_2_mapping <==
NA 0
bb 1
yy 2

==> column_3_mapping <==
NA 0
cc 1
dd 2
ee 3
tt 4
    
por 17.09.2017 / 22:13