Analisando dados das Olimpíadas com Pandas e Plotly

As Olimpíadas estão acabando… e, além de saudades e histórias de superação, cada Olimpíada deixa uma enorme quantidade de dados! Nesse artigo, vamos ver como podemos usar o Pandas para analisar parte desses dados, verificando a evolução do tempo dos atletas em provas de atletismo.

O primeiro passo é obter os dados para análise. Basicamente, há três formas:

  • obtenção direta a partir de uma fonte confiável e que disponibiliza os dados adequadamente;
  • obtenção direta a partir de uma fonte confiável mas que não disponibiliza os dados adequadamente;
  • obtenção indireta, via terceiros.

Uma fonte confiável seria a página https://olympics.com/, do Comitê Olímpico Internacional. No entanto, ela não disponibiliza os dados de uma forma adequada como, por exemplo, tabelas que poderiam ser baixadas diretamente do site. Nem fornece uma API para requisições. Logo, uma forma de obter seria fazendo scraping dos dados, usando, por exemplo, Beautiful Soup ou Selenium. Ou mesmo o Pandas, como já mostrei nesse artigo. Mas seria necessário um estudo da estrutura das páginas, especialmente porque o site faz uso de dados gerados dinamicamente.

Agora, pense… a Olimpíada é o evento esportivo mais importante do mundo, certamente muitas pessoas já buscaram os dados pelos mais diversos motivos e, eventualmente, algumas podem ter disponibilizado em algum formato adequado aos nossos propósitos. E o lugar onde provavelmente esses dados seriam encontrados é no Kaggle, um site voltado para data science e machine learning.

Experimente buscar “olympics” no Kaggle e verá uma quantidade razoável de bases de dados a respeito. No caso, escolhi essa aqui, que, segundo o criador, possui os dados de atletismo do site https://olympics.com/ de 1896 até 2016. Obviamente que precisaremos testar a consistência dos dados, visto que estamos obtendo indiretamente, mas nada muito diferente do que já faríamos mesmo obtendo diretamente. Observe também que a base de dados não inclui 2020, o que é bastante razoável tendo em vista que escrevo ainda durante o período das Olimpíadas. Mas, como veremos, não é muito difícil inserir dados atuais, ao menos em pequena escala.

Feitas essas considerações, vamos ao código. Comecemos importando o pandas e verificando o arquivo dos dados que, no caso, foi salvo em uma pasta chamada data sob o nome results.csv. O caminho para o arquivo foi atribuído à uma variável file:

import pandas as pd
file = 'data/results.csv'

Olhando as 5 primeiras e as 5 últimas linhas do arquivo, temos uma ideia geral de sua organização:

!head -5 data/results.csv
Gender,Event,Location,Year,Medal,Name,Nationality,Result
M,10000M Men,Rio,2016,G,Mohamed FARAH,USA,25:05.17
M,10000M Men,Rio,2016,S,Paul Kipngetich TANUI,KEN,27:05.64
M,10000M Men,Rio,2016,B,Tamirat TOLA,ETH,27:06.26
M,10000M Men,Beijing,2008,G,Kenenisa BEKELE,ETH,27:01.17
!tail -5 data/results.csv
W,Triple Jump Women,Athens,2004,S,Hrysopiyi DEVETZI,GRE,15.25
W,Triple Jump Women,Athens,2004,B,Tatyana LEBEDEVA,RUS,15.14
W,Triple Jump Women,Atlanta,1996,G,Inessa KRAVETS,UKR,15.33
W,Triple Jump Women,Atlanta,1996,S,Inna LASOVSKAYA,RUS,14.98
W,Triple Jump Women,Atlanta,1996,B,Sarka KASPARKOVA,CZE,14.98

A coluna chamada Result no arquivo se refere aos tempos dos atletas, então acho que faz mais sentido ter nome Time. Uma inspeção visual no arquivo como um todo mostra que algumas linhas possuem ainda uma columa com informação sobre o vento durante a competição, mesmo essa coluna não sendo nomeada no início do arquivo. Mostro essa inspeção no vídeo no início do artigo. E o porquê há dados sobre vento será explicado no decorrer do artigo.

Assim, os seguintes nomes de coluna me parecem adequados:

columns = ('Gender', 'Event', 'Location', 'Year', 'Medal', 'Name', 'Nationality', 'Time', 'Wind')

Vamos, então, criar nosso dataframe do pandas com essas colunas, lembrando de ignorar a primeira linha do arquivo já que são os nomes de coluna que não queremos usar:

df = pd.read_csv(file, names=columns, skiprows=1)
df
  Gender Event Location Year Medal Name Nationality Time Wind
0 M 10000M Men Rio 2016 G Mohamed FARAH USA 25:05.17 NaN
1 M 10000M Men Rio 2016 S Paul Kipngetich TANUI KEN 27:05.64 NaN
2 M 10000M Men Rio 2016 B Tamirat TOLA ETH 27:06.26 NaN
3 M 10000M Men Beijing 2008 G Kenenisa BEKELE ETH 27:01.17 NaN
4 M 10000M Men Beijing 2008 S Sileshi SIHINE ETH 27:02.77 NaN
2389 W Triple Jump Women Athens 2004 S Hrysopiyi DEVETZI GRE 15.25 NaN
2390 W Triple Jump Women Athens 2004 B Tatyana LEBEDEVA RUS 15.14 NaN
2391 W Triple Jump Women Atlanta 1996 G Inessa KRAVETS UKR 15.33 NaN
2392 W Triple Jump Women Atlanta 1996 S Inna LASOVSKAYA RUS 14.98 NaN
2393 W Triple Jump Women Atlanta 1996 B Sarka KASPARKOVA CZE 14.98 NaN

2394 rows × 9 columns

A estrutura do dataframe parece ser bem autoexplicativa. Mas já podemos observar alguns detalhes. O formato do tempo dos atletas não é consistente. Nas primeiras linhas vemos no formato minutos:segundos.centésimos e, nas últimas linhas, vemos apenas segundos.centésimos. Veja, obviamente que do ponto de vista esportivo não faz sentido mostrar minutos em provas de curtas, mas isso pode dificultar a análise dos tempos caso se resolva fazer alguma análise que envolva mais de um tipo de evento. Não necessariamente isso é um problema, é uma observação.

Outra observação é que há redundância na informação de genêro, visto que há uma coluna para essa informação e os eventos também apresentam em seu nome.

Para aprofundar na nossa compreensão inicial dos dados, vamos usar os métodos info e describe. Ambos servem para obter informações sobre o dataframe:

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2394 entries, 0 to 2393
Data columns (total 9 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   Gender       2394 non-null   object 
 1   Event        2394 non-null   object 
 2   Location     2394 non-null   object 
 3   Year         2394 non-null   int64  
 4   Medal        2394 non-null   object 
 5   Name         2164 non-null   object 
 6   Nationality  2394 non-null   object 
 7   Time         2394 non-null   object 
 8   Wind         12 non-null     float64
dtypes: float64(1), int64(1), object(7)
memory usage: 168.5+ KB

Vemos com o info que o Pandas reconheceu a coluna de anos como sendo do tipo inteiro, e a de vento como float. Todas as demais são do tipo object que, segundo a documentação do pacote, é o tipo para colunas com strings ou colunas com tipos misturados. A coluna de tempos ser do tipo object terá consequências mais para frente no artigo.

A coluna Wind apresenta apenas 12 valores não nulos, indicando que boa parte dos registros está sem essa informação. Pode ser por realmente não ter tido vento nesses casos, ou seja, seria equivalente a zero, ou uma falta dessa informação no banco de dados por algum motivo.

Há também registros nulos na coluna Name, vejamos como isso se reflete em nossa análise no decorrer do artigo.

df.describe()
  Year Wind
count 2394.000000 12.00000
mean 1970.379282 -0.02500
std 34.711777 0.56909
min 1896.000000 -0.90000
25% 1948.000000 -0.15000
50% 1976.000000 0.10000
75% 2000.000000 0.22500
max 2016.000000 0.60000

O método describe quando usado sem nenhum parâmetro apresenta um breve resumo estatístico das colunas de tipo numérico. No caso, a de anos e a de vento. Observe que, aparentemente, os dados realmente são de 1896 a 2016, pois estes são os valores mínimo e máximo, respectivamente. Mas isso não garante que há realmente todos os anos olímpicos nesse intervalo. Para isso:

df['Year'].unique()
array([2016, 2008, 2000, 1992, 1984, 1976, 1968, 1960, 1952, 1936, 1928,
       1920, 2012, 2004, 1996, 1980, 1972, 1964, 1956, 1948, 1932, 1924,
       1912, 1908, 1900, 1904, 1896, 1988])

O método unique apresenta os valores únicos em uma dada coluna. Observe que os anos estão fora de ordem, o que atrapalha um pouco a análise. Vamos ordenar de forma crescente com o método sort_values:

df['Year'].sort_values().unique()
array([1896, 1900, 1904, 1908, 1912, 1920, 1924, 1928, 1932, 1936, 1948,
       1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992,
       1996, 2000, 2004, 2008, 2012, 2016])

Observe que faltam os anos de 1916, 1940 e 1944 nessa lista. Mas faz sentido, são anos de guerra mundial… sem olimpíadas.

O método describe pode receber parâmetros, dentre eles o include que permite forçar a presença de outros tipos. Vamos ver uma descrição dos tipos object:

df.describe(include='object')
  Gender Event Location Medal Name Nationality Time
count 2394 2394 2394 2394 2164 2394 2394
unique 2 47 23 3 1681 97 1947
top M Marathon Men London B Merlene OTTEY USA None
freq 1632 84 253 799 7 639 43

Observe que as informações mostradas mudaram. A linha de contagem é autoexplicativa e nos mostra uma informação que já tínhamos com o info. A linha unique é similar ao método que utilizamos anteriormente, mostrando quantos valores únicos há em cada coluna. Os valores para gênero e para medalhas são fáceis de entender. No mais, descobrimos que há 47 eventos na base de dados, que 23 localidades já receberam as Olimpíadas, que atletas de 97 nacionalidades distintas já receberam medalhas no atletismo (ao menos até 2016) e que 2164 – 1681 = 483 atletas receberam mais de uma medalha.

A linha top apresenta os valores que mais se repetiram, enquanto que a linha freq mostra quantas vezes se repetiram os valores da linha top. Assim, vemos que o país que mais recebeu medalhas foram os Estados Unidos.

Mas precisamos tomar cuidado com a forma de apresentação das linhas top e freq, pois se houver mais de uma entrada que ocorrem em mesma quantidade, será apresentada apenas uma. Vamos procurar o atleta que mais ganhou medalhas de uma forma mais completa. Para isso, vamos mostrar o início da coluna de medalhas, em ordem decrescente, após agrupar os dados pelo nome dos atletas:

df.groupby(by='Name').count()['Medal'].sort_values(ascending=False).head()
Name
Merlene OTTEY         7
Paavo NURMI           7
Tirunesh DIBABA       6
Usain BOLT            6
Irena KIRSZENSTEIN    6
Name: Medal, dtype: int64

Vemos que dois atletas receberam 7 medalhas no atletismo de acordo com a base de dados: Paavo Nurmi e Merlene Ottey, vale muito a leitura sobre eles. E ainda aparece o Bolt entre os primeiros, atleta já abordado aqui no site. No entanto, esses números não batem exatamente com os que aparecem nos links que coloquei para cada um deles. Isso pode ser um indício de problemas com a base de dados. Pode ser, por exemplo, erro de digitação nos nomes dos atletas ou uma base incompleta mesmo. Vamos seguir adiante, mas já com consciência desses indícios de problemas.

Perceba que só com alguns pequenos comandos já fomos capazes de encontrar informações relevantes. Acostume-se a fazer essa análise prévia com info e describe. Ajuda muito a entender a forma de apresentação dos dados, a ter uma ideia de sua completude e consistência e a direcionar análises posteriores.

Agora é o momento de escolher o que se deseja fazer. Há várias possibilidades mas, para esse artigo, resolvi focar nos 100 m para mulheres. Afinal, já fiz um artigo sobre os dados do Usain Bolt, então vamos dar um espaço para as meninas também, especialmente com o recorde olímpico quebrado agora em 2020.

Focando em um evento

Para filtrar os dados podemos usar o método loc, buscando na coluna Event por 100M Women:

df.loc[df['Event'] == '100M Women']
  Gender Event Location Year Medal Name Nationality Time Wind
1687 W 100M Women Rio 2016 G Elaine THOMPSON JAM 10.71 NaN
1688 W 100M Women Rio 2016 S Tori BOWIE USA 10.83 NaN
1689 W 100M Women Rio 2016 B Shelly-Ann FRASER-PRYCE JAM 10.86 NaN
1690 W 100M Women Beijing 2008 G Shelly-Ann FRASER-PRYCE JAM 10.78 NaN
1691 W 100M Women Beijing 2008 S Sherone SIMPSON JAM 10.98 NaN
1692 W 100M Women Beijing 2008 S Kerron STEWART JAM 10.98 NaN
1693 W 100M Women Sydney 2000 S Ekaterini THANOU GRE 11.12 NaN
1694 W 100M Women Sydney 2000 S Tanya LAWRENCE JAM 11.18 NaN
1695 W 100M Women Sydney 2000 B Merlene OTTEY JAM 11.19 NaN
1696 W 100M Women Barcelona 1992 G Gail DEVERS USA 10.82 NaN
1697 W 100M Women Barcelona 1992 S Juliet CUTHBERT JAM 10.83 NaN
1698 W 100M Women Barcelona 1992 B Irina PRIVALOVA EUN 10.84 NaN
1699 W 100M Women Los Angeles 1984 G Evelyn ASHFORD USA 10.97 NaN
1700 W 100M Women Los Angeles 1984 S Alice BROWN USA 11.13 NaN
1701 W 100M Women Los Angeles 1984 B Merlene OTTEY JAM 11.16 NaN
1702 W 100M Women Montreal 1976 G Annegret RICHTER-IRRGANG FRG 11.08 NaN
1703 W 100M Women Montreal 1976 S Renate STECHER GDR 11.13 NaN
1704 W 100M Women Montreal 1976 B Inge HELTEN FRG 11.17 NaN
1705 W 100M Women Mexico 1968 G Wyomia TYUS USA 11.0 NaN
1706 W 100M Women Mexico 1968 S Barbara FERRELL USA 11.1 NaN
1707 W 100M Women Mexico 1968 B Irena KIRSZENSTEIN POL 11.1 NaN
1708 W 100M Women Rome 1960 G Wilma RUDOLPH USA 11.0 NaN
1709 W 100M Women Rome 1960 S Dorothy HYMAN GBR 11.3 NaN
1710 W 100M Women Rome 1960 B Giuseppina LEONE ITA 11.3 NaN
1711 W 100M Women Helsinki 1952 G Marjorie JACKSON AUS 11.5 NaN
1712 W 100M Women Helsinki 1952 S Daphne HASENJAGER RSA 11.8 NaN
1713 W 100M Women Helsinki 1952 B Shirley STRICKLAND AUS 11.9 NaN
1714 W 100M Women Berlin 1936 G Helen STEPHENS USA 11.5 NaN
1715 W 100M Women Berlin 1936 S Stanislawa WALASIEWICZ POL 11.7 NaN
1716 W 100M Women Berlin 1936 B Käthe KRAUSS GER 11.9 NaN
1717 W 100M Women Amsterdam 1928 G Elizabeth ROBINSON USA 12.2 NaN
1718 W 100M Women Amsterdam 1928 S Fanny ROSENFELD CAN None NaN
1719 W 100M Women Amsterdam 1928 B Ethel SMITH CAN None NaN
1720 W 100M Women London 2012 G Shelly-Ann FRASER-PRYCE JAM 10.75 NaN
1721 W 100M Women London 2012 S Carmelita JETER USA 10.78 NaN
1722 W 100M Women London 2012 B Veronica CAMPBELL-BROWN JAM 10.81 NaN
1723 W 100M Women Athens 2004 G Yuliya NESTSIARENKA BLR 10.93 NaN
1724 W 100M Women Athens 2004 S Lauryn WILLIAMS USA 10.96 NaN
1725 W 100M Women Athens 2004 B Veronica CAMPBELL-BROWN JAM 10.97 NaN
1726 W 100M Women Atlanta 1996 G Gail DEVERS USA 10.94 NaN
1727 W 100M Women Atlanta 1996 S Merlene OTTEY JAM 10.94 NaN
1728 W 100M Women Atlanta 1996 B Gwen TORRENCE USA 10.96 NaN
1729 W 100M Women Moscow 1980 G Lyudmila KONDRATYEVA URS 11.06 NaN
1730 W 100M Women Moscow 1980 S Marlies OELSNER-GÖHR GDR 11.07 NaN
1731 W 100M Women Moscow 1980 B Ingrid AUERSWALD-LANGE GDR 11.14 NaN
1732 W 100M Women Munich 1972 G Renate STECHER GDR 11.07 NaN
1733 W 100M Women Munich 1972 S Raelene Ann BOYLE AUS 11.23 NaN
1734 W 100M Women Munich 1972 B Silvia CHIVAS BARO CUB 11.24 NaN
1735 W 100M Women Tokyo 1964 G Wyomia TYUS USA 11.4 NaN
1736 W 100M Women Tokyo 1964 S Edith MCGUIRE USA 11.6 NaN
1737 W 100M Women Tokyo 1964 B Ewa KLOBUKOWSKA POL 11.6 NaN
1738 W 100M Women Melbourne / Stockholm 1956 G Betty CUTHBERT AUS 11.5 NaN
1739 W 100M Women Melbourne / Stockholm 1956 S Christa STUBNICK EUA 11.7 NaN
1740 W 100M Women Melbourne / Stockholm 1956 B Marlene MATHEWS-WILLARD AUS 11.7 NaN
1741 W 100M Women London 1948 G Fanny BLANKERS-KOEN NED 11.9 NaN
1742 W 100M Women London 1948 S Dorothy HALL GBR 12.2 NaN
1743 W 100M Women London 1948 B Shirley STRICKLAND AUS 12.2 NaN
1744 W 100M Women Los Angeles 1932 G Stanislawa WALASIEWICZ POL 11.9 NaN
1745 W 100M Women Los Angeles 1932 S Hilda STRIKE CAN 11.9 NaN
1746 W 100M Women Los Angeles 1932 B Wilhelmina VON BREMEN USA 12.0 NaN

Agora precisamos novamente escolher o que fazer, qual caminho de análise tomar. Poderíamos, por exemplo, avaliar a distância relativa entre os medalhistas de ouro, prata e bronze para verificar se os tempos estão ficando mais próximos no decorrer dos anos. Ou avaliar o ganho/perda de tempo entre cada edição do evento. São diversas possibilidades. Aqui, vou escolher uma análise relativamente simples, para que o artigo seja uma introdução ao pandas. Vamos avaliar a evolução dos tempos das medalhistas de ouro desde a primeira Olimpíada em que teve a competição de 100 m feminino até a edição de agora.

Portanto, precisamos adicionar mais uma condição ao nosso filtro, buscando por G (de gold, ouro em inglês) na coluna Medal. O símbolo & indica que ambas as condições devem ser satisfeitas:

df.loc[(df['Event'] == '100M Women') & (df['Medal'] == 'G')]
  Gender Event Location Year Medal Name Nationality Time Wind
1687 W 100M Women Rio 2016 G Elaine THOMPSON JAM 10.71 NaN
1690 W 100M Women Beijing 2008 G Shelly-Ann FRASER-PRYCE JAM 10.78 NaN
1696 W 100M Women Barcelona 1992 G Gail DEVERS USA 10.82 NaN
1699 W 100M Women Los Angeles 1984 G Evelyn ASHFORD USA 10.97 NaN
1702 W 100M Women Montreal 1976 G Annegret RICHTER-IRRGANG FRG 11.08 NaN
1705 W 100M Women Mexico 1968 G Wyomia TYUS USA 11.0 NaN
1708 W 100M Women Rome 1960 G Wilma RUDOLPH USA 11.0 NaN
1711 W 100M Women Helsinki 1952 G Marjorie JACKSON AUS 11.5 NaN
1714 W 100M Women Berlin 1936 G Helen STEPHENS USA 11.5 NaN
1717 W 100M Women Amsterdam 1928 G Elizabeth ROBINSON USA 12.2 NaN
1720 W 100M Women London 2012 G Shelly-Ann FRASER-PRYCE JAM 10.75 NaN
1723 W 100M Women Athens 2004 G Yuliya NESTSIARENKA BLR 10.93 NaN
1726 W 100M Women Atlanta 1996 G Gail DEVERS USA 10.94 NaN
1729 W 100M Women Moscow 1980 G Lyudmila KONDRATYEVA URS 11.06 NaN
1732 W 100M Women Munich 1972 G Renate STECHER GDR 11.07 NaN
1735 W 100M Women Tokyo 1964 G Wyomia TYUS USA 11.4 NaN
1738 W 100M Women Melbourne / Stockholm 1956 G Betty CUTHBERT AUS 11.5 NaN
1741 W 100M Women London 1948 G Fanny BLANKERS-KOEN NED 11.9 NaN
1744 W 100M Women Los Angeles 1932 G Stanislawa WALASIEWICZ POL 11.9 NaN

OK, o filtro parece fazer sentido, então vamos associar esse dataframe à uma variável de forma a ser possível fazer mais análises:

df_women_100m = df.loc[(df['Event'] == '100M Women') & (df['Medal'] == 'G')]
df_women_100m
  Gender Event Location Year Medal Name Nationality Time Wind
1687 W 100M Women Rio 2016 G Elaine THOMPSON JAM 10.71 NaN
1690 W 100M Women Beijing 2008 G Shelly-Ann FRASER-PRYCE JAM 10.78 NaN
1696 W 100M Women Barcelona 1992 G Gail DEVERS USA 10.82 NaN
1699 W 100M Women Los Angeles 1984 G Evelyn ASHFORD USA 10.97 NaN
1702 W 100M Women Montreal 1976 G Annegret RICHTER-IRRGANG FRG 11.08 NaN
1705 W 100M Women Mexico 1968 G Wyomia TYUS USA 11.0 NaN
1708 W 100M Women Rome 1960 G Wilma RUDOLPH USA 11.0 NaN
1711 W 100M Women Helsinki 1952 G Marjorie JACKSON AUS 11.5 NaN
1714 W 100M Women Berlin 1936 G Helen STEPHENS USA 11.5 NaN
1717 W 100M Women Amsterdam 1928 G Elizabeth ROBINSON USA 12.2 NaN
1720 W 100M Women London 2012 G Shelly-Ann FRASER-PRYCE JAM 10.75 NaN
1723 W 100M Women Athens 2004 G Yuliya NESTSIARENKA BLR 10.93 NaN
1726 W 100M Women Atlanta 1996 G Gail DEVERS USA 10.94 NaN
1729 W 100M Women Moscow 1980 G Lyudmila KONDRATYEVA URS 11.06 NaN
1732 W 100M Women Munich 1972 G Renate STECHER GDR 11.07 NaN
1735 W 100M Women Tokyo 1964 G Wyomia TYUS USA 11.4 NaN
1738 W 100M Women Melbourne / Stockholm 1956 G Betty CUTHBERT AUS 11.5 NaN
1741 W 100M Women London 1948 G Fanny BLANKERS-KOEN NED 11.9 NaN
1744 W 100M Women Los Angeles 1932 G Stanislawa WALASIEWICZ POL 11.9 NaN

Verificando a completude do banco de dados

Antes de continuar fazendo mais análises em cima dos dados, vamos verificar se estão completos e aparentemente coerentes. Vimos anteriormente que os anos de 1916, 1940 e 1944 não estão presentes na base de dados pois não ocorreram Olimpíadas nesses anos. Mas vamos ver se para os 100 m femininos a base está completa. Afinal, anteriormente não tínhamos uma análise tão granular, evento a evento, de forma que alguns podem estar incompletos mas não tínhamos como saber.

Seguindo o mesmo procedimento já feito, vamos ordenar a coluna de anos:

df_women_100m.sort_values(by='Year')
  Gender Event Location Year Medal Name Nationality Time Wind
1717 W 100M Women Amsterdam 1928 G Elizabeth ROBINSON USA 12.2 NaN
1744 W 100M Women Los Angeles 1932 G Stanislawa WALASIEWICZ POL 11.9 NaN
1714 W 100M Women Berlin 1936 G Helen STEPHENS USA 11.5 NaN
1741 W 100M Women London 1948 G Fanny BLANKERS-KOEN NED 11.9 NaN
1711 W 100M Women Helsinki 1952 G Marjorie JACKSON AUS 11.5 NaN
1738 W 100M Women Melbourne / Stockholm 1956 G Betty CUTHBERT AUS 11.5 NaN
1708 W 100M Women Rome 1960 G Wilma RUDOLPH USA 11.0 NaN
1735 W 100M Women Tokyo 1964 G Wyomia TYUS USA 11.4 NaN
1705 W 100M Women Mexico 1968 G Wyomia TYUS USA 11.0 NaN
1732 W 100M Women Munich 1972 G Renate STECHER GDR 11.07 NaN
1702 W 100M Women Montreal 1976 G Annegret RICHTER-IRRGANG FRG 11.08 NaN
1729 W 100M Women Moscow 1980 G Lyudmila KONDRATYEVA URS 11.06 NaN
1699 W 100M Women Los Angeles 1984 G Evelyn ASHFORD USA 10.97 NaN
1696 W 100M Women Barcelona 1992 G Gail DEVERS USA 10.82 NaN
1726 W 100M Women Atlanta 1996 G Gail DEVERS USA 10.94 NaN
1723 W 100M Women Athens 2004 G Yuliya NESTSIARENKA BLR 10.93 NaN
1690 W 100M Women Beijing 2008 G Shelly-Ann FRASER-PRYCE JAM 10.78 NaN
1720 W 100M Women London 2012 G Shelly-Ann FRASER-PRYCE JAM 10.75 NaN
1687 W 100M Women Rio 2016 G Elaine THOMPSON JAM 10.71 NaN

Algumas coisas interessantes já aparecem. A primeira edição que aparece no banco de dados é a de 1928 e isto está correto. A primeira edição feminina realmente foi em 1928, enquanto que a masculina ocorre desde 1896.

Agora, há duas ausências estranhas: 1988 e 2000. Vamos buscar tais anos no dataframe completo, começando pelo ano 2000:

df.loc[(df['Year'] == 2000) & (df['Event'] == '100M Women')]
  Gender Event Location Year Medal Name Nationality Time Wind
1693 W 100M Women Sydney 2000 S Ekaterini THANOU GRE 11.12 NaN
1694 W 100M Women Sydney 2000 S Tanya LAWRENCE JAM 11.18 NaN
1695 W 100M Women Sydney 2000 B Merlene OTTEY JAM 11.19 NaN

Observe que há, sim, registro do evento na base de dados, mas sem medalhista de ouro. E, por mais estranho que pareça, isto também está correto. A medalhista de ouro seria Marion Jones mas, devido ao uso de substâncias banidas do esporte, ela acabou perdendo a medalha e o Comitê Olímpico Internacional decidiu por não promover Ekaterini Thanou tendo em vista que esta também já esteve envolvida em casos de doping. É uma longa história que pode ser lida nos links que deixei aqui.

O que importa aqui é que a base de dados está coerente nesse ponto. Resta resolver o que fazer com essa informação. Como resolvemos filtrar apenas por medalhistas de ouro, realmente o ano 2000 ficaria sem registro de tempo. Uma outra abordagem seria considerar o tempo de Thanou, já que esse seria oficialmente o melhor tempo da final daquele ano, talvez colocando alguma indicação visual de que não se trata de uma medalhista de ouro. Tudo depende da história que se quer contar e do tipo de análise que se deseja fazer. Aqui, vou manter a decisão de incluir apenas medalhistas de ouro e, portanto, o ano 2000 ficará sem registro.

Vamos avaliar o ano de 1988 agora:

df.loc[(df['Year'] == 1988) & (df['Event'] == '100M Women')]
  Gender Event Location Year Medal Name Nationality Time Wind

Aqui o caso é diferente, não há registro do evento para 1988, o que está errado. Pode ser que esse ano esteja comprometido na base de dados, vamos verificar:

df.loc[(df['Year'] == 1988)]
  Gender Event Location Year Medal Name Nationality Time Wind
423 M 20Km Race Walk Men Seoul 1988 G Jozef PRIBILINEC TCH 1:19:57 NaN
424 M 20Km Race Walk Men Seoul 1988 S Ronald WEIGEL GDR 1:20:00 NaN
425 M 20Km Race Walk Men Seoul 1988 B Maurizio DAMILANO ITA 1:20:14 NaN
911 M 50Km Race Walk Men Seoul 1988 G Vyacheslav IVANENKO URS 3:38:29 NaN
912 M 50Km Race Walk Men Seoul 1988 S Ronald WEIGEL GDR 3:38:56 NaN
913 M 50Km Race Walk Men Seoul 1988 B Hartwig GAUDER GDR 3:39:45 NaN
1058 M Decathlon Men Seoul 1988 G Christian SCHENK GDR 8488.0 NaN
1059 M Decathlon Men Seoul 1988 S Torsten VOSS GDR 8399.0 NaN
1060 M Decathlon Men Seoul 1988 B Dave STEEN CAN 8328.0 NaN
1426 M Marathon Men Seoul 1988 G Gelindo BORDIN ITA 2:10:32 NaN
1427 M Marathon Men Seoul 1988 S Douglas WAKIIHURI KEN 2:10:47 NaN
1428 M Marathon Men Seoul 1988 B Hussein AHMED SALAH DJI 2:10:59 NaN
2162 W Heptathlon Women Seoul 1988 G Jackie JOYNER USA 7291 P. NaN
2163 W Heptathlon Women Seoul 1988 S Sabine JOHN GDR 6897.0 NaN
2164 W Heptathlon Women Seoul 1988 B Anke BEHMER GDR 6858.0 NaN
2319 W Marathon Women Seoul 1988 G Rosa MOTA POR 2:25:40 NaN
2320 W Marathon Women Seoul 1988 S Lisa ONDIEKI AUS 2:25:53 NaN
2321 W Marathon Women Seoul 1988 B Katrin DÖRRE GDR 2:26:21 NaN

Realmente há algum problema na base de dados, faltam todos os eventos de curta distância. E, infelizmente, não há qualquer aviso a respeito no link do Kaggle da base de dados. Por isso é importante fazer esses testes de coerência. Veja, mesmo que os dados tivessem como origem direta o site oficial das Olimpíadas esse tipo de checagem é fundamental, pois erros acontecem. Aqui, pode ser que o site estivesse sem os dados na época onde foi feita a coleta ou então houve algum erro no algoritmo utilizado para fazer o scraping dos dados. Enfim, inúmeras possibilidades, o importante é que percebemos o problema e é simples de resolvê-lo.

Uma rápida busca no próprio site das Olimpíadas mostra que a medalhista de ouro de 1988 foi Florence Griffith Joyner, outra esportista cuja história é interessante (e curta…). Com os dados do site, podemos atualizar nosso dataframe, aproveitando também para adicionar a medalhista de ouro de 2020, Elaine Thompson. Para isso, vamos utilizar o método loc, adicionando linhas após os registros já existentes:

df_women_100m.loc[len(df_women_100m.index)] = ['W', '100M Women', 'Seoul', 1988, 'G', 'Florence Griffith JOYNER', 'USA', 10.54, 3.0]
df_women_100m.loc[len(df_women_100m.index)] = ['W', '100M Women', 'Tokyo', 2020, 'G', 'Elaine THOMPSON', 'JAM', 10.61, -0.6]
df_women_100m
  Gender Event Location Year Medal Name Nationality Time Wind
1687 W 100M Women Rio 2016 G Elaine THOMPSON JAM 10.71 NaN
1690 W 100M Women Beijing 2008 G Shelly-Ann FRASER-PRYCE JAM 10.78 NaN
1696 W 100M Women Barcelona 1992 G Gail DEVERS USA 10.82 NaN
1699 W 100M Women Los Angeles 1984 G Evelyn ASHFORD USA 10.97 NaN
1702 W 100M Women Montreal 1976 G Annegret RICHTER-IRRGANG FRG 11.08 NaN
1705 W 100M Women Mexico 1968 G Wyomia TYUS USA 11.0 NaN
1708 W 100M Women Rome 1960 G Wilma RUDOLPH USA 11.0 NaN
1711 W 100M Women Helsinki 1952 G Marjorie JACKSON AUS 11.5 NaN
1714 W 100M Women Berlin 1936 G Helen STEPHENS USA 11.5 NaN
1717 W 100M Women Amsterdam 1928 G Elizabeth ROBINSON USA 12.2 NaN
1720 W 100M Women London 2012 G Shelly-Ann FRASER-PRYCE JAM 10.75 NaN
1723 W 100M Women Athens 2004 G Yuliya NESTSIARENKA BLR 10.93 NaN
1726 W 100M Women Atlanta 1996 G Gail DEVERS USA 10.94 NaN
1729 W 100M Women Moscow 1980 G Lyudmila KONDRATYEVA URS 11.06 NaN
1732 W 100M Women Munich 1972 G Renate STECHER GDR 11.07 NaN
1735 W 100M Women Tokyo 1964 G Wyomia TYUS USA 11.4 NaN
1738 W 100M Women Melbourne / Stockholm 1956 G Betty CUTHBERT AUS 11.5 NaN
1741 W 100M Women London 1948 G Fanny BLANKERS-KOEN NED 11.9 NaN
1744 W 100M Women Los Angeles 1932 G Stanislawa WALASIEWICZ POL 11.9 NaN
19 W 100M Women Seoul 1988 G Florence Griffith JOYNER USA 10.54 3.0
20 W 100M Women Tokyo 2020 G Elaine THOMPSON JAM 10.61 -0.6

Aqui percebemos algo interessante. O tempo de Joyner em 1988 foi melhor que o de Thompson agora em 2020. Porém, como Joyner teve um grande vento de 3,0 m/s a seu favor, não foi considerado como recorde olímpico. O recorde olímpico anterior era da própria Joyner com o tempo de 10,62 s feito nas quartas de final da Olimpíada de 1988 com um vento favorável de 1,0 m/s e, portanto, dentro da margem aceita para registro de recordes.

Aliás, uma breve tangente sobre vento. O recorde mundial até hoje é novamente de Florence Joyner, com o tempo de 10,49 s feito nas seletivas para as Olimpíadas de 1988. O vento registrado oficialmente durante a prova foi de 0 m/s. No entanto, o tempo de Joyner foi tão chamativo que foram olhar com mais cuidado os registros do evento como um todo naquele dia. Em várias modalidades foram registrados ventos na casa de 5,0 m/s, de forma que se acredita que houve uma falha no anemômetro (medidor de velocidade de um fluido) utilizado na prova. De qualquer forma, o recorde foi mantido e, mesmo que fosse retirado, o segundo melhor tempo ainda é de Joyner, com 10,61 s sem assistência de vento. Esse tempo agora foi igualado por Thompson.

Novamente, aqui temos algo a se pensar. O objetivo é mostrar a evolução do tempo das medalhistas de ouro. Sob esse olhar, o tempo de Joyner é melhor, mas houve auxílio de vento. Cabe a quem está contando a história resolver como lidar com essa situação. Se vai deixar isso evidente de alguma forma, ou vai substituir pelo melhor tempo sem auxílio. Enfim, escolhas a serem feitas e que vamos lidar com elas adiante.

Arrumando o dataframe

Agora que temos confiança na consistência de nossos dados e ganhamos mais conhecimento sobre os mesmos, vamos arrumar a casa. Podemos retirar colunas redundantes:

df_women_100m = df_women_100m.drop(columns=['Gender', 'Event', 'Medal'])
df_women_100m
  Location Year Name Nationality Time Wind
1687 Rio 2016 Elaine THOMPSON JAM 10.71 NaN
1690 Beijing 2008 Shelly-Ann FRASER-PRYCE JAM 10.78 NaN
1696 Barcelona 1992 Gail DEVERS USA 10.82 NaN
1699 Los Angeles 1984 Evelyn ASHFORD USA 10.97 NaN
1702 Montreal 1976 Annegret RICHTER-IRRGANG FRG 11.08 NaN
1705 Mexico 1968 Wyomia TYUS USA 11.0 NaN
1708 Rome 1960 Wilma RUDOLPH USA 11.0 NaN
1711 Helsinki 1952 Marjorie JACKSON AUS 11.5 NaN
1714 Berlin 1936 Helen STEPHENS USA 11.5 NaN
1717 Amsterdam 1928 Elizabeth ROBINSON USA 12.2 NaN
1720 London 2012 Shelly-Ann FRASER-PRYCE JAM 10.75 NaN
1723 Athens 2004 Yuliya NESTSIARENKA BLR 10.93 NaN
1726 Atlanta 1996 Gail DEVERS USA 10.94 NaN
1729 Moscow 1980 Lyudmila KONDRATYEVA URS 11.06 NaN
1732 Munich 1972 Renate STECHER GDR 11.07 NaN
1735 Tokyo 1964 Wyomia TYUS USA 11.4 NaN
1738 Melbourne / Stockholm 1956 Betty CUTHBERT AUS 11.5 NaN
1741 London 1948 Fanny BLANKERS-KOEN NED 11.9 NaN
1744 Los Angeles 1932 Stanislawa WALASIEWICZ POL 11.9 NaN
19 Seoul 1988 Florence Griffith JOYNER USA 10.54 3.0
20 Tokyo 2020 Elaine THOMPSON JAM 10.61 -0.6

E, também, arrumar o índice dos registros, que ainda se referem a números do dataframe completo original, e aproveitar para já deixar o dataframe ordenado por ano para facilitar a compreensão:

df_women_100m = df_women_100m.sort_values(by='Year').reset_index(drop=True)
df_women_100m
  Location Year Name Nationality Time Wind
0 Amsterdam 1928 Elizabeth ROBINSON USA 12.2 NaN
1 Los Angeles 1932 Stanislawa WALASIEWICZ POL 11.9 NaN
2 Berlin 1936 Helen STEPHENS USA 11.5 NaN
3 London 1948 Fanny BLANKERS-KOEN NED 11.9 NaN
4 Helsinki 1952 Marjorie JACKSON AUS 11.5 NaN
5 Melbourne / Stockholm 1956 Betty CUTHBERT AUS 11.5 NaN
6 Rome 1960 Wilma RUDOLPH USA 11.0 NaN
7 Tokyo 1964 Wyomia TYUS USA 11.4 NaN
8 Mexico 1968 Wyomia TYUS USA 11.0 NaN
9 Munich 1972 Renate STECHER GDR 11.07 NaN
10 Montreal 1976 Annegret RICHTER-IRRGANG FRG 11.08 NaN
11 Moscow 1980 Lyudmila KONDRATYEVA URS 11.06 NaN
12 Los Angeles 1984 Evelyn ASHFORD USA 10.97 NaN
13 Seoul 1988 Florence Griffith JOYNER USA 10.54 3.0
14 Barcelona 1992 Gail DEVERS USA 10.82 NaN
15 Atlanta 1996 Gail DEVERS USA 10.94 NaN
16 Athens 2004 Yuliya NESTSIARENKA BLR 10.93 NaN
17 Beijing 2008 Shelly-Ann FRASER-PRYCE JAM 10.78 NaN
18 London 2012 Shelly-Ann FRASER-PRYCE JAM 10.75 NaN
19 Rio 2016 Elaine THOMPSON JAM 10.71 NaN
20 Tokyo 2020 Elaine THOMPSON JAM 10.61 -0.6

Da nossa análise anterior, já vimos que a coluna Time é vista pelo Pandas como sendo do tipo object, o que está errado. Precisamos transformá-la em um tipo numérico para poder continuar com nossas análises. Mas, antes disso, preste um pouco mais de atenção nos dados de tempo. Percebe alguma diferença na forma de apresentação ao longo do tempo?

Os registros até 1968 apresentam apenas uma casa decimal. Uma história detalhada do uso de medidores automáticos de tempo no atletismo pode ser lida aqui mas, resumidamente, tempos com precisão na casa dos centésimos realmente só começaram a ser utilizados olimpicamente em 1972. No entanto, desde as Olimpíadas de 1928 já se utilizavam registradores automáticos, juntamente com registros manuais. Nas Olimpíadas da década de 60 os resultados oficiais foram obtidos com registradores automáticos, mas publicados oficialmente arredondados para a primeira casa decimal. Apenas em 1972 passaram a publicar oficialmente os valores até os centésimos. Mais uma informação interessante que pode ser obtida após olhar a forma como os dados foram apresentados.

O Pandas possui dois métodos muito úteis para lidar com registros de tempo, o to_datetime e o to_timedelta que certamente seriam necessários para lidar com os tempos das provas mais longas, onde o formato de relógio Hora:minuto:segundo.milisegundo é utilizado. No entanto, no nosso caso, os valores são similares o suficiente ao formato de float, de forma que podemos arriscar uma conversão direta para esse tipo, pois certamente dará menos trabalho:

df_women_100m['Time'].astype(float)
0     12.20
1     11.90
2     11.50
3     11.90
4     11.50
5     11.50
6     11.00
7     11.40
8     11.00
9     11.07
10    11.08
11    11.06
12    10.97
13    10.54
14    10.82
15    10.94
16    10.93
17    10.78
18    10.75
19    10.71
20    10.61
Name: Time, dtype: float64

Ótimo, o Pandas conseguiu converter sem problemas todos os registros para float. Mas perceba que perdemos a informação de que os dados anteriores a 1972 são de apenas uma casa decimal, pois a conversão coloca todos com duas casas. Por isso é importante uma análise prévia antes de fazer grandes manipulações, pois muitas vezes a análise prévia fornece muitos insights que levam a pesquisas interessantes.

Vamos gravar os dados da coluna em float:

df_women_100m['Time'] = df_women_100m['Time'].astype(float)

E, para facilitar o entendimento, vamos deixar clara a unidade desse tempo, segundos. Isso vai ajudar futuramente também nos gráficos.

df_women_100m = df_women_100m.rename(columns={'Time': 'Time / s'})

Vamos, então, avaliar info e describe do nosso dataframe:

df_women_100m.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 21 entries, 0 to 20
Data columns (total 6 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   Location     21 non-null     object 
 1   Year         21 non-null     int64  
 2   Name         21 non-null     object 
 3   Nationality  21 non-null     object 
 4   Time / s     21 non-null     float64
 5   Wind         2 non-null      float64
dtypes: float64(2), int64(1), object(3)
memory usage: 1.1+ KB
df_women_100m.describe()
  Year Time / s Wind
count 21.000000 21.000000 2.000000
mean 1975.809524 11.150476 1.200000
std 27.927798 0.453315 2.545584
min 1928.000000 10.540000 -0.600000
25% 1956.000000 10.820000 0.300000
50% 1976.000000 11.000000 1.200000
75% 1996.000000 11.500000 2.100000
max 2020.000000 12.200000 3.000000
df_women_100m.describe(include='object')
  Location Name Nationality
count 21 21 21
unique 18 17 9
top Los Angeles Gail DEVERS USA
freq 2 2 9

Veja que agora temos Year e Time como colunas numéricas, o que precisamos para nossa apresentação de resultados. No mais, nenhuma atleta ganhou mais de duas vezes o ouro nessa modalidade e o país com mais medalhas de ouro são os Estados Unidos.

Apresentando os resultados. Imagens!

Finalmente, vamos para os gráficos. O Pandas possui uma boa integração com o Matplotlib, sobre o qual já fiz alguns artigos aqui no site. Basta chamar o método plot e passar quais as colunas dos eixos x e y:

df_women_100m.plot(x='Year', y='Time / s')
<AxesSubplot:xlabel='Year'>

A melhor forma de apresentação seria um gráfico de pontos, já que não muito sentido essas ligações entre os períodos:

df_women_100m.plot.scatter(x='Year', y='Time / s')
<AxesSubplot:xlabel='Year', ylabel='Time / s'>

OK, não é um dos gráficos mais bonitos e muito pode ser melhorado. Já fiz um artigo sobre customização de gráficos com Matplotlib e poderíamos ir por esse lado. No entanto, talvez seja mais prático tentar utilizar estilos prontos:

import matplotlib.pyplot as plt
plt.style.use('ggplot')
df_women_100m.plot.scatter(x='Year', y='Time / s')
<AxesSubplot:xlabel='Year', ylabel='Time / s'>

Melhorou, realmente fica mais apresentável e talvez para um formato de mídia estático, por exemplo impresso, seja adequado com mais alguns ajustes. Mas para uma apresentação em mídia digital, seria interessante mais interatividade. Para isso, vamos substituir o motor gráfico do pandas para o Plotly, um pacote excelente para construção de gráficos interativos:

pd.options.plotting.backend = "plotly"
df_women_100m.plot.scatter(x='Year', y='Time / s')
 

Já temos uma melhora visual significativa. Mas experimente passar o mouse (ou o dedo, se estiver em algum dispositivo com touch) em cada ponto. Aparece uma informação a respeito dos valores do eixo horizontal e do vertical. Além disso, há uma barra de ferramentas no topo do gráfico, que permite que você faça zoom de regiões e até salve a figura. Excelente para mídias digitais, certo? E ainda podemos melhorar. Vamos enriquecer a informação que aparece em cada ponto, mostrando o nome da atleta e sua nacionalidade:

df_women_100m.plot.scatter(x='Year', y='Time / s', 
                           hover_name='Name', 
                           hover_data=['Nationality'])

Ainda é possível passar mais informações. Podemos brincar com o tamanho de cada ponto e sua coloração. Podemos associar esses dois aspectos ao tempo de cada atleta:

df_women_100m.plot.scatter(x='Year', y='Time / s', 
                           hover_name='Name', 
                           hover_data=['Nationality'],
                           size='Time / s',
                           color='Time / s')

Perceba o que o plotly fez. Cada circunferência tem área proporcional ao tempo do atleta. Isso pode ser meio difícil de perceber já que o intervalo de tempos é pequeno, mas olhe o tempo de 1928 e o de 2020 e talvez você perceba. No vídeo explico como poderíamos deixar essa diferença mais evidente. Mas o que chama mais atenção é que o plotly criou automaticamente uma escala de cores que varia gradativamente com o tempo de cada atleta. O nível de informação visual que isso agrega é alto, pois o leitor identifica que se trata de algo gradual mesmo a distância e localiza facilmente os máximos e mínimos.

Mesmo assim, há espaço para melhoras. Usualmente essas escalas são feitas de forma ao valor mais alto ter a cor mais chamativa ou “quente”. No entanto, em nosso contexto, o tempo menor que é mais importante, então seria legal poder mudar esse esquema de cores. Para isso, vamos importar diretamente o plotly. Assim, temos acesso aos temas e esquemas de cores.

Dentre os esquemas de cores, gosto muito do chamado Viridis. Veja que no código abaixo foi utilizado sob o nome Viridis_r, o _r é para usar o esquema de cor reverso, de forma que o menor tempo fique com a cor mais atrativa.

import plotly
df_women_100m.plot.scatter(x='Year', y='Time / s', 
                           hover_name='Name', 
                           hover_data=['Nationality'],
                           size='Time / s',
                           color='Time / s',
                           color_continuous_scale=plotly.colors.sequential.Viridis_r,
                          )

Por fim, vamos dar um título para o gráfico. Sempre deixe o mais explícito possível o que se deseja mostrar com um bom título e com os eixos devidamente identificados.

df_women_100m.plot.scatter(x='Year', y='Time / s', 
                           hover_name='Name', 
                           hover_data=['Nationality'],
                           size='Time / s',
                           color='Time / s',
                           color_continuous_scale=plotly.colors.sequential.Viridis_r,
                           title="Summer Olympics - Women's 100 m - Gold metalists time progression")

Conclusão

Certamente ainda há muito a se explorar dessa base de dados e também outras melhorias poderiam ser feitas em nosso gráfico. Mas acredito que por esse artigo muito já foi feito e quero manter ele como uma simples introdução ao Pandas. Vou reforçar alguns pontos discutidos:

  • obtenção de dados;
  • conferência da qualidade, completude e consistência dos dados;
  • sempre faça a conferência, mesmo quando os dados são de fontes originais ou obtidos por você, pois erros podem acontecer;
  • busque compreender a história e o contexto por trás dos dados;
  • info e describe são muito úteis;
  • faça transformações apenas quando elas se mostrarem necessárias;
  • pense em qual história se pode contar;
  • esteja aberto a novas possibilidades de histórias;
  • a ausência de um dado ou um outlier também contam histórias;
  • nem só de x e y vive um gráfico, explore tamanhos, formas e cores.

Vou deixar algumas ideias aqui:

  • Certamente há alguma forma de indicar que o tempo de 1988 não é o recorde Olímpico. Quem sabe um círculo preto (qualquer cor na real, desde que fora da escala já usada), ou incolor com uma borda mais larga? Ou um outro símbolo que não círculo? E, claro, uma nota de rodapé explicando;
  • Igualmente, o tempo de Thanou poderia aparecer como sendo o tempo 2000, mas com alguma distinção dos demais;
  • A base de dados é grande, a explore:
    • Será que o tempo entre os medalhistas diminuiu com o tempo? As disputas hoje estão mais próximas que no passado?

    • Quais modalidades estão faltando nessa base de dados?

    • Como a distribuição das medalhas muda com o tempo? Será que algum país que fez parte da União Soviética conseguiu manter a relevância da época da URSS? Como é a progressão de atletas de países subdesenvolvidos ou em desenvolvimento nesses esportes?

    • Seria possível construir um modelo preditivo de tempo para as próximas Olimpíadas?

Cada uma dessas ideias pode ser aplicada a cada evento e algumas ao conjunto como um todo. Divirta-se e compartilhe suas explorações nos comentários.

Até a próxima.

Compartilhe:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima