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.

01.import pylab
02. 
03.def temp_max_min(f_ent):
04.   """
05.   Lê temperaturas mensais de várias cidades, calcula valores máximos e mínimos.
06.   Mostra o resultado num gráfico.
07.   """
08.   # lê dados
09.   f_in = open(f_ent)
10.   dados = []
11.   cidade = f_in.readline()
12.   while cidade != '':
13.      dados.append([float(valor) for valor in cidade[:-1].split()])
14.      cidade = f_in.readline()
15.   # calcula máximo e mínimo
16.   lista_valores_mes = zip(*dados)
17.   maximos = [max(mes) for mes in lista_valores_mes]
18.   minimos = [min(mes) for mes in lista_valores_mes]
19.   # mostra resultados
20.   pylab.figure(1)
21.   absissa = range(1,13)
22.   pylab.xlabel('Meses')
23.   pylab.ylabel('Temperaturas')
24.   pylab.title(r'$\mathrm{M\acute aximo\,e\,M\acute inimo}$')
25.   pylab.text(8,22,'Max')
26.   pylab.text(8,18,'Min')
27.   pylab.plot(absissa,maximos,'r-o',absissa, minimos,'b-^')
28.   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.

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


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

1.# por compreenção
2.res = [float(valor) for valor in cidade[:-1].split() if float(valor) > 0 ]
3.# equivalente "normal"
4.res = []
5.for valor in cidade[:-1].split():
6.    if float(valor) > 0:
7.        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.

01.>>> x = [1,2,3]
02.>>> y = ['a','b','c']
03.>>> zip(x,y)
04.[(1, 'a'), (2, 'b'), (3, 'c')]
05.>>> tudo = [x, y]
06.>>> tudo
07.[[1, 2, 3], ['a', 'b', 'c']]
08.>>> zip(tudo)
09.[([1, 2, 3],), (['a', 'b', 'c'],)]
10.>>> zip(*tudo)
11.[(1, 'a'), (2, 'b'), (3, 'c')]
12.>>>


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

01.>
02. # com zip
03. lista_valores_mes = zip(*dados)
04. #sem zip
05. lista_valores_mes = []
06. for indice_mes in range(12):
07.     mes = []
08.     for indice_cidade in range(len(dados)):
09.         mes.append(dados[indice_cidade][indice_mes])
10.      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