sábado, 14 de novembro de 2009

Problema 6.8

Este problema já apresenta alguma complexidade. É uma boa altura para deixar de lado o teclado e pensar primeiro na solução. Claramente o problema pode ser dividido em três fases . Numa primeira, lemos os dados do ficheiro. Estamos a admitir que em cada linha do ficheiro se encontram os valores das temperaturas de uma cidade. Obtidos os dados, entramos na segunda fase, e temos o problema mais complexo: calcular a temperatura máxima e mínima em cada mês. Os comandos de leitura dos dados os permitem extrair os dados por linha. Mas precisamos de os associar por coluna. Para os mais experientes em matemática, se tudo estivesse numa matriz, isto equivalia a fazer a transposta da matriz. Na prática é o que vamos fazer, pois os dados guardados estão na forma de uma lista da listas. Finalmente, na terceira e última fase, mostramos o resultado por recurso ao módulo matplotlib. Definido o plano, devemos começar a desenvolver cada uma das partes, usando, se necessário, a mesma ideia de decompor um problema um sub-problemas. Eis o (nosso) resultado.


import pylab

def temp_max_min(f_ent):
"""
Lê temperaturas mensais de várias cidades, calcula valores máximos e mínimos.
Mostra o resultado num gráfico.
"""
# lê dados
f_in = open(f_ent)
dados = []
cidade = f_in.readline()
while cidade != '':
dados.append([float(valor) for valor in cidade[:-1].split()])
cidade = f_in.readline()
# calcula máximo e mínimo
lista_valores_mes = zip(*dados)
maximos = [max(mes) for mes in lista_valores_mes]
minimos = [min(mes) for mes in lista_valores_mes]
# mostra resultados
pylab.figure(1)
absissa = range(1,13)
pylab.xlabel('Meses')
pylab.ylabel('Temperaturas')
pylab.title(r'$\mathrm{M\acute aximo\,e\,M\acute inimo}$')
pylab.text(8,22,'Max')
pylab.text(8,18,'Min')
pylab.plot(absissa,maximos,'r-o',absissa, minimos,'b-^')
pylab.show()


Podemos visualizar o resultado.




A primeira fase está implementada entre as linhas 8 e 14. Os dados são lidos linha a linha (linhas 11 e 14). Cada linha vê os seus elementos separados e guardados numa lista, na forma de números em vírgula flutuante (linha 13). Essa lista é por sua vez guardada no contentor definido na linha 10. Na linha 13 usamos listas definidas por compreensão. O seu significado é semelhante ao dos conjuntos em matemática: A é o conjunto dos elementos x que satisfazem a condição y. O código pode ser substituído por outro mais normal.


# por compreenção
res = [float(valor) for valor in cidade[:-1].split()]
# equivalente "normal"
res = []
for valor in cidade[:-1].split():
res.append(float(valor))


As listas por compreensão podem ser mais complexas. Desde logo, podemos ter uma condição de filtragem.


# por compreenção
res = [float(valor) for valor in cidade[:-1].split() if float(valor) > 0 ]
# equivalente "normal"
res = []
for valor in cidade[:-1].split():
if float(valor) > 0:
res.append(float(valor))


Podemos ter também uma lista por compreensão dentro de outra. Uma vez habituados a elas saberemos apreciar a sua concisão e clareza.

Na segunda fase manipulamos a lista de listas (linhas 15 a 18). Aqui é crucial o uso da operação zip(*args). Dado um número variável de dados funciona como um fecho éclair: junta o que está na mesma posição. Quando, em vez de darmos os argumentos separados, os damos dentro de uma lista, temos que fazer um pouco mais de ginástica e separar esses elementos. Graças ao modo como o zip está definido basta usar a marca * antes do nome da lista.


>>> x = [1,2,3]
>>> y = ['a','b','c']
>>> zip(x,y)
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> tudo = [x, y]
>>> tudo
[[1, 2, 3], ['a', 'b', 'c']]
>>> zip(tudo)
[([1, 2, 3],), (['a', 'b', 'c'],)]
>>> zip(*tudo)
[(1, 'a'), (2, 'b'), (3, 'c')]
>>>


Mas precisamos mesmo do zip? Não, mas a solução seria mais trabalhosa!

>
# com zip
lista_valores_mes = zip(*dados)
#sem zip
lista_valores_mes = []
for indice_mes in range(12):
mes = []
for indice_cidade in range(len(dados)):
mes.append(dados[indice_cidade][indice_mes])
lista_valores_mes.append(mes)


As listas por compreensão vêm de novo em nosso auxílio para nos facilitar a vida, relativamente à obtenção dos valores máximos e mínimos. O leitor pode gerar código alternativo, caso não se sinta confortável com o uso das listas por compreensão. Com o tempo e a prática o à vontade virá!

Na terceira fase mostramos os dados (linhas 19 a 28). Não há muito a dizer. O comando plot faz o trabalho por nós. As outras coisas são basicamente cosmética.

Sem comentários:

Enviar um comentário