2014-05-09 6 views
3

J'ai un onglet données limitées qui litTrouver les valeurs uniques dans une colonne et remplacer les valeurs uniques avec des numéros

1 0 0 1 1 Black Swan 
0 0 1 0 0 Golden Duck 
1 0 0 1 0 Brown Eagle 
0 0 1 0 1 Golden Duck 
1 0 0 1 0 Black Swan 
1 0 1 0 0 Golden Duck 
1 0 0 1 1 Sparrow 

La dernière colonne est une combinaison d'un ou plusieurs mots séparés par un espace. Je veux compter le nombre de valeurs uniques dans la dernière colonne et le remplacer par un nombre qui est unique à ce groupe. Je sais que je peux compter le et lister les numéros en utilisant

awk -F '\t' '{print $NF}' infile | sort | uniq | wc -l 

Mais comment puis-je remplacer par des nombres? Par exemple, remplacer tous les Black Swan par 1, remplacer tous Golden Duck par 2 etc. Je veux que le résultat soit:

1 0 0 1 1 1 
0 0 1 0 0 2 
1 0 0 1 0 3 
0 0 1 0 1 2 
1 0 0 1 0 1 
1 0 1 0 0 2 
1 0 0 1 1 4 

et je veux aussi générer la liste des numéros donnés à des valeurs spécifiques comme

Black Swan 1 
Golden Duck 2 
Brown Eagle 3 
Sparrow 4 

Répondre

5

Vous pouvez utiliser un tableau associé à incrémenter un compteur pour chaque nom différent:

awk ' 
    BEGIN { 
     FS = OFS = "\t" 
     i = 0 
    } 
    { 
     if (! names[$NF]) { 
      names[$NF] = ++i 
     } 
     $NF = names[$NF] 
     print $0 
    } 
    END { 
     for (name in names) { 
      printf "%s %d\n", name, names[name] 
     } 
    } 
' infile 

Il donne:

1  0  0  1  1  1 
0  0  1  0  0  2 
1  0  0  1  0  3 
0  0  1  0  1  2 
1  0  0  1  0  1 
1  0  1  0  0  2 
1  0  0  1  1  4 
Golden Duck 2 
Brown Eagle 3 
Sparrow 4 
Black Swan 1 
+0

+1 bien fait! –

+0

D'accord. Pas besoin d'initialiser 'i' évidemment, et le printf à la fin pourrait être juste un print mais nbd. –

1

Ce que vous voulez faire est de créer un ensemble de données uniques. Un set est un dictionnaire, ou une table de hachage, avec tous les éléments uniques. Après avoir créé votre ensemble, vous pouvez le parcourir et remplacer la chaîne par la valeur appropriée.

Voici un autre lien pour les jeux pour vous aider:

http://world.std.com/~swmcd/steven/perl/pm/set.html

4

je commencé à écrire ce donc je finirai:

awk ' 
BEGIN {FS = OFS = "\t"} 
{ 
    last[$NF] = (last[$NF] ? last[$NF] : ++cnt) 
    $NF = last[$NF] 
    line[NR] = $0 
} 
END { 
    for (nr=1; nr<=NR; nr++) 
     print line[nr] 
    for (name in last) 
     print name, last[name] 
}' file 
1  0  0  1  1  1 
0  0  1  0  0  2 
1  0  0  1  0  3 
0  0  1  0  1  2 
1  0  0  1  0  1 
1  0  1  0  0  2 
1  0  0  1  1  4 
Brown Eagle  3 
Black Swan  1 
Sparrow   4 
Golden Duck  2 

Mise à jour:

ici est un perl remplaçant:

perl -F'\t' -lane ' 
    $h{$F[-1]} = ++$c unless exists $h{$F[-1]}; 
    $F[-1] = $h{$F[-1]}; 
    print join "\t", @F }{ print "$_ $h{$_}" for keys %h 
' file 
1  0  0  1  1  1 
0  0  1  0  0  2 
1  0  0  1  0  3 
0  0  1  0  1  2 
1  0  0  1  0  1 
1  0  1  0  0  2 
1  0  0  1  1  4 
Golden Duck 2 
Brown Eagle 3 
Black Swan 1 
Sparrow 4 

Voici une autre mise à jour sur la base mpapec's excellente commentaire:

perl -F'\t' -lane ' 
    $F[-1] = $h{$F[-1]} ||= ++$c; 
    print join "\t", @F }{ print "$_ $h{$_}" for keys %h 
' file 
+1

+1, juste '$ h {$ F [-1]} = $ h {$ F [-1]}? $ h {$ F [-1]}: ++ $ c; 'peut être écrit comme $ h {$ F [-1]} = $ h {$ F [-1]} || ++ $ c; 'ou' $ h {$ F [-1]} || = ++ $ c; 'en abrégé, et' splice @F, -1, 1, $ h {$ F [-1] }; 'comme' $ F [-1] = $ h {$ F [-1]} '. Pour des raisons de ** golfing seulement ** qui peuvent être encore raccourcies '$ F [-1] = $ h {$ F [-1]} || = ++ $ c;' –

+0

Merci @mpapec, ça a l'air vraiment génial. Mettra à jour la réponse. –

Questions connexes