Ich habe eine Datei mit gene_id und Gennamen in einer Zeile. Ich möchte das Wort nach ersetzenGen-IDmit dem Wort nachGenoder danachProduktoder danachsprot(falls etwas davon fehlt).
Hier ist ein Beispiel für eine Zeile:
chrM Gnomon CDS 8345 8513 . + 1 gene_id "cds-XP_008824843.3"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM StringTie exon 2754 3700 . + . gene_id "cds-YP_007626758.1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";
Ich habe versucht, es mit sed zu machen:
sed -E 's/[^gene_id] .*?;/[^gene] .*?;|[^sprot] .*?;|[^product] .*?;/g'
Die Ergebnisse waren jedoch falsch:
chrM Gnomon CDS 8345 8513 . + 1 gene_id "cds-XP_008824843.3"[^gene] .*?;|[^sprot] .*?;|[^product] .*?;
chrM StringTie exon 2754 3700 . + . gene_id "cds-YP_007626758.1"[^gene] .*?;|[^sprot] .*?;|[^product] .*?;
Aber ich möchte alle Zeilen speichern, aber mit einem anderen Wort danachGen-ID, so was:
chrM Gnomon CDS 8345 8513 . + 1 gene_id "semaphorin-3F"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM StringTie exon 2754 3700 . + . gene_id "ND1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";
Oder so (falls noch jemand es verpasst hat):
chrM Gnomon CDS 8345 8513 . + 1 gene_id "sp|Q13275|SEM3F_HUMAN"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM StringTie exon 2754 3700 . + . gene_id "ND1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";
Jede Hilfe wird sehr geschätzt.
Antwort1
gene
Das folgende Perl-Skript versucht, in jeder Eingabezeile , product
, und in dieser Reihenfolge zuzuordnen sprot
(d. h. es priorisiert Gene vor Product und Product vor Sprot). Wenn eines davon übereinstimmt, extrahiert es das Wort nach der Übereinstimmung. Es wird angenommen, dass das Wort in Anführungszeichen eingeschlossen ist.
Wenn eine Übereinstimmung gefunden wurde, wird das nachfolgende Wort gene_id
durch das extrahierte Wort ersetzt.
Die Zeile wird gedruckt, unabhängig davon, ob sie geändert wurde oder nicht.
#!/usr/bin/perl
while (<>) {
my $word = '';
if (m/\b(?:gene)\s+("[^"]*")/) {
$word = $1;
} elsif (m/\b(?:product)\s+("[^"]*")/) {
$word = $1;
} elsif (m/\b(?:sprot)\s+("[^"]*")/) {
$word = $1;
};
if ($word) {
s/\bgene_id\s+(?:"[^"]*")/gene_id $word/
};
print;
}
Alternativ könnte dies so geschrieben werden, dass eine Schleife zum Durchlaufen der Übereinstimmungsschlüsselwörter verwendet wird:
#!/usr/bin/perl
while (<>) {
my $word = '';
foreach my $match (qw(gene product sprot)) {
if (m/\b(?:$match)\s+("[^"]*")/) {
$word = $1;
last; # first match wins, exit this loop
}
};
if ($word) {
s/\bgene_id\s+(?:"[^"]*")/gene_id $word/
};
print;
}
Meiner Meinung nach ist diese Version besser, weil sie leichter zu lesen und zu verstehen ist (insbesondere die foreach
Schleife betont, dass es darum geht, über eine Liste von Wörtern zu iterieren). Noch wichtiger ist, dass die Wiederholung der $word = $1
Anweisung vermieden wird – Sie machen weniger wahrscheinlich einen Fehler, wenn Sie sie ändern oder zusätzlichen Code hinzufügen müssen, wenn Sie es nur einmal statt dreimal tun müssen. „Wiederholen Sie sich nicht“ ist in einem trivialen kleinen Programm wie diesem nicht so wichtig, kann aber in größeren Programmen sehr wichtig sein. Wiederholungen zu vermeiden/minimieren ist ohnehin eine gute Programmiergewohnheit.
Wenn die Reihenfolge der Übereinstimmungen nicht von Bedeutung wäre (d. h. wenn es Ihnen egal wäre, welches Ergebnis gefunden wird, solange eines gefunden wird), könnten Sie das Skript vereinfachen:
#!/usr/bin/perl
while (<>) {
my ($word) = m/\b(?:gene|product|sprot)\s+("[^"]*")/;
if ($word) {
s/\bgene_id\s+(?:"[^"]*")/gene_id $word/
};
print;
}
Unabhängig davon, welche Version des Skripts Sie verwenden, speichern Sie es beispielsweise als replace.pl
und machen Sie es mit ausführbar chmod +x replace.pl
. Oder probieren Sie sie alle als replace1.pl
, replace2.pl
, replace3.pl
aus. Führen Sie es dann folgendermaßen aus:
$ ./replace.pl input.txt
chrM Gnomon CDS 8345 8513 . + 1 gene_id "semaphorin-3F"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM StringTie exon 2754 3700 . + . gene_id "ND1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";
Antwort2
Wir nutzen die Eigenschaft eines Hashs, dass, wenn auf einen gegebenen Schlüssel mehrere Werte angewendet werden, der letzte der endgültige Wert wird.
perl -lpe 'my($l,%h)=($_);
$h{gene_id}=$_ for map {
$l =~ /\b$_\s+(".*?");/
} reverse qw(gene product sprot);
s/\bgene_id\s+\K".*?";/$h{gene_id};/;
' your_file_genes
Da die Befehle alle gleich sind und sich nur die Namen ändern, können wir die gesamte Operation problemlos tabellengesteuert gestalten, wobei wir nur die Feldnamen bereitstellen, während die For-Schleife sich um den Rest kümmert.
for i in gene product sprot;do
cat - <<\_FMT_ |\
sed -e "s/%s/$i/"
s/(\<gene_id\s+)"[^"]*"(.*\s%s\s+("[^"]*"))/\1\3\2/;t
_FMT_
done | sed -Ef - your_file_genes
Antwort3
Um die perl
Lösung zu vervollständigen, hier ist, wie Sie es mit machen würden sed
. Ich bin nicht sicher, wie Sie erwarten, dass Ihre angegebene Syntax funktioniert, aber eigentlich benötigen Sie einen regulären Ausdruck, der mit der Zeichenfolge übereinstimmt
... gene_id "remove me" ... some other stuff gene "replacement" ... more stuff
======= ====
gene_id "[^"]*" .* gene "[^"]*"
gene_id
und gene
werden mit sich selbst abgeglichen. Ein String in Anführungszeichen ist eine Aneinanderreihung eines Anführungszeichens, einer beliebigen Anzahl von Zeichen, die keine Anführungszeichen sind ( [^"]*
) und eines weiteren Anführungszeichens. Schließlich haben Sie das Zeug dazwischen.*
Nun müssen Sie \(\)
im Ersatzteil die Teile zusammenstellen, die Sie recyceln möchten:
sed 's/gene_id "[^"]*"\(.* gene \("[^"]*"\)\)/gene_id \2\1/'
Das äußere Paar umfasst alles, was unberührt bleiben soll. Dies wird wie \1
beim Ersetzen wiederverwendet. Das innere Paar ist die Zeichenfolge, die Sie als wiederverwenden möchten gene_id
.
product
Wenn Sie nun alternative Ersetzungen wünschen sprot
, können Sie alternative Zeichenfolgen erweiterter regulärer Ausdrücke verwenden:
sed -E 's/gene_id "[^"]*"(.*(gene|product|sprot) ("[^"]*"))/gene_id \3\1/'
aber dies wird nicht over bevorzugen gene
, sondern das letzte der vorhandenen. Wenn Sie diese Bevorzugungsreihenfolge haben möchten, müssen Sie separate Schritte durchführen und mit dem letzten beginnen, damit es durch ein besseres ersetzt werden kann product
:sprot
sed 's/gene_id "[^"]*"\(.* sprot \("[^"]*"\)\)/gene_id \2\1/
s/gene_id "[^"]*"\(.* product \("[^"]*"\)\)/gene_id \2\1/
s/gene_id "[^"]*"\(.* gene \("[^"]*"\)\)/gene_id \2\1/'
Oder, wenn bekannt ist, dass die Reihenfolge von gene
, product
und sprot` fest ist, können Sie zuerst die bevorzugte ID extrahieren, während Sie die eigentliche Zeile im Haltebereich parken:
sed -E 'h;s/(sprot|product|gene) ("[^"]*").*/#\2/;s/.*#//;G;s/(.*)\n(.*gene_id )"[^"]*"/\2\1/'
Der Marker #
kann jede beliebige Zeichenfolge sein, von der bekannt ist, dass sie nicht Teil der ID ist; für GNU sed
können Sie \n
zur Sicherheit verwenden. Sie ersetzen also die erste der besagten Zeichenfolgen durch den Marker und löschen den Rest der Zeile. Dann löschen Sie alles bis zum Marker, sodass jetzt nur noch die ID im Musterbereich übrig ist. Dann wird mit G
die ursprüngliche Zeile (die wir mit im Hold-Puffer beibehalten haben h
) angehängt und dann ersetzt die ID (der Teil vor der neuen Zeile) das "string"
nach gene_id
. Irgendwie leichter zu schreiben als zu erklären.