Spalten in einer Datei basierend auf Spaltenüberschriften zusammenführen

Spalten in einer Datei basierend auf Spaltenüberschriften zusammenführen

Ich möchte verschiedene Spalten in meiner Datei zusammenführen, die dieselbe Spaltenüberschrift haben. Die Datei sieht so aus und kann durch Tabulatoren oder etwas anderes getrennt sein:

AaBbN    CcDdEeN    FfN     AaBbN    FfN
1        5          4   
3        1          2
2        NA         1
                            1        3
                            3        2
                            NA       4

Es stehen also Zahlen oder die Zeichenfolge "NA" in den Feldern. Das Ergebnis sähe dann so aus:

AaBbN    CcDdEeN    FfN
1        5          4
3        1          2
2        NA         1
1                   3
3                   2
NA                  4

Es gibt viele Spalten, die nicht sortiert sind, daher müssten die Titelüberschriften automatisch gelesen werden, anstatt jede einzelne manuell anzugeben. Es gibt auch viele leere Felder. Ich habe mir die Befehle pasteund joinangesehen, um die Aufgabe zu erledigen. Besonders joinscheint das zu tun, was ich brauche, außer dass es mit separaten Dateien funktioniert, während sich meine Spalten in derselben Datei befinden.

Also habe ich versucht, die Spalten in separate Dateien aufzuteilen und sie dann mit zu kombinieren join. Ich habe einen awkBefehl verwendet, den ich von hier abgeleitet habe:

https://www.unix.com/unix-für-dummies-fragen-und-antworten/204303-aufteilen-von-textdateien-in-mehrere-dateien-spalten.html

awk ' { for( i = 1; i <= NF; i++ ) printf( "%s\n", $(i) ) >i ".txt"; } ' file.txt

Dadurch erhalte ich separate Spalten, aber hier stieß ich auf das erste Problem. Alle Spalten mit Leerzeichen zwischen Kopfzeile und Daten wurden nicht korrekt verarbeitet. Stattdessen war in diesen Dateien nur die Spaltenüberschrift vorhanden.

Mein zweites Problem betrifft join: Wenn ich versuche, die Dateien wieder zusammenzuführen, erhalte ich Fehler, weil die Eingabe nicht sortiert ist, was natürlich unmöglich ist. Jede Sortierung würde die Beziehung zerstören, die ich suche.

Hier bin ich also in einer Sackgasse. Gibt es eine bequemere Möglichkeit, die Spalten direkt in einer Datei zusammenzuführen?


Bearbeiten:

Die Lösung von AdminBees kommt dem Problem am nächsten, aber das Ergebnis ist nicht ganz richtig. Hier ist das Ergebnis des awk-Skripts, das auf das obige Beispiel angewendet wurde. Ich habe sichergestellt, dass alle Einträge mit Tabulatoren getrennt sind sed -i "s/[[:space:]]/ /g"(Tabulator wird mit STRG+V und TAB eingefügt).

AaBbN   CcDdEeN FfN     FfN
1   5   4   

3   1   2

2   NA  1

            1
            3
            NA

Antwort1

Wenn Ihre Eingabe durch Tabulatoren getrennt ist:

awk -F"\t" '
NR == 1 {for (i=1; i<=NF; i++)  COL[i] = $i
        }
        {for (i=1; i<=NF; i++) OUT[NR, COL[i]] = $i
        }
END     {for (n=1; n<=NR; n++)  {split ("", DUP)
                                 for (i=1; i<=NF; i++)  if (!DUP[COL[i]]++) printf "%s" FS, OUT[n,COL[i]]
                                 printf RS
                                }
        }
' file
A   B   C   
1   5   4   
3   1   2   
2   2   1   
1       3   
3       2   
1       4   

Es speichert Spaltenüberschriften zur späteren Verwendung als Teilindizes und sammelt dann für jede Zeile Werte in einem Array, das nach Zeilennummer und Teilindex der Überschrift indiziert ist. In dem ENDAbschnitt druckt es dieses Array in der ursprünglichen Reihenfolge und achtet dabei auf doppelte Spaltenüberschriften.

Bei komplexeren Dateistrukturen kann die Duplikatsbehandlung einen größeren Aufwand bedeuten.

Antwort2

für die tab-getrennte Eingabe.

Lesen Sie die Kopfzeile und die entsprechenden Spaltennummern in ein Array, in dem sie in der Eingabedatei erschienen sind. Teilen Sie dann die Eingabedatei für jede Spalte in dieselbe Datei mit dem Namen headerName.txt auf, die denselben headerName hat. Fügen Sie sie anschließend zusammen ein undcolumnBefehl zum Verschönern der Ausgabe.

awk -F'\t' '
    ## find all the column number(s) when same header found and store in `h` array
    ## key is the column number and value is header name. for an example:
    ## for the header value 'A', keys will be columns 1 &4
    NR==1{ while (++i<=NF) h[i]=$i; next; }

         { for (i=1; i<=NF; i++) {

    ## save the field content to a file which its key column matches with the column 
    ## number of the current field. for an example:
    ## for the first field in column 1; the column number is 1, and so 1 is the key  
    ## column for header value A, so this will be written to "A.txt" filename
    ## only if it was not empty.
               if ($i!=""){ print $i> h[i]".txt" };
         }; }

    ## at the end paste those all files and beautify output with `column` command.
    ## number of .txt files above is limit to the number of uniq headers in your input. 
END{ system("paste *.txt |column \011 -tn") }' infile

kommentarfreier Befehl:

awk -F'\t' '
    NR==1{ while (++i<=NF) h[i]=$i; next; }
         { for (i=1; i<=NF; i++) {
               if ($i!=""){ print $i> h[i]".txt" };
         }; }
END{ system("paste *.txt |column \011 -tn") }' infile

Antwort3

Ein etwas anderer Ansatz, bei dem nicht die gesamte Datei „gepuffert“ werden muss:

AWK-Skript colmerge.awk:

FNR==1{
    for (i=1; i<=NF; i++)
    {
    hdr[i]=$i;
    if (map[$i]==0) {map[$i]=i; uniq_hdr[++u]=$i; printf("%s",$i);}
    if (i==NF) printf("%s",ORS); else printf("%s",OFS);
    }
}

FNR>1{
    delete linemap;
    for (i=1; i<=NF; i++) if ($i!="") linemap[hdr[i]]=$i;
    for (i=1; i<=u; i++)
    {
    printf("%s",linemap[uniq_hdr[i]]);
    if (i==u) printf("%s",ORS); else printf("%s",OFS);
    }
}

Benutzen als

awk -F'\t' -v OFS='\t' -f colmerge.awk file

Dadurch werden alle Header erfasst und die „eindeutigen“ Header und ihr erstes Vorkommen in Zeile 1 ermittelt. Außerdem wird für jede nachfolgende Zeile eine Zuordnung zwischen Headern und nicht leeren Werten erstellt, die dann in der Reihenfolge der „eindeutigen“ Header ausgedruckt wird, die bei der Verarbeitung der ersten Zeile ermittelt wurden.

Dies funktioniert allerdings nur, wenn Ihre Eingabedatei tabulatorgetrennt ist, da nur so „leere“ Felder zuverlässig erkannt werden können.

Beachten Sie auch, dass die deleteAnweisung für das gesamte Array linemapmöglicherweise nicht von allen Implementierungen unterstützt wird (sollte jedoch auf , und awkfunktionieren ).gawkmawknawk

verwandte Informationen