Zeilen und Spalten transponieren


18

Ich habe eine Datei mit den Zeilen wie unten.

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

Wie kann ich das erreichen?

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


Bitte, bitte, bitte, benutze nicht awk. Du könntest genauso gut eine benutzerdefinierte Lösung mit Perl oder Python oder einer echten Programmiersprache rollen oder tr / cut mit mehreren Durchgängen verwenden, um das zu bekommen, was du willst
Rudolf Olah

Antworten:



9

Abgesehen von einer benutzerdefinierten Lösung, mit der Zeilen und Spalten über eine Befehlszeile transponiert werden können, ist das einzige Tool, das ich jemals gesehen habe, ein ironisch genanntes Tool transpose.

Installation

Leider ist es in keinem Repo, daher müssen Sie es herunterladen und kompilieren. Dies ist ziemlich einfach, da es keine zusätzlichen Bibliotheken gibt, von denen es abhängig ist. Dies kann folgendermaßen erreicht werden:

$ gcc transpose.c -o transpose

Verwendung

Es kann problemlos mit einfachen Textdateien umgehen. Beispielsweise:

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

Kann mit diesem Befehl transponiert werden:

$ 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

Dieser Befehl ist transposetransponieren ( -t) und das zu verwendende Feldtrennzeichen ist ein Leerzeichen ( --fsep " ").

Dein Beispiel

Da Ihre Beispieldaten ein etwas komplexeres Format haben, müssen sie in zwei Phasen bearbeitet werden. Zuerst müssen wir es in ein Format übersetzen, transposedas damit umgehen kann.

Wenn Sie diesen Befehl ausführen, werden die Daten in einem horizontaleren Format angezeigt:

$ 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

Jetzt müssen wir nur die sekundären Vorkommen von title1, title2 usw. entfernen:

$ 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

Es ist jetzt in einem Format, transposedas damit umgehen kann. Der folgende Befehl übernimmt die gesamte Umsetzung:

$ 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

Sie awkkönnen dann die Daten verarbeiten pasteund columnformatieren.

Hier nehme ich an, title1ist nur ein Beispiel in Ihrem Beitrag, und diese Daten enthalten :nur als Trennzeichen zwischen Header + Daten.

nGibt an, wie viele Spalten gedruckt werden sollen (Striche in 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")"

Wenn Sie es flexibler und wartungsfreundlicher gestalten möchten, können Sie es als Skript schreiben. Hier ist ein Beispiel für die Verwendung von Bash-Wrapper für awkund die Weiterleitung an column. Auf diese Weise können Sie auch weitere Datenprüfungen durchführen, z. B. um sicherzustellen, dass die Überschriften in allen Zeilen korrekt sind.

Wird normalerweise verwendet als:

$ ./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

Wenn die Überschriften immer kürzer sind als die Daten, können Sie auch die Breite der Überschriften speichern und dann printfmit %-*sund columnalles zusammen überspringen .

#!/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
Gute Antwort! @ JoelDavis und ich haben das gehackt, aber Ihre Antwort ist großartig!
slm

7

Hier ist eine schnelle Möglichkeit, die Datei in das gewünschte Format zu bringen:

$ 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

Wenn Sie die Spaltenüberschriften möchten:

$ 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

So funktioniert der 2. Befehl

Drucken Sie das Banner
grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t';
Setzen Sie eine Rückkehr nach dem Banner in
echo
Drucken der Datenzeilen
grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -

Der Befehl Einfügen hat meine Arbeit einfach erledigt. Danke für die Antwort ...
SK Venkat


3

Es gibt wahrscheinlich eine prägnantere Art, dies zu formulieren, aber dies scheint den allgemeinen Effekt zu erzielen:

[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 ~]$ 

Mehrfachaufrufe sedfühlen sich nicht richtig an (und ich bin mir ziemlich sicher, dass sed auch die neue Zeilenübersetzung ausführen kann), daher ist dies wahrscheinlich nicht der direkteste Weg, dies zu tun. Dadurch werden auch die potenziellen Überschriften entfernt. Sie können diese jedoch manuell generieren, sobald Sie die Zeilen / Felder ordnungsgemäß formatiert haben.

Eine bessere Antwort wäre wahrscheinlich destillieren , dass Wirkung auf nur mit sedoder awkdies zu tun , so dass Sie nur eine Sache auf einmal zu gehen. Aber ich bin müde, also konnte ich das zusammenstellen.


Joel - Ich habe den gleichen Fehler gemacht und nur bemerkt, dass er die Spalte title5 nicht in der Ausgabe haben möchte.
slm

Ah, gut durch awk zu laufen, sollte das beheben. Aber es sieht so aus, als hätte Sukminder eine Komplettlösung veröffentlicht.
Bratchley

1

pasteist wahrscheinlich die beste Wahl. Sie können die entsprechenden Bits mit extrahieren cut, grepund awkso:

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

Wenn die 5. Spalte entfernt werden soll, fügen Sie Folgendes hinzu awk 'NR%5':

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

Jetzt kolumnieren mit paste:

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

Ausgabe:

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

0

Nur für den transponierten Teil hatte ich kürzlich ein ähnliches Problem und verwendete:

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]; }'

Stellen Sie den fmt nach Bedarf ein. Für jede Eingabezeile werden die einzelnen Felder zu einem Array-Element verkettet. Beachten Sie, dass die Verkettung von awk-Zeichenfolgen implizit ist: Dies geschieht, wenn Sie zwei Dinge ohne Operator schreiben.

Beispiel 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

Ausgabe:

       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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.