Wie füge ich zwei Dateien mit unterschiedlicher Anzahl von Zeilen in der Shell zusammen?

7

Ich habe Datei1 wie folgt:

CHR                     SNP     TEST   A1   A2                 GENO   O(HET)   E(HET)            P 
   0         AFFX-SNP-000541      ALL    0    0                0/0/0      nan      nan            1
   0         AFFX-SNP-000541      AFF    0    0                0/0/0      nan      nan           NA
   0         AFFX-SNP-000541    UNAFF    0    0                0/0/0      nan      nan           NA
   0         AFFX-SNP-002255      ALL    0    0                0/0/0      nan      nan            1
   0         AFFX-SNP-002255      AFF    0    0                0/0/0      nan      nan           NA
   0         AFFX-SNP-002255    UNAFF    0    0                0/0/0      nan      nan           NA
   1                 rs12103      ALL    C    T           55/250/317   0.4019   0.4113       0.5596
   1                 rs12103      AFF    C    T                0/0/0      nan      nan           NA
   1                 rs12103    UNAFF    C    T                0/0/0      nan      nan           NA
   1         rs12103_1247494      ALL    C    T           55/250/321   0.3994   0.4097       0.5581
   1         rs12103_1247494      AFF    C    T                0/0/0      nan      nan           NA
   1         rs12103_1247494    UNAFF    C    T                0/0/0      nan      nan           NA

Und file2 wie:

CHR                     SNP   A1   A2          MAF  NCHROBS
   0         AFFX-SNP-000541    0    0           NA        0
   0         AFFX-SNP-002255    0    0           NA        0
   1                 rs12103    C    T       0.2894     1244
   1         rs12103_1247494    C    T       0.2875     1252

Ich möchte Datei2 mit Datei1 basierend auf dem SNP-Namen und dem TEST == ALL zusammenführen und CHR, SNP, P und MAF in der Ausgabedatei3 behalten. Kennt jemand den besten Weg dafür in der Terminal (Unix) Shell?

Eine gewünschte Ausgabe wäre:

  CHR                     SNP  MAF        P
   0         AFFX-SNP-000541   NA         1
   0         AFFX-SNP-002255   NA         1
   1                 rs12103   0.2894     0.5596
   1         rs12103_1247494   0.2875     0.5581
Dadong Zhang
quelle
Bitte geben Sie ein Beispiel für Ihre gewünschte Ausgabe.
Terdon
Wie groß sind die Dateien? Ist es möglich, dass am Ende von Datei1 wieder eine Zeile mit 'AFFX-SNP-000541' für das SNP-Feld steht?
Brm
Nein. Datei2 'AFFX-SNP-000541' stimmt genau mit Datei1 'AFFX-SNP-000541' + 'ALL' überein.
Dadong Zhang
Selbst wenn dies mit Textdateien möglich ist: Wäre es nicht irgendwann sinnvoll, die Daten in eine relationale Datenbank zu importieren?
Simon Richter

Antworten:

6

Mit Hilfe dieser Antwort

awk 'FNR==NR && FNR>1 {a[$2] = $5; next}
     FNR > 1 && ($2 in a) && $3 == "ALL" {
         print $1 "    " $2 "    "  a[$2] "    "  $9
     }' file2 file1

Um auch den Header zu erhalten, fügen Sie diesen einfach am Anfang des Skripts hinzu:

 BEGIN{print "CHR SNP MAF P"}

Erläuterung:

Wenn zwei Dateien an awk übergeben werden, werden sie zunächst nacheinander verarbeitet. Hier sind zwei Variablen wichtig: NRIst die Zeilennummer vom Anfang des awkBefehls und FNRdie Zeilennummer vom Anfang der aktuellen Datei. Das heißt, wenn die erste Datei verarbeitet wird (hier Datei2) NRund FNRdenselben Wert hat, der der Wert der aktuell verarbeiteten Zeile ist. Aber wenn awk zur zweiten Datei übergeht, FNRwird auf 1 zurückgesetzt, so dass NRund FNRnicht mehr gleich sind. Damit ist der Test FNR==NRein Trick, um zu wissen, ob die verarbeitete Datei die erste ist oder nicht.

Schauen wir uns also den Code an. Die Bedingung FNR==NR && FNR>1testet, ob wir die erste Datei und nicht die erste Zeile verarbeiten. Wenn dies der Fall ist, speichern wir den Wert der fünften Spalte ( MAF) in einem Array, das durch die zweite Spalte ( ) indiziert ist, SNPund die nextAnweisung besagt, dass an die folgende Zeile übergeben werden soll.

Wenn awk die zweite Datei (die Datei1 ist) verarbeitet, ist der erste Test falsch, so dass awk den zweiten Test versucht: FNR > 1 && ($2 in a) && $3 == "ALL"Das heißt: Nicht die erste Zeile der Datei + zweiter Spaltenwert ( SNP) ist in Tabelle a+ dritter Spaltenwert vorhanden ( TEST) ist "ALL". Wenn dies der Fall ist, werden die Spalten 1 ( CHR) und 2 ( SNP) gedruckt , der MAFWert aus dem Array mit abgerufen a[$2]und anschließend die Spalte 9 ( P) gedruckt .

Durch Hinzufügen einer BEGIN{...}Anweisung am Anfang wird ein Befehl hinzugefügt, der erst ausgeführt wird, bevor die erste Zeile verarbeitet wird.

JPG
quelle
Nun, wenn Header bekannt sind, kann man BEGIN{print "CHR SNP MAF P"}am Anfang hinzufügen .
JPG
Können Sie eine Erklärung geben? Danke für deinen tollen Job!
Dadong Zhang
5

terdon erwähnt es nur mit coreutils, ein bescheidener vorschlag:

(echo CHR SNP MAF P
 paste <(tail -n +2 file2) <(grep ALL file1) | 
   while read -r chr snp _ _ maf _ _ _ _ _ _ _ _ _ p; do 
     echo $chr $snp $maf $p; 
   done) | column -t

Ausgabe:

CHR  SNP              MAF     P
0    AFFX-SNP-000541  NA      1
0    AFFX-SNP-002255  NA      1
1    rs12103          0.2894  0.5596
1    rs12103_1247494  0.2875  0.5581
grebneke
quelle
+1 b / c Paste ist heimlich mein Lieblings-Unix-Tool. Es kann Ihnen sogar Weltfrieden verschaffen, wenn Sie es zulassen. 8-)
slm
@slm - "b / c", was ist das?
Grebneke
@grebneke - b / c = weil
slm
1
@slm - danke, das ist dann sortiert. Nur noch Weltfrieden und wir sind fertig.
Grebneke
4

Hier ist eine Möglichkeit, dies zu tun (kopieren Sie diese direkt in Ihr Terminal):

(
 awk -v OFS="\t" 'NR==1{print $1,$2,$5,"P"}' file2;
 awk '$3=="ALL"{print $2,$NF}' file1 | 
  while read snp p; do 
   awk -v snp="$snp" -v p=$p -v OFS="\t" '$2==snp{print $1,$2,$5,p}' file2;
  done
) > file3

Die Ausgabe sieht folgendermaßen aus:

CHR SNP MAF P
0   AFFX-SNP-000541 0   1
0   AFFX-SNP-002255 0   1
1   rs12103 C   0.5596
1   rs12103_1247494 C   0.5581

Anmerkungen:

  • Ich bin 99% sicher , dass Sie dies tun können , irgendwie mit coreutils, vielleicht eine Kombination aus sort, pasteund joindoch kann ich es nicht herausgefunden .
  • Dies ist nicht effizient und kann eine Weile dauern, wenn Sie mit großen Dateien arbeiten.
  • Dadurch werden die Feldtrennzeichen in Registerkarten geändert. Entfernen Sie alle Fälle, -v OFS="\t"wenn Sie dies nicht möchten.
  • Wie Sie sehen, sind die Felder nicht mehr ausgerichtet. Da sie alle durch Tabulatoren getrennt sind, ist dies kein Problem für Programme, die die Datei lesen. Dies ist nur dann ein Problem, wenn Sie versuchen, sie zu lesen.

Erläuterung

  • In der ersten awkZeile wird nur das erste, zweite und fünfte Feld des Headers von Datei 2 gedruckt, gefolgt von P. Dies ist der Header der neuen Datei.
  • Das zweite awkdruckt das 2. und das letzte ( $NF) Feld jeder Zeile von Datei1, in der sich das 3. Feld befindet ALL.
  • Dies wird dann durch eine Shell- whileSchleife geleitet, die die beiden Felder in die Variablen $snpund einliest $p.
  • Diese werden dann awkmit der -vOption bis zum letzten gegeben und drucken dann für jede Zeile der Datei2, wenn das zweite Feld ist $snp, die Felder 1,2,5 und den aktuellen Wert von $p.

Hier ist ein Perl-Skript, das dasselbe tut:

perl -le 'print "CHR\tSNP\tMAF\tP"; open($f1, "$ARGV[0]"); 
          while(<$f1>){
           s/^\s+//; @f=split(/\s+/); $k{$f[1]}=$f[$#f] if $f[2] eq "ALL"
          } 
          open($f2,"$ARGV[1]"); 
          while(<$f2>){
            s/^\s+//; @f=split(/\s+/); 
            next unless defined($k{$f[1]}); 
            print "$f[0]\t$f[1]\t$f[4]\t$k{$f[1]}"
          }' file1 file2
terdon
quelle
1

Hier ist eine andere Möglichkeit:

{ printf %s\\n "CHR SNP MAF P"; grep -F ALL file1 | sort -k2,2 | \
join -j2 -o 2.1,2.2,2.5,1.9 - <(sort -k2,2 file2); } | column -t

Ausgabe:

CHR  SNP              MAF     P
0    AFFX-SNP-000541  NA      1
0    AFFX-SNP-002255  NA      1
1    rs12103          0.2894  0.5596
1    rs12103_1247494  0.2875  0.5581

Wie es funktioniert:

grep -F ALL file1 | sort -k2druckt die mit der Zeichenfolge übereinstimmenden Zeilen aus ALL, diese werden dann sortim 2. Feld bearbeitet:

   0         AFFX-SNP-000541      ALL    0    0                0/0/0      nan      nan            1
   0         AFFX-SNP-002255      ALL    0    0                0/0/0      nan      nan            1
   1         rs12103_1247494      ALL    C    T           55/250/321   0.3994   0.4097       0.5581
   1                 rs12103      ALL    C    T           55/250/317   0.4019   0.4113       0.5596

Dies wird dann im 2. Feld ( -j2) mit Datei2 (ebenfalls im 2. Feld sortiert) verbunden, -o 2.1,2.2,2.5,1.9um das 1., 2. und 5. Feld aus Datei2 und das 9. Feld aus der verarbeiteten Datei1 auszugeben:

0 AFFX-SNP-000541 NA 1
0 AFFX-SNP-002255 NA 1
1 rs12103 0.2894 0.5596
1 rs12103_1247494 0.2875 0.5581

Da dies gruppiert wurde, {...}mit printf %s\\n "CHR SNP MAF P"dem der Header gedruckt wird, wird die gesamte Ausgabe mit verschönert column -t.
Beachten Sie, dass bei dieser Lösung die Zeilenreihenfolge (aus Datei2) in der Ausgabe nicht beibehalten wird. Es kommt einfach vor, dass Ihre Datei2 bereits im 2. Feld sortiert wurde, aber wenn nicht, z.

CHR                     SNP   A1   A2          MAF  NCHROBS
   0         AFFX-SNP-000541    0    0           NA        0
   1                 rs12103    C    T       0.2894     1244
   0         AFFX-SNP-002255    0    0           NA        0
   1         rs12103_1247494    C    T       0.2875     1252

und wenn Sie die Zeilenreihenfolge beibehalten möchten, können Sie die Zeilen in Datei2 nummerieren mit nl -ba -nrz file2:

000001  CHR                     SNP   A1   A2          MAF  NCHROBS
000002     0         AFFX-SNP-000541    0    0           NA        0
000003     1                 rs12103    C    T       0.2894     1244
000004     0         AFFX-SNP-002255    0    0           NA        0
000005     1         rs12103_1247494    C    T       0.2875     1252

Bevor Sie sortieren und verbinden, passen Sie den Befehl an: Verbinden Sie das 2. Feld der (verarbeiteten) Datei1 und das 3. Feld der (verarbeiteten) Datei2 und geben Sie das Zeilennummernfeld + die gleichen Felder wie die erste Lösung aus. join -1 2 -2 3 -o 2.1,2.2,2.3,2.6,1.9Sortieren Sie dann die Ausgabe mit sort -k1nund löschen Sie sie erneut Das erste Feld mit cut -d' ' -f2-vor dem Weiterleiten der gesamten Ausgabe an column -t:

{ printf %s\\n "CHR SNP MAF P"; grep -F ALL file1 | sort -k2,2 | join \
-1 2 -2 3 -o 2.1,2.2,2.3,2.6,1.9 - <(sort -k3,3 <(nl -ba -nrz file2)) | \
sort -k1n | cut -d' ' -f2-; } | column -t

Ausgabe:

CHR  SNP              MAF     P
0    AFFX-SNP-000541  NA      1
1    rs12103          0.2894  0.5596
0    AFFX-SNP-002255  NA      1
1    rs12103_1247494  0.2875  0.5581

Beachten Sie, dass beide Lösungen davon ausgehen, dass ALLin jeder Zeile nur ein Vorkommen vorkommt , nämlich das 3. Feld (daher das grep -F ALL). Wenn dies nicht der Fall ist, müssen Sie einen regulären Ausdruck verwenden, um nur die relevanten Zeilen zu filtern.

don_crissti
quelle