สร้างรายการคำตามหมายเลขไบนารี่


12

ฉันมีเมทริกซ์ที่มีลักษณะดังนี้:

อินพุต :

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  

และฉันต้องการที่จะแยกสำหรับแต่ละแถวรายการของตัวอักษรที่สอดคล้องกับค่า 1

ผลผลิต :

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

ฉันพยายามแยกส่วนหัวและจับคู่คำกับตัวเลข แต่ฉันล้มเหลว

คำตอบ:


12

ใน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 }

6
ยังสามารถใช้NR == 1 { split($0,values) }
Sundeep

นั่นคือการข้ามบรรทัดที่ 2 ลองวาง a nextที่ท้ายบรรทัดแรกเพื่อที่คุณจะได้ไม่ต้องทดสอบเงื่อนไขที่ตรงข้ามกับบรรทัดถัดไป
Ed Morton

1
ปรากฏข้อความต้นฉบับมีบรรทัดว่างพิเศษในนั้นซึ่งฉันเขียนให้ มันตั้งแต่รับการแก้ไขออกดังนั้นเพียงแค่เปลี่ยนไปNR > 2 NR > 1
Jeff Schaller

1
ขอบคุณสำหรับเคล็ดลับ "การเล่นกอล์ฟ" Sundeep! ฉันคิดว่าฉันชอบลูป 'for' อย่างชัดเจนเนื่องจากมีลูปสายตา / เหตุผลกับลูป 'for' ในร่างกาย
Jeff Schaller

1
@ fusion.slope สามารถส่งรหัสทั้งหมดในอาร์กิวเมนต์ที่ยกมาเดี่ยวawkหรือวางรหัสลงในไฟล์และเรียกใช้ด้วยawk -f that.script.file input-file
Jeff Schaller

6

อีกอันหนึ่งด้วย 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
  • -aตัวเลือกในการแบ่งบรรทัดอินพุตบนพื้นที่สีขาวที่มีอยู่ใน@Fอาร์เรย์
  • if($. == 1){ @h=@F } บันทึกส่วนหัวถ้าบรรทัดแรก
  • @i = grep {$F[$_]==1} (0..$#F) บันทึกดัชนีถ้ารายการคือ 1
  • print join ",",@h[@i]พิมพ์เฉพาะดัชนีเหล่านั้นจากอาร์เรย์ส่วนหัวโดยใช้,เป็นตัวคั่น

4

ยังเพื่อความสนุกของมันzshเวอร์ชัน:

{
   read -A a  &&
   while read -A b; do
     echo ${(j<,>)${(s<>)${(j<>)a:^b}//(?0|1)}}
   done
} < file
  • ${a:^b} รหัสไปรษณีย์ทั้งสองอาร์เรย์เพื่อให้คุณได้รับ A 0 B 0 C 0 D 0 E 1 F 0 G 0 H 0 ฉัน 1
  • ${(j<>)...} รวมองค์ประกอบเข้าด้วยกันโดยไม่มีอะไรเลยดังนั้นจึงกลายเป็น A0B0C0D0E1F0G0H0I1
  • ${...//(?0|1)}เราถอด?0และ1จากมันจึงกลายเป็น EI:
  • ${(s<>)...} แตกอะไรที่จะได้รับอาร์เรย์ขององค์ประกอบหนึ่งต่อจดหมาย: EI
  • ${(j<,>)...}เข้าร่วมกับ,-> E, I

นี่เป็นเพียงแค่ทุบตีง่ายใช่มั้ย
fusion.slope

1
@ fusion.slope ไม่นั่นคือzshเปลือกที่แตกต่างจากbash(และมีประสิทธิภาพมากกว่าและมีการออกแบบที่ดีกว่าถ้าคุณถามฉัน) bashได้ยืมเพียงส่วนเล็ก ๆ ของzsh'คุณสมบัติ s (เช่น{1..4}, <<<, **/*) ไม่ได้คนกล่าวถึงที่นี่ส่วนใหญ่เป็นคุณลักษณะที่ยืมมาจากอย่างอื่นbash ksh
Stéphane Chazelas

3

โซลูชัน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

ผลลัพธ์:

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

2

นี่คือทางออกใน 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";
}

มันทำงานได้โดยการอ่านคอลัมน์ส่วนหัวลงในอาร์เรย์จากนั้นสำหรับแต่ละแถวข้อมูลให้คัดลอกชื่อคอลัมน์ไปยังอาร์เรย์ผลลัพธ์หากคอลัมน์ข้อมูลที่ตรงกันประเมินว่าเป็นจริง จากนั้นชื่อคอลัมน์จะถูกพิมพ์โดยคั่นด้วยเครื่องหมายจุลภาค


2

sedหนึ่งสำหรับความสนุกสนานของมัน:

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

ด้วย GNU sedคุณสามารถทำให้ชัดเจนขึ้นด้วย:

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'

รุ่นที่สั้นกว่าเล็กน้อยสมมติว่ามีจำนวนหลักเท่ากันในแต่ละบรรทัด:

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'

เช่นเดียวกับข้างต้นยกเว้นว่าเราแลกเปลี่ยนส่วนแปลและดัชนีซึ่งอนุญาตให้มีการเพิ่มประสิทธิภาพบางอย่าง


หากคุณสามารถอธิบายได้จะเป็นผลดีต่อชุมชน ขอบคุณล่วงหน้า
fusion.slope

1
@ fusion.slope ดูการแก้ไข
Stéphane Chazelas

เป็นคนดีกับคำสั่ง 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

0

วิธีทุบตีบริสุทธิ์:

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

3
โปรดอธิบายว่าวิธีนี้แก้ปัญหาได้อย่างไร
สกอตต์

ที่เหลือเป็นแบบฝึกหัดสำหรับผู้อ่าน สมมติว่าความรู้พื้นฐานทุบตีLESS="+/^ {3}Array" man bashควรให้ข้อมูลทั้งหมดที่จำเป็นสำหรับการทุบตีอาร์เรย์ คุณมีอิสระที่จะแก้ไขคำตอบเพื่อเพิ่มการชี้แจงที่เป็นประโยชน์ใด ๆ
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();
            }
        }

3
โปรดอธิบายว่ามันทำงานอย่างไรและทำงานอย่างไร
สกอตต์

ได้โปรดใช้ภาษา
fusion.slope
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.