{ "cells": [ { "cell_type": "markdown", "id": "ccb2f69e", "metadata": {}, "source": [ "# Fundamentals: Channels, labels, and collections\n", "\n", "This notebook introduces the core backend data structures of `vitabel`: `Channel`, `Label`, `IntervalLabel`, and `TimeDataCollection`. Instead of loading data from external files, we build a small synthetic example from scratch to illustrate how these objects work together.\n", "\n", "It is a good starting point if you want to understand how `vitabel` organizes time series, attached versus global labels, and interactive plotting layouts.\n", "\n" ] }, { "cell_type": "markdown", "id": "427c6336", "metadata": {}, "source": [ "## 1. Imports\n", "\n", "We import the core `vitabel` classes and create a deterministic random number generator so that the synthetic signals are reproducible.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "1dfbb6e7", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "from vitabel import Channel, Label, IntervalLabel, TimeDataCollection\n", "\n", "rng = np.random.default_rng(42)\n" ] }, { "cell_type": "markdown", "id": "42010936", "metadata": {}, "source": [ "## 2. Create channels\n", "\n", "A `Channel` stores the primary signal data. Here, we create two channels with absolute timestamps: one longer, slowly varying signal and one shorter signal sampled at a higher rate.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "c652358a", "metadata": {}, "outputs": [], "source": [ "time_a = pd.date_range(\"2020-02-02 00:00\", \"2020-02-05 00:00\", periods=1001)\n", "time_b = pd.date_range(\"2020-02-02 18:00\", \"2020-02-03 20:00\", periods=4001)\n", "\n", "ch1 = Channel(\n", " name=\"synthetic trend\",\n", " time_index=time_a,\n", " data=0.6 + 0.2 * np.sin(np.linspace(0, 6 * np.pi, len(time_a))) + 0.05 * rng.standard_normal(len(time_a)),\n", " time_unit=\"s\",\n", " metadata={\"source\": \"simulated example\", \"units\": \"a.u.\"},\n", ")\n", "\n", "ch2 = Channel(\n", " name=\"synthetic waveform\",\n", " time_index=time_b,\n", " data=2.0 + 0.15 * np.sin(np.linspace(0, 20 * np.pi, len(time_b))) + 0.03 * rng.standard_normal(len(time_b)),\n", " time_unit=\"s\",\n", " metadata={\"source\": \"simulated example\", \"units\": \"a.u.\"},\n", ")\n", "\n", "ch1, ch2\n" ] }, { "cell_type": "markdown", "id": "339a6408", "metadata": {}, "source": [ "## 3. Create labels\n", "\n", "Labels store annotations or derived values. In this example we create:\n", "\n", "- a **local label** attached to `ch1`\n", "- a **global label** that is not attached to any channel\n", "- a **local interval label** attached to `ch2`\n", "- a **global interval label** spanning a broader time window\n", "\n", "Attached labels move together with their channel if a time offset is applied, whereas global labels remain independent.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "067b3932", "metadata": {}, "outputs": [], "source": [ "lab1 = Label(\n", " name=\"local markers\",\n", " time_index=[\"2020-02-02 19:34\", \"2020-02-03 12:34\", \"2020-02-04 12:34\"],\n", " data=[0.9, 0.95, 0.85],\n", " time_unit=\"s\",\n", " anchored_channel=ch1,\n", ")\n", "\n", "lab2 = Label(\n", " name=\"global events\",\n", " time_index=[\"2020-02-03 17:00\", \"2020-02-04 00:30\"],\n", " text_data=[\"review point\", \"handover\"],\n", " time_unit=\"s\",\n", ")\n", "\n", "lab3 = IntervalLabel(\n", " name=\"waveform intervals\",\n", " time_index=[\n", " \"2020-02-02 18:00\",\n", " \"2020-02-02 23:15\",\n", " \"2020-02-03 14:00\",\n", " \"2020-02-03 18:42\",\n", " ],\n", " data=[2.0, 2.0],\n", " time_unit=\"s\",\n", " anchored_channel=ch2,\n", ")\n", "\n", "lab4 = IntervalLabel(\n", " name=\"study phase\",\n", " time_index=[\"2020-02-02 06:00\", \"2020-02-04 06:00\"],\n", " time_unit=\"s\",\n", ")\n", "\n", "collection = TimeDataCollection(channels=[ch1, ch2], labels=[lab1, lab2, lab3, lab4])\n" ] }, { "cell_type": "markdown", "id": "5e28063f", "metadata": {}, "source": [ "## 4. Inspect the collection\n", "\n", "A `TimeDataCollection` groups channels and labels into one object. It keeps track of local versus global labels and provides convenience methods for plotting and querying the stored data.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "5ac11320", "metadata": {}, "outputs": [], "source": [ "print(collection)\n", "print()\n", "collection.print_summary()\n" ] }, { "cell_type": "markdown", "id": "840683d8", "metadata": {}, "source": [ "## 5. Plot channels with attached labels and automatic overviews\n", "\n", "If `include_attached_labels=True`, `vitabel` automatically includes labels attached to the displayed channels. Setting `channel_overviews=True` adds overview panels to support navigation through a longer recording.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "7e09128f", "metadata": {}, "outputs": [], "source": [ "wg1 = collection.plot_interactive(\n", " start=\"2020-02-02 00:00\",\n", " stop=\"2020-02-02 12:00\",\n", " time_unit=\"min\",\n", " include_attached_labels=True,\n", " channel_overviews=True,\n", ")\n", "wg1\n" ] }, { "cell_type": "markdown", "id": "7c773d18", "metadata": {}, "source": [ "## 6. Plot with an explicit subplot layout\n", "\n", "For more control, channels can also be assigned explicitly to subplots. This is the same mechanism used in the larger application-oriented example notebooks.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "ac4c81dc", "metadata": {}, "outputs": [], "source": [ "wg2 = collection.plot_interactive(\n", " channels=[[0], [1]],\n", " labels=[[\"global events\"], [\"study phase\"]],\n", " start=\"2020-02-02 00:00\",\n", " stop=\"2020-02-02 12:00\",\n", " time_unit=\"min\",\n", ")\n", "wg2\n" ] }, { "cell_type": "markdown", "id": "44a87fab", "metadata": {}, "source": [ "## Summary\n", "\n", "This small synthetic example shows how `vitabel` represents time series and annotations independently of any specific file format. Once you are comfortable with these core objects, the larger examples in this documentation show how the same data structures scale to real workflows with imported medical device data.\n", "\n" ] } ], "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.2" } }, "nbformat": 4, "nbformat_minor": 5 }