Eu tenho um arquivo com gene_id e nomes de genes em uma linha. Quero substituir a palavra depoisgene_idcom a palavra depoisgeneou depoisprodutosou depoisbroto(se alguma coisa faltou).
Aqui está um exemplo de linha:
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";
Eu tentei fazer isso com sed:
sed -E 's/[^gene_id] .*?;/[^gene] .*?;|[^sprot] .*?;|[^product] .*?;/g'
Mas os resultados estavam incorretos:
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] .*?;
Mas eu quero salvar toda a linha, mas com outra palavra depoisgene_id, assim:
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";
Ou assim (se outro faltou):
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";
Qualquer ajuda será muito apreciada.
Responder1
O script perl a seguir tenta corresponder gene
, product
, e sprot
em cada linha de entrada, nessa ordem (ou seja, ele prioriza gene em vez de produto e produto em vez de sprot). Se um deles for correspondido, ele extrai a palavra após a correspondência. Presume-se que a palavra esteja entre aspas duplas.
Se uma correspondência for encontrada, a palavra seguinte será substituída gene_id
pela palavra extraída.
A linha é impressa independentemente de ter sido modificada ou não.
#!/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;
}
Alternativamente, isso poderia ser escrito para usar um loop para iterar sobre as palavras-chave correspondentes:
#!/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;
}
IMO, esta versão é melhor porque é mais fácil de ler e entender (em particular, o foreach
loop enfatiza que se trata de iterar uma lista de palavras). Mais importante ainda, evita a repetição da $word = $1
instrução - é menos provável que você cometa um erro se precisar alterá-la ou adicionar código extra, se precisar fazer isso apenas uma vez, em vez de três vezes. "Don't Repeat Yourself" não é tão importante em um pequeno programa trivial como este, mas pode ser muito importante em programas maiores. De qualquer forma, evitar/minimizar a repetição é um bom hábito de programação.
Se a ordem da correspondência não fosse significativa (ou seja, se você não se importasse com qual foi encontrado, contanto que fosse), você poderia simplificar o script:
#!/usr/bin/perl
while (<>) {
my ($word) = m/\b(?:gene|product|sprot)\s+("[^"]*")/;
if ($word) {
s/\bgene_id\s+(?:"[^"]*")/gene_id $word/
};
print;
}
Independentemente da versão do script que você usa, salve-o como, por exemplo replace.pl
, e torne-o executável com chmod +x replace.pl
. Ou experimente todos eles como replace1.pl
, replace2.pl
, replace3.pl
. Então execute assim:
$ ./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";
Responder2
Exploramos a propriedade de um hash de que, se vários valores forem aplicados a uma determinada chave, o último se tornará o valor final.
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
Como os comandos são todos iguais e apenas os nomes mudam, podemos facilmente fazer com que toda a tabela de operações seja controlada, onde apenas fornecemos os nomes dos campos enquanto o loop for cuidará do resto.
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
Responder3
Para completar a perl
solução, veja como você faria isso com sed
. Não tenho certeza de como você espera que sua sintaxe funcione, mas na verdade você precisa de uma expressão regular para corresponder à string
... gene_id "remove me" ... some other stuff gene "replacement" ... more stuff
======= ====
gene_id "[^"]*" .* gene "[^"]*"
gene_id
e gene
são combinados por si mesmos. Uma string entre aspas duplas é uma concatenação de aspas duplas, qualquer número de caracteres que não sejam aspas duplas ( [^"]*
) e outras aspas duplas. Finalmente você tem o que está no meio.*
Agora você precisa colocar \(\)
as peças que precisa reciclar na reposição:
sed 's/gene_id "[^"]*"\(.* gene \("[^"]*"\)\)/gene_id \2\1/'
O par externo cobre tudo o que deve ser deixado intacto. Isso é reutilizado como \1
na substituição. O par interno é a string que você deseja reutilizar como gene_id
.
Agora, se quiser ter product
ou sprot
como substituições alternativas, você pode usar strings alternativas de expressões regulares estendidas:
sed -E 's/gene_id "[^"]*"(.*(gene|product|sprot) ("[^"]*"))/gene_id \3\1/'
mas isso não irá preferir , gene
mas preferirá o último deles que estiver presente. Se quiser ter essa ordem de preferência, você precisa de etapas separadas e começar pela última, para que possa ser substituída por uma melhor: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/'
Ou, se a ordem de gene
, product
e sprot` for fixa, você pode primeiro extrair o ID preferido enquanto estaciona a linha real no espaço de espera:
sed -E 'h;s/(sprot|product|gene) ("[^"]*").*/#\2/;s/.*#//;G;s/(.*)\n(.*gene_id )"[^"]*"/\2\1/'
O marcador #
pode ser qualquer sequência que não faça parte do ID; para GNU sed
você pode usar \n
com certeza. Então você substitui a primeira das referidas strings pelo marcador e exclui o resto da linha, depois exclui tudo até o marcador, então agora apenas o ID permanece no espaço do padrão. Então, G
a linha original (que preservamos no buffer de retenção com h
) será anexada e o ID (a parte antes da nova linha) substituirá o "string"
depois gene_id
. De alguma forma, é mais fácil escrever do que explicar.