Probabilidade E Estatística Para Previsão de Resultados Esportivos

lisandramelo

Lisandra Melo

Posted on August 11, 2020

Probabilidade E Estatística Para Previsão de Resultados Esportivos

Considerações Iniciais

Usaremos conceitos matemáticos como Valor Esperado e Distribuição de Probabilidade nesse artigo, se você não conhece muito sobre esses conceitos, é possível que você ainda entenda tudo que está sendo feito no artigo, mas se você deseja aprender mais sobre os conteúdos, indico o site Khan Academy nos módulos sobre distribuição de probabilidade e Média - Valor Esperado, são vídeos curtos e bastante explicativos sobre o conteúdo.

Introdução ao Projeto

Nesse projeto, vamos usar probabilidade e estatística para prever resultados de partidas de futebol. Para isso, vamos usar Python e sua biblioteca Numpy, acompanhados de conceitos de probabilidade e estatística.

Vamos realizar o seguinte processo, faremos a leitura de um arquivo contendo todos os resultados de partidas do AFC Ajax na liga holandesa de futebol (Eredivisie) na temporada de 18/19 e iremos, a cada rodada, realizar uma previsão de resultado da próxima partida do time, essa previsão consistirá na Esperança (Valor Esperado) de gols feitos pelo clube e na Esperança de gols sofridos.

O Que Acontece Se Usarmos Adivinhação Com Valores Aleatórios?

Para futura comparação, analisaremos o que aconteceria se tentássemos adivinhar os resultados das partidas usando valores aleatórios.

Considerando que o Ajax pode marcar de 0 (número mínimo de gols marcados em uma partida nos nossos dados) a 8 (número máximo registrado pelo Ajax em uma partida nos nossos dados) gols, ou seja, um total de 9 possibilidades de resultados, e sofrer de 0 (mínimo registrado) a 6 (máximo registrado) gols, um total de 7 possibilidades. Temos que a probabilidade de acertar uma previsão de rodada é acertar o número de gols marcados e de gols sofridos, então escolhendo valores aleatórios no intervalo essa é.

P(golsMarcados)=19P(golsSofridos)=17P(umaRodada)=P(golsMarcados)P(golsSofridos)=1917=163=0,0159 P(golsMarcados) = \frac{1}{9} \newline P(golsSofridos) = \frac{1}{7} \newline P(umaRodada) = P(golsMarcados) * P(golsSofridos) = \frac{1}{9}* \frac{1}{7} = \frac{1}{63} = 0,0159

A liga holandesa possui 34 rodadas, não faremos previsão para a primeira rodada, pois não temos nenhum dado anterior que nos ajude a calcular uma previsão. Então, considerando que temos 33 rodadas para tentar acertar pelo menos um resultado, multiplicaremos 33 pela probabilidade de uma rodada, que nos fornece um valor de 0,5238 resultados certos. O que significa que sem artifícios de probabilidade, usando valores aleatórios, é esperado que acertemos o resultado de menos de uma rodada das 33 analisadas. Já para o número de gols marcados temos um valor esperado de acertos de 3,6667 (33 * 1/9) e para o de gols sofridos 4,7143 (33 * 1/7).

Vamos então tentar melhorar esses valores (que são bem baixos) usando matemática e programação.

Implementação Do Projeto

Para criarmos nosso projeto, primeiro, vamos criar nosso arquivo de resultados, esse arquivo terá uma formatação específica e será escrito como:



golsmarcados,golssofridos


Enter fullscreen mode Exit fullscreen mode

Por exemplo, se o Ajax marcou 4 gols e sofreu 2 em uma partida de uma rodada teremos no arquivo:



4,2


Enter fullscreen mode Exit fullscreen mode

Esse arquivo será salvo como resultados.txt, e está disponível no repositório do projeto.

Agora vamos começar a produzir nosso programa, iniciaremos importando as bibliotecas que usaremos.



import numpy as np


Enter fullscreen mode Exit fullscreen mode

Depois abriremos nosso arquivo de resultados.



# Abrindo o arquivo com os resultados
"""
o arquivo está formatado da seguinte forma:
se o ajax ganhou fazendo 3 gols e sofrendo 1 - em casa ou fora de casa -
nosso arquivo registrará a rodada como:
3,1
"""
arquivo = open("resultados.txt", "r")


Enter fullscreen mode Exit fullscreen mode

Após a abertura, vamos inserir o conteúdo do arquivo em uma lista chamada resultado_rodadas usando uma list comprehension, que é uma forma de definição, criação e manutenção de listas no python. Com essa ferramenta, podemos criar um iterador e preencher listas com uma só linha.

Já no fim da iteração, fecharemos o arquivo (resultados.txt) que foi aberto no início do nosso programa.



# Inicializando nossa lista de resultados
resultado_rodadas = []

# O for vai percorrer cada linha do arquivo
for linha in arquivo:
    """
    A próxima linha irá adicionar os resultados na nossa lista,
    dentro dos parênteses temos uma 'list comprehension' que
    realiza o mesmo que o seguinte trecho de código:
    list = []
    for x in l.split(","):
        list.append(int(x))
    results.append(list)
    """
    resultado_rodadas.append([int(x) for x in linha.split(",")])

# Fecharemos o arquivo
arquivo.close()



Enter fullscreen mode Exit fullscreen mode

Agora daremos início à análise dos dados obtidos. Primeiramente, inicializaremos algumas variáveis que armazenaram nossos dados formatados.



# Agora iremos trabalhar com os dados que obtemos rodada a rodada

# Inicializaremos nossas listas de gols marcados e gols sofridos
gols_marcados = []
gols_sofridos = []

# Também calcularemos nosso número de acertos de resultados para controlarmos quão boa é nossa previsão
acertos_rodada = 0
acertos_gols_marcados = 0
acertos_gols_sofridos = 0


Enter fullscreen mode Exit fullscreen mode

Iremos, então, percorrer toda a lista resultado_rodadas separando os valores que ela contém em gols marcados e sofridos e depois calculando o valor esperado de cada uma dessas categorias para calcularmos uma previsão para a próxima rodada.

Além disso, obteremos a frequência de cada número de gols, isto é, quantas vezes o time marcou 0 gol, 1 gol, 2 gols e assim por diante. Faremos o mesmo com os gols sofridos. Com a frequência de cada gol, vamos ter os dados necessário para calcular nosso valor esperado.

Por exemplo, podemos ter uma frequência como a representada no gráfico abaixo (Essa não é a frequência real dos dados).

Gráfico de Exemplificação de como funcionará nosso dicionário de frequência
Gráfico de Exemplificação de como funcionará nosso dicionário de frequência

Para separar os gols escreveremos:



"""
Percorreremos nossa lista de resultados por rodada e calcularemos
o valor esperado de gols marcados e sofridos para cada rodada faremos
uma previsão com esses valores e depois iremos conferir se esses valores 
correspondem ao resultado que ocorreu na rodada
"""
for rodada in range(len(resultado_rodadas)):
    gols_marcados.append(resultado_rodadas[rodada][0])
    gols_sofridos.append(resultado_rodadas[rodada][1])

    # Agora iremos obter a frequência do número de gols registrados até o momento
    num_gols, freq_num_gols = np.unique(gols_marcados, return_counts=True)
    # Por questão de organização transformaremos nossos valores em um dicionário do tipo 'gols':frequencia
    dic_gols_marcados = dict(zip(num_gols, freq_num_gols))

    # Faremos o mesmo com os gols sofridos
    num_gols, freq_num_gols = np.unique(gols_sofridos, return_counts=True)
    # Por questão de organização transformaremos nossos valores em um dicionário do tipo 'gols':frequencia
    dic_gols_sofridos = dict(zip(num_gols, freq_num_gols))


Enter fullscreen mode Exit fullscreen mode

Após isso, calcularemos a esperança/valor esperado dos gols, isto é, os valores que são esperados na próxima rodada considerando os valores das rodadas anteriores. Para calcularmos tal valor iremos multiplicar todos os valores do dicionário (número de gols marcados) por sua probabilidade de ocorrência (Frequência dividida pelo número de rodadas).



    # Agora calcularemos a esperança dos gols 
    esperanca_marcados=0
    for gol in dic_gols_marcados.keys():
        esperanca_marcados += gol*(dic_gols_marcados[gol]/len(gols_marcados))

    esperanca_sofridos=0 
    for gol in dic_gols_sofridos:
        esperanca_sofridos += gol*(dic_gols_sofridos[gol]/len(gols_sofridos))


Enter fullscreen mode Exit fullscreen mode

Depois de calcular nossas esperanças, vamos realizar nossa previsão e compará-las com os resultados reais das rodadas para conferir se acertamos o resultado da partida, o número de gols marcados e o número de gols sofridos com nossa previsão.



    # Depois de calcular nossas esperanças vamos imprimi-las e compará-las com os resultados reais

    # As próximas linhas arredondam nosso resultado para um valor inteiro
    esperanca_marcados = int(np.around(esperanca_marcados))
    esperanca_sofridos = int(np.around(esperanca_sofridos))

    # Se estivermos na última rodada já realizamos nossa previsão
    # na iteração anterior então devemos não devemos imprimir nossa esperança
    if (rodada+1 == len(resultado_rodadas)):
        break
    """
    Agora imprimiremos nosso valor esperado para a rodada que virá
    como listas têm posição inicial no número 0 temos que adicionar
    1 ao valor da rodada para conseguirmos a rodada que está sendo lida atualmente,
    ou seja, temos que adicionar 2 ao numero da `rodada`
    para conseguirmos o valor da próxima rodada.
    """
    print(f'Na {rodada+2} rodada esperamos um resultado de Ajax  {esperanca_marcados} x {esperanca_sofridos} adversário')
    print(f'Na {rodada+2} rodada obtemos um resultado de Ajax  {resultado_rodadas[rodada+1][0]} x {resultado_rodadas[rodada+1][1]} adversário')

    # Veremos se acertamos os resultados
    if(esperanca_marcados==resultado_rodadas[rodada+1][0] and esperanca_sofridos==resultado_rodadas[rodada+1][1]):
        acertos_rodada += 1
    if(esperanca_marcados==resultado_rodadas[rodada+1][0]):
        acertos_gols_marcados += 1
    if(esperanca_sofridos==resultado_rodadas[rodada+1][1]):
        acertos_gols_sofridos += 1


Enter fullscreen mode Exit fullscreen mode

Depois da execução do for, iremos conferir nossa quantidade de acertos.



# Imprimiremos nossa quantidade de acertos
print("Acertamos {0:1d} previsões de uma rodada completa, isto é, {1:2.2f}%".format(acertos_rodada, (acertos_rodada/33)*100))

print("Acertamos {0:1d} previsões de gols marcados, isto é, {1:2.2f}%".format(acertos_gols_marcados, (acertos_gols_marcados/33)*100))

print("Acertamos {0:1d} previsões de gols sofridos, isto é, {1:2.2f}%".format(acertos_gols_sofridos, (acertos_gols_sofridos/33)*100))


Enter fullscreen mode Exit fullscreen mode

A saída do nosso programa será basicamente a seguinte



> Na 2 rodada esperamos um resultado de Ajax  1 x 1 adversário
> Na 2 rodada obtemos um resultado de Ajax  1 x 0 adversário
...
> Na 34 rodada esperamos um resultado de Ajax  3 x 1 adversário
> Na 34 rodada obtemos um resultado de Ajax  4 x 1 adversário
> Acertamos 4 previsões de uma rodada completa, isto é, 12.12%
> Acertamos 7 previsões de gols marcados, isto é, 21.21%
> Acertamos 15 previsões de gols sofridos, isto é, 45.45%


Enter fullscreen mode Exit fullscreen mode

Note que acertamos 4 resultados de uma rodada completa, 8 vezes mais que com o uso de valores aleatórios, 7 previsões de gols marcados, 2 vezes mais, e 15 previsões de gols sofridos, 3 vezes mais.

É perceptível que o uso de valor esperado ajudou bastante a melhorarmos nosso número de acertos de resultados. Isso representa o quanto conceitos simples de probabilidade, estatística e especialmente a matemática podem ser poderosos na análise de dados.

O programa desenvolvido nesse artigo está disponível no meu repositório do gitlab. Espero ter ajudado, se você estiver com algum problema ou dúvida sinta-se convidado a comentar esse post ou enviar um e-mail para mim ;).

💖 💪 🙅 🚩
lisandramelo
Lisandra Melo

Posted on August 11, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related