Árvore de Decisão
Introdução
O objetivo deste roteiro é utilizar as bibliotecas pandas, numpy, matplotlib e scikit-learn, além da base de dados escolhida no Kagle, para treinar e avaliar um algoritmo de árvore de decisão.
A proposta é verificar como variáveis como estádio, público, posse de bola, passes e chances criadas influenciam no resultado final da partida (class), permitindo explorar o uso de modelos de Machine Learning no contexto esportivo.
Base de dados
A base de dados utilizada neste projeto contém informações de partidas de futebol, totalizando 1140 linhas e 40 colunas. Entre as variáveis estão posse de bola, número de passes e chances criadas.
A variável alvo escolhida é a coluna class, que indica o resultado da partida (vitória do mandante, empate ou vitória do visitante), sendo o objeto da classificação pelo algoritmo de árvore de decisão.
Exploração dos Dados
A seguir foi feita uma análise do significado e composição de cada coluna presente na base, com a finalidade de identificar possíveis problemas a serem tratados posteriormente. As visualizações e estatísticas descritivas ajudam a compreender a natureza dos dados e orientar decisões de pré-processamento e modelagem.
A coluna class é a variável alvo do projeto e representa o resultado da partida: vitória do mandante ("h"), empate ("d") ou vitória do visitante ("a"). Trata-se de uma variável categórica com três possíveis valores, sendo o objeto de classificação do modelo de árvore de decisão. A análise exploratória dessa coluna é essencial para observar o balanceamento do conjunto de dados, isto é, se há proporções semelhantes ou discrepantes entre os resultados possíveis.
A coluna attendance representa o público presente em cada partida, um indicador de contexto do jogo que pode refletir fatores como mando de campo, relevância do confronto e engajamento da torcida. Em bases de futebol, o público tende a variar bastante entre estádios e rodadas, podendo apresentar assimetria (jogos muito cheios em arenas grandes) e valores atípicos (clássicos, finais).
Do ponto de vista analítico, é uma variável contínua útil para observar a distribuição de torcedores ao longo das partidas e investigar relações com o resultado (class).
A coluna stadium identifica o estádio onde a partida foi disputada. Trata-se de uma variável categórica, associada ao contexto e à capacidade do local, podendo refletir fatores como mando de campo, perfil da torcida, relevo do gramado e até particularidades logísticas. Na análise exploratória, é útil observar a frequência de jogos por estádio, verificando balanceamento da amostra (quais arenas têm mais/menos partidas) e possíveis vieses (ex.: concentração em poucos estádios). Relações mais profundas com o resultado (class) podem ser investigadas em etapas seguintes, mas aqui focamos em entender a composição dessa variável no conjunto de dados.
A coluna home_possessions representa a porcentagem de posse de bola do time mandante em cada partida. Trata-se de uma variável numérica contínua, normalmente variando entre 30% e 70% na maioria dos jogos, podendo indicar estilos de jogo (times que mantêm a bola ou que jogam mais reativamente). A análise exploratória dessa variável permite observar a distribuição da posse de bola entre os mandantes, identificar valores atípicos e comparar a média do controle de jogo ao longo das rodadas. Posteriormente, poderá ser interessante relacionar essa posse com o resultado final (class) para verificar padrões.
A coluna away_possessions representa a porcentagem de posse de bola do time visitante em cada partida. Por ser uma variável numérica contínua, sua distribuição ajuda a observar o comportamento dos visitantes em termos de controle de jogo, identificar valores atípicos e comparar a tendência média de posse fora de casa. Em etapas posteriores, pode ser relacionada ao resultado (class) para investigar padrões de desempenho como visitante.
A coluna Home Team identifica o time mandante na partida. Embora esteja codificada numericamente no dataset, sua natureza é categórica (IDs de times). Na análise exploratória, é útil observar a frequência de jogos por mandante, verificando o balanceamento da amostra entre os times que atuam em casa e possíveis concentrações. Relações com o resultado (class) podem ser exploradas depois; aqui focamos em entender a composição dessa variável.
A coluna Away Team identifica o time visitante na partida. Apesar de codificada como número, sua natureza é categórica (IDs de times). Na análise exploratória, observar a frequência de jogos por visitante ajuda a avaliar o balanceamento da amostra e possíveis concentrações de partidas em determinados clubes.
A coluna home_pass indica o número de passes realizados pelo time mandante em cada partida. É uma variável numérica contínua que ajuda a caracterizar o estilo de jogo do mandante, podendo variar bastante entre equipes mais ou menos dependentes da posse de bola. Na análise exploratória, observar a distribuição dos passes permite identificar médias, dispersão e valores atípicos.
A coluna away_pass indica o número de passes realizados pelo time visitante em cada partida. Sendo uma variável numérica contínua, sua distribuição mostra como os visitantes se comportam em termos de construção de jogadas e controle de posse fora de casa. A análise exploratória ajuda a entender a média de passes, variações entre os jogos e eventuais valores extremos.
A coluna home_chances representa a quantidade de chances de gol criadas pelo time mandante durante a partida. É uma variável numérica discreta que indica o nível de ofensividade da equipe jogando em casa. A análise exploratória permite identificar a frequência de jogos com poucas ou muitas oportunidades e verificar a dispersão desse tipo de estatística.
A coluna away_chances indica a quantidade de chances de gol criadas pelo time visitante durante a partida. É uma variável numérica discreta que ajuda a compreender a ofensividade dos times jogando fora de casa. A análise exploratória mostra como os visitantes se comportam em termos de criação de oportunidades, permitindo identificar padrões de equilíbrio ou diferenças marcantes em relação aos mandantes.
Pré-processamento
Após a exploração inicial da base, foram aplicados procedimentos de pré-processamento para preparar os dados para o treinamento do modelo.
Entre as etapas realizadas estão:
- Conversão de tipos: variáveis originalmente em texto com valores numéricos (como
attendance) foram transformadas em formato numérico. - Tratamento de valores categóricos: colunas como
stadium,Home TeameAway Teamforam mantidas como categóricas, sendo posteriormente convertidas em variáveis numéricas por meio de técnicas de codificação. - Remoção de colunas irrelevantes ou redundantes: colunas de identificação e de tempo (
date,clock,links), bem como estatísticas que representam vazamento de informação do resultado (ex.:Goals Home,Away Goals), foram descartadas do conjunto de treino. - Separação entre features e target: as variáveis explicativas (
X) foram definidas a partir de aproximadamente dez colunas relevantes da base, enquanto a variável alvo (y) é a colunaclass. - Divisão em treino e teste: o conjunto de dados foi dividido em duas partes, garantindo estratificação do alvo para manter o equilíbrio das classes.
Com essas etapas, os dados foram organizados de forma consistente, reduzindo ruídos e preparando a base para a etapa seguinte: o treinamento do modelo de árvore de decisão.
| stadium | class | attendance | Home Team | Away Team | home_possessions | away_possessions | home_pass | away_pass | home_chances | away_chances |
|---|---|---|---|---|---|---|---|---|---|---|
| 6 | 1 | 36173 | 19 | 14 | 63.3 | 36.7 | 79.4 | 62.5 | 1 | 0 |
| 7 | 1 | 59475 | 2 | 11 | 53.6 | 46.4 | 87.1 | 84.6 | 1 | 0 |
| 24 | 1 | 41830 | 7 | 1 | 27.7 | 72.3 | 70.7 | 88.2 | 0 | 4 |
| 25 | 0 | 10305 | 15 | 10 | 42.7 | 57.3 | 76.6 | 85.5 | 2 | 0 |
| 16 | 1 | 30157 | 20 | 8 | 50.4 | 49.6 | 84.9 | 83.8 | 2 | 2 |
| 12 | 2 | 34624 | 13 | 2 | 37.4 | 62.6 | 79.6 | 89.3 | 0 | 1 |
| 11 | 1 | 0 | 14 | 11 | 51.9 | 48.1 | 85.6 | 77.5 | 0 | 2 |
| 12 | 1 | 0 | 13 | 8 | 54.8 | 45.2 | 85.6 | 78.8 | 1 | 0 |
| 24 | 2 | 41400 | 7 | 1 | 36 | 64 | 80.8 | 89.5 | 1 | 1 |
| 11 | 1 | 59949 | 14 | 4 | 47 | 53 | 75.8 | 78.7 | 1 | 1 |
| 6 | 2 | 36405 | 19 | 4 | 63 | 37 | 79 | 67.1 | 1 | 1 |
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn import tree
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
df = pd.read_csv("./src/mydata.csv")
# Excluir as colunas não desejadas
df = df.drop(columns= ["date", "clock", "links", "Goals Home", "Away Goals", "home_shots", "away_shots", "home_on", "away_on",
"home_off", "away_off", "home_blocked", "away_blocked", "home_corners", "away_corners",
"home_offside", "away_offside", "home_tackles", "away_tackles", "home_duels", "away_duels",
"home_saves", "away_saves", "home_fouls", "away_fouls", "home_yellow", "away_yellow",
"home_red", "away_red"])
# Label encoding dos estadios em texto
df["stadium"] = label_encoder.fit_transform(df["stadium"])
# Transformar resultado do jogo times em números
df["class"] = df["class"].replace({'h':0, 'd': 1, 'a':2})
# Transforma o públido de string para número
df["attendance"] = df["attendance"].str.replace(',', '').astype(int)
print(df.sample(frac=.01).to_markdown(index=False))
| date | clock | stadium | class | attendance | Home Team | Goals Home | Away Team | Away Goals | home_possessions | away_possessions | home_shots | away_shots | home_on | away_on | home_off | away_off | home_blocked | away_blocked | home_pass | away_pass | home_chances | away_chances | home_corners | away_corners | home_offside | away_offside | home_tackles | away_tackles | home_duels | away_duels | home_saves | away_saves | home_fouls | away_fouls | home_yellow | away_yellow | home_red | away_red | links |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 19th May 2022 | 8:00pm | Villa Park | d | 40,468 | 7 | 1 | 23 | 1 | 71.3 | 28.7 | 22 | 10 | 9 | 5 | 7 | 2 | 6 | 3 | 90.1 | 68.4 | 0 | 1 | 13 | 6 | 1 | 0 | 62.5 | 54.5 | 48.3 | 51.7 | 3 | 8 | 11 | 20 | 0 | 1 | 0 | 1 | https://www.skysports.com/football/aston-villa-vs-burnley/stats/446458 |
| 18th October 2021 | 8:00pm | Emirates Stadium | d | 59,475 | 2 | 2 | 11 | 2 | 53.6 | 46.4 | 17 | 9 | 6 | 6 | 7 | 3 | 4 | 0 | 87.1 | 84.6 | 1 | 0 | 6 | 4 | 0 | 3 | 52.9 | 72.7 | 28.6 | 71.4 | 4 | 4 | 7 | 4 | 1 | 1 | 0 | 0 | https://www.skysports.com/football/arsenal-vs-crystal-palace/stats/446358 |
| 10th April 2022 | 4:30pm | Etihad Stadium | d | 53,197 | 1 | 2 | 5 | 2 | 55.1 | 44.9 | 11 | 6 | 5 | 4 | 4 | 2 | 2 | 0 | 85.9 | 80.6 | 2 | 3 | 4 | 1 | 5 | 2 | 35.3 | 66.7 | 50 | 50 | 2 | 2 | 9 | 11 | 1 | 4 | 0 | 0 | https://www.skysports.com/football/manchester-city-vs-liverpool/stats/446603 |
| 17/01/2021 | 7:15pm | Etihad Stadium | h | 0 | 1 | 4 | 11 | 0 | 72.2 | 27.8 | 13 | 2 | 6 | 0 | 3 | 1 | 4 | 1 | 90.4 | 72.2 | 1 | 0 | 11 | 1 | 0 | 0 | 55.6 | 60 | 53.8 | 46.2 | 0 | 2 | 9 | 8 | 0 | 0 | 0 | 0 | https://www.skysports.com/football/manchester-city-vs-crystal-palace/stats/429023 |
| 14th May 2023 | 4:30pm | Emirates Stadium | a | 60,139 | 2 | 0 | 6 | 3 | 40.9 | 59.1 | 14 | 12 | 2 | 6 | 8 | 5 | 4 | 1 | 76.7 | 83.4 | 1 | 2 | 5 | 2 | 1 | 4 | 30 | 60 | 42.1 | 57.9 | 3 | 2 | 13 | 17 | 1 | 2 | 0 | 0 | https://www.skysports.com/football/arsenal-vs-brighton-and-hove-albion/464985 |
| 6/12/2020 | 2:00pm | Bramall Lane | a | 0 | 25 | 1 | 18 | 2 | 29.7 | 70.3 | 4 | 13 | 1 | 4 | 2 | 4 | 1 | 5 | 66.9 | 85.4 | 0 | 1 | 6 | 5 | 4 | 4 | 25 | 66.7 | 54.8 | 45.2 | 2 | 0 | 13 | 6 | 1 | 3 | 0 | 0 | https://www.skysports.com/football/sheffield-united-vs-leicester-city/stats/428943 |
| 25/04/2021 | 12:00pm | Molineux | a | 0 | 13 | 0 | 23 | 4 | 59.2 | 40.8 | 12 | 14 | 2 | 7 | 5 | 6 | 5 | 1 | 77.8 | 71.7 | 0 | 3 | 8 | 5 | 2 | 2 | 68.8 | 66.7 | 47.6 | 52.4 | 3 | 2 | 8 | 9 | 1 | 2 | 0 | 0 | https://www.skysports.com/football/wolverhampton-wanderers-vs-burnley/stats/429166 |
| 19th February 2023 | 2:00pm | Old Trafford | h | 73,578 | 3 | 3 | 18 | 0 | 56.9 | 43.1 | 26 | 19 | 8 | 3 | 9 | 9 | 9 | 7 | 84.6 | 81.1 | 6 | 2 | 6 | 6 | 0 | 1 | 69.6 | 53.8 | 69.6 | 30.4 | 3 | 5 | 9 | 9 | 0 | 2 | 0 | 0 | https://www.skysports.com/football/manchester-united-vs-leicester-city/464870 |
| 19th October 2022 | 7:30pm | Anfield | h | 53,346 | 5 | 1 | 14 | 0 | 53.8 | 46.2 | 22 | 8 | 7 | 3 | 8 | 4 | 7 | 1 | 85.2 | 80.8 | 1 | 4 | 8 | 3 | 1 | 0 | 61.5 | 53.3 | 61.1 | 38.9 | 3 | 6 | 9 | 8 | 0 | 0 | 0 | 0 | https://www.skysports.com/football/liverpool-vs-west-ham-united/464752 |
| 12th December 2021 | 4:30pm | Selhurst Park | h | 24,066 | 11 | 3 | 17 | 1 | 61.2 | 38.8 | 17 | 12 | 6 | 6 | 5 | 1 | 6 | 5 | 83.4 | 70.3 | 1 | 1 | 8 | 2 | 1 | 0 | 80 | 64 | 65.9 | 34.1 | 5 | 3 | 15 | 14 | 0 | 2 | 0 | 0 | https://www.skysports.com/football/crystal-palace-vs-everton/stats/446443 |
| 7th May 2022 | 5:30pm | Amex Stadium | h | 31,637 | 6 | 4 | 3 | 0 | 42 | 58 | 17 | 15 | 6 | 5 | 7 | 7 | 4 | 3 | 78.6 | 84.6 | 3 | 1 | 7 | 6 | 3 | 3 | 81.8 | 84.6 | 40.9 | 59.1 | 5 | 1 | 13 | 9 | 0 | 2 | 0 | 0 | https://www.skysports.com/football/brighton-and-hove-albion-vs-manchester-united/stats/446640 |
Divisão dos Dados
Com a base pré-processada, realizou-se a divisão entre conjuntos de treinamento e teste.
O objetivo dessa etapa é garantir que o modelo seja avaliado em dados que ele nunca viu durante o treinamento, permitindo uma medida mais confiável de sua capacidade de generalização.
Foi utilizada a função train_test_split da biblioteca scikit-learn, com os seguintes critérios:
- 70% dos dados destinados ao treinamento, para que o modelo aprenda os padrões da base;
- 30% dos dados destinados ao teste, para avaliar o desempenho em novos exemplos;
- Estratificação pelo alvo (class), garantindo que a proporção entre vitórias do mandante, empates e vitórias do visitante fosse mantida em ambos os conjuntos;
- Random State fixado, assegurando reprodutibilidade na divisão.
Dessa forma, o conjunto de treinamento foi usado para ajustar os parâmetros da árvore de decisão, enquanto o conjunto de teste serviu para medir a precisão e robustez do modelo.
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn import tree
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
df = pd.read_csv("./src/mydata.csv")
# Excluir as colunas não desejadas
df = df.drop(columns= ["date", "clock", "links", "Goals Home", "Away Goals", "home_shots", "away_shots", "home_on", "away_on",
"home_off", "away_off", "home_blocked", "away_blocked", "home_corners", "away_corners",
"home_offside", "away_offside", "home_tackles", "away_tackles", "home_duels", "away_duels",
"home_saves", "away_saves", "home_fouls", "away_fouls", "home_yellow", "away_yellow",
"home_red", "away_red"])
# Label encoding dos estadios em texto
df["stadium"] = label_encoder.fit_transform(df["stadium"])
# Transformar resultado do jogo times em números
df["class"] = df["class"].replace({'h':0, 'd': 1, 'a':2})
# Transforma o públido de string para número
df["attendance"] = df["attendance"].str.replace(',', '').astype(int)
# Variáveis independentes (features)
x = df[[
"stadium", "attendance",
"Home Team", "Away Team",
"home_possessions", "away_possessions",
"home_pass", "away_pass",
"home_chances", "away_chances"
]]
# Variável dependente (alvo)
y = df["class"]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=27, stratify=y)
Treinamento do Modelo
Precisão da Validação: 0.47
Importância das Features:
| Feature | Importância | |
|---|---|---|
| 7 | away_pass | 0.160276 |
| 6 | home_pass | 0.152260 |
| 2 | Home Team | 0.116144 |
| 1 | attendance | 0.115863 |
| 9 | away_chances | 0.106053 |
| 3 | Away Team | 0.101649 |
| 8 | home_chances | 0.072009 |
| 4 | home_possessions | 0.069807 |
| 0 | stadium | 0.063873 |
| 5 | away_possessions | 0.042067 |
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn import tree
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
from io import StringIO
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()
df = pd.read_csv("./src/mydata.csv")
# Excluir as colunas não desejadas
df = df.drop(columns= ["date", "clock", "links", "Goals Home", "Away Goals", "home_shots", "away_shots", "home_on", "away_on",
"home_off", "away_off", "home_blocked", "away_blocked", "home_corners", "away_corners",
"home_offside", "away_offside", "home_tackles", "away_tackles", "home_duels", "away_duels",
"home_saves", "away_saves", "home_fouls", "away_fouls", "home_yellow", "away_yellow",
"home_red", "away_red"])
# Label encoding dos estadios em texto
df["stadium"] = label_encoder.fit_transform(df["stadium"])
# Transformar resultado do jogo times em números
df["class"] = df["class"].replace({'h':0, 'd': 1, 'a':2})
# Transforma o públido de string para número
df["attendance"] = df["attendance"].str.replace(',', '').astype(int)
# Variáveis independentes (features)
x = df[[
"stadium", "attendance",
"Home Team", "Away Team",
"home_possessions", "away_possessions",
"home_pass", "away_pass",
"home_chances", "away_chances"
]]
# Variável dependente (alvo)
y = df["class"]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=27, stratify=y)
# Criar e treinar o modelo de árvore de decisão
classifier = tree.DecisionTreeClassifier()
classifier.fit(x_train, y_train)
# Avaliar o modelo
y_pred = classifier.predict(x_test)
accuracy = accuracy_score(y_test, y_pred)
print(f"Precisão da Validação: {accuracy:.2f}")
feature_importance = pd.DataFrame({
'Feature': classifier.feature_names_in_,
'Importância': classifier.feature_importances_
})
print("<br>Importância das Features:")
print(feature_importance.sort_values(by='Importância', ascending=False).to_html())
plt.figure(figsize=(20, 10))
tree.plot_tree(classifier, max_depth=5, fontsize=10)
# Para imprimir na página HTML
buffer = StringIO()
plt.savefig(buffer, format="svg")
print(buffer.getvalue())
Avaliação do Modelo
Após o treinamento da árvore de decisão, o modelo foi avaliado no conjunto de teste, obtendo uma precisão (accuracy) de aproximadamente 0.48.
Esse valor mostra que o modelo acerta menos da metade das previsões, o que indica uma performance limitada.
Contudo, esse resultado deve ser entendido à luz da natureza do problema: prever o resultado de uma partida de futebol é uma tarefa de alta complexidade e incerteza.
Além das estatísticas presentes na base, fatores externos como clima, lesões, decisões de arbitragem, motivação individual e até elementos de acaso influenciam diretamente no desfecho do jogo.
Mesmo com acesso a uma base de dados mais ampla, a previsão exata de partidas continuaria sendo altamente incerta, pois o futebol não é uma ciência exata, mas um evento esportivo com variáveis muitas vezes imprevisíveis.
Algumas observações complementares ajudam a interpretar esse desempenho: - Baixa capacidade preditiva: a árvore não conseguiu capturar padrões fortes o suficiente para discriminar corretamente as três classes (h, d, a).
- Balanceamento do alvo: embora tenha sido aplicada estratificação, empates tendem a ser mais raros e difíceis de prever, prejudicando a acurácia global.
- Variáveis contextuais: as colunas usadas (como estádio, público, posse de bola, passes e chances) explicam parte do comportamento da partida, mas não determinam o resultado sozinhas.
- Complexidade inerente ao domínio: a imprevisibilidade faz parte da própria essência do futebol, o que limita naturalmente a capacidade de qualquer modelo estatístico atingir altas taxas de acerto.
Portanto, a precisão de 0.48 reflete tanto as limitações do modelo quanto a complexidade do fenômeno analisado. O exercício é válido não apenas para medir desempenho, mas para evidenciar os desafios de aplicar técnicas de Machine Learning em contextos de elevada incerteza como o esporte.
Relatório Final
O projeto teve como objetivo aplicar um algoritmo de árvore de decisão sobre uma base de partidas de futebol, explorando o uso de técnicas de Machine Learning em um contexto esportivo.
A análise foi conduzida em etapas bem definidas:
-
Exploração dos Dados (EDA): foram selecionadas e analisadas cerca de dez variáveis relevantes, entre elas
stadium,attendance,Home Team,Away Team,home_possessions,away_possessions,home_pass,away_pass,home_chanceseaway_chances. A partir de visualizações e estatísticas descritivas, foi possível compreender melhor a natureza de cada coluna e avaliar o balanceamento da variável alvoclass, que representa o resultado da partida. -
Pré-processamento: nesta fase, colunas de identificação e tempo (
date,clock,links), bem como estatísticas diretamente ligadas ao resultado (como gols, finalizações e cartões), foram removidas para evitar vazamento de informação. Além disso, foram realizadas conversões de tipos e preparação de variáveis categóricas para futura codificação numérica. -
Divisão dos Dados: o conjunto foi separado em treino (70%) e teste (30%), com estratificação do alvo para preservar a proporção entre vitórias do mandante, empates e vitórias do visitante. Essa divisão assegurou reprodutibilidade e uma avaliação justa do modelo.
-
Treinamento e Avaliação: a árvore de decisão foi treinada com os dados de treino e avaliada no conjunto de teste. O modelo alcançou uma acurácia de aproximadamente 0.48, valor que indica desempenho limitado, mas esperado dentro do contexto.
A avaliação evidenciou alguns pontos importantes: - O modelo não conseguiu capturar padrões suficientemente fortes, refletindo a alta complexidade da tarefa.
- Empates, por serem menos frequentes, foram mais difíceis de prever, prejudicando a performance global.
- As variáveis selecionadas (contexto e estatísticas gerais) ajudam a caracterizar as partidas, mas não são determinantes para o resultado final.
- O futebol, por sua própria natureza, é altamente imprevisível e sujeito a variáveis externas que não estão presentes no dataset, como clima, arbitragem, lesões e fatores psicológicos.
Conclusão
A experiência demonstrou que, embora seja possível aplicar técnicas de Machine Learning ao domínio esportivo, os resultados precisam ser interpretados com cautela. O desempenho de 0.48 de acurácia não deve ser visto apenas como limitação do modelo, mas também como reflexo da complexidade e da imprevisibilidade inerente ao futebol.
O projeto reforça a importância da análise exploratória, do cuidado no pré-processamento para evitar vazamento de dados e da avaliação crítica dos resultados. Além disso, abre espaço para trabalhos futuros que explorem variáveis adicionais — como forma recente dos times, desempenho de jogadores e fatores externos — e ajustes de hiperparâmetros para buscar melhorias no desempenho do modelo.
Mais do que prever com exatidão os resultados, este trabalho mostra o potencial e os limites do uso de algoritmos de aprendizado de máquina em contextos reais, nos quais a incerteza e a imprevisibilidade são componentes inevitáveis.