Data Introduction:

Data Source: https://www.mavenanalytics.io/data-playground?page=5&pageSize=5

In this report, I have explored a data set provided by Maven Analytics for their data visualization challenge. The dataset contains various information about the top 500 ski resorts in the world. After analyzing the data, I decided to take a location-based approach to my visualizations. The idea was to create visualizations that could help someone conducting their research of deciding where their next ski trip should be.

Quick Visualization Navigation:

  1. World Map of the Top 500 Ski Resorts
  2. Ski Resort Altitude Distribution
  3. Run Length Distribution by Continent
  4. Slope Difficulties by Continent
  5. Nightskiing by Continent

# Map - Resorts Around the World

# Create figure
plt.figure(figsize=(10, 6))

# Create a Basemap for map "background"
map = Basemap(projection="mill", llcrnrlat=-57, urcrnrlat=85, llcrnrlon=-170, urcrnrlon=180, resolution="c")

# Draw coastlines, countries, and states, include topography
map.drawcoastlines()
map.drawcountries()
map.drawstates()
map.etopo()

# Convert latitude and longitude to map coordinates
x, y = map(resorts_df["Longitude"].values, resorts_df["Latitude"].values)

# Plot the points on the map with smaller markers
map.scatter(x, y, marker="d", color="fuchsia", alpha=0.7, label="Ski Resorts", zorder=10, s=15)

# Add a title and legend
plt.title("Locations of Top 500 Ski Resorts", fontsize=20, pad=20)
plt.legend(loc="upper left")

# Show the map
plt.show()

This is a world map with pins identifying the location of the top 500 ski resorts. Right from the start, it is clear that Europe, especially the area around France is the hub of the best resorts in the world. North America is second, followed by Asia (Japan pulling most of the weight), and Oceania last.


# Scatter Plot - Resort Altitude Distribution

# Get all the unique Continent values
continents = resorts_df["Continent"].unique()
num_continents = len(continents)

# Create the figure
fig, ax = plt.subplots(figsize=(10, 8))

# Plot points by continent
for i, continent in enumerate(continents):
    continent_data = resorts_df[resorts_df["Continent"] == continent]
    lowest = continent_data["Lowest point"]
    highest = continent_data["Highest point"]
    ax.scatter(lowest, highest, label=continent, color=sns.color_palette("Set2", num_continents)[i], alpha=.7)

# Add Title, Labels, Legend
ax.set_title("Altitude Distribution of Ski Resorts", fontsize=20, pad=25)
ax.set_xlabel("Lowest Point (m)", fontsize=17, labelpad=25)
ax.set_ylabel("Highest Point (m)", fontsize=17,labelpad=25)
ax.legend(loc="lower right", title="Continent")
#ax.legend(loc="center left", bbox_to_anchor=(1.02, 0.5), title="Continent")

# Change step count for X & Y axis
ax.set_xticks(range(0, 3501, 250));
ax.set_yticks(range(0, 4001, 250));

plt.show()


This visualization illustrates the altitude range between the highest run point and the lowest point of the resort. The main thing that stands out when analyzing this graph is that for all continents except North America, the resorts are pretty concentrated in a similar altitude range. While the resorts in North America have a pretty big spread, with resorts sitting at very low altitudes and then having a good amount sitting at high altitudes. This speaks towards the different mountain ranges and terrain that are available in North America, which could be a deciding factor for someone planning their next trip.


# Box Plot - Longest Run By Continent

# Handle empty values
resorts_df["Longest run"] = resorts_df["Longest run"].replace(0, np.nan)

# Order for the plot
continent_order = ["Europe", "North America", "South America", "Asia", "Oceania"]

# Create figure
plt.figure(figsize=(10, 8))
sns.boxplot(data=resorts_df, x="Longest run", y="Continent", hue="Continent", order=continent_order, palette="PuBu")

# Set title and labels
plt.title('Distribution of Longest Run Lengths by Continent', fontsize=20, pad=25)
plt.xlabel("Longest Run Length (km)", fontsize=17,labelpad=25)
plt.gca().set_ylabel('')
plt.tick_params(axis='both', which='major', labelsize=13)

# Change the step count for x axis
plt.xticks(np.arange(start=0, stop=17, step=1));

plt.show()


This visualization illustrates the varying lengths of ski runs among top resorts across different continents, aiding individuals in selecting destinations that align with their preferences for run length. Europe and South America emerge with the widest range of options, indicating diverse terrain offerings in these continents. In contrast, Asia and Oceania consistently feature shorter runs. While North American resorts generally cover less ground compared to their European and South American counterparts, outliers within North American resorts offer longer run options for those seeking them.


# Stacked Bar Chart - Slope Difficulty

# Calculate total slopes count for each unique continent
continent_total_slopes = resorts_df.groupby("Continent")[["Beginner slopes", "Intermediate slopes", "Difficult slopes"]].sum()

# Seperate into two dataframes in ascending order
big_df = continent_total_slopes.loc[["Europe", "North America"]]
big_df["Total"] = big_df.sum(axis=1)
big_df = big_df.sort_values(by="Total", ascending=True)
big_df.drop(columns="Total", inplace=True)

small_df = continent_total_slopes.loc[["Oceania", "Asia", "South America"]]
small_df["Total"] = small_df.sum(axis=1)
small_df = small_df.sort_values(by="Total", ascending=True)
small_df.drop(columns="Total", inplace=True)

# Create a figure with two subplots
fig, axs = plt.subplots(2, 1, figsize=(14, 9), gridspec_kw={"hspace": 0.2})

# Set up the colors using the PuBu color palette
colors = sns.color_palette("PuBu")

# Plot First Plot
left = None
for i, difficulty in enumerate(["Difficult slopes", "Intermediate slopes", "Beginner slopes"]):
    axs[0].barh(big_df.index, big_df[difficulty], left=left, color=colors[i], label=difficulty)
    if left is None:
        left = big_df[difficulty]
    else:
        left += big_df[difficulty]

# Plot Second Plot
left = None
for i, difficulty in enumerate(["Difficult slopes", "Intermediate slopes", "Beginner slopes"]):
    axs[1].barh(small_df.index, small_df[difficulty], left=left, color=colors[i], label=difficulty)
    if left is None:
        left = small_df[difficulty]
    else:
        left += small_df[difficulty]

# Set main title for the entire visualization
fig.suptitle("Slope Difficulty Distribution by Continent", fontsize=17, y=.95, va="center")

# Set common x and y axis labels
fig.text(0.5, .03, "Slope Count", ha="center", fontsize=13)
fig.text(.02, 0.5, "Continent", va="center", rotation="vertical", fontsize=13)

# Set x axis step count
axs[0].set_xticks(range(0, 32000, 1500))
axs[1].set_xticks(range(0, 901, 50))

# Get artists and labels for legend from both subplots
handles, labels = axs[0].get_legend_handles_labels()

# Add legend to the last subplot
axs[1].legend(handles, labels, title="Slope Difficulty", loc="center right", fontsize=10, bbox_to_anchor=(.98, .2))

plt.show()


This visualization shows the distribution of slope difficulty totals across continents. For those that are starting out to enjoy the sport, the ability to choose a resort that has terrain you can ski is one of the most important factors. Across all continents the distribution of beginner, intermediate, and difficult slopes is pretty consistent. Intermediate slopes make up the majority of the total, followed by difficult, and then beginner. However, out of the top resorts, Asia definitely is the best continent for all difficulty levels as the distribution is almost an even split.


# Pie Chart - Night Skiing By Continent

# Get all the unique continent values
continents = resorts_df['Continent'].unique()

# Create figure with 2 rows and 3 columns for subplots
num_rows = 2
num_cols = 3
fig, axs = plt.subplots(num_rows, num_cols, figsize=(8, 5))

# Set the color palette
sns.set_palette("PuBu")

# Create a pie chart for each continent
axs = axs.flatten()
for i, continent in enumerate(continents):
    
    continent_data = resorts_df[resorts_df["Continent"] == continent]
    nightskiing_counts = continent_data["Nightskiing"].value_counts()

    axs[i].pie(nightskiing_counts,autopct="%1.1f%%", startangle=90, colors=sns.color_palette())
    axs[i].set_title(continent)
    axs[i].axis("equal"); 

# Delete 6th plot
fig.delaxes(axs[5]) 

# Create Legend, Title, and Handle spacing
legend_elements = [plt.Line2D([0], [0], marker='o', color='w', label='Allowed', markersize=8, markerfacecolor=sns.color_palette()[0]),
                   plt.Line2D([0], [0], marker='o', color='w', label='Not Allowed', markersize=8, markerfacecolor=sns.color_palette()[1])]
fig.legend(handles=legend_elements, loc='lower right', fontsize='large', title='Nightskiing', title_fontsize='large', bbox_to_anchor=(.95, 0.1))

plt.suptitle("Distribution of Nightskiing by Continent", fontsize=15, y=1)
plt.tight_layout(h_pad=2)

plt.show()


This visualization illustrates the percentage distribution of resorts across each continent that allow people to ride at night. Nightskiing is a completely different experience and could be a deal breaker for some when choosing a resort. It looks like for all continents except South America, night skiing is a hit or miss with percentages favoring night skiing but no significantly. However, in South America the top resorts greatly lean towards allowing nightskiing. It is important to note that when looking at the first visualization there are only 4 resorts located in South America, so this distribution might not hold for the rest of the resorts in the region.