ICE-B
14 - Dados Estruturados
1
Dados Estruturados
Resumo
■
Organizar os dados de forma estruturada
■
Dicionários:
•
Estruturas de dados associando chave e valor
■
Exemplo: calcular massa molecular
■
Classes (extra):
•
Criar objectos em Python
Dados Estruturados
3
Dados Estruturados
Motivação para estruturar os dados
■
Se usarmos tuplos ou listas precisamos de
•
Lembrar onde está cada valor (e.g. Id, sequência)
•
Procurar o elemento que queremos (e.g.
find)
■
É mais difícil alterar informação guardada
•
E.g. passar a ter tuplos (id, organismo, sequência)
■
O que dava jeito é poder organizar os dados por nome
Dicionários
Conjuntos não ordenados de chave:valor
■
Sintaxe: {chave1:valor1,chave2:valor2, ...}
■
A chave tem de ser um objecto imutável
•
Número, string, tuplo, por exemplo
■
O valor pode ser qualquer objecto
■
A indexação é feita pela chave
In : capitals = {'Paris':'France','Lisbon':'Portugal','London':'United Kingdom' In : capitals['Paris']
Out: 'France'
In : capitals['Lisbon'] Out: 'Portugal'
5
Dicionários
Conjuntos não ordenados de chave:valor
■
Podemos acrescentar ou substituir valores atribuindo a uma chave
In : capitals['Madrid']='Spain' In : capitals
Out:
{'Lisbon': 'Portugal',
'London': 'United Kingdom', 'Madrid': 'Spain',
'Paris': 'France'}
Dicionários
Exemplo: enzimas, da aula anterior
def enzyme_dictionary(file_name):
"""Read enzyme data to dictionary id: recon,cut)""" enzymes = {} lines = open(file_name).readlines() for el in lines: e_id,recon,cut = el.split(';') enzymes[e_id]={'recon':recon,'cut':cut} return enzymes In : enzymes = enzyme_dictionary('enzymes.txt') Out: {'AluI': {'cut': 'AG-CT', 'recon': 'AGCT'},
'BamHI': {'cut': 'G-GATCC\n', 'recon': 'GGATCC'}, 'EcoRI': {'cut': 'G-AATTC\n', 'recon': 'GAATTC'}, ...}
In : enzymes['AluI']['cut'] Out: 'AG-CT'
In : enzymes['TaqI']['recon'] Out: 'TCGA'
7
Dados Estruturados
Massa Molecular
Calcular a massa molecular dada a fórmula
In : molecular_mass('CaCO3') # calcium carbonate
Out: 100.08747
In : molecular_mass('CH3COOC6H4COOH') # aspirin
Out: 180.15829200000002
In : molecular_mass('K[PtCl3(C2H4)]') # Zeise's salt
Out: 368.59614600000003
■
Precisamos de:
•
Massas atómicas
•
Algoritmo
9
Massa Molecular
Ficheiro com massa atómica:
■
Ficheiro com valores separados por tabs (periodic_table.txt)
atomicNumber symbol name atomicMass 1 H Hydrogen 1.007944 2 He Helium 4.0026022 3 Li Lithium 6.9412 4 Be Beryllium 9.0121823 5 B Boron 10.8117 6 C Carbon 12.01078 7 N Nitrogen 14.00672 ...
Massa Molecular
Algoritmo
■
Do ficheiro criar um dicionário com símbolo:massa dos átomos
■
Da fórmula um dicionário com símbolo:número para cada átomo
•
Como? Decompor em sub problemas
•
Procurar a primeira letra maiúscula (e.g. 'CaCO3')
•
Procurar a letra maiúscula seguinte (ou o fim)
•
Retirar slice entre ambas (e.g. Ca, C, O3)
•
Letras dão símbolo, números dão o número de átomos
•
Se não tem número, o número é 1
■
Percorrer o dicionário dos átomos da fórmula, somar massa
consultando o dicionáro das massas atómicas
11
Massa Molecular
Plano do programa
def read_mass(file):
"return dictionary with atom and atomic mass"
def next_upper(formula, index):
"position of next uppercase from index"
def convert(fragment):
"converts a fragment from a formula into dictionary"
def decompose(formula):
"return list of dictionaries with element and amount"
def molecular_mass(formula):
Massa Molecular
Ler massa atómica do ficheiro para um dicionário
def read_mass(file):
"return dictionary with atom and atomic mass" lines = open(file).readlines()
masses = {}
for line in lines[1:]:
cells = line.split('\t')
masses[cells[1]] = float(cells[3]) return masses
atomicNumber symbol name atomicMass 1 H Hydrogen 1.007944 In : read_mass('periodic_table.txt') Out: {'Ac': 227.0, 'Ag': 107.86822, 'Al': 26.98153868,
13
Massa Molecular
Encontrar a próxima maiúscula
■
Usar método isupper das strings
■
Podíamos usar um while, mas o return no for serve
•
O segundo return só acontece se chegar ao fim sem encontrar maiúscula
def next_upper(formula, index):
"position of next uppercase from index" for ix in range(index,len(formula)): if formula[ix].isupper(): return ix return len(formula) In : next_upper('CaCO3',2) Out: 2 In : next_upper('CaCO3',1) Out: 2
Massa Molecular
Converter fragmento num dicionário com dois campos
def convert(fragment):
"converts a fragment from a formula into dictionary" symb='' num='' for c in fragment: if c.isdigit(): num=num+c elif c.isalpha(): symb=symb+c if num=='': number=1 else: number=int(num) return {'symbol':symb,'number':number} In : convert('O3')
Out: {'number': 3, 'symbol': 'O'} In : convert('Ca')
15
Massa Molecular
Decompor a fórmula química numa lista de dicionários
def decompose(formula):
"return list of dictionaries with element and amount" atoms = [] current = next_upper(formula,0) while current<len(formula): next_el = next_upper(formula,current+1) atoms.append(convert(formula[current:next_el])) current = next_el return atoms In : decompose('CaCO3')
Out: [{'number': 1, 'symbol': 'Ca'}, {'number': 1, 'symbol': 'C'}, {'number': 3, 'symbol': 'O'}] In : decompose('K[PtCl3(C2H4)]') Out: [{'number': 1, 'symbol': 'K'}, {'number': 1, 'symbol': 'Pt'}, {'number': 3, 'symbol': 'Cl'}, {'number': 2, 'symbol': 'C'}, {'number': 4, 'symbol': 'H'}]
Massa Molecular
Função final, junta tudo
MASS_FILE='periodic_table.txt' ...
def molecular_mass(formula): "return molecular mass"
masses = read_mass(MASS_FILE) parts = decompose(formula) mass = 0
for p in parts:
mass = mass + p['number']*masses[p['symbol']] return mass
In : molecular_mass('CaCO3') Out: 100.08747
In : molecular_mass('CH3COOC6H4COOH') Out: 180.15829200000002
17
Massa Molecular
Problema adicional: processar ficheiro
■
Temos um ficheiro com fórmulas:
CaCO3
CH3COOC6H4COOH K[PtCl3(C2H4)] CH3COOH
C6H9N3O2
■
Queremos uma tabela com fórmulas e massas, separadas por ;
CaCO3;100.09
CH3COOC6H4COOH;180.16 K[PtCl3(C2H4)];368.60 CH3COOH;60.05
Massa Molecular
Problema adicional: processar ficheiro
def mass_table(in_file,out_file):
"""writes to out_file table with molecular masses from in_file""" formulas = open(in_file).readlines()
ofil = open(out_file,'w') for formula in formulas:
mass = molecular_mass(formula) ofil.write('{0};{1:.2f}\n'.format(formula.strip(),mass)) ofil.close() In : mass_table('formulas.txt','mass_table.txt') CaCO3 CH3COOC6H4COOH K[PtCl3(C2H4)] CH3COOH C6H9N3O2 CaCO3;100.09 CH3COOC6H4COOH;180.16 K[PtCl3(C2H4)];368.60 CH3COOH;60.05 C6H9N3O2;155.16
19
Ficheiros
Classes
Uma class é uma "fábrica" de objectos
■
Permite-nos criar os objectos que quisermos
■
Pontos importantes:
•
Todos os objectos de uma classe partilham os atributos e métodos da classe
•
Os métodos recebem o objecto como primeiro argumento
•
O método __init__ serve para inicializar o objecto
■
Vamos ver um exemplo:
class Element:
def __init__(self,at_number,symbol,name,mass): "initialize element"
self.at_number = at_number self.symbol = symbol
21
Classes
■
Exemplo: uma classe para elementos químicos
class Element:
def __init__(self,at_number,symbol,name,mass): "initialize element"
self.at_number = at_number self.symbol = symbol
self.name = name self.mass = mass
■
Cada objecto fica com os atributos dados pelo __init__
In : h=Element(1,'H','Hydrogen',1) In : h.name Out: 'Hydrogen' In : h.symbol Out: 'H' In : h.at_number Out: 1 In : h.mass Out: 1 In : he=Element(2,'He','Helium',4) In : he.name Out: 'Helium' In : he.symbol Out: 'He' In : he.at_number Out: 2 In : he.mass Out: 4
Classes
■
Vamos ler a tabela periódica toda,
•
Um dicionário de Element, com o símbolo como chave
def read_elements(file):
"return dictionary with elements by symbol" lines = open(file).readlines()
elements = {}
for line in lines[1:]:
at_n,symb,name,mass = line.split('\t') elements[symb] = Element(int(at_n),symb,name,float(mass)) return elements In : elements = read_elements('periodic_table.txt') In : elements['Ar'].mass Out: 39.9481 In : elements['Pb'].at_number Out: 82 In : elements['Ca'].name Out: 'Calcium'
23
Classes
Configuração electrónica
■
Vamos criar um método mas, primeiro, precisamos de alguns
dados
■
Estes vão ser partilhados por todos os objectos Element
•
Por isso ficam na classe, e não no
__init__
class Element: orbitals = ['1s', '2s', '2p', '3s', '3p', '4s', '3d', '4p', '5s', '4d', '5p', '6s', '4f', '5d', '6p', '7s', '5f', '6d', '7p', '8s'] capacity = {'s':2,'p':6,'d':10,'f':14}
def __init__(self,at_number,symbol,name,mass): "initialize atom"
self.at_number = at_number ...
Classes
■
Agora criamos o método
•
Recebe o objecto como argumento, mas implícito
•
Os atributos
orbitals e capacity são iguais para todos os Element porque
estão definidos na classe
class Element: ...
def el_configuration(self):
"return electronic configuration as string" electrons = self.at_number orbital = 0 configuration = '' while electrons>0: current = self.orbitals[orbital] full = self.capacity[current[-1]] place_els = min(electrons, full) electrons = electrons - place_els
configuration = configuration + current + str(place_els) + ' ' orbital = orbital +1
25
Classes
■
Agora criamos o método
•
Recebe o objecto como argumento, mas implícito
•
Os atributos
orbitals e capacity são iguais para todos os Element porque
estão definidos na classe
In : elements['Ca'].orbitals Out: ['1s', '2s', '2p', '3s', '3p', '4s', '3d', '4p', '5s',...] In : elements['U'].capacity Out: {'d': 10, 'f': 14, 'p': 6, 's': 2} In : elements['Ca'].el_configuration() Out: '1s2 2s2 2p6 3s2 3p6 4s2 ' In : elements['U'].el_configuration() Out: '1s2 2s2 2p6 3s2 3p6 4s2 3d10 4p6 5s2 4d10 5p6 6s2 4f14 5d10 6p6 7s2 5f4 ' In : elements['Ar'].el_configuration() Out: '1s2 2s2 2p6 3s2 3p6 '
Dados Estruturados
27