diff --git a/CHANGELOG.md b/CHANGELOG.md index e7b7808..c185628 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,4 +12,4 @@ and this project adheres to [Semantic Versioning][]. ### Added -- Basic tool, preprocessing and plotting functions +- Basic tool, preprocessing and plotting functions diff --git a/docs/api.md b/docs/api.md index c2ab7a7..32f55e9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -37,7 +37,19 @@ dt.mimic_iv_omop dt.gibleed_omop dt.synthea27nj_omop - dt.mimic_ii + dt.physionet2012 +``` + +## Tools + +```{eval-rst} +.. module:: ehrdata.tl +.. currentmodule:: ehrdata + +.. autosummary:: + :toctree: generated + + tl.omop.EHRDataset ``` ## Plotting diff --git a/docs/contributing.md b/docs/contributing.md index 8a9b28d..1f20499 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -155,11 +155,11 @@ This will automatically create a git tag and trigger a Github workflow that crea Please write documentation for new or changed features and use-cases. This project uses [sphinx][] with the following features: -- The [myst][] extension allows to write documentation in markdown/Markedly Structured Text -- [Numpy-style docstrings][numpydoc] (through the [napoloen][numpydoc-napoleon] extension). -- Jupyter notebooks as tutorials through [myst-nb][] (See [Tutorials with myst-nb](#tutorials-with-myst-nb-and-jupyter-notebooks)) -- [sphinx-autodoc-typehints][], to automatically reference annotated input and output types -- Citations (like {cite:p}`Virshup_2023`) can be included with [sphinxcontrib-bibtex](https://sphinxcontrib-bibtex.readthedocs.io/) +- The [myst][] extension allows to write documentation in markdown/Markedly Structured Text +- [Numpy-style docstrings][numpydoc] (through the [napoloen][numpydoc-napoleon] extension). +- Jupyter notebooks as tutorials through [myst-nb][] (See [Tutorials with myst-nb](#tutorials-with-myst-nb-and-jupyter-notebooks)) +- [sphinx-autodoc-typehints][], to automatically reference annotated input and output types +- Citations (like {cite:p}`Virshup_2023`) can be included with [sphinxcontrib-bibtex](https://sphinxcontrib-bibtex.readthedocs.io/) See scanpy’s {doc}`scanpy:dev/documentation` for more information on how to write your own. @@ -183,10 +183,10 @@ please check out [this feature request][issue-render-notebooks] in the `cookiecu #### Hints -- If you refer to objects from other packages, please add an entry to `intersphinx_mapping` in `docs/conf.py`. - Only if you do so can sphinx automatically create a link to the external documentation. -- If building the documentation fails because of a missing link that is outside your control, - you can add an entry to the `nitpick_ignore` list in `docs/conf.py` +- If you refer to objects from other packages, please add an entry to `intersphinx_mapping` in `docs/conf.py`. + Only if you do so can sphinx automatically create a link to the external documentation. +- If building the documentation fails because of a missing link that is outside your control, + you can add an entry to the `nitpick_ignore` list in `docs/conf.py` (docs-building)= diff --git a/docs/index.md b/docs/index.md index 7065d92..324eab5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,4 +17,5 @@ notebooks/cohort_definition notebooks/study_design_example_omop_cdm notebooks/indwelling_arterial_catheters notebooks/tutorial_time_series_with_pypots +notebooks/omop_ml ``` diff --git a/docs/notebooks/omop_ml.ipynb b/docs/notebooks/omop_ml.ipynb new file mode 100644 index 0000000..25c6b63 --- /dev/null +++ b/docs/notebooks/omop_ml.ipynb @@ -0,0 +1,765 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Deep Learning on Timeseries for the OMOP CDM with ehrdata\n", + "ehrdata offers a deep learning convenience map-style [pytorch dataset](https://pytorch.org/docs/stable/data.html#torch.utils.data.Dataset), EHRDataset.\n", + "This is the input for pytorch's Dataloader, the canonical data loading structure for deep learning models in pytorch.\n", + "\n", + "For more information on the OMOP Common Data Model (CDM), see the notebook on the [OMOP CDM](./omop_tables_tutorial.ipynb).\n", + "\n", + "For more information on advanced time series algorithms, see the notebook on [Time Series Analysis with ehrdata and PyPOTS](./tutorial_time_series_with_pypots.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "Disclaimer: the example usecase is for demonstration purposes only. The data preprocessing, the task definition, and the model setup are meant to be introductory. And as such lack the complexity required for proper inference. But flexible enough to build exactly this on top of it.\n", + "\n", + "## Worked example: Predict in-hospital mortality of ICU patients\n", + "We consider the task of predicting the in-hospital mortality of ICU patients, using public [MIMIC-IV demo dataset in the OMOP Common Data Model](https://physionet.org/content/mimic-iv-demo-omop/0.9/).\n", + "\n", + "Dataset:
\n", + "Kallfelz, M., Tsvetkova, A., Pollard, T., Kwong, M., Lipori, G., Huser, V., Osborn, J., Hao, S., & Williams, A. (2021). MIMIC-IV demo data in the OMOP Common Data Model (version 0.9). PhysioNet. https://doi.org/10.13026/p1f5-7x35." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports\n", + "We start with the required imports" + ] + }, + { + "cell_type": "code", + "execution_count": 267, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import duckdb\n", + "import numpy as np\n", + "from torch.utils.data import DataLoader\n", + "\n", + "import ehrdata as ed\n", + "import ehrapy as ep" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Setup the database\n", + "We use the plug-and-play ehrdata dataset, and duckdb as our RDMS.\n", + "#### Setup a local database connection" + ] + }, + { + "cell_type": "code", + "execution_count": 268, + "metadata": {}, + "outputs": [], + "source": [ + "con = duckdb.connect(\":memory:\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Download the data, and load it into the database\n", + "Convenience dataset available from ehrdata." + ] + }, + { + "cell_type": "code", + "execution_count": 269, + "metadata": {}, + "outputs": [], + "source": [ + "ed.dt.mimic_iv_omop(backend_handle=con)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Define the Cohort\n", + "We start off by considering only patients with a `visit_occurrence` in ICU (`visit_concept_id` in `visit_detail` for ICU: In this dataset, we choose the OMOP Concept IDs\n", + "- 4305366 for Surgical ICU\n", + "- 40481392 for Medical ICU\n", + "- 32037 for Intensive Care\n", + "- 763903 for Trauma ICU\n", + "- 4149943 for Cardiac ICU\n", + "\n", + "If a person had multiple such ICU stays, we select the first.\n", + "\n", + "There are better ways than to delete rows in `visit_occurrence` which do not satisfy our cohort definition from our database, for the toy example this is the fastest." + ] + }, + { + "cell_type": "code", + "execution_count": 270, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 270, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "con.execute(\"\"\"\n", + " WITH RankedVisits AS (\n", + " SELECT\n", + " v.*,\n", + " vd.*,\n", + " ROW_NUMBER() OVER (PARTITION BY v.person_id ORDER BY v.visit_start_date) AS rn\n", + " FROM visit_occurrence v\n", + " JOIN visit_detail vd USING (visit_occurrence_id)\n", + " WHERE vd.visit_detail_concept_id IN (4305366, 40481392, 32037, 763903, 4149943)\n", + " ),\n", + " first_icu_visit_occurrence_id AS (\n", + " SELECT visit_occurrence_id\n", + " FROM RankedVisits\n", + " WHERE rn = 1\n", + " )\n", + " DELETE FROM visit_occurrence\n", + " WHERE visit_occurrence_id NOT IN (SELECT visit_occurrence_id FROM first_icu_visit_occurrence_id)\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Define the variables of interest and the time windows.\n", + "For more information of how we can convert the irregularly-sampled time series into a missing data problem by discretizing the time axis into non-overlapping intervals, see the Notebook on [Extracting, Representing, Validating and Vizualizing Data from an OMOP CDM Database with ehrdata, lamin, and Vitessce](./tutorial_omop_visualization.ipynb).\n", + "\n", + "Here, we decide for the following:\n", + "- We have for each person (n=100) one in-hospital stay; take the start of this hospital stay as the starting point (t=0) for each patient.\n", + "- We consider time-intervals of 1h, for 24h; that is, the first day after ICU admission. If for a patient less recorded data is available, the missing data is padded.\n", + "- We consider the data from the `measurements` table; we consider the numeric `value_as_number` values.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now set up the persons to be considered, together with their observation start and endtimes, retrieved from the `visit_occurrence` table." + ] + }, + { + "cell_type": "code", + "execution_count": 271, + "metadata": {}, + "outputs": [], + "source": [ + "edata = ed.io.omop.setup_obs(\n", + " backend_handle=con,\n", + " observation_table=\"person_visit_occurrence\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the next step, we retrieve all `value_as_number` entries from the `measurements` table:" + ] + }, + { + "cell_type": "code", + "execution_count": 272, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:multiple units for features: [[ 1]\n", + " [ 7]\n", + " [ 31]\n", + " [ 33]\n", + " [ 39]\n", + " [ 44]\n", + " [ 49]\n", + " [ 57]\n", + " [ 60]\n", + " [ 79]\n", + " [ 81]\n", + " [ 83]\n", + " [ 93]\n", + " [110]\n", + " [160]\n", + " [175]\n", + " [186]\n", + " [187]\n", + " [189]\n", + " [190]\n", + " [195]\n", + " [204]\n", + " [207]\n", + " [221]\n", + " [269]\n", + " [273]\n", + " [275]]\n" + ] + } + ], + "source": [ + "edata = ed.io.omop.setup_variables(\n", + " edata=edata,\n", + " backend_handle=con,\n", + " data_tables=[\"measurement\"],\n", + " data_field_to_keep=[\"value_as_number\"],\n", + " interval_length_number=1,\n", + " interval_length_unit=\"h\",\n", + " num_intervals=24,\n", + " concept_ids=\"all\",\n", + " aggregation_strategy=\"last\",\n", + " instantiate_tensor=False,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NOTE: this could/should become an ehrdata API call.\n", + "\n", + "We drop features which are not measured in at least 10 patients 1x." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 273, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# delete all rows for which data_table_concept_id is NOT NULL in at least 10 different patient_id s\n", + "con.execute(\"\"\"\n", + " WITH concept_ids_to_delete AS (\n", + "\n", + " SELECT\n", + " data_table_concept_id\n", + " FROM long_person_timestamp_feature_value\n", + " WHERE value_as_number IS NOT NULL\n", + " GROUP BY data_table_concept_id\n", + " HAVING COUNT(DISTINCT person_id) <= 10\n", + "\n", + " UNION\n", + "\n", + " SELECT\n", + " data_table_concept_id\n", + " FROM long_person_timestamp_feature_value\n", + " GROUP BY data_table_concept_id\n", + " HAVING COUNT(value_as_number) = 0\n", + " )\n", + "\n", + " DELETE FROM long_person_timestamp_feature_value\n", + " WHERE data_table_concept_id IN (\n", + " SELECT data_table_concept_id FROM concept_ids_to_delete\n", + " );\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "NOTE: this could/should become an ehrdata API call.\n", + "\n", + "For model simpliclity, we conduct forward filling of the variables:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 274, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "con.execute(\"\"\"\n", + "WITH filled_measurements AS (\n", + " SELECT\n", + " person_id,\n", + " interval_step,\n", + " data_table_concept_id,\n", + " COALESCE(value_as_number,\n", + " LAST_VALUE(value_as_number IGNORE NULLS)\n", + " OVER (PARTITION BY person_id, data_table_concept_id\n", + " ORDER BY interval_step\n", + " ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)\n", + " ) AS filled_value\n", + " FROM long_person_timestamp_feature_value\n", + ")\n", + "UPDATE long_person_timestamp_feature_value\n", + "SET value_as_number = fm.filled_value\n", + "FROM filled_measurements as fm\n", + "WHERE long_person_timestamp_feature_value.person_id = fm.person_id\n", + "AND long_person_timestamp_feature_value.interval_step = fm.interval_step\n", + "AND long_person_timestamp_feature_value.data_table_concept_id = fm.data_table_concept_id;\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And for all values not captured in forward fill, we impute the missing value for person x, feature f, time step t as the mean of all other persons feature f at timestep t." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 275, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "con.execute(\"\"\"\n", + "WITH feature_means AS (\n", + " SELECT\n", + " interval_step,\n", + " data_table_concept_id,\n", + " AVG(value_as_number) AS mean_value\n", + " FROM long_person_timestamp_feature_value\n", + " WHERE value_as_number IS NOT NULL\n", + " GROUP BY interval_step, data_table_concept_id\n", + "),\n", + "filled_values AS (\n", + " SELECT\n", + " lptfv.person_id,\n", + " lptfv.interval_step,\n", + " lptfv.data_table_concept_id,\n", + " COALESCE(lptfv.value_as_number, fm.mean_value) AS filled_value\n", + " FROM long_person_timestamp_feature_value lptfv\n", + " LEFT JOIN feature_means fm\n", + " ON lptfv.interval_step = fm.interval_step\n", + " AND lptfv.data_table_concept_id = fm.data_table_concept_id\n", + ")\n", + "UPDATE long_person_timestamp_feature_value\n", + "SET value_as_number = fm.filled_value\n", + "FROM filled_values as fm\n", + "WHERE long_person_timestamp_feature_value.person_id = fm.person_id\n", + "AND long_person_timestamp_feature_value.interval_step = fm.interval_step\n", + "AND long_person_timestamp_feature_value.data_table_concept_id = fm.data_table_concept_id;\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deep Learning Model\n", + "#### Data Loading" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The tensor of shape n_obs x n_vars x num_intervals has been prepared in the RDBMS.\n", + "We can now create an `EHRDataset`, which is a subclass of pytorch's Dataset, and will stream the data for a deep learning model from the database." + ] + }, + { + "cell_type": "code", + "execution_count": 276, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = ed.tl.omop.EHRDataset(con, edata, batch_size=5, idxs=None)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `EHRDataset`, as subclass of pytorch's `Dataset`, can be used right away for creating a pytorch `Dataloader`." + ] + }, + { + "cell_type": "code", + "execution_count": 277, + "metadata": {}, + "outputs": [], + "source": [ + "loader = DataLoader(dataset, batch_size=4, shuffle=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Model definition\n", + "We create a simple model for time series based on a Recurrent Neural Network in pytorch.\n", + "More advanced models interoperable with ehrdata are showcased in [Time Series Analysis with ehrdata and PyPOTS](./tutorial_time_series_with_pypots.ipynb). However, PyPOTS does not support a pytorch Dataloader as input." + ] + }, + { + "cell_type": "code", + "execution_count": 278, + "metadata": {}, + "outputs": [], + "source": [ + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "\n", + "\n", + "class RNN_Model(nn.Module):\n", + " \"\"\"RNN Model.\"\"\"\n", + "\n", + " def __init__(self, input_size, hidden_size, num_layers, num_classes):\n", + " super().__init__()\n", + " self.rnn = nn.RNN(input_size, hidden_size, num_layers, batch_first=True)\n", + " self.fc = nn.Linear(hidden_size, num_classes)\n", + "\n", + " def _prepare_batch(self, batch):\n", + " x, target = batch\n", + " return torch.transpose(x, 2, 1), target.flatten().to(torch.long)\n", + "\n", + " def forward(self, x):\n", + " \"\"\"Forward method.\"\"\"\n", + " # x: (batch_size, seq_len, input_size)\n", + " out, _ = self.rnn(x)\n", + " out = out[:, -1, :]\n", + "\n", + " # out: (batch_size, num_classes)\n", + " logits = self.fc(out)\n", + " return out, logits\n", + "\n", + " def training_step(self, batch):\n", + " \"\"\"Training step.\"\"\"\n", + " x, target = self._prepare_batch(batch)\n", + " out, logits = self(x)\n", + " loss = F.cross_entropy(logits, target)\n", + " return loss\n", + "\n", + " def fit(self, loader, epochs=10):\n", + " \"\"\"Fit method.\"\"\"\n", + " self.train_loss = []\n", + " optimizer = torch.optim.Adam(self.parameters(), lr=0.01)\n", + " for epoch in range(epochs):\n", + " batch_loss = []\n", + " for batch in loader:\n", + " optimizer.zero_grad()\n", + " loss = self.training_step(batch)\n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " batch_loss.append(loss.item())\n", + "\n", + " self.train_loss.append(np.mean(np.array(batch_loss)))\n", + " print(f\"Epoch {epoch}, Loss: {np.mean(np.array(batch_loss))}\")\n", + "\n", + " def predict(self, loader, soft=True):\n", + " \"\"\"Predict method.\"\"\"\n", + " predictions = []\n", + " with torch.no_grad():\n", + " for batch in loader:\n", + " x, target = self._prepare_batch(batch)\n", + " _, classification_logits = self(x)\n", + " if soft:\n", + " predicted = torch.softmax(classification_logits, 1)\n", + " else:\n", + " predicted = torch.max(classification_logits, 1)\n", + " predictions.append(predicted)\n", + " return torch.cat(predictions)\n", + "\n", + " def represent(self, loader):\n", + " \"\"\"Represent method.\"\"\"\n", + " representations = []\n", + " with torch.no_grad():\n", + " for batch in loader:\n", + " x, target = self._prepare_batch(batch)\n", + " output, _ = self(x)\n", + " representations.append(output)\n", + " return torch.cat(representations)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Model training" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0, Loss: 0.3569712014496326\n", + "Epoch 1, Loss: 0.3650100648403168\n", + "Epoch 2, Loss: 0.3504540580511093\n", + "Epoch 3, Loss: 0.3471323770284653\n", + "Epoch 4, Loss: 0.3453905090689659\n" + ] + } + ], + "source": [ + "model = RNN_Model(\n", + " input_size=129,\n", + " hidden_size=16,\n", + " num_layers=1,\n", + " num_classes=2,\n", + ")\n", + "model.fit(loader, epochs=5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Model prediction\n", + "Classification could look like this" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8645, 0.1355],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9056, 0.0944],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.8114, 0.1886],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9056, 0.0944],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8542, 0.1458],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.7546, 0.2454],\n", + " [0.9055, 0.0945],\n", + " [0.8678, 0.1322],\n", + " [0.9055, 0.0945],\n", + " [0.8138, 0.1862],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945],\n", + " [0.9055, 0.0945]])" + ] + }, + "execution_count": 280, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.predict(loader)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Model representation\n", + "Futher, could illustrate patient representation:" + ] + }, + { + "cell_type": "code", + "execution_count": 281, + "metadata": {}, + "outputs": [], + "source": [ + "edata.obsm[\"last_step_representation\"] = np.array(model.represent(loader))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ep.pp.neighbors(edata, use_rep=\"last_step_representation\")\n", + "ep.tl.umap(edata)\n", + "ep.pl.umap(\n", + " edata, color=\"discharge_to_source_value\", title=\"UMAP of RNN representation after 24h colored by discharge note\"\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ehrapy_venv_oct", + "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.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/notebooks/tutorial_time_series_with_pypots.ipynb b/docs/notebooks/tutorial_time_series_with_pypots.ipynb index 3fbe3d2..4e4c9d6 100644 --- a/docs/notebooks/tutorial_time_series_with_pypots.ipynb +++ b/docs/notebooks/tutorial_time_series_with_pypots.ipynb @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -41,9 +41,35 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/pypots/nn/modules/reformer/local_attention.py:31: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n", + " @autocast(enabled=False)\n", + "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/pypots/nn/modules/reformer/local_attention.py:98: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n", + " @autocast(enabled=False)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[34m\n", + "████████╗██╗███╗ ███╗███████╗ ███████╗███████╗██████╗ ██╗███████╗███████╗ █████╗ ██╗\n", + "╚══██╔══╝██║████╗ ████║██╔════╝ ██╔════╝██╔════╝██╔══██╗██║██╔════╝██╔════╝ ██╔══██╗██║\n", + " ██║ ██║██╔████╔██║█████╗█████╗███████╗█████╗ ██████╔╝██║█████╗ ███████╗ ███████║██║\n", + " ██║ ██║██║╚██╔╝██║██╔══╝╚════╝╚════██║██╔══╝ ██╔══██╗██║██╔══╝ ╚════██║ ██╔══██║██║\n", + " ██║ ██║██║ ╚═╝ ██║███████╗ ███████║███████╗██║ ██║██║███████╗███████║██╗██║ ██║██║\n", + " ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝\n", + "ai4ts v0.0.3 - building AI for unified time-series analysis, https://time-series.ai \u001b[0m\n", + "\n" + ] + } + ], "source": [ "import duckdb\n", "import ehrdata as ed\n", @@ -60,72 +86,24 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 3, "metadata": {}, "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO - Downloading Synthea27Nj_5.4.zip from https://github.com/OHDSI/EunomiaDatasets/raw/main/datasets/Synthea27Nj/Synthea27Nj_5.4.zip to /var/folders/yy/60ln_681745_fjjwvgwm_nyc0000gn/T/tmpfndmdvwt/Synthea27Nj_5.4.zip\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "254776f379994eeab1835ffe42fe89a1", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n"
-      ],
-      "text/plain": []
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "INFO - Extracted archive Synthea27Nj_5.4.zip from /var/folders/yy/60ln_681745_fjjwvgwm_nyc0000gn/T/tmpfndmdvwt/Synthea27Nj_5.4.zip to ehrapy_data/Synthea27Nj_5.4/Synthea27Nj_5.4\n",
-      "INFO - missing tables: []\n",
-      "INFO - unused files: ['EPISODE.csv', '__MACOSX', 'EPISODE_EVENT.csv']\n"
-     ]
-    },
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "multiple units for features: []\n"
+     "ename": "",
+     "evalue": "",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31mCannot execute code, session has been disposed. Please try restarting the Kernel."
      ]
     },
     {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/anndata/_core/aligned_df.py:68: ImplicitModificationWarning: Transforming to str index.\n",
-      "  warnings.warn(\"Transforming to str index.\", ImplicitModificationWarning)\n",
-      "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/pandas/core/generic.py:3331: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n",
-      "  return xarray.Dataset.from_dataframe(self)\n",
-      "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/pandas/core/generic.py:3331: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n",
-      "  return xarray.Dataset.from_dataframe(self)\n",
-      "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/pandas/core/generic.py:3331: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n",
-      "  return xarray.Dataset.from_dataframe(self)\n",
-      "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/pandas/core/generic.py:3331: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n",
-      "  return xarray.Dataset.from_dataframe(self)\n",
-      "/Users/eljas.roellin/Documents/ehrapy_workspace/ehrapy_venv_oct/lib/python3.11/site-packages/anndata/_core/aligned_df.py:68: ImplicitModificationWarning: Transforming to str index.\n",
-      "  warnings.warn(\"Transforming to str index.\", ImplicitModificationWarning)\n"
+     "ename": "",
+     "evalue": "",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[1;31mCannot execute code, session has been disposed. Please try restarting the Kernel. \n",
+      "\u001b[1;31mView Jupyter log for further details."
      ]
     }
    ],
diff --git a/pyproject.toml b/pyproject.toml
index b9f4a39..68fba82 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,7 +39,7 @@ optional-dependencies.dev = [
 ]
 optional-dependencies.doc = [
   "docutils>=0.8,!=0.18.*,!=0.19.*",
-  "ehrdata[lamin,vitessce]",
+  "ehrdata[torch,lamin,vitessce]",
   "ipykernel",
   "ipython",
   "myst-nb>=1.1",
@@ -66,9 +66,13 @@ optional-dependencies.lamin = [
 ]
 optional-dependencies.test = [
   "coverage",
-  "ehrdata[vitessce,lamin]",
+  "ehrdata[torch,vitessce,lamin]",
   "pytest",
 ]
+optional-dependencies.torch = [
+  "torch",
+]
+
 optional-dependencies.vitessce = [
   "vitessce[all]>=3.4", # the actual dependency
   "zarr<3",             # vitessce does not support zarr>=3
diff --git a/src/ehrdata/__init__.py b/src/ehrdata/__init__.py
index eb69f06..fb7769b 100644
--- a/src/ehrdata/__init__.py
+++ b/src/ehrdata/__init__.py
@@ -1,8 +1,8 @@
 from importlib.metadata import version
 
-from . import dt, io, pl
+from . import dt, io, pl, tl
 from .core import EHRData
 
-__all__ = ["EHRData", "dt", "io", "pl"]
+__all__ = ["EHRData", "dt", "io", "tl", "pl"]
 
 __version__ = version("ehrdata")
diff --git a/src/ehrdata/core/_optional_modules_import.py b/src/ehrdata/core/_optional_modules_import.py
new file mode 100644
index 0000000..aee28a4
--- /dev/null
+++ b/src/ehrdata/core/_optional_modules_import.py
@@ -0,0 +1,9 @@
+def lazy_import_torch():
+    try:
+        import torch
+
+        return torch
+    except ImportError:
+        raise ImportError(
+            "The optional module 'torch' is not installed. Please install it using 'pip install ehrdata[torch]'."
+        ) from None
diff --git a/src/ehrdata/dt/datasets.py b/src/ehrdata/dt/datasets.py
index 94a6e2e..2ddba6c 100644
--- a/src/ehrdata/dt/datasets.py
+++ b/src/ehrdata/dt/datasets.py
@@ -44,7 +44,7 @@ def _setup_eunomia_datasets(
 def mimic_iv_omop(backend_handle: DuckDBPyConnection, data_path: Path | None = None) -> None:
     """Loads the MIMIC-IV demo data in the OMOP Common Data model.
 
-    This function loads the MIMIC-IV demo dataset from its `physionet repository _` .
+    This function loads the MIMIC-IV demo dataset from its `physionet repository `_.
     See also this link for more details.
 
     DOI https://doi.org/10.13026/2d25-8g07.
@@ -85,7 +85,7 @@ def mimic_iv_omop(backend_handle: DuckDBPyConnection, data_path: Path | None = N
 def gibleed_omop(backend_handle: DuckDBPyConnection, data_path: Path | None = None) -> None:
     """Loads the GiBleed dataset in the OMOP Common Data model.
 
-    This function loads the GIBleed dataset from the `EunomiaDatasets repository _`.
+    This function loads the GIBleed dataset from the `EunomiaDatasets repository `_.
     More details: https://github.com/OHDSI/EunomiaDatasets/tree/main/datasets/GiBleed.
 
     Parameters
@@ -124,7 +124,7 @@ def gibleed_omop(backend_handle: DuckDBPyConnection, data_path: Path | None = No
 def synthea27nj_omop(backend_handle: DuckDBPyConnection, data_path: Path | None = None) -> None:
     """Loads the Synthea27Nj dataset in the OMOP Common Data model.
 
-    This function loads the Synthea27Nj dataset from the `EunomiaDatasets repository _`.
+    This function loads the Synthea27Nj dataset from the `EunomiaDatasets repository `_.
     More details: https://github.com/OHDSI/EunomiaDatasets/tree/main/datasets/Synthea27Nj.
 
     Parameters
@@ -186,13 +186,13 @@ def physionet2012(
         "142998",
     ],
 ) -> EHRData:
-    """Loads the dataset of the `PhysioNet challenge 2012 (v1.0.0) _`.
+    """Loads the dataset of the `PhysioNet challenge 2012 (v1.0.0) `_.
 
-    If interval_length_number is 1, interval_length_unit is "h" (hour), and num_intervals is 48, this is equivalent to the SAITS preprocessing (insert paper/link/citation).
+    If interval_length_number is 1, interval_length_unit is "h" (hour), and num_intervals is 48, this is equivalent to the `SAITS `_ preprocessing.
     Truncated if a sample has more num_intervals steps; Padded if a sample has less than num_intervals steps.
     Further, by default the following 12 samples are dropped since they have no time series information at all: 147514, 142731, 145611, 140501, 155655, 143656, 156254, 150309,
     140936, 141264, 150649, 142998.
-    Taken the defaults of interval_length_number, interval_length_unit, num_intervals, and drop_samples, the tensor stored in .r of edata is the same as when doing the PyPOTS  preprocessing.
+    Taken the defaults of interval_length_number, interval_length_unit, num_intervals, and drop_samples, the tensor stored in .r of edata is the same as when doing the `PyPOTS `_ preprocessing.
     A simple deviation is that the tensor in ehrdata is of shape n_obs x n_vars x n_intervals (with defaults, 3000x37x48) while the tensor in PyPOTS is of shape n_obs x n_intervals x n_vars (3000x48x37).
     The tensor stored in .r is hence also fully compatible with the PyPOTS package, as the .r tensor of EHRData objects generally is.
 
diff --git a/src/ehrdata/io/omop/_queries.py b/src/ehrdata/io/omop/_queries.py
index f1937c5..552bd3c 100644
--- a/src/ehrdata/io/omop/_queries.py
+++ b/src/ehrdata/io/omop/_queries.py
@@ -115,7 +115,7 @@ def _generate_value_query(data_table: str, data_field_to_keep: Sequence, aggrega
     return is_present_query + value_query
 
 
-def _time_interval_table(
+def _write_long_time_interval_table(
     backend_handle: duckdb.duckdb.DuckDBPyConnection,
     time_defining_table: str,
     data_table: str,
@@ -125,7 +125,7 @@ def _time_interval_table(
     aggregation_strategy: str,
     data_field_to_keep: Sequence[str] | str,
     keep_date: str = "",
-):
+) -> None:
     if isinstance(data_field_to_keep, str):
         data_field_to_keep = [data_field_to_keep]
 
@@ -139,6 +139,8 @@ def _time_interval_table(
         timedeltas_dataframe,
     )
 
+    create_long_table_query = "CREATE TABLE long_person_timestamp_feature_value AS\n"
+
     # multi-step query
     # 1. Create person_time_defining_table, which matches the one created for obs. Needs to contain the person_id, and the start date in particular.
     # 2. Create person_data_table (data_table is typically measurement), which contains the cross product of person_id and the distinct concept_id s.
@@ -196,10 +198,23 @@ def _time_interval_table(
         GROUP BY lfi.person_id, lfi.data_table_concept_id, interval_step, interval_start, interval_end
         """
 
-    query = prepare_alias_query + select_query
+    query = create_long_table_query + prepare_alias_query + select_query
 
-    df = backend_handle.execute(query).df()
+    backend_handle.execute("DROP TABLE IF EXISTS long_person_timestamp_feature_value")
+    backend_handle.execute(query)
 
-    _drop_timedeltas(backend_handle)
+    add_person_range_index_query = """
+        ALTER TABLE long_person_timestamp_feature_value
+        ADD COLUMN person_index INTEGER;
 
-    return df
+        WITH RankedPersons AS (
+            SELECT person_id,
+                ROW_NUMBER() OVER (ORDER BY person_id) - 1 AS idx
+            FROM (SELECT DISTINCT person_id FROM long_person_timestamp_feature_value) AS unique_persons
+        )
+        UPDATE long_person_timestamp_feature_value
+        SET person_index = RP.idx
+        FROM RankedPersons RP
+        WHERE long_person_timestamp_feature_value.person_id = RP.person_id;
+    """
+    backend_handle.execute(add_person_range_index_query)
diff --git a/src/ehrdata/io/omop/omop.py b/src/ehrdata/io/omop/omop.py
index 4561fca..a10f2ac 100644
--- a/src/ehrdata/io/omop/omop.py
+++ b/src/ehrdata/io/omop/omop.py
@@ -32,7 +32,7 @@
     _check_valid_observation_table,
     _check_valid_variable_data_tables,
 )
-from ehrdata.io.omop._queries import _time_interval_table
+from ehrdata.io.omop._queries import _write_long_time_interval_table
 
 DOWNLOAD_VERIFICATION_TAG = "download_verification_tag"
 
@@ -65,7 +65,7 @@ def _set_up_duckdb(path: Path, backend_handle: DuckDBPyConnection, prefix: str =
                 dtype = None
 
             # read raw csv as temporary table
-            temp_relation = backend_handle.read_csv(path / file_name, dtype=dtype, escapechar="%")  # noqa: F841
+            temp_relation = backend_handle.read_csv(path / file_name, dtype=dtype)  # noqa: F841
             backend_handle.execute("CREATE OR REPLACE TABLE temp_table AS SELECT * FROM temp_relation")
 
             # make query to create table with lowercase column names
@@ -96,26 +96,32 @@ def _set_up_duckdb(path: Path, backend_handle: DuckDBPyConnection, prefix: str =
     logging.info(f"unused files: {unused_files}")
 
 
-def _collect_units_per_feature(ds, unit_key="unit_concept_id") -> dict:
+def _collect_units_per_feature(backend_handle, unit_key="unit_concept_id") -> dict:
+    query = f"""
+    SELECT DISTINCT data_table_concept_id, {unit_key} FROM long_person_timestamp_feature_value
+    WHERE is_present = 1
+    """
+    result = backend_handle.execute(query).fetchall()
+
     feature_units = {}
-    for i in range(ds[unit_key].shape[1]):
-        single_feature_units = ds[unit_key].isel({ds[unit_key].dims[1]: i})
-        single_feature_units_flat = np.array(single_feature_units).flatten()
-        single_feature_units_unique = pd.unique(single_feature_units_flat[~pd.isna(single_feature_units_flat)])
-        feature_units[ds["data_table_concept_id"][i].item()] = single_feature_units_unique
+    for feature, unit in result:
+        if feature in feature_units:
+            feature_units[feature].append(unit)
+        else:
+            feature_units[feature] = [unit]
     return feature_units
 
 
-def _check_one_unit_per_feature(ds, unit_key="unit_concept_id") -> None:
-    feature_units = _collect_units_per_feature(ds, unit_key=unit_key)
+def _check_one_unit_per_feature(backend_handle, unit_key="unit_concept_id") -> None:
+    feature_units = _collect_units_per_feature(backend_handle, unit_key=unit_key)
     num_units = np.array([len(units) for _, units in feature_units.items()])
 
     # print(f"no units for features: {np.argwhere(num_units == 0)}")
-    print(f"multiple units for features: {np.argwhere(num_units > 1)}")
+    logging.warning(f"multiple units for features: {np.argwhere(num_units > 1)}")
 
 
-def _create_feature_unit_concept_id_report(backend_handle, ds) -> pd.DataFrame:
-    feature_units_concept = _collect_units_per_feature(ds, unit_key="unit_concept_id")
+def _create_feature_unit_concept_id_report(backend_handle) -> pd.DataFrame:
+    feature_units_concept = _collect_units_per_feature(backend_handle, unit_key="unit_concept_id")
 
     feature_units_long_format = []
     for feature, units in feature_units_concept.items():
@@ -257,12 +263,18 @@ def setup_variables(
     aggregation_strategy: str = "last",
     enrich_var_with_feature_info: bool = False,
     enrich_var_with_unit_info: bool = False,
+    instantiate_tensor: bool = True,
 ):
     """Setup the variables.
 
     This function sets up the variables for the EHRData object.
     It will fail if there is more than one unit_concept_id per feature.
     Writes a unit report of the features to edata.uns["unit_report_"].
+    Writes the setup arguments into edata.uns["omop_io_variable_setup"].
+
+    Stores a table named `long_person_timestamp_feature_value` in long format in the RDBMS.
+    This table is instantiated into edata.r if `instantiate_tensor` is set to True;
+    otherwise, the table is only stored in the RDBMS for later use.
 
     Parameters
     ----------
@@ -289,7 +301,9 @@ def setup_variables(
     enrich_var_with_feature_info
         Whether to enrich the var table with feature information. If a concept_id is not found in the concept table, the feature information will be NaN.
     enrich_var_with_unit_info
-        Whether to enrich the var table with unit information. Raises an Error if a) multiple units per feature are found for at least one feature. If a concept_id is not found in the concept table, the feature information will be NaN.
+        Whether to enrich the var table with unit information. Raises an Error if multiple units per feature are found for at least one feature. For entire missing data points, the units are ignored. For observed data points with missing unit information (NULL in either unit_concept_id or unit_source_value), the value NULL/NaN is considered a single unit.
+    instantiate_tensor
+        Whether to instantiate the tensor into the .r field of the EHRData object.
 
     Returns
     -------
@@ -331,27 +345,21 @@ def setup_variables(
         logging.warning(f"No data found in {data_tables[0]}. Returning edata without additional variables.")
         return edata
 
-    ds = (
-        _time_interval_table(
-            backend_handle=backend_handle,
-            time_defining_table=time_defining_table,
-            data_table=data_tables[0],
-            data_field_to_keep=data_field_to_keep,
-            interval_length_number=interval_length_number,
-            interval_length_unit=interval_length_unit,
-            num_intervals=num_intervals,
-            aggregation_strategy=aggregation_strategy,
-        )
-        .set_index(["person_id", "data_table_concept_id", "interval_step"])
-        .to_xarray()
+    _write_long_time_interval_table(
+        backend_handle=backend_handle,
+        time_defining_table=time_defining_table,
+        data_table=data_tables[0],
+        data_field_to_keep=data_field_to_keep,
+        interval_length_number=interval_length_number,
+        interval_length_unit=interval_length_unit,
+        num_intervals=num_intervals,
+        aggregation_strategy=aggregation_strategy,
     )
 
-    _check_one_unit_per_feature(ds)
-    # TODO ignore? go with more vanilla omop style. _check_one_unit_per_feature(ds, unit_key="unit_source_value")
-
-    unit_report = _create_feature_unit_concept_id_report(backend_handle, ds)
+    _check_one_unit_per_feature(backend_handle)
+    unit_report = _create_feature_unit_concept_id_report(backend_handle)
 
-    var = ds["data_table_concept_id"].to_dataframe()
+    var = backend_handle.execute("SELECT DISTINCT data_table_concept_id FROM long_person_timestamp_feature_value").df()
 
     if enrich_var_with_feature_info or enrich_var_with_unit_info:
         concepts = backend_handle.sql("SELECT * FROM concept").df()
@@ -381,9 +389,19 @@ def setup_variables(
                 suffixes=("", "_unit"),
             )
 
-    t = ds["interval_step"].to_dataframe()
+    t = pd.DataFrame({"interval_step": np.arange(num_intervals)})
 
-    edata = EHRData(r=ds[data_field_to_keep[0]].values, obs=edata.obs, var=var, uns=edata.uns, t=t)
+    if instantiate_tensor:
+        ds = (
+            (backend_handle.execute("SELECT * FROM long_person_timestamp_feature_value").df())
+            .set_index(["person_id", "data_table_concept_id", "interval_step"])
+            .to_xarray()
+        )
+
+    else:
+        ds = None
+
+    edata = EHRData(r=ds[data_field_to_keep[0]].values if ds else None, obs=edata.obs, var=var, uns=edata.uns, t=t)
     edata.uns[f"unit_report_{data_tables[0]}"] = unit_report
 
     return edata
@@ -403,6 +421,7 @@ def setup_interval_variables(
     enrich_var_with_feature_info: bool = False,
     enrich_var_with_unit_info: bool = False,
     keep_date: Literal["start", "end", "interval"] = "start",
+    instantiate_tensor: bool = True,
 ):
     """Setup the interval variables
 
@@ -436,6 +455,8 @@ def setup_interval_variables(
         Whether to enrich the var table with feature information. If a concept_id is not found in the concept table, the feature information will be NaN.
     date_type
         Whether to keep the start or end date, or the interval span.
+    instantiate_tensor
+        Whether to instantiate the tensor into the .r field of the EHRData object.
 
     Returns
     -------
@@ -466,23 +487,26 @@ def setup_interval_variables(
         logging.warning(f"No data in {data_tables}.")
         return edata
 
+    _write_long_time_interval_table(
+        backend_handle=backend_handle,
+        time_defining_table=time_defining_table,
+        data_table=data_tables[0],
+        data_field_to_keep=data_field_to_keep,
+        interval_length_number=interval_length_number,
+        interval_length_unit=interval_length_unit,
+        num_intervals=num_intervals,
+        aggregation_strategy=aggregation_strategy,
+        keep_date=keep_date,
+    )
+
     ds = (
-        _time_interval_table(
-            backend_handle=backend_handle,
-            time_defining_table=time_defining_table,
-            data_table=data_tables[0],
-            data_field_to_keep=data_field_to_keep,
-            interval_length_number=interval_length_number,
-            interval_length_unit=interval_length_unit,
-            num_intervals=num_intervals,
-            aggregation_strategy=aggregation_strategy,
-            keep_date=keep_date,
-        )
+        backend_handle.execute("SELECT * FROM long_person_timestamp_feature_value")
+        .df()
         .set_index(["person_id", "data_table_concept_id", "interval_step"])
         .to_xarray()
     )
 
-    var = ds["data_table_concept_id"].to_dataframe()
+    var = backend_handle.execute("SELECT DISTINCT data_table_concept_id FROM long_person_timestamp_feature_value").df()
 
     if enrich_var_with_feature_info or enrich_var_with_unit_info:
         concepts = backend_handle.sql("SELECT * FROM concept").df()
@@ -491,7 +515,7 @@ def setup_interval_variables(
     if enrich_var_with_feature_info:
         var = pd.merge(var, concepts, how="left", left_index=True, right_on="concept_id")
 
-    t = ds["interval_step"].to_dataframe()
+    t = pd.DataFrame({"interval_step": np.arange(num_intervals)})
 
     edata = EHRData(r=ds[data_field_to_keep[0]].values, obs=edata.obs, var=var, uns=edata.uns, t=t)
 
diff --git a/src/ehrdata/tl/__init__.py b/src/ehrdata/tl/__init__.py
new file mode 100644
index 0000000..1c16543
--- /dev/null
+++ b/src/ehrdata/tl/__init__.py
@@ -0,0 +1 @@
+from . import omop
diff --git a/src/ehrdata/tl/omop/__init__.py b/src/ehrdata/tl/omop/__init__.py
new file mode 100644
index 0000000..4ba6ce9
--- /dev/null
+++ b/src/ehrdata/tl/omop/__init__.py
@@ -0,0 +1 @@
+from ._dataset import EHRDataset
diff --git a/src/ehrdata/tl/omop/_dataset.py b/src/ehrdata/tl/omop/_dataset.py
new file mode 100644
index 0000000..723ab5b
--- /dev/null
+++ b/src/ehrdata/tl/omop/_dataset.py
@@ -0,0 +1,124 @@
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Literal
+
+from duckdb.duckdb import DuckDBPyConnection
+
+from ehrdata.core._optional_modules_import import lazy_import_torch
+from ehrdata.io.omop._queries import DATA_TABLE_DATE_KEYS
+
+torch = lazy_import_torch()
+
+if TYPE_CHECKING:
+    import torch
+
+
+class EHRDataset(torch.utils.data.Dataset):
+    def __init__(
+        self,
+        con: DuckDBPyConnection,
+        edata,
+        batch_size: int = 10,
+        target: Literal["mortality"] = "mortality",
+        datetime: bool = True,
+        idxs: Sequence[int] | None = None,
+    ) -> torch.utils.data.Dataset:
+        """Return a torch.utils.data.Dataset object for EHR data.
+
+        This function builds a torch.utils.data.Dataset object for EHR data. The EHR data is assumed to be in the OMOP CDM format.
+        It is a Dataset structure for the tensor in ehrdata.r, in a suitable format for pytorch.utils.data.DataLoader.
+        This allows to stream the data in batches from the RDBMS, not requiring to load the entire dataset in memory.
+
+        Parameters
+        ----------
+        con
+            The connection to the database.
+        edata
+            The EHRData object.
+        batch_size
+            The batch size.
+        target
+            The target variable to be used.
+        datetime
+            If True, use datetime, if False, use date.
+        idxs
+            The indices of the patients to be used, can be used to include only a subset of the data, for e.g. train-test splits.
+            The observation table to be used.
+
+        Returns
+        -------
+        EHRDataset
+            A torch.utils.data.Dataset object of the .r tensor in ehrdata.
+        """
+        super().__init__()
+        self.con = con
+        self.edata = edata
+        self.target = target
+        self.datetime = datetime
+        self.idxs = idxs
+
+        self.n_timesteps = con.execute(
+            "SELECT COUNT(DISTINCT interval_step) FROM long_person_timestamp_feature_value"
+        ).fetchone()[0]
+        self.n_variables = con.execute(
+            "SELECT COUNT(DISTINCT data_table_concept_id) FROM long_person_timestamp_feature_value"
+        ).fetchone()[0]
+
+    def __len__(self):
+        if self.idxs:
+            where_clause = f"WHERE person_id IN ({','.join(str(_) for _ in self.idxs)})"
+        else:
+            where_clause = ""
+        query = f"""
+            SELECT COUNT(DISTINCT person_id)
+            FROM long_person_timestamp_feature_value
+            {where_clause}
+        """
+        return self.con.execute(query).fetchone()[0]
+
+    def __getitem__(self, person_index):
+        person_id_query = (
+            f"SELECT DISTINCT person_id FROM long_person_timestamp_feature_value WHERE person_index = {person_index}"
+        )
+        person_id = self.con.execute(person_id_query).fetchone()[0]
+        where_clause = f"WHERE person_index = {person_index}"
+
+        if self.idxs:
+            where_clause += f" AND person_index IN ({','.join(str(_) for _ in self.idxs)})"
+
+        query = f"""
+            SELECT person_index, data_table_concept_id, interval_step, COALESCE(CAST(value_as_number AS DOUBLE), 'NaN') AS value_as_number
+            FROM long_person_timestamp_feature_value
+            {where_clause}
+        """
+
+        long_format_data = torch.tensor(self.con.execute(query).fetchall(), dtype=torch.float32)
+
+        # convert long format to 3D tensor
+        feature_ids, feature_idx = torch.unique(long_format_data[:, 1], return_inverse=True)
+        step_ids, step_idx = torch.unique(long_format_data[:, 2], return_inverse=True)
+
+        result = torch.zeros(len(feature_ids), len(step_ids))
+        values = long_format_data[:, 3]
+        result.index_put_((feature_idx, step_idx), values)
+
+        if self.target != "mortality":
+            raise NotImplementedError(f"Target {self.target} is not implemented")
+
+        # If person has an entry in the death table that is within visit_start_datetime and visit_end_datetime of the visit_occurrence table, report 1, else 0:
+        # Left join ensures that for every patient, 0 or 1 is obtained
+        omop_io_observation_table = self.edata.uns["omop_io_observation_table"]
+        time_postfix = "time" if self.datetime else ""
+        target_query = f"""
+        SELECT
+            CASE
+                WHEN death_datetime BETWEEN {DATA_TABLE_DATE_KEYS["start"][omop_io_observation_table]}{time_postfix} AND {DATA_TABLE_DATE_KEYS["end"][omop_io_observation_table]}{time_postfix} THEN 1
+                ELSE 0
+            END AS mortality
+        FROM {self.edata.uns["omop_io_observation_table"]}
+        LEFT JOIN death USING (person_id)
+        WHERE person_id = {person_id} AND {omop_io_observation_table}_id = {self.edata.obs[self.edata.obs["person_id"] == person_id][f"{omop_io_observation_table}_id"].item()}
+        """
+
+        targets = torch.tensor(self.con.execute(target_query).fetchall(), dtype=torch.float32)
+
+        return result, targets
diff --git a/tests/conftest.py b/tests/conftest.py
index a42fcb1..4930316 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -4,7 +4,7 @@
 from ehrdata.io.omop import setup_connection
 
 
-@pytest.fixture  # (scope="session")
+@pytest.fixture
 def omop_connection_vanilla():
     con = duckdb.connect()
     setup_connection(path="tests/data/toy_omop/vanilla", backend_handle=con)
@@ -12,7 +12,7 @@ def omop_connection_vanilla():
     con.close()
 
 
-@pytest.fixture  # (scope="session")
+@pytest.fixture
 def omop_connection_capital_letters():
     con = duckdb.connect()
     setup_connection(path="tests/data/toy_omop/capital_letters", backend_handle=con)
@@ -20,9 +20,17 @@ def omop_connection_capital_letters():
     con.close()
 
 
-@pytest.fixture  # (scope="session")
+@pytest.fixture
 def omop_connection_empty_observation():
     con = duckdb.connect()
     setup_connection(path="tests/data/toy_omop/empty_observation", backend_handle=con)
     yield con
     con.close()
+
+
+@pytest.fixture
+def omop_connection_multiple_units():
+    con = duckdb.connect()
+    setup_connection(path="tests/data/toy_omop/multiple_units", backend_handle=con)
+    yield con
+    con.close()
diff --git a/tests/data/toy_omop/multiple_units/observation.csv b/tests/data/toy_omop/multiple_units/observation.csv
new file mode 100644
index 0000000..82066fe
--- /dev/null
+++ b/tests/data/toy_omop/multiple_units/observation.csv
@@ -0,0 +1,5 @@
+observation_id,person_id,observation_concept_id,observation_date,observation_datetime,observation_type_concept_id,value_as_number,value_as_string,value_as_concept_id,qualifier_concept_id,unit_concept_id,provider_id,visit_occurrence_id,visit_detail_id,observation_source_value,observation_source_concept_id,unit_source_value,qualifier_source_value
+1,1,3001062,2100-01-01,2100-01-01 12:00:00,32817,,Anemia,0,,8587,,,,225059,2000030108,mL,
+2,1,3001062,2100-01-02,2100-01-02 12:00:00,32817,,Anemia,0,,9665,,,,225059,2000030108,uL,
+3,1,3034263,2100-01-01,2100-01-01 12:00:00,32817,5,,,,8587,,,,224409,2000030058,mL,
+4,1,3034263,2100-01-02,2100-01-02 12:00:00,32817,5,,,,9665,,,,224409,2000030058,uL,
diff --git a/tests/data/toy_omop/multiple_units/observation_period.csv b/tests/data/toy_omop/multiple_units/observation_period.csv
new file mode 100644
index 0000000..40b7351
--- /dev/null
+++ b/tests/data/toy_omop/multiple_units/observation_period.csv
@@ -0,0 +1,2 @@
+observation_period_id,person_id,observation_period_start_date,observation_period_end_date,period_type_concept_id
+1,1,2100-01-01,2100-01-31,32828
diff --git a/tests/data/toy_omop/multiple_units/person.csv b/tests/data/toy_omop/multiple_units/person.csv
new file mode 100644
index 0000000..0f13db9
--- /dev/null
+++ b/tests/data/toy_omop/multiple_units/person.csv
@@ -0,0 +1,2 @@
+person_id,gender_concept_id,year_of_birth,month_of_birth,day_of_birth,birth_datetime,race_concept_id,ethnicity_concept_id,location_id,provider_id,care_site_id,person_source_value,gender_source_value,gender_source_concept_id,race_source_value,race_source_concept_id,ethnicity_source_value,ethnicity_source_concept_id
+1,8507,2095,,,,0,38003563,,,,1234,M,0,,,,
diff --git a/tests/test_io/test_omop.py b/tests/test_io/test_omop.py
index ac426e7..0b42e51 100644
--- a/tests/test_io/test_omop.py
+++ b/tests/test_io/test_omop.py
@@ -821,3 +821,21 @@ def test_empty_observation(omop_connection_empty_observation, caplog):
     )
     assert edata.shape == (1, 0)
     assert "No data found in observation. Returning edata without additional variables." in caplog.text
+
+
+def test_multiple_units(omop_connection_multiple_units, caplog):
+    con = omop_connection_multiple_units
+    edata = ed.io.omop.setup_obs(backend_handle=con, observation_table="person_observation_period")
+    edata = ed.io.omop.setup_variables(
+        edata,
+        backend_handle=con,
+        data_tables=["observation"],
+        data_field_to_keep=["value_as_number"],
+        interval_length_number=1,
+        interval_length_unit="day",
+        num_intervals=2,
+        enrich_var_with_feature_info=False,
+        enrich_var_with_unit_info=False,
+    )
+    # assert edata.shape == (1, 0)
+    assert "multiple units for features: [[0]\n [1]]\n" in caplog.text
diff --git a/tests/test_tl/test_ehrdataset.py b/tests/test_tl/test_ehrdataset.py
new file mode 100644
index 0000000..3137b6c
--- /dev/null
+++ b/tests/test_tl/test_ehrdataset.py
@@ -0,0 +1,34 @@
+import torch
+
+import ehrdata as ed
+
+
+def test_ehrdataset_vanilla(omop_connection_vanilla):
+    num_intervals = 3
+    batch_size = 2
+    con = omop_connection_vanilla
+
+    edata = ed.io.omop.setup_obs(con, observation_table="person_observation_period", death_table=True)
+    edata = ed.io.omop.setup_variables(
+        edata,
+        backend_handle=con,
+        data_tables="measurement",
+        data_field_to_keep="value_as_number",
+        interval_length_number=1,
+        interval_length_unit="day",
+        num_intervals=num_intervals,
+        enrich_var_with_feature_info=False,
+        enrich_var_with_unit_info=False,
+        instantiate_tensor=False,
+    )
+
+    ehr_dataset = ed.tl.omop.EHRDataset(con, edata, batch_size=batch_size, datetime=False, idxs=None)
+    assert isinstance(ehr_dataset, torch.utils.data.Dataset)
+    single_item = next(iter(ehr_dataset))
+    assert single_item[0].shape == (2, num_intervals)
+    assert len(single_item[1]) == 1
+
+    loader = torch.utils.data.DataLoader(ehr_dataset, batch_size=batch_size)
+    batch = next(iter(loader))
+    assert batch[0].shape == (batch_size, 2, num_intervals)
+    assert len(batch[1]) == batch_size