{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# pip install ipywidgets\n", "# pip install plotly\n", "# pip install ipympl" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "import numpy as np\n", "import pandas as pd\n", "from typing import Any\n", "from typing import Dict\n", "from typing import List\n", "import warnings\n", "import math\n", "\n", "import ipywidgets as widgets\n", "from ipywidgets import interact, interactive, interact_manual, GridBox, Layout, VBox, HBox\n", "import matplotlib.pyplot as plt\n", "import plotly.graph_objs as go\n", "from plotly.subplots import make_subplots\n", "\n", "from data_encoder import DataEncoder\n", "\n", "# Silence xgboost warnings\n", "warnings.filterwarnings(\"ignore\")\n", "from xgboost import XGBRegressor\n", "from keras.models import load_model\n", "\n", "\n", "pd.set_option('display.max_columns', None)\n", "\n", "%matplotlib inline\n", "%matplotlib widget" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Dataset" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "LAND_USE_COLS = ['c3ann', 'c3nfx', 'c3per', 'c4ann', 'pastr', 'range', 'secdf', 'secdn', 'urban']\n", "DIFF_LAND_USE_COLS = [f\"{col}_diff\" for col in LAND_USE_COLS]\n", "PRESCRIBED_LAND_USE_COLS = [f\"{col}_prescribed\" for col in LAND_USE_COLS]\n", "OTHER_FEATURES_COLS = ['primf', 'primn', 'cell_area']\n", "ALL_LAND_USE_COLS = ['primf', 'primn'] + LAND_USE_COLS\n", "COLS_MAP = dict(zip(LAND_USE_COLS, DIFF_LAND_USE_COLS))\n", "CHART_COLS = ALL_LAND_USE_COLS + [\"nonland\"]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "CONTEXT_COLUMNS = ['c3ann', 'c3nfx', 'c3per', 'c4ann', 'pastr', 'primf', 'primn', 'range', 'secdf', 'secdn', 'urban', 'cell_area']\n", "ACTION_COLUMNS = ['c3ann_diff', 'c3nfx_diff', 'c3per_diff', 'c4ann_diff', 'pastr_diff', 'range_diff', 'secdf_diff', 'secdn_diff', 'urban_diff']\n", "OUTCOME_COLUMNS = ['ELUC', 'Change']\n", "CONTEXT_ACTION_COLUMNS = CONTEXT_COLUMNS + ACTION_COLUMNS" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "DATASET_CSV = '../data/gcb/processed/uk_eluc.csv'\n", "with open(DATASET_CSV) as df_file:\n", " data_source_df = pd.read_csv(df_file)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "data_source_df.tail()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Code" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fields = {'lat': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 53.93974, 'range': [50.125, 58.625], 'std_dev': 2.2288961, 'sum': 4630295, 'valued': 'CONTINUOUS'},\n", " 'lon': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -2.7644422, 'range': [-7.375, 1.625], 'std_dev': 1.9270877, 'sum': -237305.25, 'valued': 'CONTINUOUS'},\n", " 'ELUC': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -0.021404957, 'range': [-1.2820702, 2.3366203], 'std_dev': 0.18355964, 'sum': -1837.4443, 'valued': 'CONTINUOUS'},\n", " 'time': {'data_type': 'INT', 'has_nan': False, 'mean': 1936, 'range': [1851, 2021], 'std_dev': 49.362892, 'sum': 166190110, 'valued': 'CONTINUOUS'},\n", " 'c3ann': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.2667192, 'range': [0, 1], 'std_dev': 0.19391803, 'sum': 22895.709, 'valued': 'CONTINUOUS'},\n", " 'c3nfx': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.014878354, 'range': [0, 1], 'std_dev': 0.0128484, 'sum': 1277.1877, 'valued': 'CONTINUOUS'},\n", " 'c3per': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.00053631567, 'range': [0, 1], 'std_dev': 0.000610856, 'sum': 46.03841, 'valued': 'CONTINUOUS'},\n", " 'c4ann': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.0063492954, 'range': [0, 1], 'std_dev': 0.0056106453, 'sum': 545.0362, 'valued': 'CONTINUOUS'},\n", " 'i_lat': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 53.93974, 'range': [50.125, 58.625], 'std_dev': 2.2288961, 'sum': 4630295, 'valued': 'CONTINUOUS'},\n", " 'i_lon': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -2.7644422, 'range': [-7.375, 1.625], 'std_dev': 1.9270877, 'sum': -237305.25, 'valued': 'CONTINUOUS'},\n", " 'pastr': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.31008992, 'range': [0, 1], 'std_dev': 0.1939609, 'sum': 26618.738, 'valued': 'CONTINUOUS'},\n", " 'primf': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 3.1008868e-10, 'range': [0, 1], 'std_dev': 1.2718036e-09, 'sum': 2.6618633e-05, 'valued': 'CONTINUOUS'},\n", " 'primn': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 7.880206e-11, 'range': [0, 1], 'std_dev': 6.0690847e-10, 'sum': 6.7645265e-06, 'valued': 'CONTINUOUS'},\n", " 'range': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.058702312, 'range': [0, 1], 'std_dev': 0.12839052, 'sum': 5039.124, 'valued': 'CONTINUOUS'},\n", " 'secdf': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.18520375, 'range': [0, 1], 'std_dev': 0.19961607, 'sum': 15898.26, 'valued': 'CONTINUOUS'},\n", " 'secdn': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.06774911, 'range': [0, 1], 'std_dev': 0.1195767, 'sum': 5815.7197, 'valued': 'CONTINUOUS'},\n", " 'urban': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.030199211, 'range': [0, 1], 'std_dev': 0.06684742, 'sum': 2592.3606, 'valued': 'CONTINUOUS'},\n", " 'ELUC_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.00085764704, 'range': [-5, 5], 'std_dev': 0.091957845, 'sum': 73.62214, 'valued': 'CONTINUOUS'},\n", " 'cell_area': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 45453.707, 'range': [40233.22, 49543.36], 'std_dev': 2439.213, 'sum': 3901837300, 'valued': 'CONTINUOUS'},\n", " 'c3ann_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -0.0003815445, 'range': [-1, 1], 'std_dev': 0.0042161522, 'sum': -32.75254, 'valued': 'CONTINUOUS'},\n", " 'c3nfx_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -2.3976065e-05, 'range': [-1, 1], 'std_dev': 0.00024510472, 'sum': -2.0581534, 'valued': 'CONTINUOUS'},\n", " 'c3per_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -5.9571926e-07, 'range': [-1, 1], 'std_dev': 1.0220871e-05, 'sum': -0.05113773, 'valued': 'CONTINUOUS'},\n", " 'c4ann_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -1.0171406e-05, 'range': [-1, 1], 'std_dev': 0.00010547795, 'sum': -0.8731338, 'valued': 'CONTINUOUS'},\n", " 'pastr_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.0011081528, 'range': [-1, 1], 'std_dev': 0.0058669676, 'sum': 95.12605, 'valued': 'CONTINUOUS'},\n", " 'range_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.00036852885, 'range': [-1, 1], 'std_dev': 0.007347369, 'sum': 31.635254, 'valued': 'CONTINUOUS'},\n", " 'secdf_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -0.00081145874, 'range': [-1, 1], 'std_dev': 0.008251627, 'sum': -69.65724, 'valued': 'CONTINUOUS'},\n", " 'secdn_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': -0.0005189244, 'range': [-1, 1], 'std_dev': 0.0052026906, 'sum': -44.54551, 'valued': 'CONTINUOUS'},\n", " 'urban_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 0.00026998913, 'range': [-1, 1], 'std_dev': 0.0007861656, 'sum': 23.176407, 'valued': 'CONTINUOUS'},\n", " 'cell_area_diff': {'data_type': 'FLOAT', 'has_nan': False, 'mean': 45453.707, 'range': [40233.22, 49543.36], 'std_dev': 2439.213, 'sum': 3901837300, 'valued': 'CONTINUOUS'}}\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cao_mapping = {\n", " 'context': ['lat', 'lon', 'time', 'c3ann', 'c3nfx', 'c3per', 'c4ann', 'i_lat', 'i_lon', 'pastr', 'primf', 'primn', 'range', 'secdf', 'secdn', 'urban', 'cell_area'],\n", " 'actions': ['c3ann_diff', 'c3nfx_diff', 'c3per_diff', 'c4ann_diff', 'pastr_diff', 'range_diff', 'secdf_diff', 'secdn_diff', 'urban_diff'],\n", " 'outcomes': ['ELUC', 'Change']}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "encoder = DataEncoder(fields, cao_mapping)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "min_lat = data_source_df[\"i_lat\"].min()\n", "max_lat = data_source_df[\"i_lat\"].max()\n", "min_lon = data_source_df[\"i_lon\"].min()\n", "max_lon = data_source_df[\"i_lon\"].max()\n", "min_time = data_source_df[\"time\"].min()\n", "max_time = data_source_df[\"time\"].max()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def _is_single_action_prescriptor(actions):\n", " \"\"\"\n", " Checks how many Actions have been defined in the Context, Actions, Outcomes mapping.\n", " :return: True if only 1 action is defined, False otherwise\n", " \"\"\"\n", " return len(actions) == 1\n", "\n", "def _is_scalar(prescribed_action):\n", " \"\"\"\n", " Checks if the prescribed action contains a single value, i.e. a scalar, or an array.\n", " A prescribed action contains a single value if it has been prescribed for a single context sample\n", " :param prescribed_action: a scalar or an array\n", " :return: True if the prescribed action contains a scalar, False otherwise.\n", " \"\"\"\n", " return prescribed_action.shape[0] == 1 and prescribed_action.shape[1] == 1\n", "\n", "def _convert_to_nn_input(context_df: pd.DataFrame) -> List[np.ndarray]:\n", " \"\"\"\n", " Converts a context DataFrame to a list of numpy arrays a neural network can ingest\n", " :param context_df: a DataFrame containing inputs for a neural network. Number of inputs and size must match\n", " :return: a list of numpy ndarray, on ndarray per neural network input\n", " \"\"\"\n", " # The NN expects a list of i inputs by s samples (e.g. 9 x 299).\n", " # So convert the data frame to a numpy array (gives shape 299 x 9), transpose it (gives 9 x 299)\n", " # and convert to list(list of 9 arrays of 299)\n", " context_as_nn_input = list(context_df.to_numpy().transpose())\n", " # Convert each column's list of 1D array to a 2D array\n", " context_as_nn_input = [np.stack(context_as_nn_input[i], axis=0) for i in\n", " range(len(context_as_nn_input))]\n", " return context_as_nn_input" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def prescribe_from_model(prescriptor, context_df: pd.DataFrame) -> Dict[str, Any]:\n", " \"\"\"\n", " Generates prescriptions using the passed neural network candidate and context\n", " :param prescriptor: a Keras neural network\n", " ::param context_df: a DataFrame containing the context to prescribe for,\n", " :return: a dictionary of action name to action value or list of action values\n", " \"\"\"\n", " action_list = ['recommended_land_use']\n", " \n", " # Convert the input df\n", " context_as_nn_input = _convert_to_nn_input(context_df)\n", " row_index = context_df.index\n", " \n", " # Get the prescrib?ed actions\n", " prescribed_actions = prescriptor.predict(context_as_nn_input)\n", " actions = {}\n", "\n", " if _is_single_action_prescriptor(action_list):\n", " # Put the single action in an array to process it like multiple actions\n", " prescribed_actions = [prescribed_actions]\n", " \n", " for i, action_col in enumerate(action_list):\n", " if _is_scalar(prescribed_actions[i]):\n", " # We have a single row and this action is numerical. Convert it to a scalar.\n", " actions[action_col] = prescribed_actions[i].item()\n", " else:\n", " actions[action_col] = prescribed_actions[i].tolist()\n", " \n", " # Convert the prescribed actions to a DataFrame\n", " prescribed_actions_df = pd.DataFrame(actions,\n", " columns=action_list,\n", " index=row_index)\n", " return prescribed_actions_df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def compute_percent_changed(encoded_context_actions_df):\n", " # Sum the absolute values, but divide by 2 to avoid double counting\n", " # Because positive diff is offset by negative diff\n", " # context_action_df[DIFF_LAND_USE_COLS].abs().sum(axis=1) / 2\n", "\n", " encoded_context_actions_df = encoded_context_actions_df.reset_index(drop=True)\n", " # Decode in order to get the signed land usage diff values\n", " context_action_df = encoder.decode_as_df(encoded_context_actions_df)\n", "\n", " # Sum the positive diffs\n", " percent_changed = context_action_df[context_action_df[DIFF_LAND_USE_COLS] > 0].sum(axis=1)\n", " # Land usage is only a portion of that cell, e.g 0.8. Scale back to 1\n", " # So that percent changed really represent the percentage of change within the land use\n", " # portion of the cell\n", " # I.e. how much of the pie chart has changed?\n", " percent_changed = percent_changed / context_action_df[LAND_USE_COLS].sum(axis=1)\n", " df = pd.DataFrame(percent_changed, columns=['Change'])\n", " return df" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def run_prescriptor(prescriptor_model, sample_context_df):\n", " encoded_sample_context_df = encoder.encode_as_df(sample_context_df)\n", " prescribed_actions_df = prescribe_from_model(prescriptor_model, encoded_sample_context_df)\n", " reco_land_use_df = pd.DataFrame(prescribed_actions_df.recommended_land_use.tolist(),\n", " columns=LAND_USE_COLS)\n", "\n", " used = sum(sample_context_df[LAND_USE_COLS].iloc[0].tolist())\n", " for col in LAND_USE_COLS:\n", " reco_land_use_df[col] *= used\n", "\n", " # Reattach primf and primn\n", " reco_land_use_df[\"primf\"] = sample_context_df[\"primf\"].to_numpy()\n", " reco_land_use_df[\"primn\"] = sample_context_df[\"primn\"].to_numpy()\n", "\n", " # Assuming there's no primary land left in this cell\n", " # TODO: not correct. Need to account for primf and primn, that can't increase (no way to return to primary forest)\n", " prescribed_land_use_pct = reco_land_use_df.iloc[0][ALL_LAND_USE_COLS].sum() * 100\n", " print(f\"Presribed land usage: {prescribed_land_use_pct:.2f}% of land\")\n", " \n", " return reco_land_use_df[ALL_LAND_USE_COLS]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def run_predictor(predictor_model, context, actions):\n", " encoded_sample_context_df = encoder.encode_as_df(sample_context_df)\n", "\n", " actions = [a / 100 for a in actions]\n", " reco_land_use_df = pd.DataFrame([actions], columns=CHART_COLS)\n", " reco_land_use_df = reco_land_use_df[LAND_USE_COLS]\n", "\n", " prescribed_actions_df = reco_land_use_df[LAND_USE_COLS].reset_index(drop=True) - sample_context_df[LAND_USE_COLS].reset_index(drop=True)\n", " prescribed_actions_df.rename(COLS_MAP, axis=1, inplace=True)\n", "\n", " encoded_prescribed_actions_df = encoder.encode_as_df(prescribed_actions_df)\n", "\n", " encoded_context_actions_df = pd.concat([encoded_sample_context_df,\n", " encoded_prescribed_actions_df],\n", " axis=1)\n", " \n", " change_df = compute_percent_changed(encoded_context_actions_df)\n", " \n", " new_pred = predictor_model.predict(encoded_context_actions_df)\n", " pred_df = pd.DataFrame(new_pred, columns=[\"ELUC\"])\n", " # Decode output\n", " out_df = encoder.decode_as_df(pred_df)\n", " return out_df.iloc[0, 0], change_df.iloc[0, 0] * 100" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Predictor" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "predictor_model = XGBRegressor()\n", "predictor_model.load_model(\"predictors/xgboost_predictor.json\")" ] }, { "attachments": { "319f2a83-efbb-4017-83fb-c47e2e335906.png": { "image/png": "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" } }, "cell_type": "markdown", "metadata": {}, "source": [ "# Prescriptors\n", "![image.png](attachment:319f2a83-efbb-4017-83fb-c47e2e335906.png)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "PRESCRIPTOR_LIST = [\"1_1\", \"34_78\", \"50_67\", \"40_45\", \"30_28\", \"28_40\"]" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# User Interface" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "sample_context_df = None\n", "\n", "out = widgets.Output()\n", "\n", "\"\"\"\n", "Submits context and creates pie chart\n", "Updates sliders for pie chart accordingly\n", "\"\"\"\n", "def prescribe(b):\n", " prescriptor_model = load_prescriptor()\n", " prescribed_df = run_prescriptor(prescriptor_model, sample_context_df)\n", "\n", " # Get other col back\n", " data = prescribed_df.iloc[0].tolist()\n", " other = fig[\"data\"][0].values[-1]\n", " data.append(other)\n", " data = dict(zip(CHART_COLS, data))\n", "\n", " for feature in CHART_COLS:\n", " # Unlock everything\n", " if feature in LAND_USE_COLS:\n", " ticks[feature].value = False\n", "\n", " sliders[feature].unobserve(update_presc_plot, names=\"value\")\n", " sliders[feature].value = data[feature] * 100\n", " sliders[feature].observe(update_presc_plot, names=\"value\")\n", " \n", " # Clear figure and re-plot\n", " fig[\"data\"] = (fig[\"data\"][0], )\n", " fig.add_trace(go.Pie(values=list(data.values()), \n", " labels=CHART_COLS, \n", " domain=dict(x=[0.5, 1]), \n", " title=\"Prescribed\"), row=1, col=2)\n", "\n", "\n", "\"\"\"\n", "Locks a slider so it isn't affected by the sum to 100 computation\n", "\"\"\"\n", "def lock(change):\n", " if change[\"new\"]:\n", " locked.add(change[\"owner\"])\n", " else:\n", " locked.remove(change[\"owner\"])\n", "\n", "\n", "\"\"\"\n", "Real-time updater for prescribed pie chart\n", "\"\"\"\n", "def update_presc_plot(change):\n", " with fig.batch_update():\n", " if len(fig[\"data\"]) > 1:\n", " owner = change[\"owner\"]\n", " \n", " # First compute what percentage is locked, count locked/zero sliders, and see if this slider is locked\n", " locked_sum = 0\n", " zero_count = 0\n", " owner_locked = False\n", " for feat in sliders:\n", " if sliders[feat] != owner and (ticks[feat] in locked or sliders[feat].value == 0):\n", " locked_sum += sliders[feat].value\n", " zero_count += 1\n", " # TODO: this is yucky\n", " if sliders[feat] == owner and ticks[feat] in locked:\n", " owner_locked = True\n", " break\n", " \n", " # Block update if everything else is locked/0 or this is locked\n", " if owner_locked or zero_count == len(sliders) - 1:\n", " owner.unobserve(update_presc_plot, names=\"value\")\n", " owner.value = change[\"old\"]\n", " owner.observe(update_presc_plot, names=\"value\")\n", "\n", " else:\n", " # Add locked percentage to old and new because we don't factor\n", " # them in to the 100% in our calculating the new value\n", " old = change[\"old\"] + locked_sum\n", " new = change[\"new\"] + locked_sum\n", "\n", " for feat in sliders:\n", " slider = sliders[feat]\n", " tick = ticks[feat]\n", " if slider != owner and tick not in locked:\n", " # Unobserve so we don't infinitely recurse\n", " slider.unobserve(update_presc_plot, names=\"value\")\n", " # old value / old total = new value / new total\n", " # Must round to the same or higher place as the slider\n", " assert(math.log10(slider.step) % 1 == 0)\n", " slider.value = round(slider.value / (100 - old) * (100 - new), int(-1 * math.log10(slider.step)))\n", " slider.observe(update_presc_plot, names=\"value\")\n", "\n", " fig[\"data\"][1][\"values\"] = [slider.value for slider in sliders.values()]\n", "\n", "\n", "\"\"\"\n", "Submits context and actions and outputs prediction\n", "\"\"\"\n", "def predict(b):\n", " context = sample_context_df\n", " actions = [slider.value for slider in sliders.values()]\n", " outcome, change = run_predictor(predictor_model, context, actions)\n", " output_area.value = f\"ELUC: {outcome} tC/ha/yr\\nChange: {change}%\"\n", "\n", "\n", "\"\"\"\n", "Computes the other column and adds it on to sample_context_df\n", "\"\"\"\n", "def compute_and_add_other(sample_context_df):\n", " data = sample_context_df[ALL_LAND_USE_COLS]\n", " diff = 1 - sample_context_df[ALL_LAND_USE_COLS].iloc[0].sum()\n", " other_val = diff if diff >= 0 else 0\n", " data[\"nonland\"] = [other_val]\n", " return data\n", "\n", "\n", "\"\"\"\n", "Creates initial pie chart\n", "\"\"\"\n", "def show_context(c):\n", " sample_df = data_source_df[(data_source_df.i_lat==latitude_input.value) & \n", " (data_source_df.i_lon==longitude_input.value) &\n", " (data_source_df.time==time_input.value)]\n", " global sample_context_df\n", " sample_context_df = sample_df[CONTEXT_COLUMNS]\n", " #for testing purposes:\n", " # sample_context_df[\"pastr\"].values[0] -= .12\n", " # sample_context_df[\"primf\"].values[0] += 0.04\n", " # sample_context_df[\"primn\"].values[0] += 0.04\n", " # Plot initial context pie chart\n", " data = compute_and_add_other(sample_context_df)\n", " fig.add_trace(go.Pie(values=data.iloc[0].tolist(),\n", " labels=CHART_COLS, \n", " domain=dict(x=[0, 0.5]), \n", " title=\"Current\"), row=1, col=1)\n", "\n", "def load_prescriptor():\n", " print(f\"Selected prescriptor: {prescriptor_dropdown.value}\")\n", " prescriptor_id = prescriptor_dropdown.value\n", " prescriptor_model_filename = os.path.join(\"prescriptors\",\n", " prescriptor_id + '.h5')\n", "\n", " print(f'Loading prescriptor model: {prescriptor_model_filename}')\n", " prescriptor_model = load_model(prescriptor_model_filename, compile=False)\n", " return prescriptor_model\n", " \n", "# Context\n", "# Create the latitude input field\n", "latitude_input = widgets.FloatText(description='Latitude:', value=51.625)\n", "\n", "# Create the longitude input field\n", "longitude_input = widgets.FloatText(description='Longitude:', value=-3.375)\n", "\n", "# Create the time input field\n", "time_input = widgets.IntText(description='Year:', value=2021)\n", "\n", "\"\"\"\n", "Construct widgets and attach them to their functions\n", "\"\"\"\n", "sliders = {feature : widgets.FloatSlider(value=0.0, step=0.001, description=\"Prescribed \" + feature, style=dict(description_width='initial')) for feature in CHART_COLS}\n", "ticks = {feature : widgets.Checkbox(value=False, description=\"Lock \" + feature, style=dict(description_width='initial')) for feature in CHART_COLS}\n", "# Lock primaries and other\n", "ticks[\"primf\"].value = True\n", "ticks[\"primn\"].value = True\n", "ticks[\"nonland\"].value = True\n", "\n", "# For use in locking and unlocking sliders\n", "locked = set()\n", "locked.add(ticks[\"primf\"])\n", "locked.add(ticks[\"primn\"])\n", "locked.add(ticks[\"nonland\"])\n", "\n", "prescribe_button = widgets.Button(description=\"Prescribe\")\n", "prescribe_button.on_click(prescribe)\n", "\n", "predict_button = widgets.Button(description=\"Predict\")\n", "predict_button.on_click(predict)\n", "\n", "\n", "\"\"\"\n", "Display Interactables and Figures\n", "TODO: add titles, make layout prettier\n", "\"\"\"\n", "fig = go.FigureWidget(make_subplots(rows=1, cols=2, specs=[[{\"type\": \"pie\"}, {\"type\": \"pie\"}]]))\n", "fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))\n", "\n", "# Context\n", "context_range = f\"Latitude must be between {min_lat} and {max_lat}, in 0.250 increments.\\nLongitude must be between {min_lon} and {max_lon}, in 0.250 increments.\\nYear must be between {min_time} and {max_time}.\"\n", "text_area = widgets.Textarea(value=context_range,\n", " rows=3,\n", " layout=widgets.Layout(height=\"auto\", width=\"auto\"))\n", "display(text_area)\n", "\n", "display(latitude_input, longitude_input, time_input)\n", "\n", "show_context_button = widgets.Button(description=\"Show land use\")\n", "show_context_button.on_click(show_context)\n", "display(show_context_button)\n", "\n", "# Prescribe\n", "prescriptor_label = widgets.Label('Select a prescriptor:')\n", "prescriptor_dropdown = widgets.Dropdown(options=PRESCRIPTOR_LIST)\n", "display(prescriptor_label, prescriptor_dropdown)\n", "\n", "display(prescribe_button)\n", "\n", "# Attach sliders and boxes to their observers\n", "for feat in sliders:\n", " sliders[feat].observe(update_presc_plot, names=\"value\")\n", " ticks[feat].observe(lock, names=\"value\")\n", "\n", "# Display sliders and boxes alongside figure\n", "slider_box = VBox(list(sliders.values()))\n", "tick_box = VBox(list(ticks.values()))\n", "fig_box = VBox([fig])\n", "display(HBox([slider_box, tick_box, fig_box]))\n", "\n", "# Predict\n", "display(predict_button)\n", "output_area = widgets.Textarea(value=\"\", rows=2, layout=widgets.Layout(height=\"auto\", width=\"auto\"))\n", "display(output_area)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.11" } }, "nbformat": 4, "nbformat_minor": 4 }