{ "cells": [ { "cell_type": "markdown", "id": "a70fb1b5", "metadata": {}, "source": [ "# Bivariate data - Basics" ] }, { "cell_type": "markdown", "id": "82f70b35", "metadata": {}, "source": [ "This tutorial focuses on working with paired (bivariate) data using `plans`: loading `x`/`y` samples, visualizing their joint distribution and measuring their agreement with standard metrics." ] }, { "cell_type": "markdown", "id": "48e8135a", "metadata": {}, "source": [ "## Notebook setup\n", "\n", "For users running this tutorial as a Jupyter Notebook, this cell must be executed first:" ] }, { "cell_type": "code", "execution_count": 1, "id": "569270a7", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:25.771891Z", "iopub.status.busy": "2026-10-30T19:26:25.771465Z", "iopub.status.idle": "2026-10-30T19:26:26.097061Z", "shell.execute_reply": "2026-10-30T19:26:26.096496Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Outputs will be saved to: ./outputs\\bivariate\n" ] } ], "source": [ "import sys\n", "from pathlib import Path\n", "import numpy as np\n", "import pandas as pd\n", "\n", "# Install `plans` in `google.colab`.\n", "# Use `pip install plans` for other environments.\n", "\n", "if \"google.colab\" in sys.modules:\n", " import os\n", " os.system(f\"{sys.executable} -m pip install -q plans\")\n", "\n", "# This avoids warnings related to uninstalled fonts\n", "import logging\n", "# Set the matplotlib font manager logger to only show errors (hides warnings)\n", "logging.getLogger('matplotlib.font_manager').setLevel(logging.ERROR)\n", "\n", "# define output folder\n", "OUTPUT_DIR = Path(\"outputs/bivariate\")\n", "OUTPUT_DIR.mkdir(parents=True, exist_ok=True)\n", "print(f\"Outputs will be saved to: ./{OUTPUT_DIR}\")" ] }, { "cell_type": "markdown", "id": "20b15b52", "metadata": {}, "source": [ "## The `Bivar` object\n", "\n", "The `Bivar` object lives under the `plans.datasets.analyst` module. Like `Univar`, it is a child of the `DataSet` object from `plans.root`, so it shares the same lifecycle: create the object, feed it data, and let `.update()` refresh the derived attributes.\n", "\n", "```{seealso}\n", "Check out the {doc}`Univariate data - Basics ` tutorial first.\n", "```" ] }, { "cell_type": "markdown", "id": "c8f19c26", "metadata": {}, "source": [ "Import `Bivar` object:" ] }, { "cell_type": "code", "execution_count": 2, "id": "e040095c", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.099950Z", "iopub.status.busy": "2026-10-30T19:26:26.099689Z", "iopub.status.idle": "2026-10-30T19:26:26.487432Z", "shell.execute_reply": "2026-10-30T19:26:26.486742Z" } }, "outputs": [], "source": [ "from plans.datasets.analyst import Bivar" ] }, { "cell_type": "markdown", "id": "2bc00363", "metadata": {}, "source": [ "Create an instance of the `Bivar`:" ] }, { "cell_type": "code", "execution_count": 3, "id": "09308fc0", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.498062Z", "iopub.status.busy": "2026-10-30T19:26:26.490407Z", "iopub.status.idle": "2026-10-30T19:26:26.506425Z", "shell.execute_reply": "2026-10-30T19:26:26.505758Z" } }, "outputs": [], "source": [ "bv = Bivar(name=\"Discharge\", alias=\"Q\")" ] }, { "cell_type": "markdown", "id": "b71a269b", "metadata": {}, "source": [ "Check out the variable type:" ] }, { "cell_type": "code", "execution_count": 4, "id": "b24686d2", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.509735Z", "iopub.status.busy": "2026-10-30T19:26:26.509514Z", "iopub.status.idle": "2026-10-30T19:26:26.513200Z", "shell.execute_reply": "2026-10-30T19:26:26.512590Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(type(bv))" ] }, { "cell_type": "markdown", "id": "e0308b9b", "metadata": {}, "source": [ "## Handle data attributes" ] }, { "cell_type": "markdown", "id": "a1e03ef7", "metadata": {}, "source": [ "Columns are named by `.xfield` and `.yfield`, while `.xname`, `.yname`, `.xunits` and `.yunits` are only used as labels in visuals and metadata. Set labels **before** loading data, so the axis labels are built from them:" ] }, { "cell_type": "code", "execution_count": 5, "id": "8dfea0fa", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.516405Z", "iopub.status.busy": "2026-10-30T19:26:26.516204Z", "iopub.status.idle": "2026-10-30T19:26:26.519980Z", "shell.execute_reply": "2026-10-30T19:26:26.519291Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x column: x | y column: y\n" ] } ], "source": [ "bv.description = \"Observed and simulated daily discharge for a tutorial\"\n", "bv.xname = \"Observed\"\n", "bv.xunits = \"m3/s\"\n", "bv.yname = \"Simulated\"\n", "bv.yunits = \"m3/s\"\n", "print(f\"x column: {bv.xfield} | y column: {bv.yfield}\")" ] }, { "cell_type": "markdown", "id": "e92b8f43", "metadata": {}, "source": [ "## Working with data" ] }, { "cell_type": "markdown", "id": "aae17c79", "metadata": {}, "source": [ "### Create synthetic dataset\n", "\n", "A common bivariate case in hydrology is comparing observed against simulated discharge. Lets make one year of skewed daily observations and a simulation that slightly overestimates them with some noise. A couple of values are missing." ] }, { "cell_type": "code", "execution_count": 6, "id": "b1bb95b2", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.522908Z", "iopub.status.busy": "2026-10-30T19:26:26.522540Z", "iopub.status.idle": "2026-10-30T19:26:26.540463Z", "shell.execute_reply": "2026-10-30T19:26:26.539775Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Saved to: outputs\\bivariate\\bivariate_discharge.csv\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
q_obsq_sim
020.91817315.121868
128.35345625.281213
218.37215616.522104
316.450704NaN
430.79255333.179942
\n", "
" ], "text/plain": [ " q_obs q_sim\n", "0 20.918173 15.121868\n", "1 28.353456 25.281213\n", "2 18.372156 16.522104\n", "3 16.450704 NaN\n", "4 30.792553 33.179942" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# make synthetic data\n", "rng = np.random.default_rng(seed=42) # ensure reproducibility\n", "q_obs = rng.gamma(shape=2, scale=10, size=365)\n", "q_sim = 1.05 * q_obs + rng.normal(loc=0, scale=3, size=len(q_obs))\n", "q_sim = np.clip(q_sim, a_min=0, a_max=None)\n", "df = pd.DataFrame({\"q_obs\": q_obs, \"q_sim\": q_sim})\n", "df.loc[[3, 7], \"q_sim\"] = np.nan\n", "\n", "# Export CSV file\n", "file_csv = OUTPUT_DIR / \"bivariate_discharge.csv\"\n", "df.to_csv(file_csv, sep=\";\", index=False)\n", "print(f\"Saved to: {file_csv}\")\n", "df.head()" ] }, { "cell_type": "markdown", "id": "6d55f475", "metadata": {}, "source": [ "### Loading data from the CSV file" ] }, { "cell_type": "markdown", "id": "4002dfee", "metadata": {}, "source": [ "Call the `.load_data()` method, pointing which file columns hold `x` and `y`. They are renamed to `.xfield` and `.yfield` on loading:" ] }, { "cell_type": "code", "execution_count": 7, "id": "063a9b59", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.542650Z", "iopub.status.busy": "2026-10-30T19:26:26.542434Z", "iopub.status.idle": "2026-10-30T19:26:26.582615Z", "shell.execute_reply": "2026-10-30T19:26:26.581993Z" } }, "outputs": [], "source": [ "bv.load_data(\n", " file_data=file_csv, # file path\n", " input_xfield=\"q_obs\", # name of x column\n", " input_yfield=\"q_sim\", # name of y column\n", " in_sep=\";\", # input separator\n", ")" ] }, { "cell_type": "markdown", "id": "9b3a653a", "metadata": {}, "source": [ "Data is stored in the `.data` attribute as a `pandas.DataFrame`. Incomplete pairs are dropped and rows are sorted by `x`:" ] }, { "cell_type": "code", "execution_count": 8, "id": "5aa9e805", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.585169Z", "iopub.status.busy": "2026-10-30T19:26:26.584933Z", "iopub.status.idle": "2026-10-30T19:26:26.592082Z", "shell.execute_reply": "2026-10-30T19:26:26.591430Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
xy
00.9276254.918430
11.0464980.402015
21.0737760.000000
31.4743607.263258
41.5137743.123128
.........
35868.39441973.005240
35970.22888671.807229
36071.51012473.848619
36174.86997575.930436
36289.33595196.356605
\n", "

363 rows × 2 columns

\n", "
" ], "text/plain": [ " x y\n", "0 0.927625 4.918430\n", "1 1.046498 0.402015\n", "2 1.073776 0.000000\n", "3 1.474360 7.263258\n", "4 1.513774 3.123128\n", ".. ... ...\n", "358 68.394419 73.005240\n", "359 70.228886 71.807229\n", "360 71.510124 73.848619\n", "361 74.869975 75.930436\n", "362 89.335951 96.356605\n", "\n", "[363 rows x 2 columns]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bv.data" ] }, { "cell_type": "markdown", "id": "465b4527", "metadata": {}, "source": [ "```{tip}\n", "For in-memory arrays, use `.set_data(x=..., y=...)` instead. It applies the same cleaning and sorting.\n", "```" ] }, { "cell_type": "markdown", "id": "f85706c6", "metadata": {}, "source": [ "### Access computed attributes\n", "\n", "After loading data, some attributes are computed automatically:" ] }, { "cell_type": "code", "execution_count": 9, "id": "ecc5e5f2", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.594910Z", "iopub.status.busy": "2026-10-30T19:26:26.594692Z", "iopub.status.idle": "2026-10-30T19:26:26.598560Z", "shell.execute_reply": "2026-10-30T19:26:26.597913Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Size: 363\n", "x range: 0.93 to 89.34\n", "y range: 0.00 to 96.36\n" ] } ], "source": [ "print(f\"Size: {bv.size}\")\n", "print(f\"x range: {bv.x_min:.2f} to {bv.x_max:.2f}\")\n", "print(f\"y range: {bv.y_min:.2f} to {bv.y_max:.2f}\")" ] }, { "cell_type": "markdown", "id": "c74950c1", "metadata": {}, "source": [ "Inspect the object metadata:" ] }, { "cell_type": "code", "execution_count": 10, "id": "5ad32ea2", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.601526Z", "iopub.status.busy": "2026-10-30T19:26:26.601337Z", "iopub.status.idle": "2026-10-30T19:26:26.605702Z", "shell.execute_reply": "2026-10-30T19:26:26.605066Z" } }, "outputs": [ { "data": { "text/plain": [ "{'name': 'Discharge',\n", " 'alias': 'Q',\n", " 'size': 363,\n", " 'color': 'blue',\n", " 'source': None,\n", " 'description': 'Observed and simulated daily discharge for a tutorial',\n", " 'xunits': 'm3/s',\n", " 'yunits': 'm3/s',\n", " 'file_data': 'C:\\\\Users\\\\Ipo\\\\AppData\\\\Local\\\\Temp\\\\claude\\\\C--Users-Ipo-PycharmProjects-plans\\\\f8d04d3e-cf5c-442f-b56e-ee185207a61b\\\\scratchpad\\\\nbrun\\\\outputs\\\\bivariate\\\\bivariate_discharge.csv'}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bv.get_metadata()" ] }, { "cell_type": "markdown", "id": "c66f2016", "metadata": {}, "source": [ "## Visualizations" ] }, { "cell_type": "markdown", "id": "f9243861", "metadata": {}, "source": [ "### Standard visualization\n", "\n", "Get the standard visual using the `.view()` method. The `full` layout shows the scatter plot with the marginal histograms of `x` (top) and `y` (right):" ] }, { "cell_type": "code", "execution_count": 11, "id": "67835a5b", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.608835Z", "iopub.status.busy": "2026-10-30T19:26:26.608629Z", "iopub.status.idle": "2026-10-30T19:26:26.795680Z", "shell.execute_reply": "2026-10-30T19:26:26.794947Z" } }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "bv.view()" ] }, { "cell_type": "markdown", "id": "4a1b7e91", "metadata": {}, "source": [ "### Fine-tuning visuals\n", "\n", "Like other `plans` objects, use the `.view_specs` keys for fine tuning. When comparing the same variable, equal axis ranges make the plot easier to read:" ] }, { "cell_type": "code", "execution_count": 12, "id": "2e8403a4", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.798187Z", "iopub.status.busy": "2026-10-30T19:26:26.797960Z", "iopub.status.idle": "2026-10-30T19:26:26.947119Z", "shell.execute_reply": "2026-10-30T19:26:26.946409Z" } }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "q_max = 1.1 * max(bv.x_max, bv.y_max)\n", "bv.view_specs[\"range_x\"] = [0, q_max]\n", "bv.view_specs[\"range\"] = [0, q_max]\n", "bv.view_specs[\"color\"] = \"tab:blue\"\n", "bv.view_specs[\"bins\"] = 20 # default None uses the Freedman-Diaconis rule\n", "bv.view_specs[\"plot_mean\"] = False\n", "bv.view_specs[\"title\"] = \"Observed vs Simulated\"\n", "bv.view()" ] }, { "cell_type": "markdown", "id": "4b447f5e", "metadata": {}, "source": [ "```{seealso}\n", "The `model` layout for model assessment is covered in the {doc}`Bivariate data - Models ` tutorial.\n", "```" ] }, { "cell_type": "markdown", "id": "4a80b93a", "metadata": {}, "source": [ "## Metrics" ] }, { "cell_type": "markdown", "id": "71353469", "metadata": {}, "source": [ "All metrics are static methods, so they can be used on any pair of arrays. Correlations are symmetric and take any two variables `x` and `y`:\n", "\n", "- **Pearson** $r$ measures *linear* association.\n", "- **Spearman** $\\rho$ is the Pearson $r$ of the ranks, so it measures *monotonic* association. It reaches 1 for any increasing relation, linear or not.\n", "\n", "Agreement metrics are not symmetric and take an `observed` and a `predicted` series of the same variable:\n", "\n", "- **Coefficient of determination** $R^2 = 1 - SS_{res} / SS_{tot}$ is the fraction of observed variance explained by the predictions. It is negative when the predictions are worse than the observed mean.\n", "- **RMSE**, **MAE** and **Bias** (%) measure the error magnitude and its direction. Bias is positive when the predictions overestimate." ] }, { "cell_type": "code", "execution_count": 13, "id": "3f48e2e5", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:26.950294Z", "iopub.status.busy": "2026-10-30T19:26:26.950071Z", "iopub.status.idle": "2026-10-30T19:26:27.595762Z", "shell.execute_reply": "2026-10-30T19:26:27.594124Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "r: 0.981\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "rho: 0.963\n", "R2: 0.951\n", "RMSE: 3.138\n", "MAE: 2.506\n", "Bias (%): 5.187\n" ] } ], "source": [ "obs = bv.data[bv.xfield].values\n", "sim = bv.data[bv.yfield].values\n", "\n", "print(f\"r: {Bivar.pearson(x=obs, y=sim):.3f}\")\n", "print(f\"rho: {Bivar.spearman(x=obs, y=sim):.3f}\")\n", "print(f\"R2: {Bivar.rsq(observed=obs, predicted=sim):.3f}\")\n", "print(f\"RMSE: {Bivar.rmse(observed=obs, predicted=sim):.3f}\")\n", "print(f\"MAE: {Bivar.mae(observed=obs, predicted=sim):.3f}\")\n", "print(f\"Bias (%): {Bivar.bias(observed=obs, predicted=sim):.3f}\")" ] }, { "cell_type": "markdown", "id": "8f10fb27", "metadata": {}, "source": [ "Or get them all at once with `Bivar.get_metrics()`:" ] }, { "cell_type": "code", "execution_count": 14, "id": "716370a9", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:27.600503Z", "iopub.status.busy": "2026-10-30T19:26:27.599872Z", "iopub.status.idle": "2026-10-30T19:26:27.611233Z", "shell.execute_reply": "2026-10-30T19:26:27.609728Z" } }, "outputs": [ { "data": { "text/plain": [ "{'r': 0.9806092604096892,\n", " 'rho': 0.9631908543435195,\n", " 'R2': 0.9511562724941502,\n", " 'RMSE': 3.138448669315695,\n", " 'MAE': 2.5062203403528738,\n", " 'Bias': 5.187163628629581}" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Bivar.get_metrics(observed=obs, predicted=sim)" ] }, { "cell_type": "markdown", "id": "5180dac2", "metadata": {}, "source": [ "### Association versus agreement\n", "\n", "To see how metrics differ, compare our simulation with two noiseless but distorted ones: a *doubled* series ($2 \\cdot obs$) and a *curved* series, still increasing but no longer a straight line:" ] }, { "cell_type": "code", "execution_count": 15, "id": "7d3b3504", "metadata": { "execution": { "iopub.execute_input": "2026-10-30T19:26:27.616454Z", "iopub.status.busy": "2026-10-30T19:26:27.615949Z", "iopub.status.idle": "2026-10-30T19:26:27.639940Z", "shell.execute_reply": "2026-10-30T19:26:27.638344Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
rrhoR2RMSEMAEBias
simulated0.9806090.9631910.9511563.1384492.5062205.187164
doubled1.0000001.000000-1.98015024.51487519.982952100.000000
curved0.9800111.0000000.21666712.5685116.04019817.567995
\n", "
" ], "text/plain": [ " r rho R2 RMSE MAE Bias\n", "simulated 0.980609 0.963191 0.951156 3.138449 2.506220 5.187164\n", "doubled 1.000000 1.000000 -1.980150 24.514875 19.982952 100.000000\n", "curved 0.980011 1.000000 0.216667 12.568511 6.040198 17.567995" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sim_doubled = 2 * obs\n", "sim_curved = 20 * np.power(obs / 20, 1.5)\n", "\n", "df_metrics = pd.DataFrame(\n", " {\n", " \"simulated\": Bivar.get_metrics(observed=obs, predicted=sim),\n", " \"doubled\": Bivar.get_metrics(observed=obs, predicted=sim_doubled),\n", " \"curved\": Bivar.get_metrics(observed=obs, predicted=sim_curved),\n", " }\n", ").T\n", "df_metrics" ] }, { "cell_type": "markdown", "id": "0ca2f047", "metadata": {}, "source": [ "Both distortions keep the ranks, so $\\rho = 1$. The doubled series is still a straight line, so $r = 1$ as well, and the curved one stays close to it. Yet both have a much lower $R^2$ than the noisy simulation: correlations measure *association* (how well some line or curve relates the series), while $R^2$ and the error metrics measure *agreement* with the 1:1 line." ] } ], "metadata": { "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.13.5" } }, "nbformat": 4, "nbformat_minor": 5 }