การสลับแถวและคอลัมน์


18

ฉันมีไฟล์ที่มีบรรทัดด้านล่าง

title1:A1
title2:A2
title3:A3
title4:A4
title5:A5

title1:B1
title2:B2
title3:B3
title4:B4
title5:B5

title1:C1
title2:C2
title3:C3
title4:C4
title5:C5

title1:D1
title2:D2
title3:D3
title4:D4
title5:D5

ฉันจะบรรลุสิ่งนี้ได้อย่างไร

title1    title2     title3    title4
A1         A2         A3         A4
B1         B2         B3         B4
C1         C2         C3         C4
D1         D2         D3         D4


โปรดได้โปรดได้โปรดอย่าใช้ awk คุณอาจม้วนโซลูชันที่กำหนดเองด้วย perl หรือ python หรือภาษาการเขียนโปรแกรมจริงหรือใช้ tr / cut ด้วยหลายรอบเพื่อรับสิ่งที่คุณต้องการ
Rudolf Olah

คำตอบ:



9

นอกกลิ้งโซลูชันที่กำหนดเองแถว transpose transposeด้วยคอลัมน์จากบรรทัดคำสั่งเครื่องมือเดียวที่ฉันเคยเห็นว่าสามารถทำเช่นนี้เป็นเครื่องมือที่เรียกว่ากระแนะกระแหน

การติดตั้ง

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

$ gcc transpose.c -o transpose

การใช้

มันสามารถจัดการกับไฟล์ข้อความได้อย่างง่ายดาย ตัวอย่างเช่น:

$ cat simple.txt 
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11

สามารถย้ายโดยใช้คำสั่งนี้:

$ transpose -t --fsep " " simple.txt 
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11

คำสั่งนี้คือtransposeการแปลง ( -t) และตัวคั่นฟิลด์ที่จะใช้เป็นช่องว่าง ( --fsep " ")

ตัวอย่างของคุณ

เนื่องจากข้อมูลตัวอย่างของคุณอยู่ในรูปแบบที่ซับซ้อนกว่าเล็กน้อยจึงจำเป็นต้องจัดการใน 2 ขั้นตอน ก่อนอื่นเราต้องแปลมันเป็นรูปแบบที่transposeสามารถจัดการได้

ใช้คำสั่งนี้จะทำให้ข้อมูลในรูปแบบที่เป็นมิตรในแนวนอน:

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - -
title1 A1   title1 B1   title1 C1   title1 D1   title2 A2
title2 B2   title2 C2   title2 D2   title3 A3   title3 B3
title3 C3   title3 D3   title4 A4   title4 B4   title4 C4
title4 D4   title5 A5   title5 B5   title5 C5   title5 D5

ตอนนี้เราเพียงแค่ต้องลบรายการรองของ title1, title2 และอื่น ๆ :

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - - | sed 's/\ttitle[0-9] / /g'
title1 A1 B1 C1 D1 A2
title2 B2 C2 D2 A3 B3
title3 C3 D3 A4 B4 C4
title4 D4 A5 B5 C5 D5

ขณะนี้อยู่ในรูปแบบที่transposeสามารถจัดการได้ คำสั่งต่อไปนี้จะทำการขนย้ายทั้งหมด:

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - - | sed 's/\ttitle[0-9] / /g' \
    | transpose -t --fsep " "
title1 title2 title3 title4
A1 B2 C3 D4
B1 C2 D3 A5
C1 D2 A4 B5
D1 A3 B4 C5
A2 B3 C4 D5

8

คุณสามารถใช้awkเพื่อประมวลผลข้อมูลpasteและจัดcolumnรูปแบบได้

ที่นี่ฉันคิดว่าtitle1เป็นเพียงตัวอย่างในโพสต์ของคุณและข้อมูลนั้นไม่มี:ยกเว้นเป็นตัวคั่นระหว่างส่วนหัว + ข้อมูล

nหมายถึงจำนวนคอลัมน์ที่จะพิมพ์ (ควรตรงกับขีดกลางpaste)

awk -F":" -v n=4 \
'BEGIN { x=1; c=0;} 
 ++c <= n && x == 1 {print $1; buf = buf $2 "\n";
     if(c == n) {x = 2; printf buf} next;}
 !/./{c=0;next}
 c <=n {printf "%s\n", $2}' datafile | \
 paste - - - - | \
 column -t -s "$(printf "\t")"

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

ใช้โดยทั่วไปเป็น:

$ ./trans -f data -c 4
title one  title two  title three  title four
A1         A2         A3           A4
B1         B2         B3           B4
C1         C2         C3           C4
D1         D2         D3           D4

หากส่วนหัวสั้นกว่าเสมอข้อมูลที่คุณสามารถบันทึกความกว้างส่วนหัวได้printfด้วย%-*sและข้ามcolumnทั้งหมดเข้าด้วยกัน

#!/bin/bash

trans()
{
    awk -F":" -v ncol="$1" '
    BEGIN {
        level = 1 # Run-level.
        col   = 1 # Current column.
        short = 0 # If requested to many columns.
    }
    # Save headers and data for row one.
    level == 1 {
        head[col] = $1
        data[col] = $2
        if (++col > ncol) { # We have number of requested columns.
            level = 2
        } else if ($0 == "") { # If request for more columns then available.
            level = 2
            ncol  = col - 2
            short = 1
        } else {
            next
        }
    }
    # Print headers and row one.
    level == 2 {
        for (i = 1; i <= ncol; ++i)
            printf("%s\t", head[i])
        print ""
        for (i = 1; i <= ncol; ++i)
            printf("%s\t", data[i])
        level = 3
        col = ncol + 1
        if (!short)
            next
    }
    # Empty line, new row.
    ! /./ { print ""; col = 1; next }
    # Next cell.
    col > ncol {next}
    {
        printf "%s%s", $2, (col <= ncol) ? "\t" : ""
        ++col
    }
    END {print ""}
    ' "$2"
}

declare -i ncol=4  # Columns defaults to four.
file=""            # Data file (or pipe).

while [[ -n "$1" ]]; do
    case "$1" in
    "-c") ncol="$2"; shift;;
    "-f") file="$2"; shift;;
    *) printf "Usage: %s [-c <columns>] [-f <file> | pipe]\n" \
        "$(basename $0)" >&2;
        exit;;
    esac
    shift
done

trans "$ncol" "$file" | column -t -s "$(printf "\t")"

1
คำตอบที่ดี! @ JoelDavis และฉันแฮ็คข้อมูลนี้อยู่ แต่คำตอบของคุณยอดเยี่ยมมาก!
slm

7

นี่คือวิธีที่รวดเร็วในการใส่ไฟล์ในรูปแบบที่คุณต้องการ:

$ grep -Ev "^$|title5" sample.txt | sed 's/title[0-9]://g' | paste - - - -
A1  A2  A3  A4
B1  B2  B3  B4
C1  C2  C3  C4
D1  D2  D3  D4

หากคุณต้องการส่วนหัวของคอลัมน์:

$ grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t'; \
    echo ""; \
    grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -
title1  title2  title3  title4  
A1      A2      A3      A4
B1      B2      B3      B4
C1      C2      C3      C4
D1      D2      D3      D4

การทำงานของคำสั่งที่ 2

การพิมพ์แบนเนอร์
grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t';
วางผลตอบแทนหลังจากที่แบนเนอร์มา
echo
การพิมพ์แถวของข้อมูล
grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -

คำสั่งวางเพียงทำให้งานของฉันเสร็จ ขอบคุณสำหรับคำตอบ ...
SK Venkat


3

อาจมีวิธีที่ชัดเจนกว่านี้ในการกำหนดสิ่งนี้ แต่ดูเหมือนว่าจะบรรลุผลทั่วไป:

[jadavis84@localhost ~]$ sed 's/^title[2-9]://g' file.txt | tr '\n' '\t' | sed 's/title1:/\n/g' ; echo

A1  A2  A3  A4  A5      
B1  B2  B3  B4  B5      
C1  C2  C3  C4  C5      
D1  D2  D3  D4  D5  
[jadavis84@localhost ~]$ 

การsedเรียกใช้หลายรายการไม่ถูกต้อง (และฉันค่อนข้างมั่นใจว่าสามารถทำการแปลบรรทัดใหม่ได้เช่นกัน) ดังนั้นจึงอาจไม่ใช่วิธีการส่งต่อที่ตรงไปตรงมาที่สุด นอกจากนี้ยังตัดส่วนหัวที่ต้องการ แต่คุณสามารถสร้างสิ่งเหล่านั้นด้วยตนเองเมื่อคุณจัดรูปแบบแถว / เขตข้อมูลอย่างถูกต้อง

คำตอบที่ดีกว่าอาจจะทำให้เกิดผลนั้นลดลงเพียงแค่ใช้sedหรือawkทำสิ่งนี้เพื่อที่คุณจะได้มีเพียงสิ่งเดียวที่เกิดขึ้นในแต่ละครั้ง แต่ฉันเหนื่อยดังนั้นนี่คือสิ่งที่ฉันสามารถรวบรวม


โจเอล - ฉันทำผิดพลาดเหมือนกันและเพิ่งสังเกตว่าเขาไม่ต้องการคอลัมน์ชื่อ 5 ในผลลัพธ์
slm

อ่าการวิ่งผ่าน awk ที่ผ่านมาน่าจะแก้ไขได้ แต่ดูเหมือนว่า Sukminder จะโพสต์โซลูชั่นที่สมบูรณ์
Bratchley

1

pasteน่าจะเป็นทางออกที่ดีที่สุดของคุณ คุณสามารถแยกส่วนที่เกี่ยวข้องกับcut, grepและawkชอบสิ่งนี้:

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile)

หากคอลัมน์ที่ 5 ควรถูกลบให้ผนวกawk 'NR%5'สิ่งนี้:

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5'

ตอนนี้คอลัมน์กับpaste:

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5' | paste - - - -

เอาท์พุท:

title1  title2  title3  title4
A1  A2  A3  A4
B1  B2  B3  B4
C1  C2  C3  C4
D1  D2  D3  D4

0

สำหรับส่วนของการโยกย้ายฉันมีปัญหาที่คล้ายกันเมื่อเร็ว ๆ นี้และใช้:

awk -v fmt='\t%4s'  '{ for(i=1;i<=NF;i++){ a[i]=a[i] sprintf(fmt, $i); } } END { for (i in a) print a[i]; }'

ปรับ fmt ตามต้องการ สำหรับแต่ละบรรทัดอินพุตมันเชื่อมต่อแต่ละฟิลด์เข้ากับองค์ประกอบอาร์เรย์ โปรดทราบว่าการรวมสตริง awk เป็นค่าปริยาย: มันเกิดขึ้นเมื่อคุณเขียนสองสิ่งโดยไม่มีโอเปอเรเตอร์ใด ๆ

ตัวอย่าง I / O:

i       mark    accep   igna    utaal   bta
-22     -10     -10     -20     -10     -10
-21     -10     -10     -20     -10     -10
-20     -10     -10     -20     -10     -10
-19     -10     0       -10     -10     -10
-18     0       0       -10     0       0
-12     0       0       -10     0       0
-11     0       0       -10     0       0
-10     0       0       -10     0       0

เอาท์พุท:

       i     -22     -21     -20     -19     -18     -12     -11     -10
    mark     -10     -10     -10     -10       0       0       0       0
    accep    -10     -10     -10       0       0       0       0       0
    igna     -20     -20     -20     -10     -10     -10     -10     -10
    utaal    -10     -10     -10     -10       0       0       0       0
     bta     -10     -10     -10     -10       0       0       0       0

-1

สิ่งที่ง่ายที่สุดที่คุณสามารถทำได้คือใช้cutในการตัดฟิลด์แล้วใช้trหากคุณกำลังย้ายแถวไปยังคอลัมน์โดยการแทนที่อักขระขึ้นบรรทัดใหม่ด้วยอักขระแท็บ: http://www.gnu.org/software/coreutils/manual/ coreutils.html # tr-ภาวนา

cat file.txt | cut -d':' | tr '\n' '\t'

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