{ "cells": [ { "cell_type": "markdown", "id": "178597e4054ba0a6", "metadata": {}, "source": [ "# B Point Plotting Example\n", "\n", "This notebook demonstrates running a B-point extraction (Pale et al., 2021) on the example dataset and several\n", "plotting styles: low-level plotting, split/zoomed axes, showing reference points, detected points, and heartbeat borders." ] }, { "cell_type": "markdown", "id": "7caa710a8f6b4214", "metadata": {}, "source": [ "## Setup and imports" ] }, { "cell_type": "code", "execution_count": 1, "id": "3632144a56f5392d", "metadata": { "ExecuteTime": { "end_time": "2026-02-01T16:26:04.245053Z", "start_time": "2026-02-01T16:26:02.956217Z" } }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "from fau_colors import cmaps\n", "\n", "from pepbench.algorithms.icg import BPointExtractionPale2021, CPointExtractionScipyFindPeaks\n", "from pepbench.example_data import get_example_dataset\n", "from pepbench.plotting.algorithms import plot_b_point_extraction_pale2021\n", "\n", "%matplotlib inline\n", "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "markdown", "id": "ac6b05c6275d34ee", "metadata": {}, "source": [ "## Plotting style" ] }, { "cell_type": "code", "execution_count": 2, "id": "6c11b0c5633e3b61", "metadata": { "ExecuteTime": { "end_time": "2026-02-01T16:26:04.265481Z", "start_time": "2026-02-01T16:26:04.251001Z" } }, "outputs": [], "source": [ "plt.close(\"all\")\n", "palette = sns.color_palette(cmaps.faculties)\n", "sns.set_theme(context=\"notebook\", style=\"ticks\", font=\"sans-serif\", palette=palette)\n", "plt.rcParams[\"figure.figsize\"] = (10, 4)" ] }, { "cell_type": "markdown", "id": "7c93a99454736b04", "metadata": {}, "source": [ "## Load example dataset using get_example_dataset()" ] }, { "cell_type": "code", "execution_count": 3, "id": "4fbc5bf4e76bbc17", "metadata": { "ExecuteTime": { "end_time": "2026-02-01T16:26:04.294575Z", "start_time": "2026-02-01T16:26:04.265963Z" } }, "outputs": [ { "data": { "text/html": [ "

ExampleDataset [2 groups/rows]

\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
participant
0VP_001
1VP_002
\n", "
" ], "text/plain": [ "ExampleDataset [2 groups/rows]\n", "\n", " participant\n", " 0 VP_001\n", " 1 VP_002" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# This will download the example zip automatically if it's not present locally\n", "dataset = get_example_dataset(return_clean=True)\n", "dataset" ] }, { "cell_type": "markdown", "id": "338e3841dccd71bb", "metadata": {}, "source": [ "### Pick the first available participant/phase from the example dataset" ] }, { "cell_type": "code", "execution_count": 4, "id": "dd9cf7803c43f958", "metadata": { "ExecuteTime": { "end_time": "2026-02-01T16:26:04.358414Z", "start_time": "2026-02-01T16:26:04.311262Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dataset index (first rows):\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
participant
0VP_001
1VP_002
\n", "
" ], "text/plain": [ " participant\n", "0 VP_001\n", "1 VP_002" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Using: VP_001 None None\n" ] }, { "data": { "text/html": [ "

ExampleDataset [1 groups/rows]

\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
participant
0VP_001
\n", "
" ], "text/plain": [ "ExampleDataset [1 groups/rows]\n", "\n", " participant\n", " 0 VP_001" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(\"Dataset index (first rows):\")\n", "display(dataset.index.head(10))\n", "\n", "row = dataset.index.iloc[0]\n", "participant, condition, phase = row[\"participant\"], row.get(\"condition\", None), row.get(\"phase\", None)\n", "print(\"Using:\", participant, condition, phase)\n", "\n", "# create a datapoint subset for this entry\n", "datapoint = dataset.get_subset(participant=participant)\n", "datapoint" ] }, { "cell_type": "markdown", "id": "2d930377bf78376", "metadata": {}, "source": [ "## Run the algorithms (C-point then B-point) on the datapoint" ] }, { "cell_type": "code", "execution_count": 5, "id": "b6cada1a54898a9", "metadata": { "ExecuteTime": { "end_time": "2026-02-01T16:26:04.812050Z", "start_time": "2026-02-01T16:26:04.380786Z" } }, "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", "
b_point_samplenan_reason
heartbeat_id
0179NaN
1533NaN
21009NaN
31466NaN
41873NaN
\n", "
" ], "text/plain": [ " b_point_sample nan_reason\n", "heartbeat_id \n", "0 179 NaN\n", "1 533 NaN\n", "2 1009 NaN\n", "3 1466 NaN\n", "4 1873 NaN" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# C-point extraction (used by Pale et al. B-point algorithm)\n", "c_algo = CPointExtractionScipyFindPeaks()\n", "c_algo.extract(icg=datapoint.icg, heartbeats=datapoint.heartbeats, sampling_rate_hz=datapoint.sampling_rate_icg)\n", "\n", "# B-point extraction (Pale et al. 2021)\n", "b_algo = BPointExtractionPale2021()\n", "b_algo.extract(\n", " icg=datapoint.icg,\n", " heartbeats=datapoint.heartbeats,\n", " c_points=c_algo.points_,\n", " sampling_rate_hz=datapoint.sampling_rate_icg,\n", ")\n", "\n", "\n", "# show detected points table (head)\n", "display(b_algo.points_.head())" ] }, { "cell_type": "markdown", "id": "25f9c4ef5d5b7e75", "metadata": {}, "source": [ "## 1) High-level helper plot (recommended)\n", "Use the provided plotting helper `plot_b_point_extraction_pale2021` to visualise multiple heartbeats with\n", "reference labels, detected C/B points and heartbeat borders." ] }, { "cell_type": "code", "execution_count": 6, "id": "6592ef9d08aa7811", "metadata": { "ExecuteTime": { "end_time": "2026-02-01T16:28:53.537866Z", "start_time": "2026-02-01T16:28:53.421093Z" } }, "outputs": [ { "data": { "text/plain": [ "np.int64(0)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "datapoint.heartbeats.index.min()" ] }, { "cell_type": "code", "execution_count": null, "id": "7b1359ddd019a8bd", "metadata": { "ExecuteTime": { "end_time": "2026-02-01T16:26:05.736709Z", "start_time": "2026-02-01T16:26:04.949624Z" } }, "outputs": [], "source": [ "hb_ids = datapoint.heartbeats.index.get_level_values(\"heartbeat_id\").unique()[:5].tolist()\n", "print(\"Plotting heartbeat ids:\", hb_ids)\n", "fig, ax = plot_b_point_extraction_pale2021(datapoint, heartbeat_subset=hb_ids, normalize_time=True)" ] }, { "cell_type": "markdown", "id": "5912d0ef67fc7ebe", "metadata": {}, "source": [ "## 2) Low-level plotting: raw ICG with detected & reference markers and heartbeat borders" ] }, { "cell_type": "code", "execution_count": null, "id": "259f5054bcc865c7", "metadata": {}, "outputs": [], "source": [ "# plot raw ICG and overlay reference/detected B-points and heartbeat borders\n", "fig, ax = plt.subplots(figsize=(20, 5))\n", "icg = datapoint.icg.squeeze()\n", "ax.plot(icg.index, icg.values, label=\"ICG (preprocessed)\")\n", "\n", "# detected b points (from algorithm result)\n", "b_samples = b_algo.points_[\"b_point_sample\"].dropna().astype(int)\n", "ax.scatter(icg.index[b_samples], icg.values[b_samples], color=\"C1\", marker=\"o\", label=\"Detected B\")\n", "\n", "# reference b points if available\n", "try:\n", " ref_b = datapoint.reference_labels_icg.reindex([\"ICG\"], level=\"channel\")[\"sample_relative\"]\n", " ref_b = ref_b.droplevel([\"channel\"]).astype(int)\n", " ax.scatter(icg.index[ref_b], icg.values[ref_b], color=\"C2\", marker=\"X\", label=\"Reference B\")\n", "except Exception:\n", " pass\n", "\n", "# heartbeat borders (start samples)\n", "hb_borders = datapoint.heartbeats\n", "ax.vlines(\n", " icg.index[hb_borders[\"start_sample\"]],\n", " ymin=icg.min(),\n", " ymax=icg.max(),\n", " colors=\"C3\",\n", " linestyles=\"--\",\n", " alpha=0.6,\n", " label=\"Heartbeat borders\",\n", ")\n", "\n", "ax.legend(loc=\"upper right\")\n", "ax.set_title(\"Raw ICG with detected and reference B-points\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "7e1545bc04e017fb", "metadata": {}, "source": [ "## 4) Show heartbeat borders and annotations using plotting utilities\n", "Use the internal plotting utilities to add heartbeat borders and legend handling." ] }, { "cell_type": "code", "execution_count": null, "id": "a759603b1e1966a1", "metadata": {}, "outputs": [], "source": [ "from pepbench.plotting._utils import _add_heartbeat_borders, _add_icg_b_points, _add_pep_from_reference\n", "\n", "fig, ax = plt.subplots(figsize=(12, 3))\n", "ax.plot(icg.index, icg.values, label=\"ICG\")\n", "# heartbeat borders (use utility)\n", "_add_heartbeat_borders(datapoint.heartbeats, ax)\n", "# add detected B points using internal helper\n", "_add_icg_b_points(datapoint.icg.squeeze(), b_samples, ax)\n", "# add reference PEP spans if available\n", "try:\n", " _add_pep_from_reference(\n", " datapoint.ecg,\n", " datapoint.icg,\n", " datapoint.reference_labels_ecg.join(datapoint.reference_labels_icg),\n", " ax,\n", " pep_hatch=None,\n", " )\n", "except Exception:\n", " pass\n", "ax.set_title(\"ICG with heartbeat borders and annotations\")\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "a9cb9a9d2288679", "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.13.5" } }, "nbformat": 4, "nbformat_minor": 5 }