https://doi.org/10.5281/zenodo.3597474
make_probe.ipynb
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Creating a Kilosort4 probe dictionary\n",
"\n",
"Kilosort4 uses a dictionary to track probe interformation.\n",
"The dictionary needs the following keys, all of which correspond to NumPy ndarrays.\n",
"\n",
"```\n",
"'chanMap': the channel indices that are included in the data.\n",
"'xc': the x-coordinates (in micrometers) of the probe contact centers.\n",
"'yc': the y-coordinates (in micrometers) of the probe contact centers.\n",
"'kcoords': shank or channel group of each contact.\n",
"'n_chan': the number of channels.\n",
"```\n",
"\n",
"Note that \"contact centers\" here refers only to the centers that were actually used to generate the recording that will be sorted. For example, a neuropixels probe may have 1000 or more contacts, but no more than 384 are recorded from at one time. Values should only be specified for those 384 channels, or fewer if not all were used."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"To demonstrate, we'll create a probe dictionary corresponding to a real example, a [128-channel probe from Diagnostic Biochips](https://20374082.fs1.hubspotusercontent-na1.net/hubfs/20374082/Product%20PDFs/P128-1.pdf).\n",
"\n",
"We'll assume all channels are used, so 'chanMap' will just be the range of integers from 0 to 127. 'kcoords' can be set to all zeroes as mentioned above."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"chanMap = np.arange(128)\n",
"kcoords = np.zeros(128)\n",
"n_chan = 128"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Since the contacts are 11 micrometers wide (x) and 15 micrometers high (y),\n",
"our first contact center has coordinates (5.5, 7.5). There is a single column\n",
"of contacts, so all x-coordinates are the same. Finally, the diagram indicates\n",
"that contacts are spaced 20 micrometers apart."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"xc = np.ones(128)*5.5\n",
"yc = np.array([7.5 + 20*i for i in range(128)])"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"So, our probe dictionary looks like:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'chanMap': array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,\n",
" 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,\n",
" 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,\n",
" 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,\n",
" 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,\n",
" 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,\n",
" 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,\n",
" 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,\n",
" 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,\n",
" 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127]), 'xc': array([5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5,\n",
" 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5]), 'yc': array([ 7.5, 27.5, 47.5, 67.5, 87.5, 107.5, 127.5, 147.5,\n",
" 167.5, 187.5, 207.5, 227.5, 247.5, 267.5, 287.5, 307.5,\n",
" 327.5, 347.5, 367.5, 387.5, 407.5, 427.5, 447.5, 467.5,\n",
" 487.5, 507.5, 527.5, 547.5, 567.5, 587.5, 607.5, 627.5,\n",
" 647.5, 667.5, 687.5, 707.5, 727.5, 747.5, 767.5, 787.5,\n",
" 807.5, 827.5, 847.5, 867.5, 887.5, 907.5, 927.5, 947.5,\n",
" 967.5, 987.5, 1007.5, 1027.5, 1047.5, 1067.5, 1087.5, 1107.5,\n",
" 1127.5, 1147.5, 1167.5, 1187.5, 1207.5, 1227.5, 1247.5, 1267.5,\n",
" 1287.5, 1307.5, 1327.5, 1347.5, 1367.5, 1387.5, 1407.5, 1427.5,\n",
" 1447.5, 1467.5, 1487.5, 1507.5, 1527.5, 1547.5, 1567.5, 1587.5,\n",
" 1607.5, 1627.5, 1647.5, 1667.5, 1687.5, 1707.5, 1727.5, 1747.5,\n",
" 1767.5, 1787.5, 1807.5, 1827.5, 1847.5, 1867.5, 1887.5, 1907.5,\n",
" 1927.5, 1947.5, 1967.5, 1987.5, 2007.5, 2027.5, 2047.5, 2067.5,\n",
" 2087.5, 2107.5, 2127.5, 2147.5, 2167.5, 2187.5, 2207.5, 2227.5,\n",
" 2247.5, 2267.5, 2287.5, 2307.5, 2327.5, 2347.5, 2367.5, 2387.5,\n",
" 2407.5, 2427.5, 2447.5, 2467.5, 2487.5, 2507.5, 2527.5, 2547.5]), 'kcoords': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0., 0., 0., 0.]), 'n_chan': 128}\n"
]
}
],
"source": [
"probe = {\n",
" 'chanMap': chanMap,\n",
" 'xc': xc,\n",
" 'yc': yc,\n",
" 'kcoords': kcoords,\n",
" 'n_chan': n_chan\n",
"}\n",
"\n",
"print(probe)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"This probe dictionary can now be used as an argument for `run_kilosort`, as demonstrated above. We can also save it to a `.json` file for later use:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from kilosort.io import save_probe\n",
"\n",
"save_probe(probe, '.../probe.json')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Now Kilsort4 can load the probe file and extract the contact coordinates and other relevant information. There are two ways to provide this information when running Kilosort4:\n",
"\n",
"1. Specify the path to the probe file in the GUI.\n",
"\n",
"Or\n",
"\n",
"2. Load the probe using `kilosort.io.load_probe` and provide the resulting dictionary to `kilosort.run_kilosort` using the `probe` keyword argument (demonstrated below)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from kilosort import run_kilosort\n",
"from kilosort.io import load_probe\n",
"\n",
"# Abreviated arguments, for demonstration only.\n",
"p = load_probe('.../test_prb.prb')\n",
"results = run_kilosort(..., probe=p)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "kilosort",
"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.9.20"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}