Ao ouvir o que eu faço, as pessoas tendem a me fazer a mesma pergunta:Você pode desenvolver um sistema que preveja os resultados dos jogos de futebol? Ou resultados de medalhas olímpicas? Pessoalmente, não confio muito em previsões. Ainda assim, se tivéssemos uma grande quantidade de dados históricos e indicadores relevantes, certamente poderíamos projetar um sistema para nos ajudar a obter suposições mais precisas. Neste artigo, consideraremos um modelo que pode armazenar os resultados de partidas e torneios.
Esse modelo é focado principalmente em partidas, estatísticas e resultados de futebol europeu, mas pode ser facilmente ajustado para acomodar muitos outros esportes. Minha principal motivação para este artigo foram os dois grandes eventos de futebol deste ano:o Campeonato da UEFA Euro 2016 que acabou de acontecer e os Jogos Olímpicos de 2016 que estão acontecendo agora.
O que sabemos antes do início do torneio?
Antes do início do torneio, sabemos quase tudo sobre ele – exceto o mais importante:quem vai ganhar. Vamos dizer brevemente exatamente o que já sabemos:
- As datas em que o torneio começa e termina
- Os locais onde as partidas acontecerão
- As horas exatas em que as partidas começarão
- Quais equipes se classificaram para o torneio
- Os jogadores de cada uma dessas equipes
- O desempenho passado de cada jogador e sua forma atual
Quais detalhes de correspondência queremos armazenar?
Os torneios consistem em várias partidas. Antes de armazenarmos os detalhes da partida, precisamos:
- Relacione cada partida com o torneio
- Registre a fase do torneio quando a partida foi disputada (por exemplo, fase de grupos, semifinais)
Também precisamos armazenar detalhes para partidas individuais, incluindo:
- As equipes envolvidas na partida
- Alinhamentos iniciais e substituições
- Eventos de partidas (no futebol são:gol, pênalti, falta, cartão amarelo etc.)
- Pontuação final
- Ações dos jogadores durante a partida
Usaremos esses dados para capturar todos os eventos importantes da partida. Comparar o desempenho de um jogador antes e durante a partida pode levar a certas conclusões. Talvez não pudéssemos prever os resultados finais de seu desempenho (ou seja, uma vitória ou uma derrota), mas as estatísticas certamente poderiam nos ajudar a fazer suposições com um grau de confiabilidade.
Apresentando o modelo
O modelo é dividido em quatro áreas principais:
Tournament details
Match details
Events
Indicators and Performance
As tabelas fora dessas áreas são dicionários (
sport
, phase
, position
), catálogos (sport_event
, team
, player
) e uma única relação muitos-para-muitos (plays
). Descreveremos primeiro as tabelas não categorizadas e, em seguida, examinaremos cada área de perto.
As tabelas não categorizadas
Essas tabelas são importantes porque as tabelas de todas as quatro áreas as utilizam como dicionários ou catálogos.
O sport
table lista todos os esportes que armazenaremos em nosso banco de dados. Provavelmente teremos apenas um esporte aqui, futebol masculino, mas esta tabela nos dá a flexibilidade de adicionar esportes semelhantes (por exemplo, futebol feminino) se necessário.
No sport_event
tabela, armazenaremos os eventos relacionados ao(s) nosso(s) esporte(s). Um exemplo seriam os “Jogos Olímpicos de 2016”.
A phase
table é um dicionário que contém todas as etapas possíveis do torneio. Ele contém valores como “fase de grupo” , “rodada de 16” , “quartas-de-final” , “semifinais” , “final” .
A team
table é, como você deve imaginar, uma lista simples de todas as equipes. Os valores possíveis são “Croácia” , “Polônia” , “EUA” etc. Se usarmos o banco de dados para armazenar informações sobre competições de clubes ou ligas, também teremos valores como “Barcelona” , "Real Madrid" , “Bayern” , “Manchester United” etc.
No player
tabela, armazenaremos registros de todos os jogadores pertencentes às equipes relevantes.
O plays
table é a nossa única relação muitos-para-muitos e relaciona jogadores e equipas. Um jogador pode pertencer a mais de um time ao mesmo tempo (por exemplo, a seleção nacional e um clube), mas durante um torneio ele obviamente jogará por apenas um time.
Por fim, temos a position
tabela. Este dicionário simples irá armazenar uma lista de todas as posições necessárias. No futebol, isso inclui goleiro, meio-campista, atacante, etc.
Detalhes do torneio
Observação: Se você deseja apenas armazenar os resultados de partidas únicas, não precisa usar esta seção.
Um torneio consiste em mais de uma partida; tanto o UEFA Euro 2016 quanto os eventos de futebol nos Jogos Olímpicos de 2016 são torneios. Como dissemos antes, podemos armazenar uma única partida em nosso banco de dados, mas também podemos relacionar as partidas aos respectivos torneios. As mesas na seção Torneio são:
tournament
– Contém todos os dados básicos do torneio:o esporte, data de início, data de término, etc. Também precisamos armazenar o nome do torneio e uma descrição de onde está ocorrendo. Osport_event_id
atributo é opcional porque um torneio não precisa estar associado a um evento maior (como as Olimpíadas).group
– Isso lista todos os grupos nesse torneio. O UEFA Euro 2016 teve seis grupos, de A a F.participant
– Estas são as equipas que jogam no torneio; cada participante pode ser atribuído a um grupo. A maioria dos torneios começa com uma fase de grupos e depois continua para uma fase eliminatória (por exemplo, UEFA Euro, Copa do Mundo da UEFA, futebol olímpico). Alguns torneios terão apenas uma fase de grupos (por exemplo, ligas nacionais), enquanto outros terão apenas uma fase eliminatória (por exemplo, copas nacionais).in_team
– Esta tabela fornece uma relação muitos-para-muitos que armazena informações sobre os jogadores registrados para aquele torneio e suas posições esperadas.tournament_schedule
– Na minha opinião, esta é a tabela mais interessante desta seção. A lista de todos os jogos disputados durante este torneio é armazenada aqui. Otournament_id
atributo denota a qual torneio cada partida pertence, e ophase_id
atributo define a fase durante a qual a partida ocorrerá. Também armazenaremos o local da partida e a hora em que ela começa. Ambos os participantes serão descritos por campos de texto. Quando a fase de grupos terminar, saberemos todos os confrontos da rodada eliminatória. Por exemplo, no início do UEFA Euro 2016, sabíamos que o vencedor do Grupo E (1E) jogaria contra o vice-campeão do Grupo D (2D). Depois que todas as três rodadas da fase de grupos foram disputadas, essa dupla foi Itália x Espanha.
Detalhes da correspondência
Os Match details
A área é usada para armazenar dados para correspondências únicas. Usaremos duas tabelas:
match
– Contém todos os detalhes sobre uma única partida; esta partida pode estar relacionada a um torneio, mas também pode ser um único jogo. Portanto, otournament_schedule_id
atributo é opcional e armazenaremos osport_id
,start_time
elocation
atributos novamente aqui. Se a partida for parte de um torneio, entãotournament_schedule_id
será atribuído um valor. Oteam_1_id
eteam_2_id
atributos são referências às equipes envolvidas na partida. Ogoals_team_1
egoals_team_2
atributos contêm o resultado da partida. Eles são obrigatórios e devem ter "0" como valor padrão para ambos.in_match
– Esta tabela é uma lista de todos os jogadores que estão inscritos para aquela partida; os jogadores que não participarem terão um NULL nostarted_at
atributo, enquanto os jogadores que entraram como substitutos terãostarted_at
> 0 . Se um jogador foi substituído, ele terá umended_at
atributo que corresponde aostarted_at
atributo do jogador que os substituiu. Se o jogador permanecer durante toda a partida, seuended_at
atributo terá o mesmo valor que oend_time
atributo.
Eventos de Partida
Esta seção destina-se a armazenar todos os detalhes ou eventos que aconteceram durante o jogo. E as tabelas são:
event
– Este é um dicionário que lista todos os eventos que queremos armazenar. No futebol, são valores como “falta cometida” , “falta sofrida” , “cartão amarelo” , “cartão vermelho” , “chute livre” , “penalidade” , “meta” , “impedimento” , “substituição” , “jogador expulso da partida” .match_event
– Relaciona eventos com a partida. Armazenaremos oevent_time
bem como informações do jogador relacionadas a esse evento (in_match_id
).related_event
– É isso que reúne as informações do evento. Para explicar, vejamos um exemplo quando o Jogador A comete uma falta no Jogador B. Vamos inserir um registro nomatch_event
tabela que indica que o Jogador A cometeu uma falta e outra que indica que o Jogador B sofreu uma falta. Também adicionaremos um registro aorelated_event
tabela, onde a 'falta cometida' será o pai e a 'falta sofrida' será a criança. Também registraremos os resultados da falta:cartão amarelo, cobrança de falta ou pênalti e talvez um gol.
Indicadores e desempenho
Esta seção deve nos ajudar a analisar jogadores e equipes antes e depois da partida.
O indicator
table é um dicionário com um conjunto predefinido de indicadores para cada jogador antes de cada partida. Esses indicadores devem descrever a forma atual do jogador. Esta lista pode conter valores como:“número de gols nas últimas 10 partidas” , “distância média percorrida nas últimas 10 partidas” , “número de defesas do GK nas últimas 10 partidas” .
O performance
dicionário é muito semelhante ao indicator
, mas o usaremos para armazenar apenas valores relacionados à correspondência única:“distância coberta” , “passes precisos” , etc
O player_indicator
e performance_indicator
tabelas compartilham uma estrutura quase idêntica:
in_match_id
– refere-se ao jogador que participa de uma determinada partidaindicator_id
/performance_id
– referencia oindicator
ou "dicionários de desempenhovalue
– armazena o valor desse indicador (por exemplo, um jogador percorreu uma distância de 10,72 km)description
– contém uma descrição adicional, se necessário
O que aconteceu durante a partida?
Com todos esses dados inseridos, poderíamos obter facilmente detalhes de partidas, eventos e estatísticas para cada partida em nosso banco de dados.
Essa consulta simples retornaria detalhes básicos para uma próxima correspondência:
SELECT team_1.`team_name`, team_2.`team_name`, `match`.`start_time`, `match`.`location` FROM `match`, `team` AS team_1, `team` AS team_2 WHERE `match`.`team_1_id` = team_1.`id` AND `match`.`team_2_id` = team_2.`id`
Para obter uma lista de todos os eventos ao vivo durante uma determinada partida, usaríamos a consulta abaixo:
SELECT `event`.`event_name`, `match_event`.`event_time`, `player`.`first_name`, `player`.`last_name` FROM `match`, `match_event`, `event`, `in_match`, `player` WHERE `match_event`.`match_id` = `match`.`id` AND `event`.`id` = `match_event`.`event_id` AND `in_match`.`id` = `match_event`.`in_match_id` AND `player`.`id` = `in_match`.`player_id` AND `match`.`id` = @match ORDER BY `match_event`.`event_time` ASC
Existem inúmeras consultas adicionais nas quais posso pensar; é fácil fazer uma análise quando você tem os dados. Se você mediu e armazenou um grande número de indicadores e dados de desempenho do jogador, poderá relacionar esses parâmetros com um resultado final. Eu pessoalmente não acredito em tais previsões; há o fator sorte durante as partidas, além de vários outros fatores que você não pode saber até o início do jogo. Ainda assim, se você tiver um grande conjunto de dados e muitos parâmetros, sua chance de fazer previsões mais precisas aumenta.
O modelo apresentado neste artigo permite armazenar partidas, detalhes das partidas e um histórico do desempenho de cada jogador. Também podemos definir indicadores de forma para cada jogador antes da partida. Armazenar detalhes suficientes deve nos fornecer mais parâmetros nos quais basear nossas suposições. Não estou dizendo que poderíamos prever o resultado do jogo, mas poderíamos nos divertir com isso.
Também poderíamos ajustar facilmente esse modelo para armazenar dados para outros esportes. Essas mudanças não devem ser muito complexas. Adicionando um sport_id
atributo aos dicionários deve fazer o truque. Ainda assim, acho que seria sensato ter uma nova instância para cada esporte diferente.