Você já viu um daqueles gráficos que corre por um tempo mostrando um histórico? Tinha vários correndo a internet mostrando as maiores economias do mundo nos últimos cinquenta anos. Isso é chamado um gráfico race bar. Nesse post vamos fazer um também, mostrando o desenvolvimento do campeonato Brasileiro de 2024.
Muitas vezes, quando trabalhando com um projeto mais complicado, é necessário começar de trás pra frente. Isso é o que fiz pra chegar nesse resultado. Uma coisa era certa, eu iria precisar da posição de cada time por rodada. Esse seria o maior desafio desse trabalho.
Mas eu precisaria de mais coisas também. Vamos falar sobre esse raciocinio por trás de tudo isso.
Raciocinando o Problema
Para que o gráfico funcionasse sem problemas, a coisa principal seria dados. Onde poderia encontrar os dados?
Comecei dando uma olhada na documentação do bar-chart-race, que se encontra aqui. Isso me ajudou a determinar o que eu precisaria, e como o pacote funciona. Mas e os dados?
A princípio, procurei os dados em várias websites. O que eu precisava não era a tabela final. Eu tinha que ter a tabela de cada rodada. Infelizmente, não achei nenhuma website com essa informação. A única maneira de chegar a isso, seria eu criar os meu próprios dados.
Espera ai, não estou inventando nada.
O que eu quero dizer, é que tive que organizar os dados de acordo, para satisfazer aquilo que faltava. Às vezes, como cientista de dados, precisamos fazer exatamente isso pra chegar a solução desejável. Por exemplo, se você está trabalhando com dados de vendas, e precisa saber quanto foi vendido no primeiro trimestre, vai ter que resolver isso com algumas funções. Foi isso que fiz.
Ao invés de procurar por tabelas, mudei a minha estratégia. Eu peguei os resultados dos 380 jogos do campeonato, e gerei os dados que precisava. Vou explicar como fazer isso daqui a pouco.
Com os dados em mão, vamos começar o processo.
Preparando os Dados
Para pegar os dados, eu usei requests e BeautifulSoup. Eu não vou entrar no processo de extração nesse post. Pra conseguir os dados de maneira correta, é um procedimento tedioso, e a explicação é bem longa. Vou reservar isso para outro post.
Pra esse projeto usei o Jupyter Notebook. Se preferir pode usar qualquer IDE da sua escolha. Mas nesse tutorial, está feito com Jupyter Notebook.
Vamos começar com os dados que já limpei e pronto para usar. Vamos importar os dados no nosso workbook. Lembre-se onde salvou o documento antes de abrir.
import pandas as pd
jogos_completos = pd.read_csv("jogos_2024.csv", index_col=0)
Se o seu arquivo não está no mesmo diretório que o seu caderno (Jupyter Notebook), vai precisar direcionar aonde está. Por exemplo, se está no seu desktop, vai ter que fazer assim: ~/Desktop/jogos_2024.csv.
Esse index_col fala para o pandas usar a primeira coluna como index. Isso depende de como o arquivo csv foi salvo.
Olhando o documento, vamos ver isso aqui:

Você pode observar que o vencedor não está claro. Os gols estão resumidos dentro do Score. Vamos ter que resolver isso.
Determinando os Vencedores
Para determinarmos os vencedores do jogo, vamos ter que separar o Score. Pode ver que os gols na esquerda são os gols da casa. E o da direita os gols dos visitantes. Vamos dividir dessa maneira.
Durante o processo, descobri que o tracinho não é um tracinho comum. Então tive que copiar e colar o que eles tem ai e trocar pelo comum.
jogos_completos['gols'] = jogos_completos['Score'].str.replace('–', '-')
Agora que já temos o correto, vamos criar duas colunas novas: Casa e Visitantes.
jogos_completos[['Casa', 'Visitantes']] = jogos_completos['Score'].str.split('–', expand=True)
Temos o resultado assim:

Dessa maneira fica mais fácil trabalhar os dados.
Atribuindo os Pontos pra Cada Time
O nosso gráfico precisa da informação dos pontos. Agora que já separamos o Score, fica mais fácil determinar quem ganhou, e os pontos que receberam naquela rodada.
Para isso, escrevi uma função que atribue os pontos pra quem ganhou ou empatou:
def atribuir_pontos(row):
if row.Casa > row.Visitantes:
return pd.Series({'casa_pontos': 3, 'visitante_pontos': 0})
elif row.Casa < row.Visitantes:
return pd.Series({'casa_pontos': 0, 'visitante_pontos': 3})
else:
return pd.Series({'casa_pontos': 1, 'visitante_pontos': 1})
# colocando contra o dataframe
pts = jogos_completos.apply(atribuir_pontos, axis=1)
Observe que criei uma dataframe nova. O resultado é somente as duas colunas casa_pontos e visitante_pontos.
Finalmente, vamos juntar as duas dataframes e criar somente uma.
jogos_completos = pd.concat([jogos_completos, pts], axis=1)

Preparando o Dataframe
Estamos quase lá. Vamos dar uma limpada e organizada para o momento mais esperado.
O cabeçalho é o mesmo de quando eu puxei da internet. Vamos dar uma limpada nele pra ficar mais fácil na hora de criar o gráfico.
# criando uma copia do dataframe
df = jogos_completos.copy()
# normalizando o nome das colunas
df.rename(columns={
'Wk':'rodada',
'Home':'time_casa',
'Away':'time_vis',
'casa_pontos': 'casa_pts',
'visitante_pontos':'vis_pts',
'Casa':'gols_casa',
'Visitantes':'gols_vis',
}, inplace=True)
Eu quero ter certeza que a rodada, casa_pts, e vis_pts são todos números. Então vamos passar o código para isso.
df['rodada'] = df['rodada'].astype(int)
df['casa_pts'] = df['casa_pts'].astype(int)
df['vis_pts'] = df['vis_pts'].astype(int)
Se você observar o dataframe, vai ver que os pontos estão divididos entre casa e visitante. Por exemplo, se o Internacional ganhou em casa, vai ter 3 pontos na coluna casa_pts. E se na próxima rodada ganhou fora de casa, vai ter 3 pontos na coluna vis_pts. Queremos que tudo isso fique em uma coluna.
Para isso, vou dividir o dataframe em dois e depois juntá-los novamente. Vamos lá:
# dataframe de pontos ganho em casa
casa = df[['rodada','time_casa','casa_pts']].rename(columns={'time_casa':'time','casa_pts':'pts'})
# dataframe de pontos ganho fora de casa
visitante = df[['rodada','time_vis','vis_pts']].rename(columns={'time_vis':'time','vis_pts':'pts'})
Agora vamos juntar esses dois:
longo = pd.concat([casa, visitante], ignore_index=True)

Esse dataframe tem os pontos ganhos em cada rodada. Vamos criar a acumulação de pontos. Para isso vamos usar o cumsum. Essa função acumula a conta.
Por exemplo, o Internacional tem 3 pontos na primeira rodada, e se ganhou na segunda vai ter 6. E se empatou na terceira rodada, vai ter 7 pontos. E assim por diante.
# ordenamento das rodadas
longo = longo.sort_values(['rodada'])
# contando pontos acumulativos
longo['cum_pts'] = longo.groupby('time')['pts'].cumsum()
Pivotando a Tabela
O último passo antes de criar o gráfico animado é pivotar a tabela. Pivotando a tabela, vai nos dar os nomes dos times como nomes de colunas e o resto como os pontos acumulados. Desta maneira, é fácil criar o gráfico.
Outra coisa, você observa que a tabela começa na primeira rodada, mas eu quero começa-la do zero. Então vou criar uma linha especial que assim todos os times ficam com zero.
# pivotando a tabela
pts_wide = (
longo
.pivot(index='rodada', columns='time', values='cum_pts')
.fillna(method='ffill')
.fillna(0)
)
# criando uma linha extra, cuja vai ser a primeira linha
todas_rodadas = range(0, 39)
# adicionado
pts_wide = pts_wide.reindex(todas_rodadas, fill_value=0)
Então ai, estamos pronto pra criar o vídeo com o bar_chart_race.
Criando Gráfico Race Bar
Bem amigos, se vocês chegaram até aqui, estão de parabéns. Esse é o último passo.
# importando o pacote necessário
import bar_chart_race as bcr
# iniciando o gráfico
bcr.bar_chart_race(
df=pts_wide, # essa é o seu dataframe com os dados usados
filename='brasileirao_points_race.mp4', # o output final.
orientation='h', # horizontal
sort='desc', # do maior para o menor
n_bars=20, # número de barras
fixed_order=False, # não é fixo
steps_per_period=30, # quantos quadros por segundos
period_length=2000, # tempo em segundos
period_fmt='Rodada {x:.0f}', # rotulo no gráfico
title='Campeonato Brasileiro Série A (2024)' # titúlo do gráfico
)
Conclusão
Nesse projeto vocês podem sentir como é o dia a dia de um cientista de dados. Você observa que gastamos quase 95% do tempo preparando os dados para fazer o gráfico. Se contar o tempo que demorou para pegar os dados e tudo mais, seria 95%. Tranquilo.
Se você está querendo aprender python e entrar no ramo da ciência de dados, isso é o que você vai se deparar. Bastante manipulação de dados.
Comentários (0)
Seja o primeiro a comentar!
Deixe um comentário