Prazo de entrega recomendado:
Iremos exercitar a escrita de funções para organizar o código e facilitar a escrita de algoritmos. Iremos construir função para resolver subproblemas não triviais com aplicação no sequenciamento genético.
O sequenciamento genético corresponde a identificar a ordem em que os
nucleotídeos ou, mais precisamente, suas bases nitrogenadas,
aparecem em um DNA. Uma sequência de três bases codifica um aminoácido
e, assim, sequências maiores codificam proteínas. Nesta tarefa, cada
base será representada por um caractere correspondente, como A
, T
,
C
e G
, e o sequenciamento genético será representado por uma
string com esses caracteres, como a seguinte:
CACGTTACGAATAAATGGAGTGCGGCC
Para resolver os vários problemas relacionados ao sequenciamento
genético, recorrentemente é necessário resolver subproblemas comuns,
como medir a “distância” entre duas strings, etc. Você deverá
implementar um conjunto de funções, especificadas e documentadas no
arquivo sequencias.py
disponível no seu repositório. Esse arquivo
contém apenas os stubs das funções e você deverá completar com a
implementação que cumpra o comportamento desejado. Você usará essas
funções para auxiliar na escrita dos programas ruido.py
e
diferencas.py
, que resolverão os problemas abaixo.
1. Ruído
Ruído é um um conceito fundamental estudado no processamento de sinal, na análise de dados e em várias outras áreas da Computação e afins. Nessas disciplinas, a informação é transmitida na forma de um sinal e o ruído refere-se a modificações ou adições indesejadas nos dados do sinal com que desejamos trabalhar. Por exemplo, em comunicação por rádio, a interferência eletromagnética pode se sobrepor ao sinal real. Em dados de sensores, como o de temperatura, problemas físicos podem provocar leituras incorretas em meio a uma sequência de leituras corretas. Em processos que dependem de seres humanos, como análises laboratoriais, pode haver a entrada de dados inválidos.
A distância de Hamming
Uma técnica básica para lidar com certos tipos de ruído é o cálculo da distância de Hamming. Dadas duas sequências de mesmo tamanho $x$ e $y$, a distância de Hamming entre $x$ e $y$, denotada por $d(x,y)$, é o número de posições em que as sequências são diferentes. Por exemplo, para duas sequências de bits $x = 0100$ e $y = 1101$, a distância de Hamming é $d(x,y) = 2$, pois essas sequências têm bits diferentes nas posições 0 e 3.
Uma maneira de usarmos essa medida para lidar com ruído está descrita em seguida. Consideramos como válidas para nossa aplicação apenas algumas sequências acordadas previamente. Caso recebamos uma sequência desconhecida durante o processo de transmissão de dados, substituímos essa sequência pela sequência pré-acordada mais parecida. Para decidir qual sequência é mais parecida, escolhemos aquela com a menor distância de Hamming.
Por exemplo, dois amigos músicos resolvem conversar por sequências melódicas. Assim, uma nota curta representa um bit $0$ e uma nota longa representa um bit $1$. Como não é possível identificar a duração de notas individualmente, eles combinam que uma sequência $111$ significa sim e uma sequência $000$ significa não. Se um deles escuta uma sequência de notas correspondente a $010$, concluirá que na verdade a sequência deveria ser $000$ e interpretará a resposta como não. A razão é que $d(010,000) = 1$, enquanto $d(010,111) = 2$.
Nosso problema
Em um laboratório, você precisa identificar qual espécie de bactéria foi encontrada em um processo infeccioso. Feita uma cultura da bactéria e o sequenciamento de parte de seu genoma, sua tarefa é determinar a espécie correspondente dentre um conjunto de bactérias conhecidas.
Para isso, você deverá escrever um programa ruido.py
que receberá a
sequência da amostra a ser identificada, bem como vários trechos dos
sequenciamentos genéticos das espécies candidatas. Suponha que todas as
sequências têm o mesmo comprimento e que a espécie mais provável é
aquela que cujo trecho tem a menor distância de Hamming.
Entrada
A primeira linha contém a sequência a ser identificada. Na segunda linha, há um número $n$ de sequências candidatas. Em seguida, há $n$ linhas, cada uma com o trecho de uma espécie candidata.
ACATT
3
GCGAT
ACAGT
ACGTT
Saída
Sequência mais próxima: ACAGT
Distância de Hamming: 1
2. Diferenças
Uma nova variante de uma bactéria foi identificada. Infelizmente, ela parece ser mais resistente ao antibiótico usado normalmente. Por esse motivo, é importante identificar as diferenças genéticas entre a nova variante e o genoma de referência.
Nesta tarefa, vamos considerar que um gene é uma subsequência de tamanho
fixo do genoma, chamada de janela. Por exemplo, se a janela tem
tamanho 3, então a sequência CTTAGCACC
representa três genes: CTT
,
AGC
e ACC
, indexados nas posições 0, 3 e 6.
Sua tarefa é construir um programa diferencas.py
que identifica os
genes como maior diferença. Iremos supor que as sequências
correspondentes à nova variante e ao genoma de referência têm o mesmo
comprimento.
Entrada
A primeira linha contém o tamanho de uma janela, a segunda linha a sequência do genoma de referência e a terceira, o genoma da variante.
4
AAAATTTTGGGGCCCC
AGGATTATGGGGAAAC
Saída
Para cada gene da variante cuja distância de Hamming ao gene correspondente de referência seja pelo menos um terço do tamanho da janela, deve ser listada a diferença. Se houver vários genes diferentes, então os genes devem ser listados em ordem de posição em que aparecem.
Diferença 1:
Posição: 0
Referência: AAAA
Variante: AGGA
Distância: 2
Diferença 2:
Posição: 12
Referência: CCCC
Variante: AAAC
Distância: 3
Critérios
Nesta e nas próximas tarefas, é obrigatório organizar seus programas
usando funções adequadamente: é proibido executar instruções fora de
funções. Construa uma função principal main
e certifique-se de que
cada função criada tenha uma uma única responsabilidade. Separe
funções responsáveis pela entrada e saída dos dados das funções que
resolvem o problema.
Nos seus algoritmos, você deve exercitar as funções definidas no
módulo sequencias.py
, mesmo que você conheça algoritmos mais
simples.
Dicas e observações
Funções que devolvem mais de um item
A linguagem Python dá suporte a funções que devolvem mais do que um item. Veja o exemplo abaixo:
def soma_e_diferenca(a, b):
return a + b, a - b
soma, diferenca = soma_e_diferenca(7, 4)
print(soma) # mostra 11
print(diferenca) # mostra 3
Usando funções de um módulo
Relembre que quando definimos uma função func()
e a utilizamos no
mesmo arquivo, basta escrever
func(parametros)
para chamá-la, passando os parâmetros adequados. No entanto, se a
função está definida em um arquivo nome_do_modulo.py
, precisamos
importar o módulo antes. Uma maneira de fazer isso é:
import nome_do_modulo
nome_do_modulo.func(parametros)
Existem outras formas de importar componentes (funções e outros) de um módulo, que você irá aprender com o tempo.
Testes de unidade
Nas tarefas anteriores, usamos apenas testes que comparavam a saída do programa com a saída de referência. Isso testa o comportamento do programa como um todo.
Quando organizamos nosso programa em funções, é essencial testar cada função individualmente. Assim, os testes podem ser mais simples e potencialmente identificar erros de programação antes mesmo que eles afetem o programa em si.
Além disso, é muito comum que o comportamento de um programa esteja errado por motivos difíceis de atribuir a uma linha ou parte específica do código. Em geral, é bem mais simples depurar erros de uma função.
Um teste que verifica uma função é chamado de teste de unidade. Geralmente, um teste de unidade é um código escrito na mesma linguagem do elemento em teste e faz uso direto do código a ser testado.
import modulo
# testa função soma, presente no módulo
def teste_soma():
assert modulo.soma(7, 4) == 11, "Falha na função soma"
É importante testar combinações diferentes de parâmetros para uma mesma função, a fim de cobrir mais casos. Você pode testar suas próprias funções desta maneira, isto é, fazendo chamadas a uma função específica que deseja testar.
Nesta tarefa, além de testar a saída de seus programas, serão testadas
também as funções do módulo sequencias.py
que você implementará. É
para isso que serve o arquivo testa_funcoes.py
.
Correção
Esta tarefa será corrigida automaticamente sempre que você realizar um
git push
. Depois de terminada a tarefa, deve-se utilizar o botão na
interface de notas para solicitar a correção de um monitor.