{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Intracellular Electrophysiology in NWB" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Creating and Writing NWB files\n", "\n", "First step is to create a `NWBFile` object to store the data." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from datetime import datetime\n", "from dateutil.tz import tzlocal\n", "from pynwb import NWBFile\n", "from hashlib import sha256\n", "import numpy as np\n", "\n", "# timestamp of the zero of the file level clock\n", "now = datetime.now(tzlocal())\n", "\n", "# random identifier, should be unique across all NWB files worldwide\n", "my_id = sha256(f'Our Lab identifier: {now}'.encode()).hexdigest()\n", "\n", "\n", "nwbfile = NWBFile(session_description='My first synthetic IC ephys recording', \n", " identifier=my_id, \n", " session_start_time=now,\n", " experimenter='Dr. Bilbo Baggins',\n", " lab='Bag End Laboratory',\n", " institution='University of Middle Earth at the Shire',\n", " experiment_description='I went on an adventure with thirteen dwarves to reclaim vast treasures.',\n", " session_id='LONELYMTN')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Device and Electrode metadata\n", "\n", "\n", "\n", "Device metadata is represented by `Device` objects. To create a device, you can use the `Device` instance method `NWBFile.create_device`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Heka ITC-1600 pynwb.device.Device at 0x4396018032" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "device = nwbfile.create_device(name='Heka ITC-1600')\n", "device" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Intracellular electrode metadata is represented by `IntracellularElectrode` objects.\n", "To create an electrode, you can use the `NWBFile` instance method `NWBFile.create_icephys_electrode`.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "elec0 pynwb.icephys.IntracellularElectrode at 0x4805110608\n", "Fields:\n", " description: An intracellular electrode\n", " device: Heka ITC-1600 pynwb.device.Device at 0x4396018032\n", " seal: 2e09\n", " slice: Bath solution MIBS2" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "elec0 = nwbfile.create_icephys_electrode(name=\"elec0\",\n", " description='An intracellular electrode',\n", " device=device,\n", " slice='Bath solution MIBS2', \n", " seal='2e09')\n", "elec0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding Stimulus and Response Traces\n", "\n", "Intracellular stimulus and response data are represented with subclasses of `PatchClampSeries`. There are two classes for representing stimulus data:\n", "\n", "- `VoltageClampStimulusSeries`\n", "- `CurrentClampStimulusSeries`\n", "\n", "Here, we will use`CurrentClampStimulusSeries` to store current clamp stimulus data, and then add it to our `NWBFile` as a stimulus data trace using the method `NWBFile.add_stimulus`.\n", "\n", "" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from pynwb.icephys import CurrentClampStimulusSeries\n", "\n", "ccss0 = CurrentClampStimulusSeries(name=\"CC_Stim_Sweep0\", \n", " data=[1, 2, 3, 4, 5], \n", " starting_time=123.6, \n", " rate=10e3, \n", " electrode=elec0, \n", " gain=0.02, \n", " sweep_number=0)\n", "\n", "nwbfile.add_stimulus(ccss0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sweeps and Sweep Numbers\n", "\n", "Sweep numbers are currently used in NWB to group stimulus and response traces, an are assigned by the user when the `PatchClampSeries` is created. `PatchClampSeries` having the same starting time belong to the same sweep. These can have the same or different `Electrode` objects, e.g. for multi-patching in the same slice. For now, we will add another stimulus series belonging a different sweep.\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "from pynwb.icephys import VoltageClampStimulusSeries\n", "\n", "vcss1 = VoltageClampStimulusSeries(name=\"VC_Stim_Sweep1\",\n", " data=[2, 3, 4, 5, 6],\n", " starting_time=234.5,\n", " rate=10e3, \n", " electrode=elec0, \n", " gain=0.03, \n", " sweep_number=1)\n", "\n", "nwbfile.add_stimulus(vcss1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "We have three classes for representing responses:\n", "\n", "- `VoltageClampSeries`\n", "- `CurrentClampSeries`\n", "- `IZeroClampSeries`\n", "\n", "\n", "Here, we will use `CurrentClampSeries` to store current clamp data from sweep 0 and add it to our NWBFile as response data using the method `NWBFile.add_acquisition`." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from pynwb.icephys import CurrentClampSeries\n", "\n", "ccs0 = CurrentClampSeries(name=\"CC_Resp_Sweep0\",\n", " data=[0.1, 0.2, 0.3, 0.4, 0.5],\n", " conversion=1e-12, \n", " resolution=np.nan, \n", " starting_time=123.6, \n", " rate=20e3,\n", " electrode=elec0, \n", " gain=0.02, \n", " bias_current=1e-12, \n", " bridge_balance=70e6,\n", " capacitance_compensation=1e-12, \n", " sweep_number=0)\n", "\n", "nwbfile.add_acquisition(ccs0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now add voltage clamp data from sweep 1 using `VoltageClampSeries` and `NWBFile.add_acquisition`." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "from pynwb.icephys import VoltageClampSeries\n", "\n", "vcs1 = VoltageClampSeries(name=\"VC_Resp_Sweep1\", \n", " data=[0.1, 0.2, 0.3, 0.4, 0.5],\n", " conversion=1e-12, \n", " resolution=np.nan, \n", " starting_time=234.5, \n", " rate=20e3,\n", " electrode=elec0, \n", " gain=0.02, \n", " capacitance_slow=100e-12, \n", " resistance_comp_correction=70.0,\n", " sweep_number=1)\n", "\n", "nwbfile.add_acquisition(vcs1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once you have finished adding all of your data to the `NWBFile`, write the file with `NWBHDF5IO`.\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/hdmf/build/objectmapper.py:145: UserWarning: Value with data type int64 is being converted to data type uint64 (min specification: uint32).\n", " warnings.warn('Value with data type %s is being converted to data type %s (min specification: %s).'\n" ] } ], "source": [ "from pynwb import NWBHDF5IO\n", "\n", "with NWBHDF5IO('My_First_Icephys_File.nwb', 'w') as io:\n", " io.write(nwbfile)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reading data\n", "\n", "Now that you have written some intracellular electrophysiology data to an NWB file, you can read it back in. For additional details on features for retrieving `TimeSeries` data from an `NWBFile`, we refer the reader to the basic tutorial. We will cover the basics for loading data here, including retrieving sweeps from the `SweepTable` using sweep numbers." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "io = NWBHDF5IO('My_First_Icephys_File.nwb', 'r')\n", "read_nwbfile = io.read()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Retrieving individual stimulus and response traces\n", "\n", "First, we can examine the file to see what stimulus traces are stored, and then retrieve the `CurrentClampStimulusSeries` we added as stimulus data using the `NWBFile.get_stimulus` method:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CC_Stim_Sweep0\n", "VC_Stim_Sweep1\n" ] } ], "source": [ "stims = read_nwbfile.stimulus\n", "for i in stims:\n", " print(i)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([1, 2, 3, 4, 5])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "read_ccss0 = read_nwbfile.get_stimulus('CC_Stim_Sweep0')\n", "read_ccss0.data[()]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Grabbing response data can be done via `NWBFile.get_acquisition`. First we can check the list of responses in the file by reading out the keys from `NWBFile.acquisition`:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['CC_Resp_Sweep0', 'VC_Resp_Sweep1']" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(read_nwbfile.acquisition.keys())" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.1, 0.2, 0.3, 0.4, 0.5])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vcs = read_nwbfile.get_acquisition('VC_Resp_Sweep1')\n", "vcs.data[()]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Retrieving electrode and device metadata\n", "\n", "We can access the `NWBFile.icephys_electrodes` attribute to return a list of the intracellular electrodes in this NWB file:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'elec0': elec0 pynwb.icephys.IntracellularElectrode at 0x4805110944\n", " Fields:\n", " description: An intracellular electrode\n", " device: Heka ITC-1600 pynwb.device.Device at 0x4805108304\n", " seal: 2e09\n", " slice: Bath solution MIBS2}" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "read_nwbfile.icephys_electrodes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can retrieve a specific electrode by `name` using the `NWBFile.get_icephys_electrode` method:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "elec = nwbfile.get_icephys_electrode('elec0')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, we can also get this electrode from the `CurrentClampStimulusSeries` we retrieved above. This is exposed via the `PatchClampSeries.electrode` attribute:\n" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "elec0 pynwb.icephys.IntracellularElectrode at 0x4805110944\n", "Fields:\n", " description: An intracellular electrode\n", " device: Heka ITC-1600 pynwb.device.Device at 0x4805108304\n", " seal: 2e09\n", " slice: Bath solution MIBS2" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pcs_elec0 = read_ccss0.electrode\n", "pcs_elec0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see the names of all the `Devices` in this NWB file using the `NWBFile.devices` attribute: " ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Heka ITC-1600': Heka ITC-1600 pynwb.device.Device at 0x4805108304}" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "read_nwbfile.devices" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can retrieve specific device metadata by name using `NWBFile.get_device`:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Heka ITC-1600'" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "device = read_nwbfile.get_device('Heka ITC-1600')\n", "device.name" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Retrieving data grouped by Sweep\n", "\n", "If you have data from multiple electrodes and multiple sweeps, it can be tedious and expensive to search all `PatchClampSeries` for the `TimeSeries` from a given sweep.\n", "\n", "Fortunately you don't have to do that manually, instead you can just query the `NWBFile.SweepTable` which stores the mapping between the `PatchClampSeries` which belong to a certain sweep number via `SweepTable.get_series`.\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "sweep_table pynwb.icephys.SweepTable at 0x4802719216\n", "Fields:\n", " colnames: ['series' 'sweep_number']\n", " columns: (\n", " series_index ,\n", " series ,\n", " sweep_number \n", " )\n", " description: A sweep table groups different PatchClampSeries together.\n", " id: id " ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "read_nwbfile.sweep_table" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To retrieve a list of the sweep numbers in a `SweepTable`, we can use `SweepTable.sweep_number`. \n", "Note it will return a value for each individual `PatchClampSeries` stored." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0 1]\n" ] } ], "source": [ "sweep_nums = read_nwbfile.sweep_table.sweep_number\n", "print(np.unique(sweep_nums[()]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can retrieve the `PatchClampSeries` for sweep 0 using the `NWBFile.sweep_table.get_series` method:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[CC_Stim_Sweep0 pynwb.icephys.CurrentClampStimulusSeries at 0x4805111664\n", " Fields:\n", " comments: no comments\n", " conversion: 1.0\n", " data: [1 2 3 4 5]\n", " description: no description\n", " electrode: elec0 pynwb.icephys.IntracellularElectrode at 0x4805110608\n", " Fields:\n", " description: An intracellular electrode\n", " device: Heka ITC-1600 pynwb.device.Device at 0x4396018032\n", " seal: 2e09\n", " slice: Bath solution MIBS2\n", " \n", " gain: 0.02\n", " rate: 10000.0\n", " resolution: -1.0\n", " starting_time: 123.6\n", " starting_time_unit: seconds\n", " stimulus_description: NA\n", " sweep_number: 0\n", " unit: amperes,\n", " CC_Resp_Sweep0 pynwb.icephys.CurrentClampSeries at 0x4396017024\n", " Fields:\n", " bias_current: 1e-12\n", " bridge_balance: 70000000.0\n", " capacitance_compensation: 1e-12\n", " comments: no comments\n", " conversion: 1e-12\n", " data: [0.1 0.2 0.3 0.4 0.5]\n", " description: no description\n", " electrode: elec0 pynwb.icephys.IntracellularElectrode at 0x4805110608\n", " Fields:\n", " description: An intracellular electrode\n", " device: Heka ITC-1600 pynwb.device.Device at 0x4396018032\n", " seal: 2e09\n", " slice: Bath solution MIBS2\n", " \n", " gain: 0.02\n", " rate: 20000.0\n", " resolution: nan\n", " starting_time: 123.6\n", " starting_time_unit: seconds\n", " stimulus_description: NA\n", " sweep_number: 0\n", " unit: volts]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pcs0 = nwbfile.sweep_table.get_series(0)\n", "pcs0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also us the `SweepTable.to_dataframe` method to convert the sweep table to a dataframe that we can inspect and access to retrieve sweep numbers, stimulus series and response series." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "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", "
seriessweep_number
id
0[CC_Stim_Sweep0 pynwb.icephys.CurrentClampStim...0
1[VC_Stim_Sweep1 pynwb.icephys.VoltageClampStim...1
2[CC_Resp_Sweep0 pynwb.icephys.CurrentClampSeri...0
3[VC_Resp_Sweep1 pynwb.icephys.VoltageClampSeri...1
\n", "
" ], "text/plain": [ " series sweep_number\n", "id \n", "0 [CC_Stim_Sweep0 pynwb.icephys.CurrentClampStim... 0\n", "1 [VC_Stim_Sweep1 pynwb.icephys.VoltageClampStim... 1\n", "2 [CC_Resp_Sweep0 pynwb.icephys.CurrentClampSeri... 0\n", "3 [VC_Resp_Sweep1 pynwb.icephys.VoltageClampSeri... 1" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Viewing sweep table as a pandas dataframe\n", "stdf = read_nwbfile.sweep_table.to_dataframe()\n", "stdf" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 0, 1]\n" ] } ], "source": [ "# To retrieve a list of sweep numbers\n", "print(list(stdf.sweep_number))" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2, 3, 4, 5, 6])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stdf.series[1][0].data[()]" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "elec0 pynwb.icephys.IntracellularElectrode at 0x4805110944\n", "Fields:\n", " description: An intracellular electrode\n", " device: Heka ITC-1600 pynwb.device.Device at 0x4805108304\n", " seal: 2e09\n", " slice: Bath solution MIBS2" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stdf.series[0][0].electrode" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.8.2" } }, "nbformat": 4, "nbformat_minor": 1 }