{ "cells": [ { "cell_type": "markdown", "id": "20e9290f", "metadata": {}, "source": [ "# Concatenating neural datasets in deepSTRF\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/urancon/deepSTRF/blob/develop/examples/dataset_concatenation.ipynb)\n", "\n", "deepSTRF lets you concatenate two or more `NeuralDataset` instances along\n", "**both** the stim and neuron axes, producing a single chimeric dataset\n", "that pools recordings from different sources. This notebook walks\n", "through the feature on CRCNS AA1 + AA2 — same species (zebra finch),\n", "similar experimental preparations, but disjoint cohorts and stimulus\n", "sets.\n", "\n", "For the design rationale and the full API, see\n", "[`dataset_concatenation.md`](../docs/_source/md/dataset_concatenation.md)\n", "and [`data_paradigm.md`](../docs/_source/md/data_paradigm.md).\n", "\n", "Two worked examples below: the single-species AA1 + AA2 case (most of the\n", "notebook), and a cross-species AA1 + NS1 chimera (ferret A1 + zebra finch\n", "Field L / MLd) at the end as the most distinctive use case." ] }, { "cell_type": "markdown", "id": "7950d096", "metadata": {}, "source": "## Setup — Google Colab\n\nIf you're running on Google Colab, the cell below installs deepSTRF\nfrom source. On a local install (`pip install -e .`) it's a no-op.\n\n**Note on data**: AA1 and AA2 are authenticated CRCNS datasets. To\nauto-download them, set `$CRCNS_USERNAME` and `$CRCNS_PASSWORD` (free\naccount at https://crcns.org/) before running the dataset cells. NS1\nis fetched from OSF — no credentials needed. On a local machine that\nalready has the data extracted, it's picked up from the cache\nautomatically." }, { "cell_type": "code", "execution_count": null, "id": "44c8d067", "metadata": {}, "outputs": [], "source": [ "import sys\n", "if 'google.colab' in sys.modules:\n", " !pip install -q git+https://github.com/urancon/deepSTRF.git\n", " print(\"deepSTRF installed from GitHub.\")\n", "else:\n", " print(\"Local environment — assuming deepSTRF is already importable.\")\n" ] }, { "cell_type": "markdown", "id": "317e0071", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": null, "id": "b979d160", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:08.484293Z", "iopub.status.busy": "2026-04-25T16:51:08.483594Z", "iopub.status.idle": "2026-04-25T16:51:12.805670Z", "shell.execute_reply": "2026-04-25T16:51:12.802284Z" } }, "outputs": [], "source": "%matplotlib inline\nimport matplotlib.pyplot as plt\nimport torch\nfrom torch.utils.data import DataLoader\n\nfrom deepSTRF.datasets.audio.crcns_aa1 import CRCNSAA1Dataset\nfrom deepSTRF.datasets.audio.crcns_aa2 import CRCNSAA2Dataset\nfrom deepSTRF.utils.data import concat_neural_datasets, neural_collate\n\nDT_MS = 5\n" }, { "cell_type": "markdown", "id": "6452d93a", "metadata": {}, "source": [ "## 1. Load the two source datasets\n", "\n", "Both must share `dt_ms` and `n_mels` (audio's `F`). deepSTRF will assert\n", "on a mismatch — see the **Compatibility** section below.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "384b2d2f", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:12.812389Z", "iopub.status.busy": "2026-04-25T16:51:12.811462Z", "iopub.status.idle": "2026-04-25T16:51:45.286750Z", "shell.execute_reply": "2026-04-25T16:51:45.285951Z" } }, "outputs": [], "source": "aa1 = CRCNSAA1Dataset(download=True, dt_ms=DT_MS)\naa2 = CRCNSAA2Dataset(download=True, dt_ms=DT_MS, smooth=False)\n\nprint(f\"aa1: {aa1}\")\nprint(f\"aa2: {aa2}\")\nprint(f\"both have F = {aa1.F} mel-bands and dt = {aa1.dt} ms — concatenable.\")\n" }, { "cell_type": "markdown", "id": "af2e1888", "metadata": {}, "source": [ "## 2. Concatenate\n", "\n", "Two equivalent forms:\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "fbe2d41d", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:45.288559Z", "iopub.status.busy": "2026-04-25T16:51:45.288366Z", "iopub.status.idle": "2026-04-25T16:51:45.294076Z", "shell.execute_reply": "2026-04-25T16:51:45.293381Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "combined: AudioNeuralDataset(N_neurons=594, selected=0, N_stims=147, dt_ms=5)\n", " type : AudioNeuralDataset # most-specific common ancestor (AA1 + AA2 -> AudioNeuralDataset)\n", " N : 100 + 494 = 594\n", " S : 30 + 117 = 147\n" ] } ], "source": [ "combined = concat_neural_datasets([aa1, aa2])\n", "combined_alt = aa1 + aa2 # pairwise __add__ sugar; identical result\n", "\n", "assert combined.N_neurons == combined_alt.N_neurons\n", "assert combined.get_S() == combined_alt.get_S()\n", "\n", "print(f\"combined: {combined}\")\n", "print(f\" type : {type(combined).__name__} \"\n", " f\"# most-specific common ancestor (AA1 + AA2 -> AudioNeuralDataset)\")\n", "print(f\" N : {aa1.N_neurons} + {aa2.N_neurons} = {combined.N_neurons}\")\n", "print(f\" S : {aa1.get_S()} + {aa2.get_S()} = {combined.get_S()}\")\n" ] }, { "cell_type": "markdown", "id": "0e2fe61f", "metadata": {}, "source": [ "## 3. The block-diagonal `nrn_masks`\n", "\n", "Concatenation places each source dataset in its own `(S_i, N_i)` block on\n", "the diagonal. The cross-blocks — stims from one source paired with\n", "neurons from another — are filled with the canonical `(1, 1)` NaN\n", "sentinel, so deepSTRF's existing missingness machinery (the `nrn_masks`\n", "property derived on the fly from `responses`) handles them automatically.\n", "\n", "The total number of \"real data\" pairs in the combined mask should equal\n", "the sum of valid pairs in the sources — no double-counting, no leak\n", "across the diagonal.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "d796eaf7", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:45.296042Z", "iopub.status.busy": "2026-04-25T16:51:45.295843Z", "iopub.status.idle": "2026-04-25T16:51:46.705197Z", "shell.execute_reply": "2026-04-25T16:51:46.704594Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "valid (stim, neuron) pairs in aa1: 2960\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "valid (stim, neuron) pairs in aa2: 18902\n", "sum : 21862\n", "valid pairs in combined : 21862 (matches: True)\n" ] } ], "source": [ "expected = int(aa1.nrn_masks.sum()) + int(aa2.nrn_masks.sum())\n", "got = int(combined.nrn_masks.sum())\n", "print(f\"valid (stim, neuron) pairs in aa1: {int(aa1.nrn_masks.sum())}\")\n", "print(f\"valid (stim, neuron) pairs in aa2: {int(aa2.nrn_masks.sum())}\")\n", "print(f\"sum : {expected}\")\n", "print(f\"valid pairs in combined : {got} \"\n", " f\"(matches: {got == expected})\")\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "d1d02f0f", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:46.706872Z", "iopub.status.busy": "2026-04-25T16:51:46.706733Z", "iopub.status.idle": "2026-04-25T16:51:47.622684Z", "shell.execute_reply": "2026-04-25T16:51:47.621862Z" } }, "outputs": [ { "data": { "image/png": "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13i7zyImJgb+/v5Vxqh9+/aYN28edu/ejaFDh2o05i+99BL8/f2Rnp6OxMREVFRUoKCgAMCTuXOMjY3rvE/aHMvDhw9j4MCB0m0xRESkH3glCBERNSvm5ubo27cvPvvsM7zyyitwc3NDZGQkJk6ciDVr1kj9li9fjqSkJLRv314qNtQHIyMjbNq0Cb/++iu6d++OpUuX4uOPP671PX5+foiMjMS7776Ll19+GcXFxQgNDZXaLS0tcejQIfz5z3/GSy+9hH/+859Yvnw5hg8fXm9xz5o1C+PGjUNYWBi8vLxgYWGBUaNGqfT56KOPEBkZicWLF8PFxQXDhg3Dzp070bFjR9y6dQsTJkzA/Pnz0atXLwDAggULYGtri0mTJtW43eHDh0Mul2Pv3r0AgISEBNy+fbvKtgHAxcUFLi4uiImJwYYNG9CyZUv4+PhU6efj4wMzMzN88803AJ5M8urh4YGJEycCAF555RV4eHggISGh2pjUjffEiRPRtWtXeHp64oUXXsDRo0fVDa9G6vtzTktLw+nTpzFmzJgqbQqFAj4+PoiJidF4zK9fv46EhATk5uaiZ8+esLe3l36OHTv2TPukzbHcvHmzlBNERKQ/ZOJ5n7tGRERE9By++OILJCQkqDyNhjQzePBghIeHIzw8XNuhNCm7du3CrFmzcObMmVqfXkRERM0Pj/pERESkVW+//TYKCwtRXFwMCwsLbYdDeqCkpARxcXEsgBAR6SEe+YmIiEir5HI53n//fW2HQXrE399f2yEQEZGWcE4QIiIioiYqPDwcPXv21HYYRERETQbnBCEiIiIiIiIivcArQYiIiIiIiIhIL7AIQkRERERERER6gUUQIiIiIiIiItILLIIQERERERERkV5gEYSIiIiIiIiI9AKLIERERERERESkF1gEISIiIiIiIiK9wCIIEREREREREemF/wdEUl5iyl1kKQAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(figsize=(11, 4))\n", "ax.imshow(combined.nrn_masks.T.numpy(), aspect=\"auto\", cmap=\"Greys\",\n", " interpolation=\"nearest\")\n", "ax.axhline(aa1.N_neurons - 0.5, color=\"tab:red\", lw=1, ls=\"--\",\n", " label=\"neuron block boundary\")\n", "ax.axvline(aa1.get_S() - 0.5, color=\"tab:red\", lw=1, ls=\"--\",\n", " label=\"stim block boundary\")\n", "ax.set_xlabel(\"Stimulus index (AA1 stims | AA2 stims)\")\n", "ax.set_ylabel(\"Neuron index (AA1 neurons | AA2 neurons)\")\n", "ax.set_title(f\"`combined.nrn_masks` — black = real (stim, neuron) data; \"\n", " f\"empty quadrants are NaN sentinels\")\n", "ax.legend(loc=\"upper right\")\n", "plt.tight_layout()\n", "plt.show()\n" ] }, { "cell_type": "markdown", "id": "7acbec48", "metadata": {}, "source": [ "The two black quadrants are the AA1 and AA2 source blocks (preserving\n", "each dataset's intra-source coverage pattern). The two empty quadrants\n", "are the cross-blocks — where AA1 neurons are paired with AA2 stims and\n", "vice versa, with NaN sentinels filling those entries.\n" ] }, { "cell_type": "markdown", "id": "eb205a0d", "metadata": {}, "source": [ "## 4. Metadata flows through\n", "\n", "Each source's `stim_meta` and `nrn_meta` are concatenated in the\n", "same order as the data, so `combined.nrn_meta[42]` still returns\n", "AA1's 42nd neuron's dict, and `combined.nrn_meta[120]` returns\n", "AA2's 20th (after the 100 AA1 neurons).\n", "\n", "This means the neuron-selection helpers continue to work with no\n", "adaptation:\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "63687a87", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:47.624508Z", "iopub.status.busy": "2026-04-25T16:51:47.624346Z", "iopub.status.idle": "2026-04-25T16:51:47.628868Z", "shell.execute_reply": "2026-04-25T16:51:47.628256Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "MLd neurons selected from combined: 50\n", "MLd-or-mld neurons (case-insensitive): 193 (32.5% of pooled population)\n" ] } ], "source": [ "# select all MLd neurons in the combined dataset — picks them up from\n", "# BOTH sources (AA1 and AA2 both happen to use the \"MLd\"/\"mld\" label).\n", "combined.select_pop_by_nrn_attr(\"area\", \"MLd\")\n", "print(f\"MLd neurons selected from combined: {len(combined.I)}\")\n", "\n", "# AA2 uses lowercase 'mld' for its area label; we can union both:\n", "mld_indices = [\n", " n for n, meta in enumerate(combined.nrn_meta)\n", " if meta[\"area\"].lower() == \"mld\"\n", "]\n", "combined.select_population(mld_indices)\n", "print(f\"MLd-or-mld neurons (case-insensitive): {len(combined.I)} \"\n", " f\"({100 * len(combined.I) / combined.N_neurons:.1f}% of pooled population)\")\n" ] }, { "cell_type": "markdown", "id": "ad193349", "metadata": {}, "source": [ "\n", "## 4.5 Iteration follows the selection\n", "\n", "This is where chimeric datasets really earn their keep. `len(combined)`\n", "and `combined[i]` are both filtered by the current neuron selection — they\n", "only expose stimuli for which at least one selected neuron has valid\n", "response data. Selecting one source's neurons therefore hides the other\n", "source's stimuli from iteration entirely, with no extra bookkeeping in\n", "the training loop.\n", "\n", "(See [`data_paradigm.md`](../docs/_source/md/data_paradigm.md) §8 for\n", "the general rule across all `NeuralDataset` instances. The chimeric case\n", "is just a particularly clean illustration.)\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "8744c5e5", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:47.630706Z", "iopub.status.busy": "2026-04-25T16:51:47.630542Z", "iopub.status.idle": "2026-04-25T16:51:52.954788Z", "shell.execute_reply": "2026-04-25T16:51:52.953914Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "all neurons: len(combined) = 147 (expect 30 + 117 = 147)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "only AA1's neurons: len(combined) = 30 (expect 30)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " combined[0] stim_meta = {'name': '058767E725C83836F405A97FD7D1E751.wav', 'type': 'conspecific', 'sample_rate': 32000.0, 'n_samples': 62144, 'duration_s': 1.942}\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " combined[-1] stim_meta = {'name': 'FDD2F383A22BCFA000DEBD48FAC0ADF0.wav', 'type': 'flatrip', 'sample_rate': 32000.0, 'n_samples': 64000, 'duration_s': 2.0} # last iterable\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " combined[30] raises IndexError — AA2 stim is hidden, not silently NaN\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "only AA2's neurons: len(combined) = 117 (expect 117)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " combined[0] stim_meta = {'name': 'D54ABC42488F995C789F351A34316039.wav', 'type': 'conspecific', 'sample_rate': 32000.0, 'n_samples': 55105, 'duration_s': 1.72203125} # first iterable is AA2's first stim\n" ] } ], "source": [ "\n", "# default: every neuron selected -> every stim is iterable.\n", "combined.select_population(list(range(combined.N_neurons)))\n", "print(f\"all neurons: len(combined) = {len(combined):>3} \"\n", " f\"(expect {aa1.get_S()} + {aa2.get_S()} = {aa1.get_S() + aa2.get_S()})\")\n", "\n", "# select only AA1's neurons -> AA2's stims disappear.\n", "combined.select_population(list(range(aa1.N_neurons)))\n", "print(f\"only AA1's neurons: len(combined) = {len(combined):>3} \"\n", " f\"(expect {aa1.get_S()})\")\n", "print(f\" combined[0] stim_meta = {combined[0][3]}\")\n", "print(f\" combined[-1] stim_meta = {combined[-1][3]} # last iterable\")\n", "try:\n", " combined[aa1.get_S()]\n", "except IndexError as e:\n", " print(f\" combined[{aa1.get_S()}] raises IndexError — AA2 stim is hidden, not silently NaN\")\n", "\n", "# select only AA2's neurons -> AA1's stims disappear.\n", "combined.select_population(list(range(aa1.N_neurons, combined.N_neurons)))\n", "print(f\"only AA2's neurons: len(combined) = {len(combined):>3} \"\n", " f\"(expect {aa2.get_S()})\")\n", "print(f\" combined[0] stim_meta = {combined[0][3]} # first iterable is AA2's first stim\")\n" ] }, { "cell_type": "markdown", "id": "67ff02c4", "metadata": {}, "source": [ "\n", "The training loop for a chimeric model that's only being supervised on\n", "AA1's neurons therefore sees only AA1's stims — automatic. Same for\n", "AA2. Use `select_pop_by_nrn_attr` to slice on metadata fields (e.g.\n", "\"all MLd neurons across both sources\"), and the iterable space adapts\n", "just as cleanly.\n" ] }, { "cell_type": "markdown", "id": "0b5c913f", "metadata": {}, "source": [ "## 5. Iterating with `DataLoader`\n", "\n", "The combined dataset is just a `NeuralDataset`, so `neural_collate`\n", "batches it the same way it batches any single source. The `valid_mask`\n", "returned by the collate already reflects the block-diagonal structure\n", "of the cross-blocks — your loss code does not need any adaptation.\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "267d1b7b", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:52.956451Z", "iopub.status.busy": "2026-04-25T16:51:52.956284Z", "iopub.status.idle": "2026-04-25T16:51:57.583365Z", "shell.execute_reply": "2026-04-25T16:51:57.582635Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "stims shape : (4, 1, 32, 460)\n", "responses shape : (4, 32, 15, 460)\n", "valid_mask shape : (4, 32, 15, 460) (B, N, R, T)\n", "\n", "valid fraction in batch : 0.576\n", "stim_metas : [{'name': '058767E725C83836F405A97FD7D1E751.wav', 'type': 'conspecific', 'sample_rate': 32000.0, 'n_samples': 62144, 'duration_s': 1.942}, {'name': '0A07B255BF830083B6726388CA8510BA.wav', 'type': 'conspecific', 'sample_rate': 32000.0, 'n_samples': 73557, 'duration_s': 2.29865625}, {'name': '1470489635DD93410408CE9F8FB2F7D9.wav', 'type': 'conspecific', 'sample_rate': 32000.0, 'n_samples': 69890, 'duration_s': 2.1840625}, {'name': '42FED9F3EF45A238202B050B06F91652.wav', 'type': 'conspecific', 'sample_rate': 32000.0, 'n_samples': 52761, 'duration_s': 1.64878125}]\n" ] } ], "source": [ "combined.select_population(list(range(min(combined.N_neurons, 32)))) # take 32 neurons for a small batch demo\n", "loader = DataLoader(combined, batch_size=4, shuffle=False, collate_fn=neural_collate)\n", "\n", "batch = next(iter(loader))\nstims, responses, valid_mask, stim_metas = batch['stims'], batch['responses'], batch['valid_mask'], batch['stim_meta']\n", "print(f\"stims shape : {tuple(stims.shape)}\")\n", "print(f\"responses shape : {tuple(responses.shape)}\")\n", "print(f\"valid_mask shape : {tuple(valid_mask.shape)} (B, N, R, T)\")\n", "print()\n", "print(f\"valid fraction in batch : {valid_mask.float().mean().item():.3f}\")\n", "print(f\"stim_metas : {stim_metas}\")\n" ] }, { "cell_type": "markdown", "id": "49e52cce", "metadata": {}, "source": [ "## 6. What deepSTRF refuses to concatenate\n", "\n", "deepSTRF rejects mismatches that would silently produce wrong results.\n", "You'd see an `AssertionError` from the relevant `_concat_check_compat`\n", "hook. A couple of examples:\n" ] }, { "cell_type": "code", "execution_count": null, "id": "105c848b", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:51:57.585273Z", "iopub.status.busy": "2026-04-25T16:51:57.585102Z", "iopub.status.idle": "2026-04-25T16:52:53.217089Z", "shell.execute_reply": "2026-04-25T16:52:53.216413Z" } }, "outputs": [], "source": "# dt mismatch -> rejected\naa2_finer_bin = CRCNSAA2Dataset(download=True, dt_ms=1, smooth=False)\ntry:\n combined = aa1 + aa2_finer_bin # aa1 is dt=5, aa2_finer_bin is dt=1\nexcept AssertionError as e:\n print(f\"AssertionError (expected): {e}\")\n" }, { "cell_type": "code", "execution_count": null, "id": "4920cc12", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:52:53.218624Z", "iopub.status.busy": "2026-04-25T16:52:53.218469Z", "iopub.status.idle": "2026-04-25T16:53:20.982680Z", "shell.execute_reply": "2026-04-25T16:53:20.982015Z" } }, "outputs": [], "source": "# F mismatch -> rejected\naa2_more_mels = CRCNSAA2Dataset(download=True, dt_ms=DT_MS, n_mels=64, smooth=False)\ntry:\n combined = aa1 + aa2_more_mels # aa1 has F=32, aa2_more_mels has F=64\nexcept AssertionError as e:\n print(f\"AssertionError (expected): {e}\")\n" }, { "cell_type": "markdown", "id": "896accb3", "metadata": {}, "source": [ "## 7. Across species: pooling AA1 (zebra finch) + NS1 (ferret)\n", "\n", "The block-diagonal layout shines when sources come from different species,\n", "labs, or stimulus banks. Since `aa1` and `ns1` have totally disjoint stim sets,\n", "neurons, and recording protocols, every cross-block entry is a `(1, 1)` NaN\n", "sentinel — there's no risk of accidental aliasing — and the resulting\n", "chimeric dataset is something a single model can be fit to via the\n", "`area`-filtered selection.\n", "\n", "The only real requirement for cross-species concat: matching `dt_ms` and\n", "`n_mels`. NS1's stimulus tensor is precomputed at `dt=5ms`, `F=34`, so we\n", "load AA1 with the same parameters here." ] }, { "cell_type": "code", "execution_count": null, "id": "eeed456f", "metadata": { "execution": { "iopub.execute_input": "2026-04-25T16:53:20.984180Z", "iopub.status.busy": "2026-04-25T16:53:20.984043Z", "iopub.status.idle": "2026-04-25T16:53:28.115060Z", "shell.execute_reply": "2026-04-25T16:53:28.114421Z" } }, "outputs": [], "source": "from deepSTRF.datasets.audio.ns1 import NS1Dataset\n\nns1 = NS1Dataset(download=True, dt_ms=5) # ferret A1, 119 neurons, 20 nat stims\naa1_34 = CRCNSAA1Dataset(download=True, dt_ms=5,\n n_mels=34) # match NS1's F=34\nchimera = ns1 + aa1_34\nprint(chimera)\nprint(f\" area=A1 -> {len(chimera.select_pop_by_nrn_attr('area', 'A1'))} ferret neurons\")\nchimera.I = []\nprint(f\" area=Field_L -> {len(chimera.select_pop_by_nrn_attr('area', 'Field_L'))} zebra-finch neurons\")\nchimera.I = []\nprint(f\" area=MLd -> {len(chimera.select_pop_by_nrn_attr('area', 'MLd'))} zebra-finch neurons\")\n\n# block-diagonal mask: NS1 covers stims 0..19 / neurons 0..118; AA1 covers stims 20..49 / neurons 119..218\nfig, ax = plt.subplots(figsize=(11, 4))\nax.imshow(chimera.nrn_masks.T.numpy(), aspect='auto', cmap='gray_r', interpolation='nearest')\nax.axhline(ns1.N_neurons - 0.5, color='red', linestyle='--', linewidth=1)\nax.axvline(ns1.get_S() - 0.5, color='red', linestyle='--', linewidth=1)\nax.set_xlabel('stim index')\nax.set_ylabel('neuron index')\nax.set_title('NS1 (ferret A1) + AA1 (zebra finch Field_L/MLd) — block-diagonal coverage')\nplt.tight_layout()\nplt.show()" }, { "cell_type": "markdown", "id": "76cb8f76", "metadata": {}, "source": [ "## Recap\n", "\n", "- `concat_neural_datasets([a, b, ...])` (or `a + b`) returns a single\n", " `NeuralDataset` whose stim and neuron axes are the unions of the\n", " inputs', with cross-block entries filled by NaN sentinels.\n", "- The result's `nrn_masks` derives the block-diagonal coverage\n", " automatically from those sentinels — no extra bookkeeping.\n", "- The combined dataset is a drop-in replacement for any of its\n", " source datasets in `DataLoader`, `neural_collate`, `select_*`, etc.\n", "- deepSTRF rejects `dt_ms` and `F` (or `(H, W)`) mismatches at concat\n", " time — no silent resampling.\n", "\n", "> Cross-species concatenation example (AA1 + NS1) will land here once\n", "> NS1 is modernized to the new base API.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.15" } }, "nbformat": 4, "nbformat_minor": 5 }