Olympic Data Analyzer Web App : End to End Streamlit Project

Harsh Mishra
7 min readFeb 12, 2022

--

Hello people, I hope everyone is doing well. Today in this blog we will be making a Olympic Analyzer Web App using Streamlit.

Streamlit is an open source app framework in Python language. It helps us create web apps for data science and machine learning in a short time. It is compatible with major Python libraries such as scikit-learn, Keras, PyTorch, SymPy(latex), NumPy, pandas, Matplotlib etc.

Also all the code written in this blog is available in my github : https://github.com/HarshMishra2002/olympic_data_analysis

The project is deployed using heroku: https://oly-analysis.herokuapp.com/

Link for the dataset used- https://www.kaggle.com/heesoo37/120-years-of-olympic-history-athletes-and-results

So lets start. we will be using python programming language for all the data analysis and pycharm would be used as IDE.

We would be creating 3 files in our project

  1. preprocessor.py : The input we take is olympics data so we need to do some preprocessing and get an specific dataframe out of it. This process would be done in this file.
  2. app.py : This would be the main file where we would be creating the streamlit app.
  3. helper.py : We would be making some functions in this file that would be used in the main app.py file.

First lets create a stramlit app.

In app.py write the following code to create a streamlit app

import streamlit as st

Now we will import the dataset

df = pd.read_csv("athlete_events.csv")
region_df = pd.read_csv("noc_regions.csv")

Now we have got the data in variable named “df”. After that we will apply the preprocess function of the preprocessor.py file. So lets write the code of preprocessor.py file first

In preprocessor.py

import pandas as pd


def perprocess(df, region_df):
#filtering for summer olymppics
df = df[df['Season'] == "Summer"]
#merge with region_df
df = df.merge(region_df, on = "NOC", how = "left")
#droping duplicates
df.drop_duplicates(inplace=True)
#one hot encoding medals
df = pd.concat([df, pd.get_dummies(df["Medal"])], axis=1)
return df

This is the end of preprocessor.py. Now we will use this in the main file app.py

In app.py

df = preprocessor.perprocess(df, region_df)
# creating a sidebar
st.sidebar.title("Olympics Analysis")
user_menu = st.sidebar.radio(
'Select an option',
('Medal Tally', 'Overall Analysis', 'Country-wise Analysis', 'Athlete wise Analysis')
)

Now we will create a function which will fetch the medal tally in helper.py file. We will provide the dataset, year and the country as an input and the function will return the number of medals of each type accordingly.

In helper.py

def fetch_medal_tally(df, year, country):
medal_df = df.drop_duplicates(subset=['Team', 'NOC', 'Games', 'Year', 'City', 'Sport', 'Event', 'Medal'])
flag = 0
if year == 'Overall' and country == 'Overall':
temp_df = medal_df
if year == 'Overall' and country != 'Overall':
flag = 1
temp_df = medal_df[medal_df['region'] == country]
if year != 'Overall' and country == 'Overall':
temp_df = medal_df[medal_df['Year'] == int(year)]
if year != 'Overall' and country != 'Overall':
temp_df = medal_df[(medal_df['Year'] == year) & (medal_df['region'] == country)]

if flag == 1:
x = temp_df.groupby('Year').sum()[['Gold', 'Silver', 'Bronze']].sort_values('Year').reset_index()
else:
x = temp_df.groupby('region').sum()[['Gold', 'Silver', 'Bronze']].sort_values('Gold', ascending=False).reset_index()

x['total'] = x['Gold'] + x['Silver'] + x['Bronze']

x['Gold'] = x['Gold'].astype('int')
x['Silver'] = x['Silver'].astype('int')
x['Bronze'] = x['Bronze'].astype('int')
x['total'] = x['total'].astype('int')

return x
# the below function is to simply get the list of countries participated in Olympics and years in which it was conducted. def country_year_list(df):
years = df['Year'].unique().tolist()
years.sort()
years.insert(0, 'Overall')

country = np.unique(df['region'].dropna().values).tolist()
country.sort()
country.insert(0, 'Overall')

return years, country

Now we will use this function in app.py file. If the user has chosen medal tally in sidebar than he has to select the year and country from dropdown and the above function will give the medal tally.

In app.py

if user_menu == 'Medal Tally':
st.sidebar.header("Medal Tally")

years, country = helper.country_year_list(df)

selected_years = st.sidebar.selectbox("Select Years", years)
selected_country = st.sidebar.selectbox("Select Country", country)

medal_tally_df = helper.fetch_medal_tally(df, selected_years, selected_country)
if selected_years == 'Overall' and selected_country == 'Overall':
st.title("Overall Tally")
if selected_years != 'Overall' and selected_country == "Overall":
st.title("Medal tally in " + str(selected_years) + " Olympics")
if selected_years == 'Overall' and selected_country != "Overall":
st.title("Medal tally of " + str(selected_country))
if selected_years != 'Overall' and selected_country != "Overall":
st.title("Medal tally of " + str(selected_country) + " in " + str(selected_years) + " Olympics")

st.table(medal_tally_df)

Now in helper.py file we will add two more functions, one to get the data (like number of countries and athletes participating, etc) over time and other to get most successful atheltes in a soprt

In helper.py

def data_over_time(df, col):
nations_over_time = df.drop_duplicates(['Year', col])['Year'].value_counts().reset_index().sort_values('index')
nations_over_time.rename(columns={'index': 'Edition', 'Year': col}, inplace=True)
return nations_over_time


def most_successful(df, sport):
temp_df = df.dropna(subset=['Medal'])

if sport != 'Overall':
temp_df = temp_df[temp_df['Sport'] == sport]

x = temp_df['Name'].value_counts().reset_index().head(15).merge(df, left_on='index', right_on='Name', how='left')[
['index', 'Name_x', 'Sport', 'region']].drop_duplicates('index')

x.rename(columns={'index': 'Name', 'Name_x': 'Medals'}, inplace=True)
return x

Now in app.py we will get these functions to give the overall analysis

In app.py

if user_menu == 'Overall Analysis':
editions = df['Year'].unique().shape[0] - 1
cities = df['City'].unique().shape[0]
sports = df['Sport'].unique().shape[0]
events = df['Event'].unique().shape[0]
athletes = df['Name'].unique().shape[0]
nations = df['region'].unique().shape[0]

st.title("Top Statistics")

col1, col2, col3 = st.columns(3)
with col1:
st.header("Editions")
st.title(editions)

with col2:
st.header("Hosts")
st.title(cities)

with col3:
st.header("Sports")
st.title(sports)

col1, col2, col3 = st.columns(3)
with col1:
st.header("Events")
st.title(events)

with col2:
st.header("Nations")
st.title(nations)

with col3:
st.header("Athletes")
st.title(athletes)

nations_over_time = helper.data_over_time(df, 'region')
fig = px.line(nations_over_time, x="Edition", y="region")
st.title("Participating Nations over the Year")
st.plotly_chart(fig)

events_over_time = helper.data_over_time(df, 'Event')
fig = px.line(events_over_time, x="Edition", y="Event")
st.title("Events over the Year")
st.plotly_chart(fig)

athlete_over_time = helper.data_over_time(df, 'Name')
fig = px.line(athlete_over_time, x="Edition", y="Name")
st.title("Athletes over the Year")
st.plotly_chart(fig)

st.title("No of Events over Time (Every Sport)")
fig, ax = plt.subplots(figsize = (20, 20))
x = df.drop_duplicates(['Year', 'Sport', 'Event'])
ax = sns.heatmap(x.pivot_table(index='Sport', columns='Year', values='Event', aggfunc='count').fillna(0).astype('int'), annot=True)
st.pyplot(fig)

st.title("Most successful Athletes")
sport_list = df['Sport'].unique().tolist()
sport_list.sort()
sport_list.insert(0, 'Overall')

selected_sport = st.selectbox('Select a Sport', sport_list)
x = helper.most_successful(df, selected_sport)
st.table(x)

Now in helper.py file we will create few functions that we will use in app.py to create athlete wise analysis and country wise analysis.

In helper.py

def yearwise_mdeal_tally(df, country):
temp_df = df.dropna(subset=['Medal'])
temp_df.drop_duplicates(subset=['Team', 'NOC', 'Games', 'Year', 'City', 'Sport', 'Event', 'Medal'], inplace=True)
new_df = temp_df[temp_df['region'] == country]
final_df = new_df.groupby('Year').count()['Medal'].reset_index()
return final_df


def country_event_heatmap(df, country):
temp_df = df.dropna(subset=['Medal'])
temp_df.drop_duplicates(subset=['Team', 'NOC', 'Games', 'Year', 'City', 'Sport', 'Event', 'Medal'], inplace=True)
new_df = temp_df[temp_df['region'] == country]
pt = new_df.pivot_table(index = 'Sport', columns = 'Year', values = 'Medal', aggfunc = 'count').fillna(0)
return pt


def most_successful_countrywise(df, country):
temp_df = df.dropna(subset=['Medal'])

temp_df = temp_df[temp_df['region'] == country]

x = temp_df['Name'].value_counts().reset_index().head(10).merge(df, left_on='index', right_on='Name', how='left')[
['index', 'Name_x', 'Sport', 'region']].drop_duplicates('index')

x.rename(columns={'index': 'Name', 'Name_x': 'Medals'}, inplace=True)
return x


def weight_v_height(df,sport):
athlete_df = df.drop_duplicates(subset=['Name', 'region'])
athlete_df['Medal'].fillna('No Medal', inplace=True)
if sport != 'Overall':
temp_df = athlete_df[athlete_df['Sport'] == sport]
return temp_df
else:
return athlete_df


def men_vs_women(df):
athlete_df = df.drop_duplicates(subset=['Name', 'region'])

men = athlete_df[athlete_df['Sex'] == 'M'].groupby('Year').count()['Name'].reset_index()
women = athlete_df[athlete_df['Sex'] == 'F'].groupby('Year').count()['Name'].reset_index()

final = men.merge(women, on='Year', how='left')
final.rename(columns={'Name_x': 'Male', 'Name_y': 'Female'}, inplace=True)

final.fillna(0, inplace=True)

return final

Almost all the above functions are easy to understand and are self explanatory so we will move on app.py file to import these functions are complete the web app.

In app.py

if user_menu == 'Country-wise Analysis':

st.sidebar.title('Country-wise Analysis')

country_list = df['region'].dropna().unique().tolist()
country_list.sort()

selected_country = st.sidebar.selectbox('Select Country', country_list)

country_df = helper.yearwise_mdeal_tally(df, selected_country)
fig = px.line(country_df, x="Year", y="Medal")
st.title(selected_country + " Medal Tally over the Year")
st.plotly_chart(fig)

st.title(selected_country + " excels in the following sports")
pt = helper.country_event_heatmap(df, selected_country)
fig, ax = plt.subplots(figsize=(20, 20))
ax = sns.heatmap(pt, annot=True)
st.pyplot(fig)

st.title("Top 10 athletes of " + selected_country)
top10_df = helper.most_successful_countrywise(df, selected_country)
st.table(top10_df)

if user_menu == 'Athlete wise Analysis':
athlete_df = df.drop_duplicates(subset=['Name', 'region'])

x1 = athlete_df['Age'].dropna()
x2 = athlete_df[athlete_df['Medal'] == 'Gold']['Age'].dropna()
x3 = athlete_df[athlete_df['Medal'] == 'Silver']['Age'].dropna()
x4 = athlete_df[athlete_df['Medal'] == 'Bronze']['Age'].dropna()

fig = ff.create_distplot([x1, x2, x3, x4], ['Overall Age', 'Gold Medalist', 'Silver Medalist', 'Bronze Medalist'],show_hist=False, show_rug=False)
fig.update_layout(autosize=False,width=1000,height=600)
st.title("Distribution of Age")
st.plotly_chart(fig)

x = []
name = []
famous_sports = ['Basketball', 'Judo', 'Football', 'Tug-Of-War', 'Athletics',
'Swimming', 'Badminton', 'Sailing', 'Gymnastics',
'Art Competitions', 'Handball', 'Weightlifting', 'Wrestling',
'Water Polo', 'Hockey', 'Rowing', 'Fencing',
'Shooting', 'Boxing', 'Taekwondo', 'Cycling', 'Diving', 'Canoeing',
'Tennis', 'Golf', 'Softball', 'Archery',
'Volleyball', 'Synchronized Swimming', 'Table Tennis', 'Baseball',
'Rhythmic Gymnastics', 'Rugby Sevens',
'Beach Volleyball', 'Triathlon', 'Rugby', 'Polo', 'Ice Hockey']
for sport in famous_sports:
temp_df = athlete_df[athlete_df['Sport'] == sport]
x.append(temp_df[temp_df['Medal'] == 'Gold']['Age'].dropna())
name.append(sport)

fig = ff.create_distplot(x, name, show_hist=False, show_rug=False)
fig.update_layout(autosize=False, width=1000, height=600)
st.title("Distribution of Age wrt Sports(Gold Medalist)")
st.plotly_chart(fig)

sport_list = df['Sport'].unique().tolist()
sport_list.sort()
sport_list.insert(0, 'Overall')

st.title('Height Vs Weight')
selected_sport = st.selectbox('Select a Sport', sport_list)
temp_df = helper.weight_v_height(df,selected_sport)
fig,ax = plt.subplots()
ax = sns.scatterplot(temp_df['Weight'],temp_df['Height'],hue=temp_df['Medal'],style=temp_df['Sex'],s=60)
st.pyplot(fig)

st.title("Men Vs Women Participation Over the Years")
final = helper.men_vs_women(df)
fig = px.line(final, x="Year", y=["Male", "Female"])
fig.update_layout(autosize=False, width=1000, height=600)
st.plotly_chart(fig)

Here are some screenshots of our web app:

So, this is the end of our project here. We created a Olympic data analyzer web app end to end in this blog using stramlit. And that is it for today guys.

I hope you guys got to learn something new and enjoyed this blog. If you do like it than share it with your friends. Take care. keep learning.

You could also reach me through my Linkedin account- https://www.linkedin.com/in/harsh-mishra-4b79031b3/

--

--

Harsh Mishra
Harsh Mishra

Written by Harsh Mishra

Data science / ML enthusiast | Front-end developer | CS engineering student

Responses (1)