Zusammenführen mehrerer Spalten aus unterschiedlichen Dateien mit awk

Zusammenführen mehrerer Spalten aus unterschiedlichen Dateien mit awk

Ich habe 4 TSV-Dateien (durch Tabulatoren getrennt), die so aussehen:

Datei_1:

abc 1
def 2
ghi 3

Datei_2:

abc 2
ghi 3

Datei_3:

def 1
ghi 2
jkl 4

Datei_4:

ghi 3
jkl 4

Ich möchte diese Dateien zusammenfügen, um eine TSV-Datei wie diese zu erhalten:

dataset file_1 file_2 file_3 file_4
abc     1      2             
def     2      4            
ghi     3      3      2      3
jkl                   4      4

Ich habe versucht,awk

$ awk '
    BEGIN{OFS=FS="\t"} 
    FNR==1{f = f "\t" FILENAME} 
    NR==FNR{a[$1] = $2} 
    NR!=FNR{a[$1] = a[$1] "\t" $2} 
    END{printf "dataset%s\n", f; for(i in a) print i, a[i]}
  ' file_{1..4}

Dieser Befehl funktioniert, aber ich habe einen verschobenen Wert erhalten. Angenommen, die erste und zweite Spalte haben einen leeren Wert und die dritte und vierte Spalte haben die Werte 4 und 4. Die Ausgabe, die ich von diesem Befehl erhalte, ist, dass die erste und zweite Spalte den Wert 4 haben, aber die dritte und vierte Spalte einen leeren Wert. Ich versuche also, meine TSV-Datei separat mit awkdem von mir erwähnten zu verbinden. Zuerst nur für file_1und file_2um zu erhalten output_1, dann verbinden file_3und file_4um zu erhalten output_2. Danach verwende ich $ join output_1 output_2zum Zusammenführen von Ausgabe_1 und Ausgabe_2, aber ich erhalte nur Werte, die in 4 Dateien vorhanden sind. Ich habe Daten verloren, die nur in einer Datei vorhanden sind.

Ich wäre Ihnen sehr dankbar, wenn Sie mir einen Rat geben könnten.

Danke

Antwort1

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ datasets[$1]; fnames[FILENAME]; vals[$1,FILENAME] = $2 }
END {
    printf "%s", "dataset"
    for (fname in fnames) {
        printf "%s%s", OFS, fname
    }
    print ""
    for (dataset in datasets) {
        printf "%s", dataset
        for (fname in fnames) {
            printf "%s%s", OFS, vals[dataset,fname]
        }
        print ""
    }
}

$ tail -n +1 file?
==> file1 <==
a       1
b       2
c       3

==> file2 <==
a       2
c       3

$ awk -f tst.awk file1 file2
dataset file1   file2
a       1       2
b       2
c       3       3

Fügen Sie der Liste so viele Dateien hinzu, wie Sie möchten.

Antwort2

Mit join (GNU coreutils) 8.30und "Prozesssubstitution" können Sie versuchen,

join -a1 -a2 -t"    " -oauto  -e " " <(join -a1 -a2 -t" " -oauto  -e "" file[12]) <(join -a1 -a2 -t"    " -oauto  -e " " file[34])
abc 1   2        
def 2       1    
ghi 3   3   2   3
jkl         4   4

Die -tOptionen haben einen <TAB>Zeichenwert.

Antwort3

Tipp für jetzt: Code für später, falls Sie ihn brauchen.

Ich würde drei Arrays behalten, da Sie alle Dateien lesen.

(a) Für jede neue Datei eine Hash-Liste mit Dateinamen.
(b) Für jeden neuen Datensatz eine Hash-Liste mit $1.
(c) Für jede Zeile eine Hash-Liste mit Werten

FNR == 1 { ++htFile[FILENAME]; }
! ($1 in htSet) { ++htSet[$1]; }
{ htVal [FILENAME, $1] = $2; }

Iterieren Sie in einer End-Funktion über htFile und htSet.

function Table (r, c, buf) {
    buf = "dataset";
    for (c in htFile)
        buf = sprint ("%s\t%s", buf, htFile[c]);
    print buf;
    for (r in htSet) {
        buf = "";
        for (c in htFile)
            buf = sprint ("%s\t%s", buf, htVal[c, r]);
        print substr (buf, 2);
    }
}
END { Table( ); }

Dadurch wird die Reihenfolge der Dateien und Datensätze in der Ausgabetabelle nicht beibehalten. Wenn dies wichtig ist, können Sie eine sequenzierte Version der Tabellen beibehalten und in der ursprünglichen Reihenfolge iterieren.

verwandte Informationen