Créer des listes de mots selon des nombres binaires

12

J'ai une matrice qui ressemble à ceci:

Entrée :

A   B   C   D   E   F   G   H   I 
0   0   0   0   1   0   0   0   1
0   0   0   1   0   0   0   0   0  
0   0   0   1   0   0   0   0   0  
1   0   0   0   0   0   0   0   0  
1   0   1   0   0   0   1   0   0  
1   0   0   1   0   0   0   1   0  
1   0   0   0   1   1   1   0   0  

Et je voudrais extraire pour chaque ligne la liste des lettres correspondant à la valeur 1.

Sortie :

E,I 
D
D
A
A,C,G  
A,D,H  
A,E,F,G  

J'ai essayé de diviser l'en-tête et de faire correspondre les mots avec les chiffres, mais j'ai échoué.

fusion.slope
la source

Réponses:

12

Dans awk:

NR == 1 { for(column=1; column <= NF; column++) values[column]=$column; }
NR > 1 { output=""
        for(column=1; column <= NF; column++)
                if($column) output=output ? output "," values[column] : values[column]
        print output }
Jeff Schaller
la source
6
peut également utiliserNR == 1 { split($0,values) }
Sundeep
C'est sauter la 2ème ligne. Pensez à mettre un nextà la fin de la première ligne afin de ne pas avoir à tester une condition opposée pour les lignes suivantes.
Ed Morton
1
Apparaît que le texte d'entrée original avait une ligne vierge supplémentaire, que j'ai codée. Il a depuis été édité, alors passez NR > 2à NR > 1.
Jeff Schaller
1
Merci pour l'astuce "golf", Sundeep! Je pense que je préfère la boucle explicite «for» car elle s'aligne visuellement / logiquement avec la boucle «for» dans le corps.
Jeff Schaller
1
@ fusion.slope, passez le code entier dans un argument entre guillemets awk, ou collez le code dans un fichier et exécutez-le avecawk -f that.script.file input-file
Jeff Schaller
6

Un autre avec perl

$ perl -lane 'if($. == 1){ @h=@F }
              else{@i = grep {$F[$_]==1} (0..$#F); print join ",",@h[@i]}
             ' ip.txt
E,I
D
D
A
A,C,G
A,D,H
A,E,F,G
  • -aoption pour diviser la ligne d'entrée sur les espaces blancs, disponible en @Ftableau
  • if($. == 1){ @h=@F } enregistrer l'en-tête si la première ligne
  • @i = grep {$F[$_]==1} (0..$#F) enregistrer l'index si l'entrée est 1
  • print join ",",@h[@i]imprimer uniquement les index du tableau d'en-têtes en utilisant ,comme séparateur
Sundeep
la source
4

Toujours pour le plaisir, une zshversion:

{
   read -A a  &&
   while read -A b; do
     echo ${(j<,>)${(s<>)${(j<>)a:^b}//(?0|1)}}
   done
} < file
  • ${a:^b} zippe les deux tableaux, vous obtenez donc A 0 B 0 C 0 D 0 E 1 F 0 G 0 H 0 I 1
  • ${(j<>)...} joint les éléments sans rien entre eux, il devient donc A0B0C0D0E1F0G0H0I1
  • ${...//(?0|1)}nous en retirons le ?0et 1il devient donc EI:
  • ${(s<>)...} diviser sur rien pour obtenir un tableau d'un élément par lettre: EI
  • ${(j<,>)...}rejoindre ceux avec ,-> E, I.
Stéphane Chazelas
la source
c'est juste un simple bash non?
fusion.slope
1
@ fusion.slope, non, c'est zshun shell différent de bash(et beaucoup plus puissant, et avec un bien meilleur design si vous me le demandez). basha emprunté une infime fraction de zsh« la fonction de (comme {1..4}, <<<, **/*) pas ceux mentionnés ici, la plupart des bash» caractéristiques de sont par ailleurs empruntés ksh.
Stéphane Chazelas
3

Une autre solution awk :

awk 'NR==1{ split($0,a); next }   # capture and print `header` fields
     { for (i=1;i<=NF;i++)         # iterating through value fields `[0 1 ...]`
           if ($i) { printf "%s",(f?","a[i]:a[i]); f=1 } 
       f=0; print "" 
     }' file

Le résultat:

E,I
D
D
A
A,C,G
A,D,H
A,E,F,G
RomanPerekhrest
la source
2

Voici une solution en Perl:

use strict;

my @header = split /\s+/, <>;
<>; ## Skip blank line
while (<>) {
    my @flags = split /\s+/;
    my @letters = ();
    for my $i (0 .. scalar @flags - 1) {
        push @letters, $header[$i] if $flags[$i];
    }

    print join(',', @letters), "\n";
}

Il fonctionne en lisant les colonnes d'en-tête dans un tableau puis, pour chaque ligne de données, en copiant le nom de la colonne dans un tableau de sortie si la colonne de données correspondante est évaluée comme vraie. Les noms de colonne sont ensuite imprimés séparés par des virgules.

dhag
la source
2

Un sedpour le plaisir:

sed '
  s/ //g
  1{h;d;}
  G;s/^/\
/
  :1
    s/\n0\(.*\n\)./\
\1/
    s/\n1\(.*\n\)\(.\)/\2\
\1/
  t1
  s/\n.*//
  s/./&,/g;s/,$//'

Avec GNU sed, vous pouvez le rendre un peu plus lisible avec:

sed -E '
  s/ //g # strip the spaces

  1{h;d} # hold the first line

  G;s/^/\n/ # append the held line and prepend an empty line so the
            # pattern space becomes <NL>010101010<NL>ABCDEFGHI we will
            # build the translated version in the part before the first NL
            # eating one character at a time off the start of the
            # 010101010 and ABCDEFGHI parts in a loop:
  :1
    s/\n0(.*\n)./\n\1/     # ...<NL>0...<NL>CDEFGHI becomes
                           # ...<NL>...<NL>DEFGHI (0 gone along with C)

    s/\n1(.*\n)(.)/\2\n\1/ # ...<NL>1...<NL>CDEFGHI becomes
                           # ...C<NL>...<NL>DEFGHI (1 gone but C moved to 
                           #                        the translated part)
  t1 # loop as long as any of those s commands succeed

  s/\n.*// # in the end we have "ADG<NL><NL>", strip those NLs

  s/./,&/2g # insert a , before the 2nd and following characters'

Une version légèrement plus courte, en supposant qu'il y a toujours le même nombre de chiffres sur chaque ligne:

sed -E '
  s/ //g
  1{H;d}
  G
  :1
    s/^0(.*\n)./\1/
    s/^1(.*\n)(.*\n)(.)/\1\3\2/
  t1
  s/\n//g
  s/./,&/2g'

Comme ci-dessus, sauf que nous échangeons les parties traduites et indexées, ce qui permet certaines optimisations.

Stéphane Chazelas
la source
si vous pouvez expliquer, ce serait bon pour la communauté. Merci d'avance
fusion.slope
1
@ fusion.slope, voir modifier.
Stéphane Chazelas
belle boucle avec la commande t1!
fusion.slope
1

python3

python3 -c '
import sys
header = next(sys.stdin).rstrip().split()
for line in sys.stdin:
  print(*(h*int(f) for (h, f) in zip(header, line.rstrip().split()) if int(f)), sep=",")

  ' <file
E,I
D
D
A
A,C,G
A,D,H
A,E,F,G
iruvar
la source
0

Solution pure bash:

read -a h
while read -a r
do (
    for i in ${!r[@]}
    do 
        (( r[i] == 1 )) && y[i]=${h[i]}
    done
    IFS=,
    echo "${y[*]}")
done
David Ongaro
la source
3
Veuillez expliquer comment cela résout le problème.
Scott
Cela reste un exercice pour le lecteur. En supposant que les connaissances bash bash LESS="+/^ {3}Array" man bashdevraient fournir toutes les informations nécessaires pour les tableaux bash. Vous êtes libre de modifier la réponse pour ajouter toute clarification utile.
David Ongaro
-1
 void Main(string[] args)
        {
            int[,] numbers = new int[,]
            {
            {0, 0, 0, 0, 1, 0, 0, 0, 1},
            {0, 0, 0, 1, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 1, 0, 0, 0, 1, 0, 0},
            {1, 0, 0, 1, 0, 0, 0, 1, 0},
            {1, 0, 0, 0, 1, 1, 1, 0, 0}
            };
            string letters = "ABCDEFGHI";
            for (int row = 0; row < 7; row++)
            {
                for (int col = 0; col < 9; col++)
                {
                    if (numbers[row, col] == 1)
                        Console.Write(letters[col]);
                }
                Console.WriteLine();
            }
        }
George Leake
la source
3
Veuillez expliquer ce que cela fait et comment cela fonctionne.
Scott
aussi la langue, s'il vous plaît.
fusion.slope