<jupyter_start><jupyter_text>Analyzing my Peloton workout data with Seaborn  Importing Packages & Loading Data<jupyter_code>import numpy as np
import pandas as pd
import seaborn as sns
sns.set_style('whitegrid')
import matplotlib.pyplot as plt
from datetime import datetime
import warnings 
warnings.filterwarnings("ignore")
df = pd.read_csv('https://raw.githubusercontent.com/sejaldua/pelo-wolfpack/master/workout_data.csv', parse_dates=['timestamp'])
df<jupyter_output><empty_output><jupyter_text>Data Cleaning & Wrangling<jupyter_code># convert duration from seconds to minutes
df['duration'] = df['duration'].apply(lambda x: x // 60)
# creating new datetime-based features
df['timestamp'] = df['timestamp'].apply(lambda x: pd.to_datetime(x).tz_convert('US/Eastern'))
df['year'] = df.timestamp.dt.year
df['month'] = df.timestamp.dt.month
df['hour'] = df.timestamp.dt.hour
df['weekday'] = df.timestamp.dt.day_name()
df['week_num'] = df['timestamp'].apply(lambda x: x.isocalendar()[1] % 52)
df[['timestamp'] + list(df.columns[-5:].values)].head(n=1)
# get weekday_num in order of MTWTFSS because we want to sort the rows of the heatmap in order
weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
mapping = {k: v for k, v in zip(weekday_order, [i+1 for i in range(7)])}
df['weekday_num'] = df['weekday'].apply(lambda x: mapping[x])
# let's also save a variable of month abbreviations for tick labeling purposes later
month_order = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug']
# filter out 'Just Run' because only interested in Peloton on-demand classes
df = df[~df.title.str.contains('Just Run')]
df = df[df['fitness_discipline'] == 'running']

# strip the first part of the titles, which indicates the duration (e.g. '45 min ...')
run_titles = list(df['title'])
run_titles = [x[7:] if int(x[:2]) else x for x in run_titles]

# inspect the unique types of runs in the dataset
print(set(run_titles))
# devise a mapping strategy to partition runs into categories
def get_run_type(x):
    types = ['Endurance', 'Intervals', 'HIIT & Hills', 'HIIT', 'Tempo', 'Progression']
    for run_type in types:
        if run_type in x:
            return run_type
        elif 'Breakthrough' in x:
            return 'Progression'
        elif any([keyword in x for keyword in ['Warm Up', 'Cool Down', 'Extra Mile', 'Recovery']]):
            return 'Warm Up / Cool Down'
        
    # there are many runs based on musical genre (Beyonce, <NAME>, <NAME>, EDM, ...)
    # serves as a catch-all if no specific training category applies
    return "Musical Genre"

df['run_type'] = df['title'].apply(lambda x: get_run_type(x))
# filter dataset to only look at 2021 data
data_2021 = df[df['year'] == 2021]
data_2021.tail(n=3)
sns.scatterplot(data=data_2021, x='calories', y='distance', hue='duration');
# remove those 3 outliers due to wearable tech glitches
print("Prev dataframe size:", len(data_2021))
temp_df = data_2021.copy()
temp_df['cal/dist'] = data_2021['calories'] / data_2021['distance']
temp_df.sort_values(by='cal/dist', ascending=False, inplace=True)
temp_df.head()
data_2021 = temp_df.iloc[4:,:]
print("New dataframe size:", len(data_2021))
data_2021.sort_values(by='timestamp', ascending=False, inplace=True)<jupyter_output>New dataframe size: 115<jupyter_text>Seaborn Visualizations  QUANTITATIVE DATA: Workout Metrics (distance, calories)<jupyter_code>fig = plt.figure(figsize=(10,6));
ax = sns.scatterplot(data=data_2021, x='calories', y='distance', hue='duration');
fig.savefig('dist_vs_calories.png')
fig = plt.figure(figsize=(10,6));
ax = sns.scatterplot(data=data_2021[data_2021['duration'] == 30], x='timestamp', y='distance', hue='instructor');
ax.set_xticklabels(month_order, rotation=90);
plt.xlabel('Month');
fig.savefig('30min_distance_over_time.png')
fig = plt.figure(figsize=(10,6));
sns.histplot(data=data_2021, x='distance', hue='instructor', multiple='fill', bins=7);
fig.savefig('hist_distance_fill.png')
fig = plt.figure(figsize=(10,6));
sns.histplot(data=data_2021, x='distance', hue='instructor', multiple='stack', bins=7);
fig.savefig('hist_distance_stack.png')<jupyter_output><empty_output><jupyter_text>CATEGORICAL DATA: analyzing class frequency (instructor, run type)<jupyter_code>fig, ax = plt.subplots(1,2,figsize=(8,8));
sns.countplot(data=data_2021, x='instructor', 
                   order=data_2021['instructor'].value_counts().index,
                   color='teal', ax=ax[0]);
sns.countplot(data=data_2021, x='run_type', 
                   order=data_2021['run_type'].value_counts().index,
                   color='purple', ax=ax[1]);
ax[0].set_xticklabels(ax[0].get_xticklabels(), rotation=90);
ax[1].set_xticklabels(ax[1].get_xticklabels(), rotation=90);
plt.tight_layout();
fig.savefig('class_frequency_by_cat_vars.png')
fig = plt.figure(figsize=(8,8));
sns.catplot(data=data_2021, x='run_type', y='instructor', size=7, kind='swarm',
                   order=list(data_2021['run_type'].value_counts().index));
plt.xticks(rotation=90);
plt.tight_layout();
fig.savefig('instructor_vs_run_type_catplot.png');
fig = plt.figure(figsize=(8,8));
sns.displot(data=data_2021, x='run_type', y='instructor');
plt.xticks(rotation=90);
plt.tight_layout();
fig.savefig('instructor_vs_run_type_displot.png');
fig = plt.figure(figsize=(10,6));
sns.boxplot(data=data_2021, x='instructor', y='calories',
                   order=data_2021['instructor'].value_counts().index);
plt.xticks(rotation=90);
plt.tight_layout();
fig.savefig('calories_vs_instructor_boxplot.png');
fig = plt.figure(figsize=(10,6));
ax = sns.boxplot(data=data_2021, x='run_type', y='calories',
                   order=data_2021['run_type'].value_counts().index);
plt.xticks(rotation=90);
plt.tight_layout();
fig.savefig('calories_vs_run_type_boxplot.png');
fig = plt.figure(figsize=(10,4));
df_pivoted = pd.pivot_table(data_2021[['run_type', 'instructor', 'calories']], 
                           values='calories', index=['instructor'], 
                           columns=['run_type'], aggfunc=np.mean)
df_pivoted = df_pivoted.reindex(list(data_2021['instructor'].value_counts().index))
df_pivoted = df_pivoted.reindex(list(data_2021['run_type'].value_counts().index), axis=1)
sns.heatmap(df_pivoted, cmap='YlGn', square=True, linewidths=3);
plt.ylabel("");
plt.xlabel("");
plt.tight_layout();
fig.savefig('instructor_run_type_cal_heatmap.png')

fig = plt.figure(figsize=(10,6));
ax = sns.swarmplot(data=data_2021, y='week_num', x='instructor', 
                  order=data_2021['instructor'].value_counts().index);
ax = ax.set_xticklabels(ax.get_xticklabels(), rotation=90);
plt.tight_layout();
fig.savefig('instructors_over_time_swarmplot.png')
fig = plt.figure(figsize=(10,6));
ax = sns.violinplot(data=data_2021, x='weekday', y='distance', order=weekday_order);
ax = ax.set_xticklabels(ax.get_xticklabels(), rotation=90);
plt.tight_layout();
fig.savefig('distance_vs_weekday_violinplot.png')
ax = sns.swarmplot(x=data_2021['timestamp']);
plt.xticks(rotation=90);
df_to_pivot = data_2021[['week_num', 'weekday_num', 'distance']]
pivoted_data = pd.pivot_table(df_to_pivot, values='distance', index=['weekday_num'], columns=['week_num'], aggfunc=np.mean)
pivoted_data = pivoted_data.reindex([i+1 for i in range(max(pivoted_data.columns))], axis=1)
pivoted_data
fig = plt.figure(figsize=(20,4));
space = [" ", " ", " "]
sns.heatmap(pivoted_data, linewidths=6, vmin=0, vmax=7, cmap='YlGn', cbar=True,
                 linecolor='white', square=True, yticklabels=weekday_order, 
            xticklabels=[*space, 'Jan', *space, 'Feb', *space, 'Mar', *space, 'Apr', 
                         *space, 'May', *space, 'Jun', *space, 'Jul']);
plt.ylabel("");
plt.xlabel("");
fig.savefig('github_style_distance_heatmap_calendar.png')
fig, ax = plt.subplots(1,2,figsize=(8,8));
sns.countplot(data=data_2021, x='weekday', order=weekday_order, color='teal', ax=ax[0]);
sns.countplot(data=data_2021, x='month', color='purple',
                   order=[i+1 for i in range(max(data_2021['month']))], ax=ax[1]);
ax[0].set_xticklabels(ax[0].get_xticklabels(), rotation=90);
ax[1].set_xticklabels(month_order, rotation=90);
plt.tight_layout();
fig.savefig('class_frequency_vs_time_increments.png')
fig = plt.figure(figsize=(10,4));
df_pivoted = pd.pivot_table(data_2021[['weekday_num', 'hour', 'distance']], 
                           values='distance', index=['weekday_num'], 
                           columns=['hour'], aggfunc=np.mean)
sns.heatmap(df_pivoted, cmap='YlGn', yticklabels=weekday_order, square=True, linewidths=3,
            xticklabels = [str(x % 12) + ":00" if x != 12 else "12:00" for x in df_pivoted.columns]);
plt.ylabel("");
fig.savefig('weekday_vs_time_vs_dist_heatmap.png')
fig = plt.figure(figsize=(10,4));
df_pivoted = pd.pivot_table(data_2021[['month', 'hour', 'distance']], 
                           values='distance', index=['month'], 
                           columns=['hour'], aggfunc=np.mean)
sns.heatmap(df_pivoted, cmap='YlGn', square=True, linewidths=3,
            yticklabels=month_order, 
            xticklabels = [str(x % 12) + ":00" if x != 12 else "12:00" for x in df_pivoted.columns]);
plt.ylabel("");
fig.savefig('month_vs_time_vs_dist_heatmap.png')
arr = list(df['hour'])
N = 12
bottom = 2

# create theta for 12 hours
theta = np.linspace(0.0, np.pi, N, endpoint=False)

# make the histogram that bined on 24 hour
_, tick = np.histogram(arr, bins = N)
radii = df['hour'].value_counts().sort_index().reindex([i+1 for i in range(5, 5+N)]).fillna(0)

# width of each bin on the plot
width = (np.pi) / N

# make a polar plot
fig = plt.figure(figsize = (12, 8))
ax = plt.subplot(111, polar=True)
bars = ax.bar(theta, radii, width=width, bottom=bottom)

# set the lable go clockwise and start from the left-hand side (West)
ax.set_theta_zero_location("W")
ax.set_theta_direction(-1) # clockwise

# we just want half of the polar chart
ax.set_thetamin(0)
ax.set_thetamax(180)

# setting xticks and xticklabels
ax.set_xticks(np.pi/180 * np.linspace(0, 180, N, endpoint=False))
ticks = [str(x % 12) + ":00" if x != 12 else "12:00" for x in radii.index]
ax.set_xticklabels(ticks);
ax.set_rgrids([4*i + 2 for i in range(5)], labels=[str(x-2) for x in [4*i + 2 for i in range(5)]])

fig.savefig('polar_chart_run_time_freq.png')
plt.show()<jupyter_output><empty_output><jupyter_text>Finished!<jupyter_code># commit to jovian
message = input("Enter a commit message: ")
jovian.commit(message=message, outputs=['dist_vs_calories.png',
                                        '30min_distance_over_time.png',
                                        'hist_distance_fill.png',
                                        'hist_distance_stack.png',
                                        'class_frequency_by_cat_vars.png',
                                        'instructor_vs_run_type_catplot.png',
                                        'instructor_vs_run_type_displot.png',
                                        'calories_vs_instructor_boxplot.png',
                                        'calories_vs_run_type_boxplot.png',
                                        'instructor_run_type_cal_heatmap.png',
                                        'instructors_over_time_swarmplot.png',
                                        'distance_vs_weekday_violinplot.png',
                                        'github_style_distance_heatmap_calendar.png',
                                        'class_frequency_vs_time_increments.png',
                                        'weekday_vs_time_vs_dist_heatmap.png',
                                        'month_vs_time_vs_dist_heatmap.png',
                                        'polar_chart_run_time_freq.png'
                                       ])<jupyter_output>Enter a commit message: apply tight layout
{'fraction_non_alphanumeric': 0.15676497929519143, 'fraction_numerical': 0.029831826248626722, 'mean_word_length': 4.0250530785562635, 'pattern_counts': {'":': 3, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 17, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '259417', 'license': 'MIT', 'n_tokens_mistral': 4346, 'n_tokens_neox': 4073, 'n_words': 770, 'path': 'playground.ipynb', 'repo_name': 'sejaldua/pelo-wolfpack', 'size': '11836'}