{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ATLAS Ranking Tool\n",
"\n",
"ArcheType Library for Alternative Seafood ([ATLAS][pisces-atlas]) is a resource for data about seafood archetypes with a focus on quantifying the impacts of conventional seafood production. This allows researchers and companies pursuing alternative seafood to focus on archetypes where new production methods will have the greatest positive impact.\n",
"\n",
"However, since there's no \"right\" answer to the question of how to weight the various available metrics relative to one another, we built this tool so that you can decide for yourself.\n",
"\n",
"There are nine different metrics by which archetypes can be ranked, divided into four general categories: Sustainability, Animal Welfare, Health, and US Market Size. Each of these categories is made up of a weighted average of several relevant metrics (except for Health, which is currently based on mercury content only). For more details on where these metrics come from, see the [PISCES/ATLAS user guide][user-guide]. You are free to change the weights for the four categories as a whole, or change the weights for metrics within a category. Please note that the weights for individual metrics matter only relative to those in the same category.\n",
"\n",
"For a given set of weights, this tool provides a visualization of how the different metrics contribute to the total score (for the top archetypes), as well as a ranked list of all the archetypes. It also displays a graphic of your chosen weights for both categories and metrics.\n",
"\n",
"#### The widgets can take a couple minutes to load — please be patient!\n",
"\n",
"[pisces-atlas]: https://www.gfi.org/pisces\n",
"[user-guide]: https://docs.google.com/document/d/1bBpRhsCNEKlnv9Ad0cKteNVQFYP2fZecsIBF-m59W98/edit?usp=sharing"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [],
"source": [
"# nbi:hide_in\n",
"\n",
"# Imports\n",
"\n",
"import os\n",
"import pandas as pd\n",
"import ipywidgets as widgets\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"import seaborn as sns\n",
"from IPython.core.display import display, HTML\n",
"from IPython.display import FileLink"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [],
"source": [
"# nbi:hide_in\n",
"\n",
"# Load data\n",
"\n",
"df = pd.read_csv('ATLAS-Scores Only.csv', index_col = 0)\n",
"df.drop(['Scientific Name (PISCES)', 'Average Score'], axis = 1, inplace = True)\n",
"df.columns = df.columns.str.replace('Score: *', '')\n",
"\n",
"# df_details = pd.read_csv('ATLAS-Details View.csv', index_col = 0)\n",
"# This is the full view of ATLAS.\n",
"# You don't need it to use the ranking tool, but it can be loaded in if you need it for any other analyses."
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [],
"source": [
"# nbi:hide_in\n",
"\n",
"# Define relationships between categories and metrics\n",
"categories = ['Sustainability', 'Animal Welfare', 'Health', 'US Market Size']\n",
"metrics = ['Overall Sustainability', 'GHG Emissions', \n",
" 'Max Number of Individuals (Log)', 'Edible Coefficient', \n",
" 'Mercury Concentration', \n",
" 'U.S. Retail Sales (Log)', 'U.S. Menu Prevalence', \n",
" 'Import Value (Log)', 'Import Volume (Log)']\n",
"metric_dict = {'Sustainability': ['Overall Sustainability', 'GHG Emissions'],\n",
" 'Animal Welfare': ['Edible Coefficient', 'Max Number of Individuals (Log)'],\n",
" 'Health': ['Mercury Concentration'], \n",
" 'US Market Size': ['U.S. Retail Sales (Log)', 'U.S. Menu Prevalence', \n",
" 'Import Value (Log)', 'Import Volume (Log)']}\n",
"\n",
"# Widget outputs\n",
"output = widgets.Output()\n",
"graph_output = widgets.Output()\n",
"pie_output = widgets.Output()\n",
"links_output = widgets.Output()\n",
"\n",
"# Make sliders\n",
"style = {'description_width' : '105px'}\n",
"sust_slider = widgets.FloatSlider(value = 0.25, min = 0, max = 1, step = 0.01, description = 'Sustainability', style = style, continuous_update = False)\n",
"welf_slider = widgets.FloatSlider(value = 0.25, min = 0, max = 1, step = 0.01, description = 'Animal Welfare', style = style, continuous_update = False)\n",
"health_slider = widgets.FloatSlider(value = 0.25, min = 0, max = 1, step = 0.01, description = 'Public Health', style = style, continuous_update = False)\n",
"market_slider = widgets.FloatSlider(value = 0.25, min = 0, max = 1, step = 0.01, description = 'US Market Size', style = style, continuous_update = False)\n",
"\n",
"overall_slider = widgets.FloatSlider(value = 0.5, min = 0, max = 1, step = 0.01, description = 'Overall', style = style, continuous_update = False)\n",
"ghg_slider = widgets.FloatSlider(value = 0.5, min = 0, max = 1, step = 0.01, description = 'GHG Emissions', style = style, continuous_update = False)\n",
"individuals_slider = widgets.FloatSlider(value = 0.5, min = 0, max = 1, step = 0.01, description = 'Max # Individuals', style = style, continuous_update = False)\n",
"edible_slider = widgets.FloatSlider(value = 0.5, min = 0, max = 1, step = 0.01, description = 'Edible Portion', style = style, continuous_update = False)\n",
"retail_slider = widgets.FloatSlider(value = 0.33, min = 0, max = 1, step = 0.01, description = 'Retail Sales', style = style, continuous_update = False)\n",
"menu_slider = widgets.FloatSlider(value = 0.33, min = 0, max = 1, step = 0.01, description = 'Menu Prevalence', style = style, continuous_update = False)\n",
"value_slider = widgets.FloatSlider(value = 0.33, min = 0, max = 1, step = 0.01, description = 'Import Value', style = style, continuous_update = False)\n",
"volume_slider = widgets.FloatSlider(value = 0, min = 0, max = 1, step = 0.01, description = 'Import Volume', style = style, continuous_update = False)\n",
"\n",
"category_sliders = [sust_slider, welf_slider, health_slider, market_slider]\n",
"metric_sliders = [overall_slider, ghg_slider, \n",
" individuals_slider, edible_slider, \n",
" retail_slider, menu_slider, value_slider, volume_slider]\n",
"category_slider_dict = {category: slider for category, slider in zip(categories, category_sliders)}\n",
"metric_slider_dict = {metric: slider for metric, slider in zip([m for m in metrics if m != 'Mercury Concentration'], metric_sliders)}\n",
"\n",
"def compute_scores(): \n",
" sum_of_cat_weights = sum([slider.value for slider in category_sliders])\n",
" \n",
" if sum_of_cat_weights == 0:\n",
" with graph_output:\n",
" display(\"Please don't set all the weights to zero. Fish are important.\")\n",
" return\n",
" \n",
" category_weights = []\n",
" weight_list = []\n",
" metric_list = []\n",
" \n",
" # Normalize weights within and between categories\n",
" for category in categories:\n",
" category_weight = category_slider_dict[category].value / sum_of_cat_weights\n",
" category_metrics = metric_dict[category]\n",
" if category == 'Health':\n",
" weights = [category_weight]\n",
" else:\n",
" raw_weights = [metric_slider_dict[metric].value for metric in metric_dict[category]]\n",
" sum_of_weights = sum(raw_weights)\n",
" if sum_of_weights == 0:\n",
" with graph_output:\n",
" display(\"If you're trying to set a category to zero, use the 'Categories' tab.\")\n",
" return\n",
" weights = [(raw_weight / sum_of_weights) * category_weight for raw_weight in raw_weights]\n",
" weight_list = weight_list + weights\n",
" metric_list = metric_list + category_metrics\n",
" category_weights.append(category_weight)\n",
" \n",
" # Calculate weighted scores for each metric, fill in missing values with means\n",
" score_breakdown = pd.DataFrame([pd.to_numeric(df[metric]).fillna(pd.to_numeric(df[metric]).mean()) * weight for weight, metric in zip(weight_list, metric_list)]).T\n",
"\n",
" # Calculate weighted scores only for those with data - this will be used for the bar graph\n",
" score_breakdown_wo_na = pd.DataFrame([pd.to_numeric(df[metric]) * weight for weight, metric in zip(weight_list, metric_list)]).T\n",
" \n",
" # Calculate total scores and sort dataframes by score\n",
" score_breakdown['Score'] = score_breakdown.sum(axis = 1) \n",
" score_breakdown_wo_na['Score'] = score_breakdown['Score'] \n",
" df['Score'] = score_breakdown['Score']\n",
" \n",
" score_breakdown.sort_values('Score', inplace = True, ascending = False)\n",
" score_breakdown_wo_na.sort_values('Score', inplace = True, ascending = False)\n",
" df.sort_values('Score', inplace = True, ascending = False) \n",
"\n",
" # Make color palettes\n",
" sust_colors = sns.cubehelix_palette(start = 1.8, rot = 0, dark = 0.3, light = 0.95, n_colors = 7)\n",
" welf_colors = sns.cubehelix_palette(start = 0.5, rot = 0, dark = 0.3, light = 0.95, n_colors = 7)\n",
" health_colors = sns.cubehelix_palette(start = 3, rot = 0, dark = 0.3, light = 0.95, n_colors = 7)\n",
" market_colors = sns.cubehelix_palette(start = 2.5, rot = 0, dark = 0.3, light = 0.95, n_colors = 7)\n",
" \n",
" # Display ranked list of archetypes. Check whether both import value and volume are weighted and warn user if so.\n",
" with output:\n",
" with pd.option_context('display.max_rows', None, 'display.max_columns', None):\n",
" greens = cmap = sns.light_palette(sust_colors[2], as_cmap=True)\n",
" pinks = cmap = sns.light_palette(welf_colors[2], as_cmap=True)\n",
" purples = cmap = sns.light_palette(health_colors[2], as_cmap=True)\n",
" blues = cmap = sns.light_palette(market_colors[2], as_cmap=True)\n",
" yellows = cmap = sns.light_palette('gold', as_cmap=True)\n",
" pretty_df = df.style.format('{:.3}', na_rep=\"-\").\\\n",
" background_gradient(subset = metric_dict['Sustainability'], cmap = greens).\\\n",
" background_gradient(subset = metric_dict['Animal Welfare'], cmap = pinks).\\\n",
" background_gradient(subset = metric_dict['Health'], cmap = purples).\\\n",
" background_gradient(subset = metric_dict['US Market Size'], cmap = blues).\\\n",
" background_gradient(subset = ['Score'], cmap = yellows).\\\n",
" highlight_null('white')\n",
" display(pretty_df)\n",
" with links_output:\n",
" df.to_csv('Archetype_Ranking.csv')\n",
" pd.DataFrame(weight_list, index = metric_list).T.to_csv('Weights.csv')\n",
" display(FileLink('Archetype_Ranking.csv'))\n",
" display(FileLink('Weights.csv'))\n",
" with graph_output:\n",
" if value_slider.value != 0 and volume_slider.value != 0:\n",
" display('Warning: Import value and import volume are redundant. You probably should set one or the other to zero.') \n",
"\n",
" # Define colors for bar graph\n",
" colors = sust_colors[2:4] + welf_colors[2:4] + health_colors[2:3] + market_colors[2:6]\n",
"\n",
" # Plot score breakdown as bar graph\n",
" n_archetypes = 20\n",
" with graph_output:\n",
" bottom = [0] * n_archetypes\n",
" ind = np.arange(n_archetypes)\n",
" score_breakdown = score_breakdown.head(n_archetypes)\n",
" score_breakdown_wo_na = score_breakdown_wo_na.head(n_archetypes)\n",
" plt.bar(ind, score_breakdown['Score'], color = 'lightgrey', label = 'No data — using mean score')\n",
" score_breakdown.drop('Score', axis = 1, inplace = True)\n",
" for column, color in zip(score_breakdown.columns, colors):\n",
" plt.bar(ind, score_breakdown_wo_na[column], bottom = bottom, label = column, color = color)\n",
" bottom = bottom + score_breakdown[column]\n",
" plt.xticks(ind, score_breakdown.index[:n_archetypes], rotation = 90)\n",
" ax = plt.gca()\n",
" ax.set_title('Score Breakdown for Top 20 Archetypes')\n",
" ax.set_ylabel('Score')\n",
" plt.legend(loc = 'upper left', bbox_to_anchor = (1,1))\n",
" plt.show()\n",
"\n",
" # Display weights as a donut chart\n",
" with pie_output:\n",
" display()\n",
" plt.pie(category_weights, colors = [sust_colors[0], welf_colors[0], health_colors[0], market_colors[0]], \n",
" wedgeprops = {'width' : 0.2}, \n",
" labels = [cat if weight > 0 else '' for cat, weight in zip(categories, category_weights)], normalize = False)\n",
" plt.pie(weight_list, colors = colors, radius = 0.75, wedgeprops = {'width' : 0.2}, \n",
" labels = [round(n, 3) if n > 0 else '' for n in weight_list], labeldistance = 0.9, normalize = False)\n",
" plt.gca().set_title('Your chosen weights (out of 1)')\n",
" plt.show()\n",
"\n",
"# This runs every time you move a slider\n",
"def handler(change):\n",
" output.clear_output(wait = True)\n",
" graph_output.clear_output(wait = True)\n",
" pie_output.clear_output(wait = True)\n",
" links_output.clear_output(wait = True)\n",
" compute_scores()\n",
"\n",
"for slider in category_sliders + metric_sliders:\n",
" slider.observe(handler, names = 'value')\n",
" \n",
"# Make the layout pretty\n",
"categories_title = widgets.HTML(\"Adjust the sliders to set the weights for each of these four categories. You can further customize how the scores for each category are calculated below.\")\n",
"metrics_title = widgets.HTML('
Within each column, adjust the sliders to customize the weights for individual metrics.')\n",
"\n",
"category_layout = widgets.VBox([categories_title, sust_slider, welf_slider, health_slider, market_slider])\n",
"\n",
"sust_layout = widgets.VBox([widgets.HTML('