Erstellen Sie Wortlisten nach Binärzahlen

12

Ich habe eine Matrix, die wie folgt aussieht:

Eingabe :

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  

Und ich möchte für jede Zeile die Liste der Buchstaben extrahieren, die dem Wert 1 entsprechen.

Ausgabe :

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

Ich habe versucht, die Überschrift zu teilen und die Wörter mit Zahlen abzugleichen, aber ich bin gescheitert.

fusion.slope
quelle

Antworten:

12

In 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
quelle
6
kann auch verwendenNR == 1 { split($0,values) }
Sundeep
Das überspringt die 2. Zeile. Ziehen Sie in Betracht, ein nextam Ende der ersten Zeile zu setzen, damit Sie für nachfolgende Zeilen keine gegenteilige Bedingung testen müssen.
Ed Morton
1
Der ursprüngliche Eingabetext enthielt eine zusätzliche Leerzeile, für die ich codiert habe. Es ist seither herausgeschnitten worden, also wechsle einfach NR > 2zu NR > 1.
Jeff Schaller
1
Vielen Dank für den "Golftipp", Sundeep! Ich glaube, ich bevorzuge die explizite 'for'-Schleife, da sie visuell / logisch mit der' for'-Schleife im Körper übereinstimmt.
Jeff Schaller
1
@ fusion.slope, übergeben Sie entweder den gesamten Code in einfachen Anführungszeichen an awkoder fügen Sie den Code in eine Datei ein und führen Sie ihn mitawk -f that.script.file input-file
Jeff Schaller aus.
6

Noch einer mit 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 zum Teilen der Eingabezeile auf Leerzeichen, verfügbar im @FArray
  • if($. == 1){ @h=@F } Speichern Sie die Kopfzeile in der ersten Zeile
  • @i = grep {$F[$_]==1} (0..$#F) Index speichern, wenn Eintrag ist 1
  • print join ",",@h[@i]Gibt nur den Index aus dem Header-Array ,als Trennzeichen aus
Sundeep
quelle
4

Zum Spaß noch eine zshVersion:

{
   read -A a  &&
   while read -A b; do
     echo ${(j<,>)${(s<>)${(j<>)a:^b}//(?0|1)}}
   done
} < file
  • ${a:^b} Zippt die beiden Arrays, sodass Sie A 0 B 0 C 0 D 0 E 1 F 0 G 0 H 0 I 1 erhalten
  • ${(j<>)...} Verbindet die Elemente mit nichts dazwischen, so dass es zu A0B0C0D0E1F0G0H0I1 wird
  • ${...//(?0|1)}Wir entfernen das ?0und 1davon, damit es zu EI wird:
  • ${(s<>)...} Teilen Sie nichts auf, um ein Array mit einem Element pro Buchstabe zu erhalten: EI
  • ${(j<,>)...}verbinde diese mit ,-> E, I.
Stéphane Chazelas
quelle
Das ist nur eine einfache Bash, oder?
fusion.slope
1
@ fusion.slope, Nein, das zshist eine andere Shell als bash(und viel leistungsfähiger und mit einem viel besseren Design, wenn Sie mich fragen). bashnur einen winzigen Bruchteil ausgeliehen hat zsh‚s - Funktion (wie {1..4}, <<<, **/*) nicht diejenigen , die hier erwähnt, die meisten bash‘ s Funktionen sind sonst entlehnt ksh.
Stéphane Chazelas
3

Eine andere awk Lösung:

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

Die Ausgabe:

E,I
D
D
A
A,C,G
A,D,H
A,E,F,G
RomanPerekhrest
quelle
2

Hier ist eine Lösung in 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";
}

Es funktioniert, indem die Kopfspalten in ein Array eingelesen werden und dann für jede Datenzeile der Spaltenname in ein Ausgabearray kopiert wird, wenn die übereinstimmende Datenspalte als wahr ausgewertet wird. Die Spaltennamen werden dann durch Kommas getrennt gedruckt.

dhag
quelle
2

Eine sedzum Spaß:

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

Mit GNU sedkönnen Sie die Lesbarkeit verbessern, indem Sie:

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'

Eine etwas kürzere Version unter der Annahme, dass in jeder Zeile immer die gleiche Anzahl von Ziffern steht:

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'

Wie oben, außer dass wir den übersetzten Teil und den Index-Teil austauschen, was einige Optimierungen zulässt.

Stéphane Chazelas
quelle
wenn du es erklären kannst wäre es gut für die gemeinschaft. Vielen Dank im Voraus
fusion.slope
1
@ fusion.slope, siehe edit.
Stéphane Chazelas
schön die schleife mit t1 befehl!
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
quelle
0

Reine bash Lösung:

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
quelle
3
Bitte erläutern Sie, wie das Problem dadurch behoben wird.
Scott
Dies ist eine Übung für den Leser. Unter der Annahme, dass grundlegende Bash-Kenntnisse LESS="+/^ {3}Array" man bashalle Informationen enthalten, die für Bash-Arrays erforderlich sind. Es steht Ihnen frei, die Antwort zu bearbeiten, um hilfreiche Erläuterungen hinzuzufügen.
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
quelle
3
Bitte erläutern Sie, was dies bewirkt und wie es funktioniert.
Scott
bitte auch die sprache.
fusion.slope