1- simular o lançamento de dois dados guardando o resultado da sua soma numa lista,
2- guardar os resultados num ficheiro, com um resultado por linha;
3- construir um dicionário de frequências em que as chaves fossem os números e os valores o número de vezes que ocorreram;
5- apresentar um gráfico de barras com o histograma das frequências. O gráfico seria feito com a ajuda do módulo turtle.
Os três primeiros sub-problemas eram revisitações de algo que já tínhamos feito. A nossa ideia era consolidar os conceitos e aproveitar para mais um exemplo de decomposição de problemas em sub-problemas e das quesdtões que se levantam.
Cada um deles podia ser resolvido de modo simples. Comecemos pelo problema da geração.
import random def gera_1(n): lista = list() for i in range(n): lista.append(random.randint(1,6)+random.randint(1,6)) return lista def gera_2(n): return [random.randint(1,6) + random.randint(1,6) for i in range(n)] def dado(): return random.randint(1,6) def gera_3(n): return [dado()+dado() for i in range(n)]A primeira solução é a mais básica. A outras duas usam listas por compreensão, sendo que a última abstrai o problema de lançar o dado. Guardar os dados gerados num ficheiro também é trivial.
def guarda(ficheiro,dados): with open(ficheiro,'w') as f_out: for dado in dados: f_out.write(str(dado)+'\n')Aqui usámos o gestor de contexto with. Finalmente ir buscar os dados ao ficheiro e construir um dicionário de frequências.
def freq(ficheiro): with open(ficheiro) as f_in: dados_str = f_in.read().split() dados_num = [eval(dado) for dado in dados_str] # dicio frequências dicio = dict() for dado in dados_num: dicio[dado] = dicio.get(dado,0) + 1 return dicioEstas três soluções têm que ser integradas num único programa. Só precisamos ter em atenção a interface.
def main(n, ficheiro): # gerar dados = gera_2(n) # guardar guarda(ficheiro, dados) # frequência frequencia = freq(ficheiro)Agora fica a questão das barras e do uso do turtle. Admitamos que queremos fazer um gráfico como o da figura: Vamos tentar decompor tudo em sub-problemas mais simples: os eixos, as barras e os valores. Vamos começar pelas barras e fazer algo como no caso do lançamento dos dados: começar pela questão de visualizar uma barra.
import turtle def barra(pos,altura,cor, espessura): # posiciona turtle.penup() turtle.goto(pos) turtle.pendown() turtle.setheading(90) # atributos turtle.color(cor) turtle.width(espessura) # Desenha turtle.forward(altura)A ideia desta solução é trivial: posicionar a tartaruga, colocá-la a apontar para norte e avançar um valor igual à altura da barra.
Desenhar um gráfico de barras pode ser agora feito chamando repetidas vezes a função barra.
def grafico_barras(pos_inicial,lista_barras, cor, espessura,delta): pos = pos_inicial for altura in lista_barras: barra(pos,altura,cor, espessura) # posiciona) pos = (turtle.xcor()+ delta,0) turtle.hideturtle()O parâmetro delta controla o afastamento entre as barras. Passemos aos eixos. Pode ser feito de diferentes maneiras. Um exemplo:
def eixos(pos_x, pos_y, tam_x, tam_y): # posiciona turtle.penup() turtle.goto(pos_x, pos_y) turtle.pendown() # eixo x turtle.forward(tam_x) turtle.write('X', font=('Arial',8,'bold')) # posiciona turtle.penup() turtle.goto(pos_x, pos_y) turtle.pendown() # eixo y turtle.setheading(90) turtle.forward(tam_y) turtle.write('Y', font=('Arial',8,'bold')) turtle.hideturtle()Usamos a função write para escrever o nome dos eixos. Notar como se controla a escrita. É evidente que podemos ter o desenho dos eixos e das barras tudo junto:
def grafico_barras_eixos(pos_inicial,lista_barras, cor, espessura,delta): eixos(pos_inicial[0] -delta, pos_inicial[1], len(lista_barras) * (espessura + delta), max(lista_barras) + delta) pos = pos_inicial for altura in lista_barras: barra(pos,altura,cor, espessura) # posiciona) pos = (turtle.xcor()+ delta,0) turtle.hideturtle()Notar como se controla a dimensão dos eixos! Falta acrescentar os números. Mas isso pode ser feito com uma pequena alteração no programa que desenha as barras.
def grafico_barras_eixos(pos_inicial,lista_barras, cor, espessura,delta): eixos(pos_inicial[0] -delta, pos_inicial[1], len(lista_barras) * (espessura + delta), max(lista_barras) + delta) pos = pos_inicial for altura in lista_barras: barra(pos,altura,cor, espessura) turtle.pencolor('black') turtle.write(altura, font=('Arial',8,'bold')) # posiciona) pos = (turtle.xcor()+ delta,0) turtle.hideturtle()Podemos juntar esta solução ao resto do programa, definindo um novo main.
def main(n, ficheiro): # gerar dados = gera_2(n) print(dados) # guardar guarda(ficheiro, dados) # frequência frequencia = freq(ficheiro) print(frequencia) # gráfico valores = [frequencia.get(chave,0) for chave in range(1,13)] grafico_barras_eixos((0,0),valores,'red',5,15)Se executarmos o programa, no entanto, o resultado não é muito famoso. O problema está no facto de os valores serem muito pequenos. Uma solução simples é definir um factor de escala que dependerá do número de lançamentos de dados. Interessa-nos uma alteração que aumente muito os valores pequenos de n e pouco os valores grandes. Uma função candidata a tal é a função logaritmo!
def main(n, ficheiro): # gerar dados = gera_2(n) print(dados) # guardar guarda(ficheiro, dados) # frequência frequencia = freq(ficheiro) print(frequencia) # gráfico escala = int(math.log(n,10)) # <--- valores = [escala * frequencia.get(chave,0) for chave in range(1,13)] grafico_barras_eixos((0,0),valores,'red',5,15) turtle.exitonclick()Mas podemos promover outra pequena alteração para poder colocar a identificação dos números ao longo dos eixos dos XX. A mudança principal envolve a função barra.
def barra(pos,altura,cor, espessura,num): if altura: # posiciona turtle.penup() turtle.goto(pos[0],pos[1]-20) turtle.write(num) turtle.goto(pos) turtle.pendown() turtle.setheading(90) # atributos turtle.color(cor) turtle.width(espessura) # Desenha turtle.forward(altura) turtle.pencolor('black') turtle.write(altura, font=('Arial',8,'bold'))Notar que passámos a fazer aqui toda a parte que interssa saber da barra: valor de x, de y e o desenho.
Para quem gosta de tudo muito direitinho aqui fica o programa final completo.
import random import operator import math def gera_2(n): return [random.randint(1,6) + random.randint(1,6) for i in range(n)] # Guardar def guarda(ficheiro,dados): with open(ficheiro,'w') as f_out: for dado in dados: f_out.write(str(dado)+'\n') # Frequência def freq(ficheiro): with open(ficheiro) as f_in: dados_str = f_in.read().split() dados_num = [eval(dado) for dado in dados_str] # dicio frequências dicio = dict() for dado in dados_num: dicio[dado] = dicio.get(dado,0) + 1 return dicio # Ver import turtle def barra(pos,altura,cor, espessura,num): if altura: # posiciona turtle.penup() turtle.goto(pos[0],pos[1]-20) turtle.write(num) turtle.goto(pos) turtle.pendown() turtle.setheading(90) # atributos turtle.color(cor) turtle.width(espessura) # Desenha turtle.forward(altura) turtle.pencolor('black') turtle.write(altura, font=('Arial',8,'bold')) def grafico_barras_eixos(pos_inicial,lista_barras, cor, espessura,delta): eixos(pos_inicial[0] - delta, pos_inicial[1], len(lista_barras) * (espessura + delta), max(lista_barras) + delta) pos = pos_inicial for num,altura in enumerate(lista_barras): barra(pos,altura,cor, espessura,num) # posiciona pos = (turtle.xcor()+ delta,pos_inicial[1]) turtle.hideturtle() def eixos(pos_x, pos_y, tam_x, tam_y): # posiciona turtle.penup() turtle.goto(pos_x, pos_y) turtle.pendown() # eixo x turtle.forward(tam_x) turtle.write('X', font=('Arial',8,'bold')) # posiciona turtle.penup() turtle.goto(pos_x, pos_y) turtle.pendown() # eixo y turtle.setheading(90) turtle.forward(tam_y) turtle.write('Y', font=('Arial',8,'bold')) turtle.hideturtle() # MAIN ---------------- def main(n, ficheiro): # gerar dados = gera_2(n) print(dados) # guardar guarda(ficheiro, dados) # frequência frequencia = freq(ficheiro) # gráfico escala = int(math.log(n,10)) # <-- valores = [escala * frequencia.get(chave,0) for chave in range(1,13)] grafico_barras_eixos((0,0),valores,'red',5,15) turtle.exitonclick() if __name__ == '__main__': main(1000,'/Users/ernestojfcosta/tmp/tudo.txt')Para quem gosta de experiências, tente executar o programa para diferentes valores crescentes de n. Os gráficos de barras que são desenhados vão convergir para uma forma de distribuição conhecida em probabilidades: a distribuição normal. é uma boa altura para revisitar a Lei dos Grandes Números.
Boas Festas!
Sem comentários:
Enviar um comentário