Tradução e modificação do material associado a programmingforbiology.org, associado a disciplina "CEN0336 - Introdução a Programação de Computadores Aplicada a Ciências Biológicas"
Criador e Instrutor da versão em Português Diego M. Riaño-Pachón
Criadores do material na versão em Inglês Simon Prochnik Sofia Robb
Python é uma linguagem de script. Ela é útil para desenvolvimento de projetos científicos de médio porte. Quando você executa um script de Python, o interpretador da linguagem irá gerar um código em bytes e interpretá-lo. Esse processo acontece automaticamente, você não precisa se preocupar com isso. Linguagens compiladas como C e C++ vão rodar muito mais rapidamente, mas são também muito mais complicadas de programar. Programas usando linguagens como Java (que também são compiladas) são adequados para projetos grandes com programação colaborativa, mas não são executados tão rapidamente como C e são mais complexos de escrever que Python.
Python tem
- tipos de dados
- funções
- objetos
- classes
- métodos
Tipos de dados correspondem aos diferentes tipos de dados que serão discutidos em mais detalhes posteriormente. Exemplos de tipos de dados incluem números inteiros e cadeias de caracteres (texto). Eles podem ser armazenados em variáveis.
Funções fazem algo com dados, como cálculos. Algumas funções estão disponíveis de forma nativa em Python. Você pode também criar suas próprias funções.
Objetos correspondem a maneiras de agrupar conjuntos de dados e funções (métodos) que agem nestes dados.
Classes correspondem a uma maneira de encapsular (organizar) variáveis e funções. Objetos usam variáveis e métodos da classe às quais pertencem.
Métodos são funções que pertencem a uma classe. Objetos que pertencem a uma classe podem usar métodos daquela classe.
Há duas versões de Python: Python 2 e Python 3. Nós usaremos Python 3. Esta versão conserta problemas de Python 2 e é incompatível em alguns aspectos com Python 2. Muitos códigos já foram desenvolvidos em Python 2 (é uma versão mais antiga), mas muitos códigos já foram e estão sendo desenvolvidos em Python 3.
Python pode ser executado em uma linha por vez em um interpretador interativo. É como se usasse a linha de comando de Shell (que estudamos nas duas primeiras aulas/ capítulos), mas agora com a linguagem Python. Para executar o interpretador, execute o seguinte código no seu terminal:
$ python3
Nota: '$' indica o prompt de comando. Lembre-se do Unix 1 que cada computador tem seu próprio prompt!
Primeiros comandos em Python:
>>> print("Olá, turma 2022!")
Olá, turma 2022!
Nota:
print()
- O mesmo código acima é digitado em um arquivo usando um editor de texto.
- Scripts em Python são sempre salvos em arquivos cujos nomes têm a extensão '.py' (o nome do arquivo termina com '.py').
- Poderíamos executar o código
ola.py
Conteúdos do arquivo:
print("Olá, turma 2022!")
Digitar o comando python3
seguido do nome do script faz com que Python execute o código. Lembre-se que nós vimos que podemos também executar o código de forma interativa executando apenas python3
(ou python
) na linha de comando.
Execute o script desta forma (% representa o prompt):
% python3 ola.py
Este procedimento gera o seguinte resultado no terminal:
print("Olá, turma 2022!")
Se você tornar script em um executável, você pode executá-lo sem precisar digitar python3
antes. Use o comando chmod
para alterar as permissões do script desta forma:
chmod +x ola.py
Você pode verificar as permissões assim:
% ls -l ola.py
-rwxr-xr-x 1 sprochnik staff 60 Oct 16 14:29 ola.py
Os primiros 10 caracteres que ver na tela possuem significados especiais. O primeiro (-
) diz a você qual tipo de arquivo ola.py
é. -
significa um arquivo normal, 'd' um diretório, '1' um link. Os próximos nove caracteres aparecem em três sets de três. O primeiro set se refere às suas permissões, o segundo as permissões do grupo, e o último de quaisquer outros. Cada set de trÊs caracteres mostra em ordem 'rwx' para leitura, escrita, execução. Se alguém não tem uma permissão, um -
é mostrado ao invés de uma letra. Os três caracteres 'x' significam que qualquer um pode executar ou rodar o script.
Nós também precisamos adicionar uma linha no começo do script que pede para o python3 interpretar o script. Essa linha começa com #
, então aparece como um comentário para o python. O '!' é importante como o espaço entre env
e python3
. O programa /usr/bin/env
procura por onde python3
está instalado e roda o script com python3
. Os detalhes podem parecer um pouco complexos, mas você pode apenas copiar e colar essa linha 'mágica'.
Esse arquivo ola.py agora se parece com isso
#!/usr/bin/env python3
print("Olá, turma 2022!")
Agora você pode simplesmente digitar o símbolo para o diretório atual .
seguido por um /
e o nome do script para rodá-lo. Como isso:
% ./ola.py
Olá, turma 2022!
Um nome de variável em Python é o nome usado para identificar uma variável, função, classe, módulo ou outro objeto. Um nome de variável inicia com uma letra, de A
a Z
ou de a
a z
, ou então com um travessão (_
), seguido de zero ou mais letras, travessões e dígitos (0
a 9
).
Python não permite caracteres como @
, $
e %
dentro do nome de variável. Python é uma linguagem "sensível a minúsculas e maiúsculas" (muitas vezes referido com o anglicismo "case sentitive"). Portanto, seq_id
e seq_ID
são dois nomes diferentes de variável em Python.
- A primeira letra deve ser minúscula, exceto em nomes de classes. Classes devem começar com letra maiúscula (p.e.
Seq
). - Private variable names begin with an underscore (ex.
_private
). - Strong private variable names begin with two underscores (ex.
__private
). - Nomes especiais de variável definidas pela linguagem começam e terminam com dois travessões (p.e.
__special__
).
Selecionar bons nomes de variável para objetos que você nomeia é muito importante. Não chame suas variáveis de item
ou minha_lista
ou dados
ou var
, exceto em casos que você esteja trabalhando com trechos de códigos muito simples (a título de testes) ou fazendo algum gráfico. Não dê x
ou y
como nome de variáveis. Todos estes nomes não são descritivos para o tipo de informação encontrado naquela variável ou objeto.
Uma escolha ainda pior é dar nomes de variáveis que contêm nomes de genes como sequencias
. Por que é uma ideia ruim? Pense no que poderia acontecer se você encher seu carro de combustível em um comércio chamado "posto de gasolina" que vendesse limonada em vez de gasolina ou etanol combustível.
Em Ciência da Computação, os nomes devem sempre descrever de forma acurada os objetos aos quais estejam vinculados. Isso reduz a possibilidade de bugs
no seu código, torna muito mais fácil o seu entendimento se você volta ao seis meses depois ou por pessoas com as quais compartilha seu código. Embora pensar em bons nomes para variáveis tome um pouco mais de tempo e esforço, isso prenive problemas no futuro!
A lista a seguir compreende as palavras reservadas de Python. Elas são palavras especiais que já têm um propósito em Python e, portanto, não podem ser usadas como nomes de variáveis.
and exec not
as finally or
assert for pass
break from print
class global raise
continue if return
def import try
del in while
elif is with
else lambda yield
except list hash
Python considera como um bloco de código linhas adjacentes que apresentam o mesmo nível de indentação. Isso mantém organizadas as linhas de código que são executadas de forma conjunta. Espaçamento e/ou indentação incorretos irão causar erros ou podem fazer que seu código seja executado de uma forma que você não espera. Ambientes de Desenvolvimento Interativo (IDEs) e editores de texto podem ajudar a indentar códigos corretamente.
O número de espaços na indentação precisa ser consistente, mas este número não é específico. Todas as linhas de código ou sentenças dentro de um bloco precisa ser identado com o mesmo número. Por exemplo, usando quatro espaços:
#!/usr/bin/env python3
mensagem = '' # cria uma variável vazia
for x in (1,2,3,4,5):
if x > 4:
print("Olá")
mensagem = 'x é grande'
else:
print(x)
mensagem = 'x é pequeno'
print(mensagem)
print('Pronto!')
Incluir comentários no seu código é uma prática essencial. Anotar o que uma linha ou bloco de código faz ajudará o programador e os leitores do código. Incluindo você!
Comentários iniciam com o símbolo #
. Todos os caracteres depois deste símbolo, até o final da linha, são parte do comentário e serão ignorados pelo interpretador de Python.
A primeira linha de um script começa com #!
, um exemplo especial de comentário que tem a função especial no Unix de informar ao Shell como executar o script.
#!/usr/bin/env python3
# este é meu primeiro código
print("Olá, turma 2022!") # esta linha imprema o conteúdo na tela
Linhas em branco são importantes para aumentar a legibilidade do código. Você deve separar com uma linha em branco trechos de código que vão juntos, organizando em "parágrafos" de código. Linhas em branco são ignoradas pelo interpretador de Python.
Esta é a sua primeira oportunidade de olhar para variáveis e tipos de dados. Cada tipo será discutido em mais detalhes nas seções subsequentes.
O primeiro conceito a ser considerado é que os tipos de dados de Python podem ser ou não mutáveis. Números literais, strings e tuplas não podem ser alterados. Listas, dicionários e sets podem. Da mesma forma, variáveis individuais também podem ser alteradas. Você pode armazenar dados na memória por meio da atribução de variáveis, o que pode ser feito usando o sinal "=".
Números e strings são dois tipos comuns de dados. Números literais e strings como 5
ou meu nome é
são imutáveis. No entanto, seus valores podem ser armazenados em variáveis, as quais podem ser alteradas.
Por exemplo:
contagem_genes = 5
# alterando o valor de contagem_genes
contagem_genes = 10
Lembre-se que da seção anterior sobre nomes de variáveis e objetos (e variáveis são objetos em Python).
Diferentes tipos de dados podem ser atribuídos a variáveis, como inteiros (1
,2
,3
), números de ponto flutuante (3.1415
) e strings ("texto"
).
Por exemplo:
contagem = 10 # este é um inteiro
média = 2.531 # este é um número de ponto flutuante
mensagem = "Bem-vindo ao interpretador de Python" # isso é uma string
10
, 2.531
, e "Bem-vindo ao interpretador de Python"
são peças de dados singulares (escalares) e cada um é armazenado em sua própria variável.
Coleções de dados podem também ser armazenados em tipos de dados especiais, i.e., tuplas, listas, sets, e dicionários. Você deveria sempre tentar armazenar semelhantes com semelhantes, de forma tal que cada elemento da coleção deveria ser do mesmo tipo de dado, como um valor de expressão de RNA-seq ou uma contagem de quantos exons estão em um gene ou uma sequência de leitura. Para o quê você imagina que isso deve ser?
- Listas são usadas para armazenar coleções de dados ordenados (indexados).
- Listas são mutáveis: o número de elementos em uma lista e o que é armazenado em cada elemento podem ser alterados.
- Listas são delimitadas por colchetes e seus itens separados por vírgula.
[ 'atg' , 'aaa' , 'agg' ]
Índice | Valor |
---|---|
0 | atg |
1 | aaa |
2 | agg |
A indexação de listas começa em 0
- Tuplas são similares a listas e contêm coleçaões de dados ordenados (indexados).
- Tuplas são imutáveis: você não consegue alterar os valores ou número de elementos
- A tupla é delimitada por parênteses e seus itens são separados por vírgula.
( 'Jan' , 'Fev' , 'Mar' , 'Abr' , 'Mai' , 'Jun' , 'Jul' , 'Ago' , 'Set' , 'Out' , 'Nov' , 'Dez' )
Índice | Valor |
---|---|
0 | Jan |
1 | Fev |
2 | Mar |
3 | Abr |
4 | Mai |
5 | Jun |
6 | Jul |
7 | Ago |
8 | Set |
9 | Out |
10 | Nov |
11 | Dez |
-
Dicionários são bons para armazenar dados que podem ser representados em uma tabela de duas colunas.
-
Eles armazenam coleções de dados em pares de chave/valor, sem ordenação específica.
-
Um dicionário é delimitado por chaves e conjuntos de Chave/Valor separados por vírgula.
-
Um sinal de dois-pontos é colocado entre cada chave e valor. Vírgulas separam pares de chave:valor.
{ 'TP53' : 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC' , 'BRCA1' : 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA' }
Chave | Valor |
---|---|
TP53 | GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC |
BRCA1 | GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA |
Parâmetros de linha de comando são colocados após o nome do script ou programa. Antes do primeiro parâmetro e entre parâmetros adicionais há espaçamento.
Os parâmetros permitem ao usuário fornecer informação ao script quando ele está sendo executado. Python armazena cada trecho do comando em uma lista especial chamada sys.argv
.
Você precisará importar o módulo chamado sys
no início do seu script desta forma:
#!/usr/bin/env python3
import sys
Vamos imaginar que um script é chamado amigos.py
. Se você escrever isso na linha de comando:
$ amigos.py Maria Carlos
Isso acontece dentro do script:
o nome do script 'amigos.py' e as strings 'Maria' e 'Carlos' aparecem na lista chamada
sys.argv
.
Estes são os parâmetros da linha de comando, ou argumentos que queira passar para o script.
sys.argv[0]
é o nome do script. Você pode acessar valores dos outros parâmetros pelos seus índices, começando com 1, entãosys.argv[1]
contém 'Maria' esys.argv[2]
contém 'Carlos'. Você acessa elementos em uma lista adicionando colchetes e o ínidce numérico depois do nome da lista.
Se você quisesse imprimir uma mensagem dizendo que estas duas pessoas são amigas, você poderia escrever um código como este
#!/usr/bin/env python3
import sys
friend1 = sys.argv[1] # get first command line parameter
friend2 = sys.argv[2] # get second command line parameter
# now print a message to the screen
print(friend1,'and',friend2,'are friends')
A vantagem de obter input do usuário da linha de comando é que você pode escrever um script que é genérico. Ele pode imprimir uma mensagem com qualquer input que o usuário fornecer. Isso o torna flexível. O usuário também fornece todos os dados que o script precisa na linha de comando de forma que o script não precisa pedir ao usuário para inserir o nome e esperar até que o usuário o faça. O script pode rodar por conta própria sem mais interações do usuário. Isso permite que o usuário trabalhe em outra coisa. Muito prático!
Você tem um identificador no seu código chamado dados
. Isso representa uma string, uma lista ou um dicionário? Python tem algumas funções que ajudam a descobrir isso.
Função | Descrição |
---|---|
type(dados) |
diz a qual classe seu objeto pertence |
dir(dados) |
diz quais métodos estão disponíveis para o seu objeto |
id(dados) |
diz qual o identificador único do seu objeto |
Nós cobriremos dir()
em mais detalhes mais adiante.
>>> data = [2,4,6]
>>> type(data)
<class 'list'>
>>> data = 5
>>> type(data)
<class 'int'>
>>> id(data)
44990666544
Um operador em uma linguagem de programação é um símbolo que faz o cumpridor ou intérprete para performar operações matemáticas, relativas ou lógicas e produzir um resultado. Aqui explicaremos o conceito de operadores.
Em Python nós podemos escrever declarações que performam cálculos matemáticos. Para fazer isso nós precisamos usar operadores que são específicos para este propósito. Aqui estão operadores aritméticos:
Operador | Descrição | Exemplo | Resultado |
---|---|---|---|
+ |
Adição | 3+2 |
5 |
- |
Subtração | 3-2 |
1 |
* |
Multiplicação | 3*2 |
6 |
/ |
Divisão | 3/2 |
1.5 |
% |
Módulo (divide o operador da esquerda pelo da direita e retorna o lembrete) | 3%2 |
1 |
** |
Expoente | 3**2 |
9 |
// |
Divisão de piso (resultado é o quociente com os dígitos depois do ponto removidos). | 3//2 -11//3 |
1 -4 |
Módulo
Exemplos de piso
>>> 3/2
1.5
>>> 3//2
1
>>> -11/3
-3.6666666666666665
>>> -11//3
-4
>>> 11/3
3.6666666666666665
>>> 11//3
3
Nós usamos operadores de atribuição para atribuir valores para variáveis. Você tem usado =
como operador de atribuição. aqui estão outros:
Operador | Equivalente a | Exemplo | resultado assume o valor |
---|---|---|---|
= |
a = 3 |
result = 3 |
3 |
+= |
result = result + 2 |
result = 3 ; result += 2 |
5 |
-= |
result = result - 2 |
result = 3 ; result -= 2 |
1 |
*= |
result = result * 2 |
result = 3 ; result *= 2 |
6 |
/= |
result = result / 2 |
result = 3 ; result /= 2 |
1.5 |
%= |
result = result % 2 |
result = 3 ; result %= 2 |
1 |
**= |
result = result ** 2 |
result = 3 ; result **= 2 |
9 |
//= |
result = result // 2 |
result = 3 ; result //= 3 |
1 |
Estes operadores comparam dois valores e retornam verdadeiro ou falso.
Operador | Descrição | Exemplo | Resultado |
---|---|---|---|
== |
equal to | 3 == 2 |
Falso |
!= |
not equal | 3 != 2 |
Verdadeiro |
> |
greater than | 3 > 2 |
Verdadeiro |
< |
less than | 3 < 2 |
Falso |
>= |
greater than or equal | 3 >= 2 |
Verdadeiro |
<= |
less than or equal | 3 <= 2 |
Falso |
Operadores lógicos permitem combinar dois ou mais conjuntos de comparações. Você pode combinar os resultados de diferentes formas. Por exemplo você pode 1) querer que todos as declarações sejam verdade, 2) que apenas uma declaração precise ser verdadeira, ou 3) que a declaração precise ser falsa.
Operador | Descrição | Exemplo | Resultado |
---|---|---|---|
and |
Verdadeiro se o operador da esquerda e o da direita forem verdade | 3>=2 and 2<3 |
Verdadeiro |
or |
Verdadeiro se o operador da esquerda ou o da direita forem verdade | 3==2 or 2<3 |
Falso |
not |
Inverte o status lógico | not False |
Verdadeiro |
Você pode testar para ver se o valor é incluído em uma string, tupla ou lista. Você pode também testar que o valor não está incluso na string, tupla ou lista.
Operador | Descrição |
---|---|
in |
Verdadeiro se o valor é incluso em uma lista, tupla ou string |
not in |
Verdadeiro se o valor é ausente em uma lista, tupla ou string |
Por Exemplo:
>>> dna = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
>>> 'TCT' in dna
True
>>>
>>> 'ATG' in dna
False
>>> 'ATG' not in dna
True
>>> codons = [ 'atg' , 'aaa' , 'agg' ]
>>> 'atg' in codons
True
>>> 'ttt' in codons
False
Operadores são listados em ordem de precedência. Os maiores listados primeiro. Nem todos operadores listados aqui são mencionados acima.
Operador | Descrição |
---|---|
** |
Exponenciação (Eleva o poder) |
~ + - |
Complemento, unário mais e menos (nomes de métodos que os dois últimos são +@ e -@) |
* / % // |
Multiplica, divide, módulo e divisão de piso |
+ - |
Adição e subtração |
>> << |
Deslocamento parte por parte de direita e esquerda |
& |
Deslocamento 'AND' |
^ | |
Bitwise exclusivo 'OR' e regular 'OR' |
<= < > >= |
Operadores de comparação |
<> == != |
Operadores de igualdade |
= %= /= //= -= += *= **= |
Operadores de atribuição |
is |
Operadores de identidade |
is not |
Operador de não identidade |
in |
Operador de filiação |
not in |
Operador de filiação negativa |
not or and |
Operadores lógicos |
Nota: Saiba mais a respeito bitwise operators.
Vamos voltar um pouco... O que é verdade?
Tudo é verdade, exceto por:
expressão | VERDADEIRO/FALSO |
---|---|
0 |
FALSO |
None |
FALSO |
False |
FALSO |
'' (string vazia) |
FALSO |
[] (lista vazia) |
FALSO |
() (tupla vazia) |
FALSO |
{} (dicionário vazio) |
FALSO |
O que significa que estes são verdade:
expressão | VERDADEIRO/FALSO |
---|---|
'0' |
VERDADEIRO |
'None' |
VERDADEIRO |
'False' |
VERDADEIRO |
'True' |
VERDADEIRO |
' ' (string de um espaço vazio) |
VERDADEIRO |
bool()
é uma função que testará se um valor é verdade.
>>> bool(True)
True
>>> bool('True')
True
>>>
>>>
>>> bool(False)
False
>>> bool('False')
True
>>>
>>>
>>> bool(0)
False
>>> bool('0')
True
>>>
>>>
>>> bool('')
False
>>> bool(' ')
True
>>>
>>>
>>> bool(())
False
>>> bool([])
False
>>> bool({})
False
Declarações de controle são usadas para direcionar o fluxo do seu código e criar oportunidade para tomada de decisão. Os fundamentos das declarações de controle são construindo a verdade.
- Use a declaração
if
para testar a verdade e executar linhas do código caso seja verdade. - Quando a expressão avalia como verdade cada uma das declarações recuadas abaixo da declaração
if
, também conhecidas como o bloco de declarações aninhadas, serão executadas.
if
expressão if :
declaração
declaração
Por Exemplo:
dna = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
if 'AGC' in dna:
print('found AGC in your dna sequence')
Returns:
found AGC in your dna sequence
else
- A porção
if
da declaração if/else statement se comporta como antes. - O primeiro bloco recuado é executado se a condição é verdadeira. .
- Se a condição for falsa, o segundo bloco else recuado é executado.
dna = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
if 'ATG' in dna:
print('found ATG in your dna sequence')
else:
print('did not find ATG in your dna sequence')
Returns:
did not find ATG in your dna sequence
- A condição
if
é testada como antes, e o bloco recuado é executado caso a condição for verdadeira. - Se for falsa, o bloco recuado seguindo o
elif
é executado se a primeira condiçãoelif
for verdadeira. - Quaisquer condições restantes
elif
serão testadas em ordem até que uma verdadeira for encontrada. Se nenhuma for, o bloco recuadoelse
é executado.
count = 60
if count < 0:
message = "is less than 0"
print(count, message)
elif count < 50:
message = "is less than 50"
print (count, message)
elif count > 50:
message = "is greater than 50"
print (count, message)
else:
message = "must be 50"
print(count, message)
Returns:
60 is greater than 50
Vamos mudar a contagem para 20, qual declaração será executada?
count = 20
if count < 0:
message = "is less than 0"
print(count, message)
elif count < 50:
message = "is less than 50"
print (count, message)
elif count > 50:
message = "is greater than 50"
print (count, message)
else:
message = "must be 50"
print(count, message)
Returns:
20 is less than 50
O que acontece quando a contagem é 50?
count = 50
if count < 0:
message = "is less than 0"
print(count, message)
elif count < 50:
message = "is less than 50"
print (count, message)
elif count > 50:
message = "is greater than 50"
print (count, message)
else:
message = "must be 50"
print(count, message)
Returns:
50 must be 50
Python reconhece 3 tipos de números: inteiros, números de ponto flutuante e números complexos.
- Conhecidos como int
- Um int pode ser positivo ou negativo
- e não contém um ponto decimal ou expoente.
- Conhecido como float
- Um ponto flutuante pode ser positivo ou negativo
- E contém um ponto decimal (
4.875
) ou expoente (4.2e-12
)
- conhecido como complex
- está na forma de a+bi onde bi é uma parte imaginária.
As vezes um tipo de número precisa ser mudado por outro para a função poder trabalhar. Aqui está a lista de funções para converter tipos de números:
função | Descrição |
---|---|
int(x) |
para converter x para um inteiro simples |
float(x) |
para converter x para um número de ponto flutuante |
complex(x) |
para converter x para um número complexo com parte real x e parte imaginária zero |
complex(x, y) |
para converter x e y para um número complexo com parte real x e parte imaginária y |
>>> int(2.3)
2
>>> float(2)
2.0
>>> complex(2.3)
(2.3+0j)
>>> complex(2.3,2)
(2.3+2j)
Aqui está a lista de funções que usam números como argumentos. Elas são úteis como arredondamento.
função | Descrição |
---|---|
abs(x) |
O valor absoluto de x: a distância (positiva) entre x e zero. |
round(x [,n]) |
x arredondado para n dígitos do ponto decimal. round() arredonda para um inteiro se o valor é exatamente entre dois inteiros, então round(0.5) é 0 e round(-0.5) é 0. round(1.5) é 2. Arredondar para um número fixo de lugares decimais pode fornecer resultados imprevisíveis. |
max(x1, x2,...) |
O último argumento é retornado |
min(x1, x2,...) |
O menor argumento é retornado |
>>> abs(2.3)
2.3
>>> abs(-2.9)
2.9
>>> round(2.3)
2
>>> round(2.5)
2
>>> round(2.9)
3
>>> round(-2.9)
-3
>>> round(-2.3)
-2
>>> round(-2.009,2)
-2.01
>>> round(2.675, 2) # note this rounds down
2.67
>>> max(4,-5,5,1,11)
11
>>> min(4,-5,5,1,11)
-5
Muitas funções numéricas não são construídas dentro da central do Python e precisam ser importadas para dentro do script se quisermos usá-las. Para incluir elas, no topo do script digite:
import math
Estas próximas funções são encontradas no módulo matemático e precisam ser importadas. Para usá-las, preceda a função com o nome do módulo, i.e, math.ceil(15.5)
math.function | Descrição |
---|---|
math.ceil(x) |
retorna o menor inteiro maior ou igual que x |
math.floor(x) |
retorna o maior inteiro menor ou igual que x. |
math.exp(x) |
O exponencial de x: ex é retornado |
math.log(x) |
O logarítmo natural de x, para x > 0 é retornado |
math.log10(x) |
O logarítmo de base 10 de x para x > 0 é retornado |
math.modf(x) |
As partes fracionárias e inteiras de x são retornadas em uma tupla de dois itens |
math.pow(x, y) |
O valor de x criado pelo poder y é retornado |
math.sqrt(x) |
Retorna a raíz quadrada de x para x >= 0 |
>>> import math
>>>
>>> math.ceil(2.3)
3
>>> math.ceil(2.9)
3
>>> math.ceil(-2.9)
-2
>>> math.floor(2.3)
2
>>> math.floor(2.9)
2
>>> math.floor(-2.9)
-3
>>> math.exp(2.3)
9.974182454814718
>>> math.exp(2.9)
18.17414536944306
>>> math.exp(-2.9)
0.05502322005640723
>>>
>>> math.log(2.3)
0.8329091229351039
>>> math.log(2.9)
1.0647107369924282
>>> math.log(-2.9)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
>>>
>>> math.log10(2.3)
0.36172783601759284
>>> math.log10(2.9)
0.4623979978989561
>>>
>>> math.modf(2.3)
(0.2999999999999998, 2.0)
>>>
>>> math.pow(2.3,1)
2.3
>>> math.pow(2.3,2)
5.289999999999999
>>> math.pow(-2.3,2)
5.289999999999999
>>> math.pow(2.3,-2)
0.18903591682419663
>>>
>>> math.sqrt(25)
5.0
>>> math.sqrt(2.3)
1.51657508881031
>>> math.sqrt(2.9)
1.70293863659264
Algumas vezes, é necessário comparar dois números e descobrir se o primeiro é menor, igual ou maior que o segundo.
A simples função cmp(x,y)
não é disponível em Python 3.
Use este idioma ao invés:
cmp = (x>y)-(x<y)
Ele retorna três diferentes valores dependendo do x e do y
-
se x<y, o -1 é retornado
-
se x>y, o 1 é retornado
-
x == y, o 0 é retornado
Na próxima seção, nós iremos aprender sobre as strings, tuplas, e listas. Todos estes são exemplos de sequências em python. uma sequência de caracteres 'ACGTGA'
, uma tupla (0.23, 9.74, -8.17, 3.24, 0.16)
, e uma lista ['dog', 'cat', 'bird']
são sequências de diferentes tipos de dados. Veremos mais detalhes em breve.
Em Python, um tipo de objeto consegue operações que pertencem àquele tipo. Sequências tem operações sequenciais então as strings podem também usar operações sequenciais. Strings também possuem suas próprias operações específicas.
Você pode perguntar qual a extensão de qualquer sequência
>>>len('ACGTGA') # extensão de uma string
6
>>>len( (0.23, 9.74, -8.17, 3.24, 0.16) ) # extensão de uma tupla, precisa de dois parênteses (( ))
5
>>>len(['dog', 'cat', 'bird']) # extensão de uma lista
3
Você pode também usar funções de strings específicas, mas não em listas e vice versa. Nós vamos aprender mais sobre isso posteriormente. rstrip()
é um método de string ou função. Você obtém um erro se você tentar usar isso em uma lista.
>>> 'ACGTGA'.rstrip('A')
'ACGTG'
>>> ['dog', 'cat', 'bird'].rstrip()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'rstrip'
Como descobrir quais funções servem com um objeto? Existe uma função prática dir()
. Como um exemplo quais funções você pode acionar em sua string 'ACGTGA'
?
>>> dir('ACGTGA')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
dir()
irá retornar todos os atributos de um objeto, dentre eles estão funções. Tecnicamente, funções pertencentes a uma classe específica (tipo de objeto) são chamadas de métodos.
Você pode chamar dir()
em qualquer objeto, mais comumente, você usará isso na esfera interativa do Python.
- Uma string é uma série de caracteres começando e terminando com marcas de aspas únicas ou duplas.
- Strings são um exemplo de uma sequência de Python. Uma sequência é definida como um grupo ordenado posicionalmente. Isso significa que cada elemento no grupo tem uma posição, começando com zero, i.e. 0,1,2,3 e assim até você chegar no final da string.
- Única (')
- Dupla (")
- Tripla (''' or """)
Notas sobre as aspas:
- Aspas únicas e duplas são equivalentes.
- O nome de uma variável dentro das sentenças é apenas o identificador da string, não o valor armazenado dentro da variável.
format()
é útil para interpolação de variáveis em python - Sentenças triplas (únicas ou dobradas) são usadas antes e depois de uma string que abrange múltiplas linhas.
Uso de exemplos das aspas:
palavra = 'word'
sentença = "This is a sentence."
parágrafo = """This is a paragraph. Isso é feito de múltiplas linhas e sentenças.
E assim vai.
"""
Nós vimos exemplos de print()
antes. Vamos conversar sobre isso um pouco mais. print()
é uma função que assume um ou mais argumentos separados por vírgulas.
Vamos usar a função print()
para imprimir uma string.
>>>print("ATG")
ATG
Vamos atribuir uma string a uma variável e imprimir a variável.
>>>dna = 'ATG'
ATG
>>> print(dna)
ATG
O que acontece se nós colocarmos a variável nas sentenças?
>>>dna = 'ATG'
ATG
>>> print("dna")
dna
A string literal 'dna' é impressa na tela, não os conteúdos 'ATG'
Vamos ver o que acontece quando nós demos print()
em duas strings literais como argumentos.
>>> print("ATG","GGTCTAC")
ATG GGTCTAC
Nós conseguimos as duas strings literais impressas na tela separadas por um espaço
E se vocÊ não quiser suas strings separadas por um espaço? use o operador concatenação para concatenar as duas strings antes ou dentro da função print()
.
>>> print("ATG"+"GGTCTAC")
ATGGGTCTAC
>>> combined_string = "ATG"+"GGTCTAC"
ATGGGTCTAC
>>> print(combined_string)
ATGGGTCTAC
Nós conseguimos duas strings impressas na tela sem ser separadas por um espaço. Você pode também usar isso
>>> print('ATG','GGTCTAC',sep='')
ATGGGTCTAC
Agora, vamos imprimir uma variável e uma string literal.
>>>dna = 'ATG'
ATG
>>> print(dna,'GGTCTAC')
ATG GGTCTAC
Nós conseguimos o valor da variável e a string literal impressa na tela separada por um espaço
Como poderíamos imprimir os dois sem um espaço?
>>>dna = 'ATG'
ATG
>>> print(dna + 'GGTCTAC')
ATGGGTCTAC
Algo para se pensar sobre: valores de variáveis são variáveis. Em outras palavras, eles são mutáveis e alteráveis.
>>>dna = 'ATG'
ATG
>>> print(dna)
ATG
>>>dna = 'TTT'
TTT
>>> print(dna)
TTT
O novo valor da variável 'dna' é impresso no visor quando
dna
é um argumento para a funçãoprint()
.
Vamos olhar os erros típicos que você encontrará quando usar a função print()
.
O que acontecerá se você esquecer de fechar suas sentenças?
>>> print("GGTCTAC)
File "<stdin>", line 1
print("GGTCTAC)
^
SyntaxError: EOL while scanning string literal
Nós obtemos um'SyntaxError' se a sentença de encerramento não for usada.
O que acontecerá se você se esquecer de incluir uma string que você quer imprimir nas sentenças?
>>> print(GGTCTAC)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'GGTCTAC' is not defined
Nós obtemos um 'NameError' quando a string literal não for inclusa nas sentenças porque o Python está procurando uma variável com o nome GGTCTAC
>>> print "boo"
File "<stdin>", line 1
print "boo"
^
SyntaxError: Missing parentheses in call to 'print'
Em python2, o comando era print
, mas isso mudou para print()
em python3, então não se esqueça dos parênteses!
Como você incluiria uma nova linha, retorno de transporte, ou tab em sua string?
Caractere de escape | Descrição |
---|---|
\n | Nova linha |
\r | Retorno de transporte |
\t | Tab |
Vamos incluir alguns caracteres de escape em suas strings e funções print()
.
>>> string_with_newline = 'this sting has a new line\nthis is the second line'
>>> print(string_with_newline)
this sting has a new line
this is the second line
Nós imprimimos uma nova linha na tela
print()
adiciona espaços entre argumentos e uma nova linha ao final. Você pode mudar isso com sep=
e end=
. Aqui está um exemplo:
print('one line', 'second line' , 'third line', sep='\n', end = '')
Uma forma mais limpa para fazer isso é expressar uma string de múltiplas linhas inclusa em aspas triplas (""").
>>> print("""this string has a new line
... this is the second line""")
this string has a new line
this is the second line
Vamos imprimir um caractere tab (\t).
>>> line = "value1\tvalue2\tvalue3"
>>> print(line)
value1 value2 value3
Nós obtemos as três palavras separadas por caracteres tab. Um formato comum para dados é separar colunas com tabs como isso.
Você pode adicionar uma barra invertida antes de qualquer caractere para forçar de ser impresso como um literal. Isso é chamado 'escaping'. Só é realmente útil para imprimir sentenças literais ' and "
>>> print('this is a \'word\'') # if you want to print a ' inside '...'
this is a 'word'
>>> print("this is a 'word'") # maybe clearer to print a ' inside "..."
this is a 'word'
Em ambos os casos a sentença atual única é impressa na tela
Se você quiser todos caracteres em sua string para permanecer exatamente como são, declare sua string uma string crua literal com 'r' antes da primeira sentença. Isso parece feio, mas funciona.
>>> line = r"value1\tvalue2\tvalue3"
>>> print(line)
value1\tvalue2\tvalue3
Nossos caracteres de escape '\t' declare como nós digitamos, eles não são convertidos para caracteres tab de fato.
Para concatenar strings use o operador de concatenação '+'
>>> promoter= 'TATAAA'
>>> upstream = 'TAGCTA'
>>> downstream = 'ATCATAAT'
>>> dna = upstream + promoter + downstream
>>> print(dna)
TAGCTATATAAAATCATAAT
O operador de concatenação pode ser usado para combinar strings. A nova combinação de strings pode ser armazenada em uma variável.
O que acontece se você usar +
com números (estes são inteiros ou ints)?
>>> 4+3
7
Para strings, +
concatena; para inteiros, +
soma.
Você precisa converter os números para strings antes de poder concatená-las
>>> str(4) + str(3)
'43'
Use a função len()
para calcular a extensão de uma string. Essa função assume a sequência como um argumento e retorna uma int
>>> print(dna)
TAGCTATATAAAATCATAAT
>>> len(dna)
20
A extensão de uma string, incluindo espaços, é calculada e apresentada.
O valor que len()
retorna pode ser armazenado em uma variável.
>>> dna_length = len(dna)
>>> print(dna_length)
20
Você pode misturar strings e ints em print()
, mas não em concatenação.
>>> print("The lenth of the DNA sequence:" , dna , "is" , dna_length)
The lenth of the DNA sequence: TAGCTATATAAAATCATAAT is 20
Alterando o caso da string é um pouco distinto do que você pode esperar inicialmente. Por exemplo, para diminuir uma string precisamos utilizar um método. Um método é uma função específica para um objeto. Quando nós assumimos uma string a uma variável estamos criando uma instância de um objeto de string. Esse objeto tem uma série de métodos que funcionarão nos dados que estão armazenados no objeto. Lembre-se que dir()
irá te dizer todos os métodos que estão disponíveis para um objeto. A função lower()
é um método de string.
Vamos criar um novo objeto de string.
dna = "ATGCTTG"
Parece familiar?
Agora que nós temos um objeto de string nós podemos usar os métodos de string. A forma que você utiliza um método consiste em inserir um '.' entre o objeto e o nome do método.
>>> dna = "ATGCTTG"
>>> dna.lower()
'atgcttg'
o método lower() retorna os conteúdos armazenados na variável 'dna' em letra minúscula.
Os conteúdos da variável 'dna' não se alteraram. Strings são imutáveis. Se você quiser manter a versão minúscula de uma string, armazene ela em uma nova variável.
>>> print(dna)
ATGCTTG
>>> dna_lowercase = dna.lower()
>>> print(dna)
ATGCTTG
>>> print(dna_lowercase)
atgcttg
O método de string pode ser guardado dentro de outras funções.
>>> dna = "ATGCTTG"
>>> print(dna.lower())
atgcttg
Os conteúdos de 'dna' são transformados em minúsculos e trasnportados para a função
print()
.
Se você tentar usar um método de string em um objeto que não é uma string você receberá um erro.
>>> nt_count = 6
>>> dna_lc = nt_count.lower()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'lower'
Você obtém um AttributeError quando você usa um método em um tipo de objeto incorreto. Nós recebemos que o objeto int (um int é retornado por
len()
) não tem uma função chamada inferior.
Vamos tornar uma string maiúscula agora.
>>> dna = 'attgct'
>>> dna.upper()
'ATTGCT'
>>> print(dna)
attgct
Os conteúdos de uma variável 'dna' são retornados em maiúsculo. Os conteúdos de 'dna' não foram alterados.
O índice posicional de uma string exata em uma string maior pode ser encontrado e retornado com o método de string
find()
. Uma string exata é dada como um argumento e o índice de sua primeira ocorrência é retornado. -1 é retornado se nada for encontrado.
>>> dna = 'ATTAAAGGGCCC'
>>> dna.find('T')
1
>>> dna.find('N')
-1
O subtermo 'T' é encontrado pela primeira vez no índice 1 na string 'dna' então 1 é retornado. O subtermo 'N' não foi encontrado, então -1 é retornado.
count(str)
retorna o número (como um int) que se encaixa exatamente com a string que encontrou
>>> dna = 'ATGCTGCATT'
>>> dna.count('T')
4
O número de vezes que 'T' for encontrado é retornado. A string armazenada em 'dna' não é alterada.
replace(str1,str2)
retorna uma nova string com todas as combinações de str1
em uma string substituída com str2
.
>>> dna = 'ATGCTGCATT'
>>> dna.replace('T','U')
'AUGCUGCAUU'
>>> print(dna)
ATGCTGCATT
>>> rna = dna.replace('T','U')
>>> print(rna)
AUGCUGCAUU
Todos as ocorrências de T são substitupidas por U. A nova string é retornada. A string original não foi de fato alterada. Se você quiser reutilizar a nova string, armazene ela em uma variável.
Partes de uma string podem ser localizadas baseadas na posição e retornadas. Isso é porque uma string é uma sequência. Coordenadas começam em 0. Você adiciona a coordenada em colchetes depois do nome da string.
Você pode chegar a qualquer parte da string com a seguinte sentença [start : end : step].
Essa string 'ATTAAAGGGCCC' é feita da seguinte sequência de caracteres, e posições (começando em zero).
Posição/Índice | Caractere |
---|---|
0 | A |
1 | T |
2 | T |
3 | A |
4 | A |
5 | A |
6 | G |
7 | G |
8 | G |
9 | C |
10 | C |
11 | C |
Vamos retornar os 4°, 5° e 6° nucleotídeos. Para isso, nós precisamos começar contando em 0 e lembrando que o python conta os vãos entre cada caractere, começando com zero.
index 0 1 2 3 4 5 6 7 8 ...
string A T T A A A G G ...
>>> dna = 'ATTAAAGGGCCC'
>>> sub_dna = dna[3:6]
>>> print(sub_dna)
AAA
Os caracteres com índices 3, 4, 5 são retornados. Em outras palavras, todo caractere começando com o índice 3 e acima mas não incluindo, o índice de 6 que retornado.
Vamos retornar os primeiros 6 caracteres.
>>> dna = 'ATTAAAGGGCCC'
>>> sub_dna = dna[0:6]
>>> print(sub_dna)
ATTAAA
Todo caractere começando no índice 0 e acima mas não incluindo o de índice 6 são retornados. Esse é o mesmo que dna[:6]
Vamos retornar todos os caracteres do índice 6 até o fim da string.
>>> dna = 'ATTAAAGGGCCC'
>>> sub_dna = dna[6:]
>>> print(sub_dna)
GGGCCC
Quando o segundo argumento é deixado em branco, todos caracteres do índice 6 e acima são retornados.
Vamos retornar os últimos 3 caracteres.
>>> sub_dna = dna[-3:]
>>> print(sub_dna)
CCC
Quando o segundo argumento é deixado em branco e o primeiro argumento é negativo (-X), X caracteres do final da string são retornados.
Não existe função de reverso, você precisa usar uma fatia com patamar -1 e início e fim vazios.
Para uma string, se parece com isso
>>> dna='GATGAA'
>>> dna[::-1]
'AAGTAG'
Desde que estes são métodos, se certifique de utilizar na sentença string.method()
.
função | Descrição |
---|---|
s.strip() |
retorna uma string com o espaço em branco removido do começo e fim |
s.isalpha() |
testa se todos caracteres da string são alfabéticos. Retorna verdadeiro ou falso. |
s.isdigit() |
testa se todos caracteres da string são nnuméricos. Retorna verdadeiro ou falso. |
s.startswith('other_string') |
testa se a string começa com a string fornecida como argumento. Retorna verdadeiro ou falso. |
s.endswith('other_string') |
testa se a string termina com a string fornecida como argumento. Retorna verdadeiro ou falso. |
s.split('delim') |
separa a string no delimitador exato fornecido. Retorna a lista de subtermos. Se o argumento é fornecido, a string será separada no espaço em branco. |
s.join(list) |
O oposto de split() . Os elementos de uma lista serão concatenados juntos usando a string armazenada em 's' como um delimitadoras. |
split
split
é um método ou forma de partir uma string em um grupo de caracteres. O que é retornado é uma lista de elementos com caracteres que são usados para partir removidos. Iremos através das listas em mais detalhes na próxima sessão. Não se preocupe com isso.
Vamos olhar para essa string:
00000xx000xx000000000000xx0xx00
Vamos separar em 'xx' e obter uma lista dos 0's
O que é o What 's' em s.split(delim)
?
O que é 'delim' em s.split(delim)
?
Vamos tentar isso:
>>> string_to_split='00000xx000xx000000000000xx0xx00'
>>> string_to_split.split('xx')
['00000', '000', '000000000000', '0', '00']
>>> zero_parts = string_to_split.split('xx')
>>> print(zero_parts)
['00000', '000', '000000000000', '0', '00']
Nós começamos com uma string e agora temos uma lista com todos os delimitadores removidos
Aqui está outro exemplo. Vamos dividir em tabs para obter uma lista dos números em colunas separadas tab.
>>> input_expr = '4.73\t7.91\t3.65'
>>> expression_values = input_expr.split('\t')
>>> expression_values
['4.73', '7.91', '3.65']
join
join
é um método ou uma forma de pegar uma lista de elementos, de coisas, e transformar em uma string com algo posto entre cada elemento. A lista será coberta na próxima seção com mais detalhes.
Vamos aplicar em uma lista de Ns list_of_Ns = ['NNNNN', 'NNN', 'N', 'NNNNNNNNNNNNNNN', 'NN']
em 'xx' para obter essa string:
NNNNNxxNNNxxNxxNNNNNNNNNNNNNNNxxNN
O que é o 's' em s.join(list)
?
O que é a 'list' em s.join(list)
?
>>> list_of_Ns = ['NNNNN', 'NNN', 'N', 'NNNNNNNNNNNNNNN', 'NN']
>>> list_of_Ns
['NNNNN', 'NNN', 'N', 'NNNNNNNNNNNNNNN', 'NN']
>>>
>>> string_of_elements_with_xx = 'xx'.join(list_of_Ns)
>>> string_of_elements_with_xx
'NNNNNxxNNNxxNxxNNNNNNNNNNNNNNNxxNN'
Nós começamos com uma lista e agora temos todos os elementos em uma string com o delimitador adicionado entre cada elemento.
Vamos pegar uma lista de valores de expressão e criar uma string delimitada tab que abrirá bem em uma planilha com cada valor em sua própria coluna:
>>> expression_values = ['4.73', '7.91', '3.65']
>>>expression_values
['4.73', '7.91', '3.65']
>>> expression_value_string = '\t'.join(expression_values)
>>> expression_value_string
'4.73\t7.91\t3.65'
imprima isso em um arquivo e abra ele em Excel, é lindo!!
Strings podem ser formatadas usando a função format()
. Bem intuitivo, mas espere até ver os detalhes! Por exemplo, se você quiser incluir strings literais e variáveis em sua declaração de impressão e não quer concatenar ou usar múltiplos argumentos na função print()
você pode usar formatação de string.
>>> string = "This sequence: {} is {} nucleotides long and is found in {}."
>>> string.format(dna,dna_len,gene_name)
'This sequence: TGAACATCTAAAAGATGAAGTTT is 23 nucleotides long and is found in Brca1.'
>>> print(string) # string.format() does not alter string
This sequence: {} is {} nucleotides long and is found in {}.
>>> new_string = string.format(dna,dna_len,gene_name)
>>> print(new_string)
This sequence: TGAACATCTAAAAGATGAAGTTT is 23 nucleotides long and is found in Brca1.
Nós colocamos juntamente três variáveis e strings literais em uma string única usando a função format()
. A string original não é alterada, uma nova string é retornada e incorpora os argumentos. Você pode salvar o valor retornado em uma nova variável. Cada {}
é um espaço reservado para a string que precisa ser inserida.
Algo legal sobre format()
é que você pode imprimir int e tipos variáveis de string sem converter primeiramente.
Você pode também chamar diretamente format()
dentro de uma função print()
. Aqui estão dois exemplos
>>> string = "This sequence: {} is {} nucleotides long and is found in {}."
>>> print(string.format(dna,dna_len,gene_name))
This sequence: TGAACATCTAAAAGATGAAGTTT is 23 nucleotides long and is found in Brca1.
Ou use a função format()
em uma string literal:
>>> print( "This sequence: {} is {} nucleotides long and is found in {}.".format(dna,dna_len,gene_name))
This sequence: TGAACATCTAAAAGATGAAGTTT is 23 nucleotides long and is found in Brca1.
Até agora, nós usamos apenas {}
para mostrar onde inserir o valor de uma variável em uma string. Você pode adicionar caracteres especiais dentro de {}
para mudar a forma que a variável é formatada quando é inserida dentro da string.
Você pode numerar estes, não necessariamente em ordem.
>>> '{0}, {1}, {2}'.format('a', 'b', 'c')
'a, b, c'
>>> '{2}, {1}, {0}'.format('a', 'b', 'c')
'c, b, a'
Para mudar o espaçamento das strings e a forma que os números são formatados, você adiciona :
e outros caracteres especiais como isso {:>5}
para corrigir uma string em um campo de cinco caracteres
Vamos corrigir justificando alguns números.
>>> print( "{:>5}".format(2) )
2
>>> print( "{:>5}".format(20) )
20
>>> print( "{:>5}".format(200) )
200
E sobre preencher com zeros? Isso significa que o campo de cinco caracteres será preenchido conforme preciso com zeros a esquerda de quaisquer números que você quer apresentar
>>> print( "{:05}".format(2) )
00002
>>> print( "{:05}".format(20) )
00020
Use um <
para indicar justificação à esquerda.
>>> print( "{:<5} genes".format(2) )
2 genes
>>> print( "{:<5} genes".format(20) )
20 genes
>>> print( "{:<5} genes".format(200) )
200 genes
Alinhamento ao centro é feito com ^
ao invés de >
ou <
. Você pode também preencher com caracteres sem ser 0. Aqui vamos tentar _
ou sublinhar como em :_^
. O símbolo de preencher vai antes do símbolo de alinhamento.
>>> print( "{:_^10}".format(2) )
____2_____
>>> print( "{:_^10}".format(20) )
____20____
>>> print( "{:_^10}".format(200) )
___200____
Aqui estão algumas das opções de ALINHAMENTO:
Opção | Significado | |
---|---|---|
< |
Força o campo para estar alinhado a esquerda com o espaço disponível (Isso é o padrão para a maioria dos objetos). | |
> |
Força o campo para estar alinhado a direita com o espaço disponível (Isso é o padrão para números). | |
= |
Força o campo para o preenchimento ser posto de pois do sinal (se tiver) mas antes dos dígitos. Isso é usado para imprimir campos na forma ‘+000000120’. Essa opção de alinhamento é apenas válida para tipos numéricos. | |
^ |
Força o campo para ser centralizado com o espaço disponível. |
Aqui está um exemplo
{ : x < 10 s}
preencher com
x
justificamento à esquerda<
10
um campo com dez caracteress
uma string
Tipos comuns
tipo | descrição |
---|---|
b | converte para binário |
d | inteiro decimal |
e | expoente, precisão padrão é 6, usa e |
E | expoente, usa E |
f | ponto de flutuação, precisão padrão é 6 (também F) |
g | número genérico, flutua para valores próximos de 0, expoente para outros; também G |
s | string, tipo padrão (conforme exemplo acima) |
x | converte para hexadecimal, também X |
% | converte para % multiplicando por 100 |
Muito pode ser feito com a função format()
. Aqui está um último exemplo, mas não a última funcionalidade desta função. vamos circular um número de ponto de flutuação para algumas casas decimais, começando com muitos. (o padrão é 6). Note que a função circula para a casa decimal mais próxima, mas nem sempre exatamente da forma que você espera por conta da forma que os computadores representam decimais com 1s e 0s.
'{:f}'.format(3.141592653589793)
'3.141593'
>>> '{:.4f}'.format(3.141592653589793)
'3.1416'
Listas são tipos de dados que armazenam uma coleção de dados.
- Listas são usadas para armazenar uma coleção de dados ordenada e indexada.
- Valores são separados por vírgulas
- Valores são anexados entre colchetes '[]'
- Listas podem crescer e encolher
- Valores são mutáveis
[ 'atg' , 'aaa' , 'agg' ]
- Tuplas são usadas para armazenar uma coleção de dados ordenada e indexada
- Valores são separados por vírgulas
- Valores são anexados entre parenteses '()'
- Tuplas NÃO podem crescer ou encolher
- Valores são imutáveis
( 'Jan' , 'Fev' , 'Mar' , 'Abr' , 'Mai' , 'Jun' , 'Jul' , 'Ago' , 'Set' , 'Out' , 'Nov' , 'Dez' )
Muitas funções e métodos retornam tuplas como math.modf(x)
. Essa função retorna as partes fracionais e inteiras de x
em uma tupla de dois itens. Aqui não existe motivos para mudar a sequência.
>>> math.modf(2.6)
(0.6000000000000001, 2.0)
Para recuperar um valor em uma lista utilize o índice do valor nesse formato list[index]. Isso retornará o valor do índice especificado, começando com 0.
Aqui está uma lista:
>>> codons = [ 'atg' , 'aaa' , 'agg' ]
Existem 3 valores com os índices 0, 1, 2
Índice | Valor |
---|---|
0 | atg |
1 | aaa |
2 | agg |
Vamos acessar o valor de índice 0. Você vai precisar de um número de índice (0
) dentro de colchetes desta forma [0]
. Isso vai após o nome da lista (codons
)
>>> codons = [ 'atg' , 'aaa' , 'agg' ]
>>> codons[0]
'atg'
O valor pode ser salvo em uma variável, e ser usado depois.
>>> codons = [ 'atg' , 'aaa' , 'agg' ]
>>> first_codon = codons[0]
>>> print(first_codon)
atg
Cada valor pode ser salvo em uma nova variável para usar posteriormente.
Os valores podem ser recuperados e usados diretamente.
>>> codons = [ 'atg' , 'aaa' , 'agg' ]
>>> print(codons[0])
atg
>>> print(codons[1])
aaa
>>> print(codons[2])
agg
Os 3 valores são acessados independentemente e impressos imediatamente. Eles não são armazenados em uma variável.
Se você deseja acessar os valores começando pelo fim da lista, use índices negativos.
>>> codons = [ 'atg' , 'aaa' , 'agg' ]
>>> print(codons[-1])
agg
>>> print(codons[-2])
aaa
Usar um índice negativo retornará os valores do final da lista. Por exemplo, -1 é o índice do último valor 'agg'. Esse valor também possui um índice de 2.
Valores individuais podem ser alterados usando o valor de índice e o operador de atribuição.
>>> print(codons)
['atg', 'aaa', 'agg']
>>> codons[2] = 'cgc'
>>> print(codons)
['atg', 'aaa', 'cgc']
E sobre atribuir um valor para um índice que não existe?
>>> codons[5] = 'aac'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
codon[5] não existe, e quando tentamos atribuir valor para esse índice ocorre um IndexError. Se você deseja adicionar novos elementos no final da lista use
codons.append('taa')
oucodons.extend(list)
. Veja abaixo mais detalhes.
Isso funciona da mesma forma com as listas como com as strings. Isso é porque ambos são sequências, ou cooleções ordenadas de dados com informação posicional. Lembre-se que Python conta as divisões entre os elementos, começando com 0.
Índice | Valor |
---|---|
0 | atg |
1 | aaa |
2 | agg |
3 | aac |
4 | cgc |
5 | acg |
use a syntaxe [start : end : step] para dividir sua sequência python
>>> codons = [ 'atg' , 'aaa' , 'agg' , 'aac' , 'cgc' , 'acg']
>>> print (codons[1:3])
['aaa', 'agg']
>>> print (codons[3:])
['aac', 'cgc', 'acg']
>>> print (codons[:3])
['atg', 'aaa', 'agg']
>>> print (codons[0:3])
['atg', 'aaa', 'agg']
codons[1:3]
retorna todo valor começando com o valor de códons[1] até mas não incluindo os códons[3]
codons[3:]
retorna todo valor começando com o valor de códons[3] e todos os valores posteriores.
codons[:3]
retorna todo valor até mas não incluindo códons[3]
codons[0:3]
é o mesmo quecodons[:3]
Operador | Descrição | Exemplo |
---|---|---|
+ |
Concatenação | [10, 20, 30] + [40, 50, 60] retorna [10, 20, 30, 40, 50, 60] |
* |
Repetição | ['atg'] * 4 retorna ['atg','atg','atg','atg'] |
in |
Filiação | 20 in [10, 20, 30] retorna True |
Funções | Descrição | Exemplo |
---|---|---|
len(list) |
retorna o comprimento ou o número de valores em uma lista | len([1,2,3]) retorna 3 |
max(list) |
retorna o valor com o maior ASCII (=último no alfabeto ASCII) | max(['a','A','z']) retorna 'z' |
min(list) |
retorna o valor com o menor ASCII (=primeiro no alfabeto ASCII) | min(['a','A','z']) retorna 'A' |
list(seq) |
converte uma tupla em uma lista | list(('a','A','z')) retorna ['a', 'A', 'z'] |
sorted(list, key=None, reverse=False) |
retorna uma lista organizada baseada na chave fornecida | sorted(['a','A','z']) retorna ['A', 'a', 'z'] |
sorted(list, key=str.lower, reverse=False) |
str.lower() faz com que todos os elementos fiquem minúsculos antes de organizar |
sorted(['a','A','z'],key=str.lower) retorna ['a', 'A', 'z'] |
Lembre-se que métodos são utilizados no seguinte formato list.method().
Para esses exemplos utilize: nums = [1,2,3]
e codons = [ 'atg' , 'aaa' , 'agg' ]
Métodos | Descrição | Exemplo |
---|---|---|
list.append(obj) |
anexa um objeto no final de uma lista | nums.append(9) ; print(nums) ; retorna [1,2,3,9] |
list.count(obj) |
conta as ocorrências de um objeto em uma lista | nums.count(2) retorna 1 |
list.index(obj) |
retorna o menor índice em que o objeto fornecido é encontrado | nums.index(2) retorna 1 |
list.pop() |
remove e retorna o últio valor de uma lista. A lista é agora um elemento mais curta | nums.pop() retorna 3 |
list.insert(index, obj) |
insere um valor ao índice fornecido. Lembre-se de pensar sobre as divisões entre os elementos | nums.insert(0,100) ; print(nums) retorna [100, 1, 2, 3] |
list.extend(new_list) |
anexa new_list ao final de list |
nums.extend([7, 8]) ; print(nums) retorna [1, 2, 3, 7,8] |
list.pop(index) |
remove e retorna o valor do argumento indexado. A lista é agora um valor mais curta | nums.pop(0) retorna 1 |
list.remove(obj) |
encontra o menor índice do objeto fornecido e remove ele da lista. A lista é agora um elemento mais curta | codons.remove('aaa') ; print(codons) retorna [ 'atg' , 'agg' ] |
list.reverse() |
inverte a ordem da lista | nums.reverse() ; print(nums) retorna [3,2,1] |
list.copy() |
Retorna uma cópia rasa da lista. Rasa vs Deep apenas importa em estruturas de data multidimensionais. | |
list.sort([func]) |
organiza uma lista utilizando a função fornecida. Não retorna uma lista. A lista foi alterada. Uma organização de lista avançada será coberta assim que escrever suas próprias funções for discutido. | codons.sort() ; print(codons) retorna ['aaa', 'agg', 'atg'] |
Tome cuidado em como você faz uma cópia de sua lista
>>> my_list=['a', 'one', 'two']
>>> copy_list=my_list
>>> copy_list.append('1')
>>> print(my_list)
['a', 'one', 'two', '1']
>>> print(copy_list)
['a', 'one', 'two', '1']
Não foi o que esperava?! Ambas listas foram alteradas porque nós apenas copiamos um ponteiro para a lista original quando escrevemos
copy_list=my_list
.
Vamos copiar a lista utilizando o método copy()
.
>>> my_list=['a', 'one', 'two']
>>> copy_list=my_list.copy()
>>> copy_list.append('1')
>>> print(my_list)
['a', 'one', 'two']
Agora sim, nós obtivemos o esperado desta vez!
Agora que você já viu a função append()
nós podemos ir em como construir uma lista valor por vez.
>>> words = []
>>> print(words)
[]
>>> words.append('one')
>>> words.append('two')
>>> print(words)
['one', 'two']
Nós começamos com uma lista vazia chamada 'words'. Nós usamos
append()
para adicionar o valor 'one' depois o valor 'two'. Finalizamos a lista com dois valores. Você pode adicionar uma lista inteira em outra lista comwords.extend(['three','four','five'])
Todas as codificações pelas quais percorremos até então foram executadas linha por linha. Algumas vezes existem blocos de códigos que queremos executar mais do que uma vez. Loops permitem que façamos isso.
Existem dois tipos de loops:
- while loop
- for loop
O while loop vai continuar a executar um bloco de código enquanto a expressão de teste apresentar Verdadeiro
.
while expression:
# estas declarações são executadas sempre que o código entrar em loop
statement1
statement2
more_statements
# código logo abaixo é executado depois que o while loop existir
rest_of_code_goes_here
more_code
A condição é a expressão. O bloco de código while loop é uma coleção de declarações recuadas/indentadas seguindo a expressão.
Código:
#!/usr/bin/env python3
contagem = 0
while contagem < 5:
print("contagem:" , contagem)
contagem+=1
print("Done")
Saída:
$ python while.py
contagem: 0
contagem: 1
contagem: 2
contagem: 3
contagem: 4
Done
A condição while foi verdadeira 5 vezes e o bloco de código while foi executado 5 vezes.
- contagem é igual a 0 quando começamos
- 0 é menos que 5 então executamos o bloco while
- contagem é impressa
- contagem é incrementada (contagem = count + 1)
- contagem é agora igual a 1.
- 1 é menor que 5 então executamos o bloco while pela segunda vez.
- isso permanece até que a contagem seja 5.
- 5 não é menor que 5 então nós saímos do bloco while
- A primeira linha seguindo a declaração while é executada, "Done" é impresso
Um loop infinito ocorre quando um condição while é sempre verdadeira. Aqui está um exemplo de um loop infinito.
#!/usr/bin/env python3
contagem = 0
while contagem < 5: # isso é normalmente um bug!!
print("contagem:" , contagem) # esqueça de incrementar contagem no loop!!
print("Done")
Saída:
$ python infinite.py
contagem: 0
contagem: 0
contagem: 0
contagem: 0
contagem: 0
contagem: 0
contagem: 0
contagemcontagem: 0
...
...
O que fez com que a condição seja sempre
Verdadeira
? A condição que incrementa a contagem está faltando, então sempre será inferior a 5. Para impedir o código de imprimir para sempre utilize ctrl+c. Um comportamento como esse é quase sempre devido a um bug no código.
Uma forma melhor de escrever um loop infinito é com True
. Você precisará incluir algo como if ...: break
#!/usr/bin/env python3
contagem=0
while True:
print("contagem:",contagem)
# Provavelmente você precisará adicionar um "if...: break"
# para poder sair do loop infinito
print('Finished the loop')
Um for loop é um loop que executa o bloco de códigos for para qualquer membro de uma sequência, por exemplo os elementos de uma lista ou as letras de uma string.
for iterating_variable in sequence:
statement(s)
Um exemplo de sequência é uma lista. Vamos usar um for loop com uma lista de palavras.
Código:
#!/usr/bin/env python3
words = ['zero','um','dois','três','quatro']
for word in words:
print(word)
Perceba como eu nomeei minhas variáveis, a lista é plural e a variável interativa é singular
Saída:
python3 list_words.py
zero
um
dois
três
quatro
Esse próximo exemplo é utilizando um for loop para interagir em uma string. Lembre-se que uma string é uma sequência como uma lista. Cada caractere possui uma posição. Olhe novamente em "Extraindo uma substring, ou Recortando" na seção Strings para ver outras formas em que strings podem ser tratadas como listas.
Código:
#!/usr/bin/env python3
dna = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
for nt in dna:
print(nt)
Saída:
$ python3 for_string.py
G
T
A
C
C
T
T
...
...
Essa é uma forma fácil de acessar cada caractere em uma string. É especialmente bom para sequências de DNA.
Outro exemplo de interagir em uma lista de variáveis, estes números de tempo.
Código:
#!/usr/bin/env python3
numbers = [0,1,2,3,4]
for num in numbers:
print(num)
Saída:
$ python3 list_numbers.py
0
1
2
3
4
Python tem uma função chamada range()
que retornará números que podem ser convertidos em lista.
>>> range(5)
range(0, 5)
>>> list(range(5))
[0, 1, 2, 3, 4]
A função range()
pode ser utilizada em conjunto com um for loop para interar em um faixa de números. AlcaA faixa (range) também começa com 0 e opera sobre os espaços entre os números.
Código:
#!/usr/bin/env python3
for num in range(5):
print(num)
Saída:
$ python list_range.py
0
1
2
3
4
Como pode ver esta é a mesma saída que quando utilizamos a lista
numbers = [0, 1, 2, 3, 4]
E esse tem a mesma funcionalidade que um while loop com a condiçãocount = 0
;count < 5
.
Esse é o while loop equivalente
Código:
count = 0
while count < 5:
print(count)
count+=1
Saída:
0
1
2
3
4
As declarações de controle de loop permitem alteração no fluxo normal de execução.
Declaração de controle | Descrição |
---|---|
break |
Um loop é terminado quando uma declaração break é executada. Todas as linhas de código após o break, mas dentro do bloco de loop não são executadas. Sem mais interações do loop sendo executadas |
continue |
Uma única interação de uma loop é terminada quando a declaração continue é executada. A próxima interação vai proceder normalmente. |
Código:
#!/usr/bin/env python3
count = 0
while count < 5:
print("count:" , count)
count+=1
if count == 3:
break
print("Done")
Saída:
$ python break.py
count: 0
count: 1
count: 2
Done
Quando a contagem é igual a 3, a execução do while loop é terminada, no entanto a condição inicial permanece verdadeira (count < 5).
Código:
#!/usr/bin/env python3
count = 0
while count < 5:
print("count:" , count)
count+=1
if count == 3:
continue
print("line after our continue")
print("Done")
Saída:
$ python continue.py
count: 0
line after our continue
count: 1
line after our continue
count: 2
count: 3
line after our continue
count: 4
line after our continue
Done
Quando a contagem é igual a 3 o continue é executado. Isso faz com que todas as linhas contendo o bloco de loop sejam puladas. "Linha após o nosso continue" não é impresso quando a contagem é igual a 3. O próximo loop é executado normalmente.
Um iterável é qualquer tipo de dado que pode ser iterado, ou pode ser usado em uma interação. Um interável pode ser transformado em um iterador com a função iter()
. Isso significa que você pode utilizar a função next()
.
>>> codons = [ 'atg' , 'aaa' , 'agg' ]
>>> codons_iterator=iter(codons)
>>> next(codons_iterator)
'atg'
>>> next(codons_iterator)
'aaa'
>>> next(codons_iterator)
'agg'
>>> next(codons_iterator)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Um iterador permite que você obtenha o próximo elemento no iterador até que não existam mais elementos. Se você quer ir através de cada elemento novamente, você precisará redefinir o iterador.
Exemplo de utilização de um iterador em um for loop:
codons = [ 'atg' , 'aaa' , 'agg' ]
>>> codons_it = iter(codons)
>>> for codon in codons_it :
... print( codon )
...
atg
aaa
agg
Isso é bom se você tem uma lista muito larga que você não deseja manter na memória. Um iterador permite que você vá através de cada elemento mas sem manter a lista completa na memória. Sem iteradores toda a lista permanece na memória.
Compreensão de lista é uma forma de fazer uma lista sem digitar cada elemento. Existem muitas formas de usar compreensão de lista para gerar listas. Alguns são relativamente complexos, mas úteis.
Aqui está um exemplo fácil:
>>> dna_list = ['TAGC', 'ACGTATGC', 'ATG', 'ACGGCTAG']
>>> lengths = [len(dna) for dna in dna_list]
>>> lengths
[4, 8, 3, 8]
Isso é como você pode fazer o mesmo com um for loop:
>>> lengths = []
>>> dna_list = ['TAGC', 'ACGTATGC', 'ATG', 'ACGGCTAG']
>>> for dna in dna_list:
... lengths.append(len(dna))
...
>>> lengths
[4, 8, 3, 8]
Utilizando condições:
Isso vai apenas retornar o comprimento de um elemento que começa com 'A':
>>> dna_list = ['TAGC', 'ACGTATGC', 'ATG', 'ACGGCTAG']
>>> lengths = [len(dna) for dna in dna_list if dna.startswith('A')]
>>> lengths
[8, 3, 8]
Esse gera a seguinte lista: [8, 3, 8]
Aqui está um exemplo de utilização de operadores matemáticos para gerar uma lista:
>>> two_power_list = [2 ** x for x in range(10)]
>>> two_power_list
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
Isso cria uma lista do produto de [2^0 , 2^1, 2^2, 2^3, 2^4, 2^5, 2^6, 2^7, 2^8, 2^9 ]
Dicionários são outra estrutura iterável, semelhante a uma string e uma lista. Ao contrário de strings e listas, os dicionários não são uma sequência, ou em outras palavras, eles são não ordenados e a posição não é importante.
Dicionários são uma coleção de pares chave/valor. Em Python, cada chave é separada de seu valor por dois pontos (:), os itens são separados por vírgulas, e tudo é envolto por chaves. Um dicionário vazio sem itens é escrito apenas com duas chaves, assim: {}
Cada chave em um dicionário é única, enquanto os valores podem não ser. Os valores de um dicionário podem ser de qualquer tipo, mas as chaves devem ser de um tipo de dado imutável, como strings, números ou tuplas.
Dados apropriados para dicionários são duas informações que naturalmente estão relacionadas, como o nome de um gene e sua sequência.
Chave | Valor |
---|---|
TP53 | GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC |
BRCA1 | GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA |
genes = { 'TP53' : 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC' , 'BRCA1' : 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA' }
Dividir os pares chave/valor em várias linhas facilita a leitura.
genes = {
'TP53' : 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC' ,
'BRCA1' : 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
}
Para recuperar um único valor em um dicionário, use a chave do valor neste formato dict[chave]
. Isso retornará o valor na chave especificada.
>>> genes = { 'TP53' : 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC' , 'BRCA1' : 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA' }
>>>
>>> genes['TP53']
GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC
A sequência do gene TP53 está armazenada como um valor da chave 'TP53'. Podemos acessar a sequência usando a chave neste formato dict[chave].
O valor pode ser acessado e passado diretamente para uma função ou armazenado em uma variável.
>>> print(genes['TP53'])
GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC
>>>
>>> seq = genes['TP53']
>>> print(seq)
GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC
Valores individuais podem ser alterados usando a chave e o operador de atribuição.
>>> genes = { 'TP53' : 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC' , 'BRCA1' : 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA' }
>>>
>>> print(genes)
{'BRCA1': 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA', 'TP53': 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC'}
>>>
>>> genes['TP53'] = 'atg'
>>>
>>> print(genes)
{'BRCA1': 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA', 'TP53': 'atg'}
O conteúdo do dicionário foi alterado.
Outros operadores de atribuição também podem ser usados para alterar um valor de uma chave de dicionário.
>>> genes = { 'TP53' : 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC' , 'BRCA1' : 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA' }
>>>
>>> genes['TP53'] += 'TAGAGCCACCGTCCAGGGAGCAGGTAGCTGCTGGGCTCCGGGGACACTTTGCGTTCGGGCTGGGAGCGTG'
>>>
>>> print(genes)
{'BRCA1': 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA', 'TP53': 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTCTAGAGCCACCGTCCAGGGAGCAGGTAGCTGCTGGGCTCCGGGGACACTTTGCGTTCGGGCTGGGAGCGTG'}
Aqui, usamos o operador de atribuição de concatenação '+='. Isso é equivalente a
genes['TP53'] = genes['TP53'] + 'TAGAGCCACCGTCCAGGGAGCAGGTAGCTGCTGGGCTCCGGGGACACTTTGCGTTCGGGCTGGGAGCGTG'
.
Já que um dicionário é um objeto iterável, podemos percorrer os seus conteúdos.
Um loop for pode ser usado para recuperar cada chave de um dicionário de cada vez:
>>> for gene in genes:
... print(gene)
...
TP53
BRCA1
Depois de obter a chave, você pode recuperar o valor:
>>> for gene in genes:
... seq = genes[gene]
... print(gene, seq[0:10])
...
TP53 GATGGGATTG
BRCA1 GTACCTTGAT
Construir um dicionário uma chave/valor de cada vez é semelhante ao que acabamos de ver quando alteramos o valor de uma chave. Normalmente, você não fará isso. Falaremos sobre maneiras de construir um dicionário a partir de um arquivo em uma palestra posterior.
>>> genes = {}
>>> print(genes)
{}
>>> genes['Brca1'] = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
>>> genes['TP53'] = 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC'
>>> print(genes)
{'Brca1': 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA', 'TP53': 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC'}
Começamos criando um dicionário vazio. Em seguida, adicionamos cada par chave/valor usando a mesma sintaxe que usamos ao alterar um valor.
dict[key] = new_value
Python gera um erro (NameError) se você tentar acessar uma chave que não existe.
>>> print(genes['HDAC'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'HDAC' is not defined
Operador | Descrição |
---|---|
in |
key in dict retorna True se a chave existe no dicionário |
not in |
key not in dict retorna True se a chave não existe no dicionário |
Como o Python gera um NameError se você tentar usar uma chave que não existe no dicionário, é necessário verificar se uma chave existe antes de tentar usá-la.
A melhor maneira de verificar se uma chave existe é usando in
.
>>> gene = 'TP53'
>>> if gene in genes:
... print('found')
...
found
>>>
>>> if gene in genes:
... print(genes[gene])
...
GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC
>>>
Agora temos todas as ferramentas para construir um dicionário um par de chave/valor usando um loop for. Isso é como você construirá dicionários com mais frequência na vida real.
Aqui, vamos contar e armazenar contagens de nucleotídeos:
#!/usr/bin/env python3
# cria um dicionário vazio
nt_count={}
# exemplo de loop
dna = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
for nt in dna:
# 'nt' está no dicionário?
if nt in nt_count:
# se sim, aumentamos a contagem
previous_count = nt_count[nt]
new_count = previous_count + 1
nt_count[nt] = new_count
else:
# senão, adcionamos e contamos 1
nt_count[nt] = 1;
print(nt_count)
{'G': 20, 'T': 21, 'A': 13, 'C': 16}
Qual outra maneira podemos contar?
nt_count={}
dna = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
for nt in dna:
if nt in nt_count:
nt_count[nt] += 1
else:
nt_count[nt] = 1;
print(nt_count)
lembre-se que
count=count+1
é o mesmo quecount+=1
Se você deseja imprimir o conteúdo de um dicionário, deve classificar as chaves e, em seguida, iterar sobre as chaves com um loop for. Por que você gostaria de classificar as chaves?
for gene_key in sorted(genes): # python permite que voce use atalhos em um "for loop"
# você não precisa escrever genes.keys() em um for loop
# para iterar sobre as chaves
print(gene_key, '=>' , genes[gene_key])
Isso imprimirá as chaves na mesma ordem toda vez que você executar seu script. Dicionários são desordenados, então sem ordenação, você obterá uma ordem diferente cada vez que executar o script, o que pode ser confuso.
Função | Descrição |
---|---|
len(dict) |
retorna o número total de pares chave/valor |
str(dict) |
retorna uma representação em string do dicionário |
type(variable) |
Retorna o tipo ou classe da variável passada para a função. Se a variável for um dicionário, ela retornará o tipo "dicionário". |
Essas funções também funcionam com vários outros tipos de dados!
Métodos | Descrição |
---|---|
dict.clear() |
Remove todos os elementos do dicionário dict . |
dict.copy() |
Retorna uma cópia rasa (shallow copy) do dicionário. Shallow vs. deep A cópia só é relevante em estruturas de dados multidimensionais. |
dict.fromkeys(seq,value) |
Crie um novo dicionário com chaves de seq (tipo de sequência Python) e valores definidos como valor. |
dict.items() |
Retorna uma lista de tuplas (chave, valor). |
dict.pop(key) |
Remove o par chave: valor e retorna o valor. |
dict.keys() |
Retorna uma lista de chaves |
dict.get(key, default = None) |
Obtenha o valor de dict[key], use o padrão se não estiver presente. |
dict.setdefault(key, default = None) |
Semelhante a get(), mas definirá dict[key] = default se a chave ainda não estiver em dict. |
dict.update(dict2) |
Adiciona os pares chave-valor do dicionário dict2 ao dicionário dict. |
dict.values() |
Retorna uma lista dos valores do dicionário dict . |
Um conjunto é outro tipo de dado em Python. Essencialmente, é um dicionário com chaves, mas sem valores.
- Um conjunto é não ordenado.
- Um conjunto é uma coleção de dados sem elementos duplicados.
- Usos comuns incluem buscar diferenças e eliminar duplicatas em conjuntos de dados.
Chaves {}
ou a função set()
podem ser usadas para criar conjuntos.
Observação: para criar um conjunto vazio, você precisa usar
set()
, não{}
, este último cria um dicionário vazio.
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)
{'orange', 'banana', 'pear', 'apple'}
Veja, os duplicados foram removidos.
Testar se um valor está no conjunto.
>>> 'orange' in basket
True
>>> 'crabgrass' in basket
False
O operador
in
funciona da mesma forma com conjuntos como funciona com listas e dicionários.
União, interseção, diferença e diferença simétrica podem ser feitas com conjuntos.
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a
{'a', 'r', 'b', 'c', 'd'}
Conjuntos contêm elementos únicos; portanto, mesmo que elementos duplicados sejam fornecidos, eles serão removidos..
Diferença
A diferença entre dois conjuntos são os elementos que são exclusivos do conjunto à esquerda do operador -
, com duplicatas removidas..
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a - b
{'r', 'd', 'b'}
Isso resulta nas letras que estão em "a", mas não em "b".
União
A união entre dois conjuntos é uma sequência de todos os elementos dos conjuntos primeiro e segundo combinados, com duplicatas removidas.
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a | b
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
Isso retorna as letras que estão em ambos os conjuntos a e b.
Interseção
A interseção entre dois conjuntos é uma sequência dos elementos que estão em ambos os conjuntos, com duplicatas removidas.
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a & b
{'a', 'c'}
Isso retorna as letras que estão em ambos os conjuntos
a
eb
.
Diferença simétrica
A diferença simétrica é composta pelos elementos que estão apenas no primeiro conjunto, mais os elementos que estão apenas no segundo conjunto, com duplicatas removidas.
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a ^ b
{'r', 'd', 'b', 'm', 'z', 'l'}
Isso retorna as letras que estão em
a
oub
, mas não em ambos (também conhecido como ou exclusivo).
Funções | Descrição |
---|---|
all() |
retorna True se todos os elementos do conjunto forem True (ou se o conjunto estiver vazio). |
any() |
retorna True se algum elemento do conjunto for True . Se o conjunto estiver vazio, retorna False . |
enumerate() |
retorna um objeto enumerate . Ele contém o índice e o valor de todos os itens do conjunto como um par. |
len() |
retorna o número de itens no conjunto. |
max() |
retorna o maior item no conjunto. |
min() |
retorna o menor item no conjunto. |
sorted() |
retorna uma nova lista ordenada a partir dos elementos no conjunto (não altera o conjunto original). |
sum() |
retorna a soma de todos os elementos no conjunto. |
Métodos | Descrição |
---|---|
set.add(new) |
adiciona novos elementos |
set.clear() |
remove todos elementos |
set.copy() |
retorna uma cópia rasa de um conjunto |
set.difference(set2) |
retorna a diferença entre o conjunto e o conjunto2 |
set.difference_update(set2) |
remove todos os elementos de outro conjunto deste conjunto |
set.discard(element) |
remove um elemento do conjunto se ele for encontrado no conjunto. (Não faz nada se o elemento não estiver no conjunto) |
set.intersection(sets) |
retorna a interseção do conjunto com outros conjuntos fornecidos |
set.intersection_update(sets) |
atualiza o conjunto com a interseção do conjunto e os outros conjuntos fornecidos. |
set.isdisjoint(set2) |
retorna Verdadeiro se o set e o set2 não têm interseção. |
set.issubset(set2) |
retorna Verdadeiro se o set2 contém o conjunto. |
set.issuperset(set2) |
retorna Verdadeiro se o set contém o set2. |
set.pop() |
remove e retorna um elemento arbitrário do conjunto. |
set.remove(element) |
remove um elemento de um conjunto. |
set.symmetric_difference(set2) |
retorna a diferença simétrica entre o conjunto e o conjunto2. |
set.symmetric_difference_update(set2) |
atualiza o conjunto com a diferença simétrica entre o conjunto e o conjunto2 |
set.union(sets) |
retorna a união do conjunto e dos outros conjuntos fornecidos. |
set.update(set2) |
atualiza o conjunto com a união do conjunto e o conjunto2. |
Vamos dar uma reviravolta no nosso script de contagem de NT. Vamos usar um conjunto para encontrar todos os NTs únicos e, em seguida, usar o método count()
da string para contar o nucleotídeo, em vez de incrementar a contagem como fizemos anteriormente.
Código:
#!/usr/bin/env python3
# crie um novo dicionário
nt_count = {}
# obtenha um conjunto de caracteres únicos em nossa sequência de DNA.
dna = 'GTACCNTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'
unique = set(dna)
print('unique nt: ', unique) ## {'C', 'A', 'G', 'T', 'N'}
# Itere através de cada nucleotídeo único.
for nt in unique:
# Conte o número deste nucleotídeo único no DNA.
count = dna.count(nt)
# Adicione nossa contagem ao nosso dicionário.
nt_count[nt] = count
print('nt count:', nt_count)
Output:
unique nt: {'N', 'C', 'T', 'G', 'A'}
nt count: {'G': 20, 'T': 21, 'A': 13, 'C': 16, 'N': 1}
Temos a contagem para todos os NTs, até mesmo aqueles que talvez não esperássemos.
I/O significa input/output. O "in" e "out" referem-se a obter dados dentro e fora do seu script. Pode ser um pouco surpreendente no início, mas escrever na tela, ler do teclado, ler de um arquivo e escrever em um arquivo são todos exemplos de I/O.
Você deve estar bem familiarizado com a escrita na tela. Temos usado a função print()
para isso.
>>> print ("Olá, PFB2019!")
Olá, PFB2019!
Lembra-se desse exemplo de uma das nossas primeiras lições?
Isso é algo novo. Há uma função que imprime uma mensagem na tela e espera uma entrada do teclado. Essa entrada pode ser armazenada em uma variável. Ela sempre começa como uma string. Converta para int ou float se quiser um número. Quando terminar de digitar o texto, pressione a tecla Enter para encerrar a entrada. Um caractere de nova linha não está incluído na entrada.
>>> user_input = input("Digite algo agora: ")
Digite algo agora: Oi
>>> print(user_input)
Oi
>>> type(user_input)
<class 'str'>
A maioria dos dados com os quais lidaremos estará contida em arquivos.
O primeiro passo com um arquivo é abri-lo. Podemos fazer isso com a função open()
. A função open()
leva o nome do arquivo e o modo de acesso como argumentos e retorna um objeto de arquivo.
Os modos de acesso mais comuns são leitura (r) e escrita (w).
>>> objeto_arquivo = open("seq.nt.txt", "r")
'objeto_arquivo' é o nome de uma variável. Pode ser qualquer coisa, mas faça um nome útil que descreva que tipo de arquivo você está abrindo.
Agora que abrimos um arquivo e criamos um objeto de arquivo, podemos fazer coisas com ele, como ler. Vamos ler todo o conteúdo de uma vez.
Vamos para a linha de comando e use cat
para ver o conteúdo do arquivo primeiro.
$ cat seq.nt.txt
ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAG
ACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG
$
Observe as novas linhas. Agora, vamos imprimir o conteúdo na tela com Python. Vamos usar read()
para ler todo o conteúdo do arquivo em uma variável.
>>> arquivo = open("seq.nt.txt", "r")
>>> conteudo = arquivo.read()
>>> print(conteudo) # observe que os caracteres de nova linha fazem parte do arquivo!
ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAG
ACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG
>>> arquivo.close()
O conteúdo completo pode ser obtido com o método
read()
. Observe que as novas linhas são mantidas quando oconteudo
é impresso na tela.print()
adiciona outra nova linha quando termina de imprimir. É uma boa prática fechar seu arquivo. Use o métodoclose()
.
Aqui está outra maneira de ler dados de um arquivo. Um loop for
pode ser usado para iterar pelo arquivo uma linha de cada vez.
#!/usr/bin/env python3
arquivo = open("seq.nt.txt", "r")
for linha in arquivo: # Magia do Python: lê uma linha do arquivo
print(linha)
Output:
$ python3 file_for.py
ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAG
ACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG
Observe a linha em branco após cada linha que imprimimos.
print()
adiciona uma nova linha e temos uma nova linha no final de cada linha em nosso arquivo. Use o métodorstrip()
para remover a nova linha de cada linha.
Vamos usar o método rstrip()
para remover a nova linha da entrada do nosso arquivo.
$ cat arquivo_for_rstrip.py
#!/usr/bin/env python3
arquivo_objeto = open("seq.nt.txt", "r")
for linha in arquivo_objeto:
linha = linha.rstrip()
print(linha)
rstrip()
sem nenhum parâmetro retorna uma string com espaços em branco removidos do final.
Output:
$ python3 file_for_rstrip.py
ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAG
ACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG
De onde vêm as novas linhas na saída acima?
Muitas pessoas adicionam isso porque ele fecha o arquivo automaticamente para você. Boa prática de programação. Seu código limpará conforme ele é executado. Para codificação mais avançada, with ... as ...
economiza recursos limitados, como identificadores de arquivo e conexões de banco de dados. Por enquanto, só precisamos saber que with ... as ...:
faz o mesmo que fh = open(...) ... fh.close()
. Portanto, aqui está como o código adaptado se parece:
#!/usr/bin/env python3
with open("seq.nt.txt", "r") as objeto_arquivo: # limpa após sair do bloco 'with'
for linha in objeto_arquivo:
linha = linha.rstrip()
print(linha)
# o arquivo é fechado para você aqui.
Escrever em um arquivo requer apenas abrir um arquivo para escrita e depois usar o método write()
.
O método write()
é como a função print()
. A maior diferença é que ele escreve no objeto do seu arquivo em vez da tela. Ao contrário do print()
, ele não adiciona uma nova linha por padrão. write()
recebe um único argumento de string.
Vamos escrever algumas linhas em um arquivo chamado "writing.txt".
#!/usr/bin/env python3
fo = open("writing.txt", "w")
fo.write("Uma linha.\n")
fo.write("Segunda linha.\n")
fo.write("Terceira linha" + " tem texto adicional\n")
alguma_var = 5
fo.write("Quarta linha tem " + str(alguma_var) + " palavras\n")
fo.close()
print("Escrito no arquivo 'writing.txt'") # é legal informar ao usuário que você escreveu um arquivo
Output:
$ python3 file_write.py
Escrito no arquivo 'writing.txt'
$ cat writing.txt
Uma linha.
Segunda linha.
Terceira linha tem texto adicional
Quarta linha tem 5 palavras
Agora, vamos ficar loucos! Vamos ler de um arquivo uma linha de cada vez. Faça algo com cada linha e escreva os resultados em um novo arquivo.
#!/usr/bin/env python3
total_nts = 0
# abre dois objetos de arquivo, um para leitura e outro para escrita
with open("seq.nt.txt", "r") as seq_read, open("nt.counts.txt", "w") as seq_write:
for linha in seq_read:
linha = linha.rstrip()
contagem_nt = len(linha)
total_nts += contagem_nt
seq_write.write(str(contagem_nt) + "\n")
seq_write.write("Total: " + str(total_nts) + "\n")
print("Escrito 'nt.counts.txt'")
Output:
$ python3 file_read_write.py
$ cat nt.counts.txt
71
71
Total: 142
O arquivo do qual estamos lendo é chamado de "seq.nt.txt" O arquivo para o qual estamos escrevendo é chamado de "nt.counts.txt" Leemos cada linha, calculamos o comprimento de cada linha e imprimimos o comprimento Também criamos uma variável para acompanhar a contagem total de nt No final, imprimimos o total de nt Finalmente, fechamos cada um dos arquivos
Esta é uma tarefa muito comum. Ela usará um loop, E/S de arquivo e um dicionário.
Suponha que temos um arquivo chamado "sequence_data.txt" que contém nomes de genes e sequências delimitadas por tabulação, que se parece com isto:
TP53 GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC
BRCA1 GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA
Como podemos ler todo este arquivo em um dicionário?
#!/usr/bin/env python3
genes = {}
with open("sequence_data.txt", "r") as seq_read:
for linha in seq_read:
linha = linha.rstrip()
gene_id, seq = linha.split() # dividir no espaço em branco
genes[gene_id] = seq
print(genes)
Output:
{'TP53': 'GATGGGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCAAAAGTC', 'BRCA1': 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCTTAGCGGTAGCCCCTTGGTTTCCGTGGCAACGGAAAA'}
Expressões regulares são uma linguagem para correspondência de padrões. Muitas linguagens de programação diferentes incorporam expressões regulares, assim como alguns comandos Unix, como grep e sed. Até agora, vimos algumas funções para encontrar correspondências exatas em strings, mas isso nem sempre é suficiente.
Funções que utilizam expressões regulares permitem a correspondência de padrões não exatos.
Essas funções especializadas não estão incluídas no núcleo do Python. Precisamos importá-las digitando
import re
no topo do seu script
#!/usr/bin/env python3
import re
Primeiro, veremos alguns exemplos e depois entraremos nos detalhes mecânicos com mais detalhes.
Vamos começar com algo simples e encontrar uma correspondência exata para o site de restrição EcoRI em uma string.
>>> dna = 'ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG'
>>> if re.search(r"GAATTC",dna):
... print("Found an EcoRI site!")
...
Found an EcoRI site!
>>>
Como podemos pesquisar por caracteres de controle como um tab (\t), é bom criar o hábito de usar a função de string raw
r
ao definir padrões.
Aqui usamos a função
search()
com dois argumentos, 1) nosso padrão e 2) a string que queremos pesquisar.
Vamos descobrir o que é retornado pela função search()
.
>>> dna = 'ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG'
>>> found=re.search(r"GAATTC",dna)
>>> print(found)
<_sre.SRE_Match object; span=(70, 76), match='GAATTC'>
As informações sobre a primeira correspondência são retornadas
E uma correspondência não exata. Vamos procurar por um site de metilação que deve corresponder aos seguintes critérios:
- G ou A
- seguido por C
- seguido por qualquer coisa ou nada
- seguido por um G
Isso pode corresponder a qualquer um destes:
- GCAG
- GCTG
- GCGG
- GCCG
- GCG
- ACAG
- ACTG
- ACGG
- ACCG
- ACG
Podemos testar cada um desses ou usar expressões regulares. É exatamente isso que as expressões regulares podem fazer por nós.
>>> dna = 'ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG'
>>> found=re.search(r"[GA]C.?G",dna)
>>> print(found)
<_sre.SRE_Match object; span=(7, 10), match='ACG'>
Aqui você pode ver nas informações retornadas que ACG começa na posição da string 7 (nt 8).
A primeira posição após o final da correspondência está na posição da string 10 (nt 11).
E outras correspondências potenciais em nossa string de DNA? Podemos usar a função findall()
para encontrar todas as correspondências.
>>> dna = 'ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG'
>>> found=re.findall(r"[GA]C.?G",dna)
>>> print(found)
['ACG', 'GCTG', 'ACTG', 'ACCG', 'ACAG', 'ACCG', 'ACAG']
findall()
retorna uma lista de todas as partes da string que correspondem à regex.
Uma contagem rápida de todos os sites correspondentes pode ser feita contando o comprimento da lista retornada.
>>> len (re.findall(r"[GA]C.?G",dna))
7
Existem 7 sites de metilação.
Aqui temos outro exemplo de aninhamento.
Chamamos a função
findall()
, procurando todas as correspondências de um site de metilação.Esta função retorna uma lista, a lista é passada para a função
len()
, que por sua vez retorna o número de elementos na lista.
-
Se você quiser encontrar apenas a primeira ocorrência de um padrão, que método você usa?
-
Se você quiser encontrar todas as ocorrências de um padrão, que método você usa?
-
Que operador vimos que reportará se uma correspondência exata está em uma sequência (string, lista, etc.)?
-
Que método de string vimos que contará o número de ocorrências de uma correspondência exata em uma string?
Vamos falar um pouco mais sobre todos os novos caracteres que vemos no padrão.
O padrão é composto por átomos. Cada átomo representa UM caractere.
Átomo | Descrição |
---|---|
a-z, A-Z, 0-9 e algumas pontuações | São caracteres comuns que correspondem a si mesmos |
"." | O ponto, ou período. Isso corresponde a qualquer caractere único, exceto a nova linha. |
Um grupo de caracteres que podem ser correspondidos uma vez. Existem algumas classes predefinidas, que são símbolos que significam uma série de caracteres.
Átomo | Descrição |
---|---|
[ ] |
Uma lista entre colchetes de caracteres, como [GA] . Isso indica que um único caractere pode corresponder a qualquer caractere na lista entre colchetes. |
\d |
Dígitos. Também pode ser escrito como [0-9] |
\D |
Não dígitos. Também pode ser escrito como [^0-9] |
\w |
Caractere de palavra. Também pode ser escrito como [A-Za-z0-9_] . Note que o sublinhado faz parte dessa classe |
\W |
Não é um caractere de palavra, ou [^A-Za-z0-9_] |
\s |
Caractere de espaço em branco. Também pode ser escrito como [ \r\t\n] . Note o caractere de espaço após o primeiro [ |
\S |
Não é um espaço em branco. Também pode ser escrito como [^ \r\t\n] |
[^] |
Um acento circunflexo dentro de uma lista entre colchetes de caracteres indica qualquer coisa, exceto os caracteres que o seguem |
Um padrão pode ser ancorado a uma região na string:
Átomo | Descrição |
---|---|
^ |
Corresponde ao início da string |
$ |
Corresponde ao final da string |
\b |
Corresponde a um limite de palavra entre \w e \W |
Exemplos:
g..t
corresponde a "gaat", "goat" e "gotta get a goat" (duas vezes)
\d\d\d-\d\d\d\d
corresponde a 867-5309 e 5867-5309, mas não a 8-67-5309.
^\d\d\d-\d\d\d\d
corresponde a 867-5309 e 867-53091, mas não a 5867-5309.
^\d\d\d-\d\d\d\d$
corresponde apenas a 3 dígitos seguidos de um traço seguido de 4 dígitos, nenhum caractere extra é permitido em qualquer lugar
Os quantificadores quantificam quantos átomos devem ser encontrados. Por padrão, um átomo corresponde apenas uma vez. Esse comportamento pode ser modificado seguindo um átomo com um quantificador.
Quantificador | Descrição |
---|---|
? |
o átomo corresponde zero ou exatamente uma vez |
* |
o átomo corresponde zero ou mais vezes |
+ |
o átomo corresponde uma ou mais vezes |
{3} |
o átomo corresponde exatamente 3 vezes |
{2,4} |
o átomo corresponde entre 2 e 4 vezes, inclusive |
{4,} |
o átomo corresponde pelo menos 4 vezes |
Exemplos:
goa?t
corresponde a "goat" e "got". Também qualquer texto que contenha essas palavras.
g.+t
corresponde a "goat", "goot" e "grant", entre outros.
g.*t
corresponde a "gt", "goat", "goot" e "grant", entre outros.
^\d{3}-\d{4}$
corresponde a números de telefone dos EUA (nenhum texto extra é permitido).
- Qual seria um padrão para reconhecer um endereço de e-mail?
- Qual seria um padrão para reconhecer a parte de ID de um registro de sequência em um arquivo FASTA?
Variáveis podem ser usadas para armazenar padrões.
>>> padrao = r"[GA]C.?G"
>>> len(re.findall(padrao, dna))
7
Neste exemplo, armazenamos nosso padrão de metilação na variável chamada 'padrao' e a utilizamos como primeiro argumento para
findall
.
Um pipe '|' pode ser usado para indicar que o padrão antes ou depois do '|' pode ser correspondido. Coloque as duas opções entre parênteses.
grande mau (lobo|ovelha)
Este padrão deve corresponder a uma string que contém:
- "grande" seguido de um espaço seguido por
- "mau" seguido de
- um espaço seguido por
- ou "lobo" ou "ovelha"
Isso corresponderia a:
- "grande lobo mau"
- "grande ovelha má"
- Qual seria um padrão para reconhecer um endereço de e-mail?
- Qual seria um padrão para reconhecer a parte de ID de um registro de sequência em um arquivo FASTA?
Subpadrões, ou partes do padrão contidas entre parênteses, podem ser extraídos e armazenados para uso posterior.
Quem tem medo do grande mau lobo(.+)f
Este padrão possui apenas um subpadrão (.+)
Você pode combinar parênteses e quantificadores para quantificar subpadrões inteiros.
Quem tem medo do grande (mau )?lobo\?
Isto corresponde a:
- "Quem tem medo do grande mau lobo?"
- Bem como "Quem tem medo do grande lobo?".
O 'mau ' é opcional, ele pode estar presente 0 ou 1 vez em nossa string.
Isso também mostra como corresponder literalmente a caracteres especiais. Use um '\' para escapá-los.
- Que padrão você usaria para capturar o ID em um registro de sequência de um arquivo FASTA em um subpadrão.
Exemplo de registro de sequência FASTA.
>ID Descrição Opcional
SEQUÊNCIA
SEQUÊNCIA
SEQUÊNCIA
Isso é útil quando você deseja encontrar um subpadrão e, em seguida, corresponder ao conteúdo novamente. Eles podem ser usados dentro da chamada da função e depois dela.
Subpadrões dentro da chamada da função
Uma vez que um subpadrão corresponde, você pode se referir a ele dentro da mesma expressão regular. O primeiro subpadrão se torna \1, o segundo \2, o terceiro \3 e assim por diante.
Who's afraid of the big bad w(.)\1f
Isso corresponderia a:
- "Who's afraid of the big bad woof"
- "Who's afraid of the big bad weef"
- "Who's afraid of the big bad waaf"
Mas não a:
- "Who's afraid of the big bad wolf"
- "Who's afraid of the big bad wife"
Da mesma forma,
\b(\w+)s love \1 food\b
Este padrão irá corresponder a:
- "dogs love dog food"
- But not "dogs love monkey food".
Fomos capazes de usar o subpadrão dentro da expressão regular usando
\1
Se houvesse mais subpadrões, eles seriam
\2
,\3
,\4
, etc.
Os subpadrões podem ser recuperados após a chamada da função search()
, ou fora da expressão regular, usando o método group()
. Este é um método e pertence ao objeto que é retornado pela função search()
.
Os subpadrões são recuperados por um número. Este será o mesmo número que poderia ser usado dentro da expressão regular, ou seja,
\1
dentro do subpadrão pode ser usado fora comsearch_found_obj.group(1)
\2
dentro do subpadrão pode ser usado fora comsearch_found_obj.group(2)
\3
dentro do subpadrão pode ser usado fora comsearch_found_obj.group(3)
- e assim por diante
Example:
>>> dna = 'ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG'
>>> found=re.search( r"(.{50})TATTAT(.{25})" , dna )
>>> upstream = found.group(1)
>>> print(upstream)
TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA
>>> downstream = found.group(2)
>> print(downstream)
CCGGTTTCCAAAGACAGTCTTCTAA
- Este padrão reconhecerá um sítio de início de transcrição de consenso (TATTAT)
- E armazenará os 50 pares de bases a montante do sítio
- E os 25 pares de bases a jusante do sítio
Se você quiser encontrar as sequências a montante e a jusante de TODOS os sítios 'TATTAT', use a função findall()
.
>>> dna="ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG"
>>> found = re.findall( r"(.{50})TATTAT(.{25})" , dna )
>>> print(found)
[('TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA', 'CCGGTTTCCAAAGACAGTCTTCTAA'), ('TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA', 'CCGGTTTCCAAAGACAGTCTTCTAA')]
Os subpadrões são armazenados em tuplas dentro de uma lista. Mais sobre esse tipo de estrutura de dados mais tarde.
Outra opção para recuperar os subpadrões a montante e a jusante é colocar o findall()
em um loop for
>>> dna="ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG"
>>> for (upstream, downstream) in re.findall( r"(.{50})TATTAT(.{25})" , dna ):
... print("upstream:" , upstream)
... print("downstream:" , downstream)
...
upstream: TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA
downstream: CCGGTTTCCAAAGACAGTCTTCTAA
upstream: TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA
downstream: CCGGTTTCCAAAGACAGTCTTCTAA
- Este código executa a função
findall()
uma vez- Os objetos de tupla são retornados
- Os subpadrões são armazenados nas variáveis a montante e a jusante
- O bloco for é executado
- O
findall()
busca novamente- Uma correspondência é encontrada
- Novos subpadrões são retornados e armazenados nas variáveis a montante e a jusante
- O bloco for de código é executado novamente
- O
findall()
busca novamente, mas nenhuma correspondência é encontrada- O loop for termina
Outra maneira de fazer isso é com um iterador, usando a função finditer()
em um loop for. Isso permite que você não armazene todas as correspondências na memória. finditer()
também permite que você recupere a posição da correspondência.
>>> dna="ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG"
>>> for match in re.finditer(r"(.{50})TATTAT(.{25})" , dna):
... print("upstream:" , match.group(1))
... print("downstream:" , match.group(2))
...
upstream: TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA
downstream: CCGGTTTCCAAAGACAGTCTTCTAA
upstream: TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA
downstream: CCGGTTTCCAAAGACAGTCTTCTAA
- Este código executa a função
finditer()
uma vez. - O objeto de correspondência é retornado. Um objeto de correspondência terá todas as informações sobre a correspondência.
- No bloco for, chamamos o método
group()
no primeiro objeto de correspondência retornado. - Imprimimos o primeiro e o segundo subpadrão usando o método
group()
. - A função
finditer()
é executada uma segunda vez e uma correspondência é encontrada. - O segundo objeto de correspondência é retornado.
- Os segundos subpadrões são obtidos do objeto de correspondência usando o método
group()
. - A função
finditer()
é executada novamente, mas não são encontradas correspondências, portanto o loop termina.
O objeto de correspondência contém informações sobre a correspondência que podem ser recuperadas com métodos de correspondência como start()
e end()
#!/usr/bin/env python3
import re
dna="ACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGACAAAATACGTTTTGTAAATGTTGTGCTGTTAACACTGCAAATAAACTTGGTAGCAAACACTTCCAAAAGGAATTCACCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGG"
for found in re.finditer(r"(.{50})TATTAT(.{25})" , dna):
whole = found.group(0)
up = found.group(1)
down = found.group(2)
up_start = found.start(1) + 1 # need to convert from 0 to 1 notation
up_end = found.end(1)
dn_start = found.start(2) + 1
dn_end = found.end(2)
print( whole , up , up_start, up_end , down , dn_start , dn_end , sep="\t" )
podemos usar esses métodos de objeto de correspondência
group()
,start()
,end()
para obter a string, a posição de início e a posição de fim de cada subpadrão.
$ python3 re.finditer.pos.py
TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAA TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA 98 148 CCGGTTTCCAAAGACAGTCTTCTAA 154 179
TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGATATTATCCGGTTTCCAAAGACAGTCTTCTAA TCTAATTCCTCATTAGTAATAAGTAAAATGTTTATTGTTGTAGCTCTGGA 320 370 CCGGTTTCCAAAGACAGTCTTCTAA 376 401
FYI: A função match()
é outra função de expressão regular que procura padrões. É semelhante a search
, mas ela olha apenas para o início da string em busca do padrão, enquanto search
procura em toda a string. Geralmente, finditer()
, search()
e findall()
serão mais úteis.
Por padrão, as expressões regulares são "gananciosas". Elas tentam corresponder o máximo possível. Use o quantificador '?' para tornar a correspondência não gananciosa. A correspondência não gananciosa é chamada de 'preguiçosa'
>>> str = 'The fox ate my box of doughnuts'
>>> found = re.search(r"(f.+x)",str)
>>> print(found.group(1))
fox ate my box
O padrão f.+x não corresponde ao que você pode esperar, ele corresponde a partir de 'fox' até 'fox ate my box'. O '.+' é ganancioso. Ele encontra o máximo possível de caracteres entre 'f' e 'x'.
Vamos tornar essa correspondência preguiçosa usando '?'
>>> found = re.search(r"(f.+?x)",str)
>>> print(found.group(1))
fox
A correspondência agora é preguiçosa e corresponderá apenas a 'fox'
A extração de códons de uma sequência de DNA pode ser realizada usando um subpadrão em uma função findall()
. Lembre-se de que a função findall()
retornará uma lista das correspondências.
>>> dna = 'GTTGCCTGAAATGGCGGAACCTTGAA'
>>> codons = re.findall(r"(.{3})",dna)
>>> print(codons)
['GTT', 'GCC', 'TGA', 'AAT', 'GGC', 'GGA', 'ACC', 'TTG']
Ou você pode usar um loop for para fazer algo com cada correspondência.
>>> for codon in re.findall(r"(.{3})",dna):
... print(codon)
...
GTT
GCC
TGA
AAT
GGC
GGA
ACC
TTG
>>>
finditer()
também funcionaria neste loop for. Cada códon pode ser acessado usando o métodogroup()
.
As funções search()
, match()
, findall()
e finditer()
podem ser usadas em testes condicionais. Se uma correspondência não for encontrada, uma lista vazia ou 'None' é retornada. Ambos são Falsos.
>>> found=re.search( r"(.{50})TATTATZ(.{25})" , dna )
>>> if found:
... print("encontrado")
... else:
... print("não encontrado")
...
não encontrado
>>> print(found)
None
None é False, então o bloco else é executado
e "não encontrado" é impresso.
Aninhe isso!
>>>
>>> if re.search( r"(.{50})TATTATZ(.{25})" , dna ):
... print("encontrado")
... else:
... print("não encontrado")
...
não encontrado
>>> print(found)
None
Anteriormente, vimos como encontrar um padrão exato e substituí-lo usando o método replace()
. Para encontrar um padrão, ou correspondência inexata, e fazer uma substituição, é usada a função de expressão regular sub()
. Esta função recebe o padrão, a substituição, a string a ser pesquisada, o número de vezes que a substituição deve ser feita e as flags.
>>> str = "Who's afraid of the big bad wolf?"
>>> re.sub(r'w.+f' , 'goat', str)
"Who's afraid of the big bad goat?"
>>> print(str)
Who's afraid of the big bad wolf?
A função
sub()
retorna "Quem tem medo do lobo mau?" O valor da variávelstr
não foi alterado A nova string pode ser armazenada em uma nova variável para uso posterior.
Vamos salvar a nova string que é retornada em uma variável
>>> str = "He had a wife."
>>> new_str = re.sub(r'w.+f' , 'goat', str)
>>> print(new_str)
He had a goate.
>>> print(str)
He had a wife.
Os caracteres entre 'w' e 'f' foram substituídos por 'goat'. A nova string é salva em
new_str
Às vezes, você deseja encontrar um padrão e usá-lo na substituição.
>>> str = "Who's afraid of the big bad wolf?"
>>> new_str = re.sub(r"(\w+) (\w+) wolf" , r"\2 \1 wolf" , str)
>>> print(new_str)
Who's afraid of the bad big wolf?
Encontramos duas palavras antes de 'lobo' e trocamos a ordem. \2 refere-se ao segundo subpadrão \1 refere-se ao primeiro subpadrão
- Como você usaria expressões regulares para encontrar todas as ocorrências de 'ATG' e substituir por '-M-' nesta sequência 'GCAGAGGTGATGGACTCCGTAATGGCCAAATGACACGT'?
Modificador | Descrição |
---|---|
re.I re.IGNORECASE |
Executa correspondência sem diferenciação entre maiúsculas e minúsculas. |
re.M re.MULTILINE |
Faz com que $ corresponda ao final de uma linha (não apenas ao final da string) e faz com que ^ corresponda ao início de qualquer linha (não apenas ao início da string). |
re.S re.DOTALL |
Faz com que um ponto (.) corresponda a qualquer caractere, incluindo uma nova linha. |
re.U |
Interpreta letras de acordo com o conjunto de caracteres Unicode. Esta bandeira afeta o comportamento de \w, \W, \b, \B. |
re.X VERBOSE |
Essa bandeira permite que você escreva expressões regulares que parecem mais bonitas e sejam mais legíveis, permitindo separar visualmente seções lógicas do padrão e adicionar comentários. Os espaços em branco dentro do padrão são ignorados, exceto quando estão em uma classe de caracteres ou quando precedidos por uma barra invertida não escapada. Quando uma linha contém um # que não está em uma classe de caracteres e não é precedido por uma barra invertida não escapada, todos os caracteres do # mais à esquerda até o final da linha são ignorados. |
>>> dna = "atgcgtaatggc"
>>> re.search(r"ATG",dna)
>>>
>>> re.search(r"ATG",dna , re.I)
<_sre.SRE_Match object; span=(0, 3), match='atg'>
>>>
Podemos tornar nossa pesquisa insensível a maiúsculas e minúsculas usando a bandeira
re.I
oure.IGNORECASE
.
Você pode usar mais de uma bandeira concatenando-as com |
. re.search(r"ATG",dna , re.I|re.M)
Existem muitas ferramentas online para realmente ver o que está acontecendo em sua expressão regular. Procure por Python Regular Expression Tester
Sometimes a simple list or dictionary just doesn't do what you want. Sometimes you need to organize data in a more complex way. You can nest any data type inside any other type. This lets you build multidimensional data tables easily.
List of lists, often called a matrix are important for organizing and accessing data
Here's a way to make a 3 x 3 table of values.
>>> M = [[1,2,3], [4,5,6],[7,8,9]]
>>> M[1] # second row (starts with index 0)
[4,5,6]
>>>M[1][2] # second row, third element
6
Here's a way to store sequence alignment data:
Four sequences aligned:
AT-TG
AATAG
T-TTG
AA-TA
The alignment in a list of lists.
aln = [
['A', 'T', '-', 'T', 'G'],
['A', 'A', 'T', 'A', 'G'],
['T', '-', 'T', 'T', 'G'],
['A', 'A', '-', 'T', 'A']
]
Get the full length of one sequence:
>>> seq = aln[2]
>>> seq
['T', '-', 'T', 'T', 'G']
Use the outermost index to access each sequence
Retrieve the nucleotide at a particular position in a sequence.
>>> nt = aln[2][3]
>>> nt
'T'
Use the outermost index to access the sequence of interest and the inner most index to access the position
Get every nucleotide in a single column:
>>> col = [seq[3] for seq in aln]
>>> col
['T', 'A', 'T', 'T']
Retrieve each sequence from the aln list then the 4th column for each sequence.
You can nest dictionaries in lists as well:
>>> records = [
... {'seq' : 'actgctagt', 'accession' : 'ABC123', 'genetic_code' : 1},
... {'seq' : 'ttaggttta', 'accession' : 'XYZ456', 'genetic_code' : 1},
... {'seq' : 'cgcgatcgt', 'accession' : 'HIJ789', 'genetic_code' : 5}
... ]
>>> records[0]['seq']
'actgctagt'
>>> records[0]['accession']
'ABC123'
>>> records[0]['genetic_code']
1
Here you can retrieve the accession of one record at a time by using a combination of the outer index and the key 'accession'
And, if you haven't guessed, you can nest lists in dictionaries
Here is a dictionary of kmers. The key is the kmer and its values is a list of postions
>>> kmers = {'ggaa': [4, 10], 'aatt': [0, 6, 12], 'gaat': [5, 11], 'tgga':
... [3, 9], 'attg': [1, 7, 13], 'ttgg': [2, 8]}
>>> kmers
{'tgga': [3, 9], 'ttgg': [2, 8], 'aatt': [0, 6, 12], 'attg': [1, 7, 13], 'ggaa': [4, 10], 'gaat': [5, 11]}
>>>
>>> kmers['ggaa']
[4, 10]
>>> len(kmers['ggaa'])
2
Here we can get a list of the positions of a kmer by using the kmer as the key. We can also do things to the returned list, like determining its length. The length will be the total count of this kmers.
You can also use the get()
method to retrieve records.
>>> kmers['ggaa']
[4, 10]
>>> kmers.get('ggaa')
[4, 10]
These two statements returns the same results, but if the key does not exist you will get nothing and not an error.
Dictionaries of dictionaries is my favorite!! You can do so many useful things with this data structure. Here we are storing a gene name and some different types of information about that gene, such as its, sequence, length, description, nucleotide composition and length.
>>> genes = {
... 'gene1' : {
... 'seq' : "TATGCC",
... 'desc' : 'something',
... 'len' : 6,
... 'nt_comp' : {
... 'A' : 1,
... 'T' : 2,
... 'G' : 1,
... 'C' : 2,
... }
... },
...
... 'gene2' : {
... 'seq' : "CAAATG",
... 'desc' : 'something',
... 'len' : 6,
... 'nt_comp' : {
... 'A' : 3,
... 'T' : 1,
... 'G' : 1,
... 'C' : 1,
... }
... }
... }
>>> genes
{'gene1': {'nt_comp': {'C': 2, 'G': 1, 'A': 1, 'T': 2}, 'desc': 'something', 'len': 6, 'seq': 'TATGCC'}, 'gene2': {'nt_comp': {'C': 1, 'G': 1, 'A': 3, 'T': 1}, 'desc': 'something', 'len': 6, 'seq': 'CAAATG'}}
>>> genes['gene2']['nt_comp']
{'C': 1, 'G': 1, 'A': 3, 'T': 1}
Here we store a gene name as the outermost key, with a second level of keys for qualities of the gene, like sequence, length, nucleotide composition. We can retrieve a quality by using the gene name and quality in conjunction.
To retrieve just one gene's nucleotide composition
>>> genes['gene1']['nt_comp']
{'C': 2, 'G': 1, 'A': 1, 'T': 2}
Alter one gene's nucleotide count with =
assignment operator:
>>> genes['gene1']['nt_comp']
{'C': 2, 'G': 1, 'A': 1, 'T': 2}
>>>
>>> genes['gene1']['nt_comp']['T']=6
>>> genes['gene1']['nt_comp']
{'C': 2, 'G': 1, 'A': 1, 'T': 6}
Alter one gene's nucleotide count with +=
assignment operator:
>>> genes['gene1']['nt_comp']
{'C': 2, 'G': 1, 'A': 1, 'T': 6}
>>>
>>> genes['gene1']['nt_comp']['A']+=1
>>>
>>> genes['gene1']['nt_comp']
{'C': 2, 'G': 1, 'A': 2, 'T': 6}
>>>
>>>
To retrieve the A composition of every gene use a for loop.
>>> for gene in sorted(genes):
... A_comp = genes[gene]['nt_comp']['A']
... print(gene+":","As=", A_comp)
...
gene1: As= 2
gene2: As= 3
Below is an example of building a list with a mixed collection of value types. Remember that all elements inside a list or dictionary should be the same type. In other words, the values in a list should all be lists or dictonaries or scalar values. This allows you to loop over the data structure.
The dictionary which is a list value has a key that has a dictionary as a value.
[{'gene1' : {'sequence' : [1, 2, 3], [4, 5, 6], [7,8,9]]
Just spaced differently:
[
[1, 2, 3],
[4, 5, 6],
{
'key': 'value',
'key2':
{
'something_new': 'Yay'
}
}
]
Building this data structure in the interpreter:
>>> new_data = []
>>> new_data
[]
>>> new_data.append([1,2,3])
>>> new_data
[[1, 2, 3]]
>>> new_data[0]
[1, 2, 3]
>>> new_data.append([4,5,6])
>>> new_data
[[1, 2, 3], [4, 5, 6]]
>>> new_data[1]
[4, 5, 6]
>>> new_data[1][2]
6
>>> new_data.append({})
>>> new_data
[[1, 2, 3], [4, 5, 6], {}]
>>> new_data[2]['key']='value'
>>> new_data
[[1, 2, 3], [4, 5, 6], {'key': 'value'}]
>>> new_data[2]['key2']={}
>>> new_data
[[1, 2, 3], [4, 5, 6], {'key2': {}, 'key': 'value'}]
>>> new_data[2]['key2']['something_new']='Yay'
>>> new_data
[[1, 2, 3], [4, 5, 6], {'key2': {'something_new': 'Yay'}, 'key': 'value'}]
>>>
Same example in a script file: Building Complex Datastructures
Course T-shirt Organization and Counting
We have a spreadsheet of everyone's style, size, color. We want to know how many of each unique combination of style-size-color we need to order
mens small heather seafoam
womens medium Heather Purple
womens medium berry
mens medium heather coral silk
womens Small Kiwi
Mens large Graphite Heather
mens large sport grey
mens small Carolina Blue
We want something like this:
womens small antique heliconia 2
womens xs heather orange 1
womens medium kiwi 2
womens medium royal heather 1
#!/usr/bin/env python3
shirts = {}
with open("shirts.txt","r") as file_object:
for line in file_object:
line = line.rstrip()
[style, size, color] = line.split("\t")
style = style.lower()
size = size.lower()
color = color.lower()
if style not in shirts:
shirts[style] = {}
if size not in shirts[style]:
shirts[style][size] = {}
if color not in shirts[style][size]:
shirts[style][size][color] = 0
shirts[style][size][color] += 1
for style in shirts:
for size in shirts[style]:
for color in shirts[style][size]:
count = shirts[style][size][color]
print(style,size,color,count,sep="\t")
Output:
sro$ python3 shirts.py
mens small heather maroon 1
mens small royal blue 1
mens small olive 1
mens large graphite heather 1
womens medium heather purple 3
womens medium berry 2
womens medium royal heather 1
womens medium kiwi 2
...
This is what the data structure we just built looks likes
{
'mens':
{
'small':
{
'heather seafoam': 1,
'carolina blue': 1,
'cornsilk': 1,
'dark heather': 1,
'heather maroon': 1,
'royal blue': 1,
'olive': 1
},
'large':
{
'graphite heather': 1,
'sport grey': 1,
'heather purple': 1,
'heather coral silk': 1,
'heather irish': 1,
'heather royal': 1,
'carolina blue': 1
},
'medium':
{
'heather coral silk': 1,
'heather royal': 2,
'heather galapagos blue': 1,
'heather forest': 1,
'gold': 1,
'heather military green': 1,
'dark heather': 1,
'carolina blue': 1,
'iris': 1
},
'xs':
{
'white': 1
},
'xl':
{
'heather cardinal': 1,
'indigo blue': 1
}
},
'womens':
{
'medium':
{
'heather purple': 3,
'berry': 2,
'royal heather': 1,
'kiwi': 2,
'carolina blue': 1
},
'small':
{
'kiwi': 1,
'berry': 1,
'antique heliconia': 2
},
'large':
{
'kiwi': 1
},
'xs':
{
'heather orange': 1
}
},
'child':
{
'4t':
{
'green': 2
},
'3t':
{
'pink': 1
},
'2t':
{
'orange': 1
},
'6t':
{
'pink': 1
}
}
}
There are also specific data table and frame handling libraries like Pandas.
Here is a intro to data structures in Panda.
Here is a very nice interactive tutorial
There are a few different types of errors when coding. Syntax errors, logic errors, and exceptions. You have probably encountered all three. Syntax and logic errors are issues you need to deal with while coding. An exception is a special type of error that can be informative and used to write code to respond to this type of error. This is especially relavent when dealing with user input. What if they don't give you any, or it is the wrong kind of input. We want our code to be able to detect these types of errors and respond accordingly.
#!/usr/bin/env python3
import sys
file = sys.argv[1]
print("User provided file:" , file)
This code takes user provided input and prints it
Run it.
$ python scripts/exceptions.py test.txt
User provided file: test.txt
What happens if the user does not provide any input and we try to print it?
$ python scripts/exceptions.py
Traceback (most recent call last):
File "scripts/exceptions.py", line 4, in <module>
file = sys.argv[1]
IndexError: list index out of range
We get an IndexError exception, which is raised when an index is not found in a sequence.
We have already seen quite a few exceptions throughout the lecture notes, here are some:
- ValueError: math domain error
- AttributeError: 'list' object has no attribute 'rstrip'
- SyntaxError: EOL while scanning string literal
- NameError: name 'GGTCTAC' is not defined
- SyntaxError: Missing parentheses in call to 'print'
- AttributeError: 'int' object has no attribute 'lower'
- IndexError: list assignment index out of range
- NameError: name 'HDAC' is not defined
Link to Python Documentation of built in types of exceptions
We can use the exception to our advantage to help the people who are running the script. We can use a try/except condition like an if/else block to look for exceptions and to execute specific code if we do not have an exception and do something different if we do have an exception.
#!/usr/bin/env python3
import sys
file = ''
try:
file = sys.argv[1]
print("User provided file:" , file)
except:
print("Please provide a file name")
We need to "try" to get a user provided argument. If we are successful then we can print it out. If we try and fail, we execute the code in the except portion of our try/except and print that we need a file name.
Let's run it WITH user input
$ python3 scripts/exceptions_try.py test.txt
User provided file: test.txt
It runs as expected
Let's run it WITHOUT user input
$ python scripts/exceptions_try.py
Please provide a file name
Yeah, the user is informed that they need to provide a file name to the script
What if the user provides input but it is not a valid file or the path is incorrect? Or if you want to check to see if the user provided input as well as if it can open the input.
We can add multiple exception tests, like if/elif block. Each except statement can specify what kind of exception it is waiting to recieve. If that kind of exception occures, that block of code will be executed.
import sys
file = ''
try:
file = sys.argv[1]
print("User provided file name:" , file)
FASTA = open(file, "r")
for line in FASTA:
line = line.rstrip()
print(line)
except IndexError:
print("Please provide a file name")
except IOError:
print("Can't find file:" , file)
Here we test for an IndexError: Raised when an index is not found in a sequence. The IndexError occurs when we try to access a list element that does not exists. And we test for a IOError: Raised when an input/ output operation fails, such as the print statement or the open() function when trying to open a file that does not exist.
The IOError happens when we try to access a file that does not exist.
Let's run it with a file that does not exist.
$ python scripts/exceptions_try_files.py test.txt
User provided file name: test.txt
Can't find file: test.txt
This informs the user that they did provide input but that the file listed can not be found.
Let's run it with no input
$ python scripts/exceptions_try_files.py
Please provide a file name
This informs the user that they need to provide a file.
Lets summarize what we have covered and add on else
and finally
.
try:
# try block is executed until an exception is raised
except _ExceptionType_:
# if there is an exception of "ExceptionType" this block will be executed
# there can be more than one except block, just like an elif
except:
# if there are any exceptions that are not of "ExceptionType" this except block will be executed
else:
# the else block is executed after the try block has been completed, which means there were no exceptions raised
finally:
# the finally block is executed if exceptions are or are not raised (no matter what happens)
Some exceptions can be thrown for multiple reasons, for example, ErrorIO will occur if the file does not exist as well as if you don't have permissions to read it. We can get more information by viewing the contents of our Exception Object. Yes, an exception is an object too! The system errors get stored in the exception object. To access the object use as
and supply a variable name, like 'ex'
file = ''
try:
file = sys.argv[1]
print("User provided file name:" , file)
FASTA = open(file, "r")
for line in FASTA:
line = line.rstrip()
print(line)
except IndexError:
print("Please provide a file name")
except IOError as ex:
print("Can't find file:" , file , ': ' , ex.strerror )
Here we added
except IOError as ex
and now we can get the 'strerror' message from ex.
Run it.
$ python scripts/exceptions_try_files_as.py test.txt
User provided file name: test.txt
Can't find file: test.txt : No such file or directory
Now we know that this file name or path is not valid
We can call or raise exceptions too!! This is accomplished by using a raise
statement.
- First, create a new Exception Object, i.e.,
ValueError()
- Use the Exception Object in a Raise statment
raise ValueError('your message')
Let's raise an exception if the file name does not end in 'fa'
import sys
file = ''
try:
file = sys.argv[1]
print("User provided file name:" , file)
if not file.endswith('.fa'):
raise ValueError("Not a FASTA file")
FASTA = open(file, "r")
for line in FASTA:
print(line)
except IndexError:
print("Please provide a file name")
except IOError as ex:
print("Can't find file:" , file , ': ' , ex.strerror )
Here we raise a known exception, 'ValueError', if the file does not end with (uses
endswith()
method).
Let's run it.
$ python scripts/exceptions_try_files_raise.py test.txt
User provided file name: test.txt
Traceback (most recent call last):
File "scripts/exceptions_try_files_raise.py", line 10, in <module>
raise ValueError("Not a FASTA file")
ValueError: Not a FASTA file
Our exception get's raised, now lets do something with it.
import sys
file = ''
try:
file = sys.argv[1]
print("User provided file name:" , file)
if not file.endswith('.fa'):
raise ValueError("Not a FASTA file")
FASTA = open(file, "r")
for line in FASTA:
print(line)
except IndexError:
print("Please provide a file name")
except ValueError:
print("File needs to be a FASTA file and end with .fa")
except IOError as ex:
print("Can't find file:" , file , ': ' , ex.strerror )
Here we created an exception to catch any ValueError
Let's Run it.
$ python scripts/exceptions_try_files_raise_value.py test.txt
User provided file name: test.txt
File needs to be a FASTA file and end with .fa
We get a great error message now.
But what if there is another ValueError, how can we tell if has anything to do with the FASTA file extension or not? Answer: the message will be different.
We can create our own custom exception. We will need to create a new class of exception. Below is the syntax to do this.
import sys
class NotFASTAError(Exception):
pass
file = ''
try:
file = sys.argv[1]
print("User provided file name:" , file)
if not file.endswith('.fa'):
raise NotFASTAError("Not a FASTA file")
FASTA = open(file, "r")
for line in FASTA:
print(line)
except IndexError:
print("Please provide a file name")
except NotFASTAError:
print("File needs to be a FASTA file and end with .fa")
except IOError as ex:
print("Can't find file:" , file , ': ' , ex.strerror )
Here we created a new class of exception called 'NotFASTAError'. Then we raised this new exception.
Let's Run it.
$ python scripts/exceptions_try_files_raise_try.py test.txt
User provided file name: test.txt
File needs to be a FASTA file and end with .fa
Our new class of exception, NotFASTAError, works just like the built in exceptions.
Functions consist of several lines of code that do something useful and that you want to run more than once. There are built-in functions in python. You can also write your own. You also give your function a name so you can refer to it in your code. This avoids copying and pasting the same code to many places in your script and makes your code easier to read.
Let's see some examples.
Python has built-in functions
>>> print('Hello world!')
Hello world!
>>> len('AGGCT')
5
You can define your own functions with def
Let's write a function that calculates the GC content. Let's define this as the fraction of nucleotides in a DNA sequence that are G or C. It can vary from 0 to 1.
First we can look at the code that makes the calculation, then we can convert those lines of code into a function.
Code to find GC content:
dna = 'GTACCTTGATTTCGTATTCTGAGAGGCTGCTGCT'
c_count = dna.count('C') # count is a string method
g_count = dna.count('G')
dna_len = len(dna) # len is a function
gc_content = (c_count + g_count) / dna_len # fraction from 0 to 1
print(gc_content)
We use def
do define our own function. It is followed by the name of the function (gc_content
) and parameters it will take in parentheses. A colon is the last character on the def
line. The parameter variables will be available for your code inside the function to use.
def gc_content(dna): # give our function a name and parameter 'dna'
c_count = dna.count('C')
g_count = dna.count('G')
dna_len = len(dna)
gc_content = (c_count + g_count) / dna_len
return gc_content # return the value to the code that called this function
Here is a custom function that you can use like a built in Python function
This is just like any other python function. You write the name of the function with any variables you want to pass to the function in parentheses. In the example below the contents of dna_string
get passed into gc_content()
. Inside the function this data is passed to the variable dna
.
dna_string = "GTACCTTGATTTCGTATTCTGAGAGGCTGCT"
print(gc_content(dna_string))
This code will print 0.45161290322580644 to the screen. You can save this value in a variable to use later in your code like this
dna_gc = gc_content('GTACCTTGATTTCGTATTCTGAGAGGCTGCT')
As you can see we can write a nice clear line of python to call this function and because the function has a name that describes what it does it's easy to understand how the code works. Don't give your functions names like this def my_function(a):
!
How could you convert the GC fraction to % GC. Use format()
.
dna_string = "GTACCTTGATTTCGTATTCTGAGAGGCTGCT"
dna_gc = gc_content(dna_string)
pc_gc = '{:.2%}'.format(dna_gc)
print('This sequence is' , pc_gc , 'GC')
Here's the output
This sequence is 45.16% GC
- You define a function with
def
. You need to define a function before you can call it. - The function must have a name. This name should clearly describe what the function does. Here is our example
gc_content
- You can pass variables to functions but you don't have to. In the definition line, you place variables your function needs inside parentheses like this
(dna)
. This variable only exists inside the function. - The first line of the function must end with a
:
so the complete function definition line looks like thisdef gc_content(dna):
- The next lines of code, the function body, need to be indented. This code comprises what the function does.
- You can return a value as the last line of the function, but this is not required. This line
return gc_content
at the end of our function definition passes the value of gc_content back to the code that called the function in your main script.
You can name your argument variables anything you want, but the name should describe the data contained. The name needs to be consistent within your function.
Arguments can be named and these names can be used when the function is called. This name is called a 'keyword'
>>> dna_string = "GTACCTTGATTTCGTATTCTGAGAGGCTGCT"
>>> print(gc_content(dna_string))
0.45161290322580644
>>> print(gc_content(dna=dna_string)
0.45161290322580644
The keyword must be the same as the defined function argument. If a function has multiple arguments, using the keyword allows for calling the function with the arguments in any order.
As defined above, our function is expecting an argument (dna
) in the definition. You get an error if you call the function without any parameters.
>>> gc_content()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: gc_content() missing 1 required positional argument: 'dna'
You can define default values for arguments when you define your function.
def gc_content(dna='N'): # give our function a name and parameter 'dna'
c_count = dna.count('C')
g_count = dna.count('G')
dna_len = len(dna)
gc_content = (c_count + g_count) / dna_len
return gc_content # return the value to the code that called this function
If you call the function with no arguments, the default will be used. In this case a default is pretty useless, and the function will return '0' if called without providing a DNA sequence.
Lambda expressions can be used to define simple (one-line) functions. There are some uses for lambda which we won't go into here. We are showing it to you because sometimes you will come across it.
Here is a one line custom function, like the functions we have already talked about:
def get_first_codon(dna):
return dna[0:3]
print(get_first_codon('ATGTTT'))
This will print
ATG
Here is the same function written as a lambda
get_first_codon = lambda dna : dna[0:3]
print(get_first_codon('ATGTTT'))
This also prints
ATG
. lambdas can only contain one line and there is noreturn
statement.
List comprehensions can often be used instead of lambdas and may be easier to read. You can read more about lambda
, particularly in relation to map
which will perform an operation on a list, but generally a for
loop is easier to read.
Almost all python variables are global. This means they are available everywhere in your code. Remember that python blocks are defined as code at the same level of indentation.
#!/usr/bin/env python3
print('Before if block')
x = 100
print('x=',x)
if True: # this if condition will always be True
# we want to make sure the block gets executed
# so we can show you what happens
print('Inside if block')
x = 30
y = 10
print("x=", x)
print("y=", y)
print('After if block')
print("x=", x)
print("y=", y)
Let's Run it:
$ python3 scripts/scope.py
Before if block
x= 100
Inside if block
x= 30
y= 10
After if block
x= 30
y= 10
The most important exception to variables being global is that variables that are defined in functions are local i.e. they only exist inside their function. Inside a function, global variables are visible, but it's better to pass variables to a function as arguments
def show_n():
print(n)
n = 5
show_n()
The output is this 5
as you would expect, but the example below is better programming practice. Why? We'll see a little later.
def show_n(n):
print(n)
n = 5
show_n(n)
Variables inside functions are local and therefore can only been accessed from within the function block. This applies to arguments as well as variables defined inside a function.
#!/usr/bin/end python3
def set_local_x_to_five(x):
print('Inside def')
x = 5 # local to set_local_x_to_five()
y=5 # also local
print("x =",x)
print("y = ",y)
print('After def')
x = 100 # global x
y = 100 # global
print('x=',x)
print('y=',y)
set_local_x_to_five(500)
print('After function call')
print('x=',x)
print('y=',y)
Here we have added a function
set_local_x_to_five
with an argument named 'x'. This variable exists only within the function where is replaces any variable with the same name outside thedef
. Inside thedef
we also initialize a variabley
that also replaces any globaly
within thedef
Let's run it:
$ python3 scope_w_function.py
After def
x= 100
y= 100
Inside def
x = 5
y = 5
After function call
x= 100
y= 100
There is a global variable,
x
= 100, but when the function is called, it makes a new local variable, also calledx
with value = 5. This variable disappears after the function finishes and we go back to using the global variablex
= 100. Same fory
You can make a local variable global with the statement global
. Now a variable you use in a function is the same variable as in the rest of the code. It is best not to define any variables as global until you know you need to because you might modify the contents of a variable without meaning to.
Here is an example use of global
.
#!/usr/bin/env python3
def set_global_variable():
global greeting # make greeting global
greeting = "I say hello"
greeting = 'Good morning'
print('Before function call')
print('greeting =',greeting)
#make call to function
set_global_variable()
print('After function call')
print('greeting =',greeting)
Let's look at the output
$ python3 scripts/scope_global.py
Before function call
greeting = Good morning
After function call
greeting = I say hello
Note that the function has changed the value of the global variable. You might not want to do this.
By creating new local variables inside function definitions, python stops variables with the same name from over-writing each other by mistake.
Python comes with some core functions and methods. There are many useful modules that you will want to use. import
is the statement for telling your script you want to use code in a module. As we've already seen with regular expresions, you can bring in code that handles regular expressions with import re
How do you find out information about a module? Python has help pages built into the command line, like man
we met earlier in the unix lecture. Online information may be more up to date. Search at https://docs.python.org/3.6/. But if you don't have internet access, you can always use pydoc
.
To find out about the re
module, type pydoc re
on the command line. The last line in the output tells you where the python module is actually installed.
% pydoc re
Help on module re:
NAME
re - Support for regular expressions (RE).
MODULE REFERENCE
https://docs.python.org/3.6/library/re
The following documentation is automatically generated from the Python
source files. It may be incomplete, incorrect or include features that
are considered implementation detail and may vary between Python
implementations. When in doubt, consult the module reference at the
location listed above.
DESCRIPTION
This module provides regular expression matching operations similar to
those found in Perl. It supports both 8-bit and Unicode strings; both
the pattern and the strings being processed can contain null bytes and
characters outside the US ASCII range.
Regular expressions can contain both special and ordinary characters.
Most ordinary characters, like "A", "a", or "0", are the simplest
regular expressions; they simply match themselves. You can
concatenate ordinary characters, so last matches the string 'last'.
...
FILE
/anaconda3/lib/python3.6/glob.py
Here are some of the most common and useful modules, along with their methods and objects. It's a lightning tour.
os.path
has common utilities for working file paths (filenames and directories). A path is either a relative or absolute list of directories (often ending with a filename) that tells you where to find a file or directory.
function | description |
---|---|
os.path.basename(path) | what's the last element of the path? Note /home/tmp/ returns '' , rather than tmp |
os.path.dirname(path) | what's the directory the file is in? |
os.path.exists(path) | does the path exist? |
os.path.getsize(path) | returns path (file) size in bytes or error |
os.path.isfile(path) | does the path point to a file? |
os.path.isdir(path) | does the path point to a directory? |
os.path.splitext(path) | splits before and after the file extension (e.g. '.txt') |
Replaced by subprocess.
This is the current module for running command lines from python scripts
import subprocess
subprocess.run(["ls","-l"]) # same as running ls -l on the command line
more complex than os.system()
. You need to specify where input and output go. Let's look at this in some more detail.
Let's say we want to find all the files that have user amanda (or in the filename)
ls -l | grep amanda
becomes this 'shortcut' which will capture the output of the two unix commands in the variable output
import subprocess
output = subprocess.check_output('ls -l | grep amanda', shell = True)
This is better than alternatives with subprocess.run()
. This is equivalent to the unix backtick quoted string.
output
contains a bytes object (more or less a string of ASCII character encodings)
b'-rw-r--r-- 1 amanda staff 161952 Oct 2 18:03 test.subreads.fa\n-rw-r--r-- 1 amanda staff 126 Oct 2 13:23 test.txt\n'
You can covert by decoding the bytes object into a string
>>>output.decode('utf-8')
'-rw-r--r-- 1 amanda staff 161952 Oct 2 18:03 test.subreads.fa\n-rw-r--r-- 1 amanda staff 126 Oct 2 13:23 test.txt\n'
Let's assume that ls -l
generates some output something like this
total 112
-rw-r--r-- 1 amanda staff 69 Jun 14 17:41 data.cfg
-rw-r--r-- 1 amanda staff 161952 Oct 2 18:03 test.subreads.fa
-rw-r--r-- 1 amanda staff 126 Oct 2 13:23 test.txt
How do we run ls -l
in Python and capture the output (stdout)?
import subprocess
rtn = subprocess.run(['ls','-l'], stdout=subprocess.PIPE ) # specify you want to capture STDOUT
bytes = rtn.stdout
stdout = bytes.decode('utf-8')
# something like
lines = stdout.splitlines()
lines
now contains elements from every line of the ls -l
output, including the header line, which is not a file
>>> lines[0]
'total 112'
>>> lines[1]
'-rw-r--r-- 1 amanda staff 69 Jun 14 17:41 data.cfg'
To run a command and check the exit status (really to check the exit status was ok or zero), use
oops = subprocess.check_call(['ls', '-l'])
# or, simpler...
oops = subprocess.check_call('ls -l', shell=True)
You can't write ls -l > listing.txt
to redirect stdout in the subprocess method, so use this instead
tmp_file = 'listing.txt'
with open(tmp_file,'w') as ofh:
oops = subprocess.check_call(['ls', '-l'], stdout=ofh )
A couple of useful variables for beginners. Many more advanced system parameters and settings that we are not covering here.
function | description |
---|---|
sys.argv | list of command line parameters |
sys.path | where Python should look for modules |
See notes on regular expressions
Better lists etc.
from collections import deque
copy.copy()
and
copy.deepcopy()
Link to more info for more on deep vs shallow copying
function | description |
---|---|
math.exp() | e**x |
math.log2() | log base 2 |
math.log10() | log base 10 |
math.sqrt() | square root |
math.sin() | sine |
math.pi(), math.e() | constants |
etc |
see also numpy
Random numbers generated by computers are not truly random, so python calls these pseudo-random.
example | description |
---|---|
random.seed(1) | set starting seed for random sequence to 1 to enable reproducibility |
random.randrange(9) | integer between 0 and 8 |
random.randint(1,5) | integer between 1 and 5 |
random.random() | float between 0 and 1 |
random.uniform(1,2) | float between 1 and 2 |
random.choice(my_genes) | return a random element of the sequence |
To get a random index from an element of list
use i=random.randrange(len(list))
Typical statistical quantities
example | description |
---|---|
statistics.mean([1,2,3,4,5]) | mean or average |
statistics.median([ 2,3,4,5]) | median = 3.5 |
statistics.stdev([1,2,3,4,5]) | standard deviation of sample (square root of sample variance) |
statistics.pstdev([1,2,3,4,5])q | estimate of population standard deviation |
Does unix-like wildcard file path expansion.
>>> import glob
>>> glob.glob('pdfs/*.pdf')
['pdfs/python1.pdf', 'pdfs/python2.pdf', 'pdfs/python3.pdf', 'pdfs/python4.pdf', 'pdfs/python6.pdf', 'pdfs/python8.pdf', 'pdfs/unix1.pdf', 'pdfs/unix2.pdf']
>>> fasta_files = glob.glob('sequences/*.fa')
>>>
Great (if quite complicated) tool for parsing command line arguments and automatically generating help messages for scripts (very handy!). Here's a simple script that explains a little of what it does.
#!/usr/bin/env python3
import argparse
parser = argparse.ArgumentParser(description="A test program that reads in some number of lines from an input file. The output can be screen or an output file")
# we want the first argument to be the filename
parser.add_argument("file", help="path to input fasta filename")
# second argument will be line number
# default type is string, need to specify if expecting an int
parser.add_argument("lines", type=int, help ="how many lines to print")
# optional outfile argument specified with -o or --out
parser.add_argument("-o","--outfile", help = "optional: supply output filename, otherwise write to screen", dest = 'out')
args = parser.parse_args()
# arguments appear in args
filename = args.file
lines = args.lines
if args.out:
print("writing output to", args.out)
With this module, -h help comes for free. --outfile type arguments are optional unless you write 'required=True' like this
parser.add_argument('-f', "-fasta", required=True, help='Output fasta filename', dest='outfile')
time, HTML, XML, email, CGI, sockets, audio, GUIs with Tk, debugging, testing, unix utils
Also, non-core: BioPython for bioinformatics, Numpy for mathematics, statistics, pandas for data, scikitlearn for machine learning.
The advantages of writing classes and writing functions are very similar.
When we write functions we group core Python functions and methods to create a unique collection statements that occur in a specific order.
These new functions make our code easier to read and to write, especially if you will use the function many times.
A conceptual difference between a function and a class is that a function usually does one thing, while a class will do many related things to help solve a problem.
What is a class really, what does it do? A class doesn't really do anything except for setting a list of rules for creating a new custom object. Every time you use the class you are creating an instance of a type of object.
You have already been using classes to create objects. Here we are using the open
function to create two instances of a file object. One instance holds information about a FASTA file while the other holds information about a GFF file.
fa_input = open("somedata.fa")
gff_input = open("somedata.gff")
Classes create objects, these objects will have attributes and methods associated with them.
methods
Methods are functions which belong to objects of a particular class.
attributes
Attributes are variables that are associated with an object of a particular class.
Defining a class is straightforward.
The first step is to decide what attributes and what methods it will have.
Create a DNARecord Class.
When we create a class, we are really setting up a series of rules that a DNARecord object must follow.
DNARecord Rules:
- DNARecord must have a sequence [attribute]
- DNARecord must have a name [attribute]
- DNARecord must have an organism [attribute]
- DNARecord will be able to calculate AT content [method]
- DNARecord will be able to calculate the reverse complement [method]
Here is the first, but not final draft of our class. We will go through each section of this code below:
class DNARecord(object):
# define class attributes
sequence = 'ACGTAGCTGACGATC'
gene_name = 'ABC1'
species_name = 'Drosophila melanogaster'
# define methods
def reverse_complement(self):
replacement1 = self.sequence.replace('A', 't')
replacement2 = replacement1.replace('T', 'a')
replacement3 = replacement2.replace('C', 'g')
replacement4 = replacement3.replace('G', 'c')
reverse_comp = replacement4[::-1]
return reverse_comp.upper()
def get_AT(self):
length = len(self.sequence)
a_count = self.sequence.count('A')
t_count = self.sequence.count('T')
at_content = (a_count + t_count) / length
return at_content
## create a new DNARecord Object
dna_rec_obj = DNARecord()
## Use New DNARecord object
print('Created a record for ' + dna_rec_obj.gene_name + ' from ' + dna_rec_obj.species_name)
print('AT is ' + str(dna_rec_obj.get_AT()))
print('complement is ' + dna_rec_obj.reverse_complement())
Now let's go through each section:
We start with the keyword class
, followed by the name of our class DNARecord
with
the name of the base class in parentheses object
.
class DNARecord(object):
Then we define class attributes. These are variables with data that belongs to the class, and therefore to any object that is created using this class
# define class attributes
sequence = 'ACGTAGCTGACGATC'
gene_name = 'ABC1'
species_name = 'Drosophila melanogaster'
Next, we define our class methods:
# define methods
def reverse_complement(self):
replacement1 = self.sequence.replace('A', 't')
replacement2 = replacement1.replace('T', 'a')
replacement3 = replacement2.replace('C', 'g')
replacement4 = replacement3.replace('G', 'c')
reverse_comp = replacement4[::-1]
return reverse_comp.upper()
def get_AT(self):
length = len(self.sequence)
a_count = self.sequence.count('A')
t_count = self.sequence.count('T')
at_content = (a_count + t_count) / length
return at_content
The methods are using an argument called self
, i.e., length = len(self.sequence)
. This is a special variable that you use inside a class. With it you can access all the data that is contained inside the object when it is created.
Use self.attribute
format to retrieve the value of variables created within the class. Here we use self.sequence
to retrieve the information stored in our attribute named sequence
.
replacement1 = self.sequence.replace('A', 't')
The above class is a set of rules that need to be followed when creating a new DNARecord object. Now let's create a new DNARecord object:
dna_rec_obj = DNARecord()
dna_rec_obj
is our new DNARecord object that was creating using the rules we put into place in the class definition.
Now that a new DNARecord object has been created, and assigned to the variable dna_rec_obj
, we can access its attributes using the following format, object.attribute_name
To get the gene name of the object we created, we simply write dna_rec_obj.gene_name
.
This is possible because within our class definition we create a gene_name
variable.
Let's try it:
>>> dna_rec_obj.gene_name
'ABC1'
>>> dna_rec_obj.sequence
'ACGTAGCTGACGATC'
To call a method associated with our new object, we use a similar format object.method_name
.
So to call the get_AT()
method, we would use dna_rec_obj.get_AT()
. This should look familiar, you have done used class methods over and over again: some_string.count('A')
Let's try it with our dna_rec_obj
:
>>> dna_rec_obj.sequence
'ACGTAGCTGACGATC'
>>> dna_rec_obj.get_AT()
0.4666666666666667
Now let's use the reverse_complement()
method
>>> dna_rec_obj.sequence
'ACGTAGCTGACGATC'
>>> dna_rec_obj.reverse_complement()
GATCGTCAGCTACGT
Wow!! Getting the reverse complement in one line is pretty nice!
Great!!!
We can now create a DNARecord object and retrieve the object attributes and use the cool methods we created.
But..... It always contains the same gene_name, sequence, and species information 😟
Let's make our class more generic, or in other words, make it so that a user can provide a new gene name, gene sequence, and source organism everytime a DNARecord object is created.
To do this we need to add an __init__
function to our Object Rules, or Class.
The init
function will automatically get called when you create an object.
It contains specific instructions for creating a new DNARecord Object.
It specifies how many pieces of data we want to collect from the creator of a DNARecord object to use within a DNARecord object.
Below our __init__ instructions indicate that we want to create object attributes called sequence
, gene_name
, and species_name
and to set them with the values provided as arguments when the object was created.
Here is our new class definition and new object creation when using the __init__ function:
#!/usr/bin/env python3
class DNARecord(object):
# define class attributes
def __init__(self, sequence, gene_name, species_name): ## note that '__init__' is wrapped with two underscores
#sequence = 'ACGTAGCTGACGATC'
#gene_name = 'ABC1'
#species_name = 'Drosophila melanogaster'
self.sequence = sequence
self.gene_name = gene_name
self.species_name = species_name
# define methods
def reverse_complement(self):
replacement1 = self.sequence.replace('A', 't')
replacement2 = replacement1.replace('T', 'a')
replacement3 = replacement2.replace('C', 'g')
replacement4 = replacement3.replace('G', 'c')
reverse_comp = replacement4[::-1]
return reverse_comp.upper()
def get_AT(self):
length = len(self.sequence)
a_count = self.sequence.count('A')
t_count = self.sequence.count('T')
at_content = (a_count + t_count) / length
return at_content
## Create new DNARecord Objects with user defined data
dna_rec_obj_1 = DNARecord('ACTGATCGTTACGTACGAGT', 'ABC1', 'Drosophila melanogaster')
dna_rec_obj_2 = DNARecord('ATATATTATTATATTATA', 'COX1', 'Homo sapiens')
for d in [ dna_rec_obj_1, dna_rec_obj_2 ]:
print('name:' , d.gene_name , ' ' , 'seq:' , d.sequence)
Output:
$ python3 dnaRecord_init.py
name: ABC1 seq: ACTGATCGTTACGTACGAGT
name: COX1 seq: ATATATTATTATATTATA
Now you can create as many DNASequence Objects as you like, each can contain information about a different sequence.