Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save neuromusic/8ef9cfc8a4b4053bd700 to your computer and use it in GitHub Desktop.
Save neuromusic/8ef9cfc8a4b4053bd700 to your computer and use it in GitHub Desktop.
Generating Channel Graphs for KlustaKwik Automatically
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to build the masks for KlustaKwik, SpikeDetekt2 needs to know the graph of adjacencies between all of the recording sites on a recording array. However, a bad channel for a recording session can drastically change the requirements for a probe geometry.\n",
"\n",
"Luckily, there's some math out there that can help us to build these adjacency matrices based solely on the geometry of the recording sites. I've [already demonstrated how to use Delauney triangulation to do this for electrode arrays that are very large](http://www.justinkiggins.com/blog/defining-large-array-geometries-for-klustakwik/), where it would be a burden to define the geometry by hand. That was fun, but the technique has an application that I've already implemented into my own pipeline to extract population spiking activity.\n",
"\n",
"## We start with site maps in terms of Neuronexus recording sites\n",
"\n",
"We are using we NeuroNexus probes. The recording sites are 1-indexed, whereas KlustaKwik uses 0-indexed channels (where each channel is a column in a 2D array). Further, due to our hardware configuration, NeuroNexus \"Site 1\" doesn't necessarily correspond to KlustaKwik's \"Channel 0\". So we are going to define a site map that gives us the channel/index for each neuronexus site. The dictionary `s` is composed of site:channel pairs, where 'site' is the Neuronexus recording site and 'channel' is the index of that site's recording in the Klustakwik `*.raw.kwd` file."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# site:channel\n",
"s = {11: 0,\n",
" 12: 1,\n",
" 22: 2,\n",
" 10: 3,\n",
" 21: 4,\n",
" 23: 5,\n",
" 9: 6,\n",
" 13: 7,\n",
" 24: 8,\n",
" 8: 9,\n",
" 20: 10,\n",
" 25: 11,\n",
" 7: 12,\n",
" 14: 13,\n",
" 26: 14,\n",
" 6: 15,\n",
" 19: 16,\n",
" 27: 17,\n",
" 5: 18,\n",
" 15: 19,\n",
" 28: 20,\n",
" 4: 21,\n",
" 18: 22,\n",
" 29: 23,\n",
" 1: 24,\n",
" 16: 25,\n",
" 32: 26,\n",
" 2: 27,\n",
" 17: 28,\n",
" 31: 29,\n",
" 3: 30,\n",
" 30: 31,\n",
" }"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sometimes you lose channels\n",
"\n",
"Sometimes you don't have all of your channels. Maybe a channel is bad on your headstage or you're using one channel as a reference. Things happen. That shouldn't prevent us from using the most complete adjacency matrix that we can.\n",
"\n",
"We're going pretend like we've have some dead (or unrecorded) sites by setting their channel to `None`"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{1: 24,\n",
" 2: 27,\n",
" 3: 30,\n",
" 4: 21,\n",
" 5: 18,\n",
" 6: 15,\n",
" 7: 12,\n",
" 8: 9,\n",
" 9: 6,\n",
" 10: 3,\n",
" 11: None,\n",
" 12: None,\n",
" 13: 7,\n",
" 14: 13,\n",
" 15: 19,\n",
" 16: 25,\n",
" 17: 28,\n",
" 18: 22,\n",
" 19: None,\n",
" 20: 10,\n",
" 21: None,\n",
" 22: 2,\n",
" 23: None,\n",
" 24: 8,\n",
" 25: 11,\n",
" 26: 14,\n",
" 27: 17,\n",
" 28: 20,\n",
" 29: 23,\n",
" 30: 31,\n",
" 31: 29,\n",
" 32: 26}\n"
]
}
],
"source": [
"from numpy import random\n",
"from pprint import pprint\n",
"\n",
"for dead_site in random.choice(s.values(),5):\n",
" s[dead_site]=None\n",
"\n",
"pprint(s)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define our probe geometry in terms of the site map\n",
"\n",
"If I were to manually define the adjacencies for each site on the NeuroNexus A1x32-Poly3-6mm-50 probe, this is what I would sketch:\n",
"\n",
"\n",
" (3) (30)\n",
" | \\ / |\n",
" (2)-(17)-(31)\n",
" | / \\ |\n",
" (1)-(16)-(32)\n",
" | \\ / |\n",
" (4)-(18)-(29)\n",
" | / \\ |\n",
" (5)-(15)-(28)\n",
" | \\ / |\n",
" (6)-(19)-(27)\n",
" | / \\ |\n",
" (7)-(14)-(26)\n",
" | \\ / |\n",
" (8)-(20)-(25)\n",
" | / \\ |\n",
" (9)-(13)-(24)\n",
" | \\ / |\n",
" (10)-(21)-(23)\n",
" | / \\ |\n",
" (11)-(12)-(22)\n",
"\n",
"Each number is the NeuroNexus site number and I've drawn my edges between them. This particular probe is a grid, so I have each square drawn and (for the sake of completeness) each square is split into two triangles. For KlustaKwik, I would need to write these out as a list of tuples.\n",
"\n",
"Instead, we're just going to define the probe *geometry*, giving the x,y coordinates of each site, where `s[1]` indicates site 1. If a different electrophysiology rig has a different hardware configuration, this will change, so it's more robust to define this geometry in terms of the NeuroNexus sites.\n",
"\n",
"Even though KlustaKwik doesn't care about the geometry dict (except to plot the channels in Klustaviewa), we do. We are going to generate the graph on the fly from this geometry information, so I've defined it in microns, where the lower left recording site (Site 11) is (0,0)."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{0: {'channels': [24,\n",
" 27,\n",
" 30,\n",
" 21,\n",
" 18,\n",
" 15,\n",
" 12,\n",
" 9,\n",
" 6,\n",
" 3,\n",
" None,\n",
" None,\n",
" 7,\n",
" 13,\n",
" 19,\n",
" 25,\n",
" 28,\n",
" 22,\n",
" None,\n",
" 10,\n",
" None,\n",
" 2,\n",
" None,\n",
" 8,\n",
" 11,\n",
" 14,\n",
" 17,\n",
" 20,\n",
" 23,\n",
" 31,\n",
" 29,\n",
" 26],\n",
" 'geometry': {None: (100, 100),\n",
" 2: (100, 0),\n",
" 3: (0, 50),\n",
" 6: (0, 100),\n",
" 7: (50, 100),\n",
" 8: (100, 50),\n",
" 9: (0, 150),\n",
" 10: (50, 150),\n",
" 11: (100, 150),\n",
" 12: (0, 200),\n",
" 13: (50, 200),\n",
" 14: (100, 200),\n",
" 15: (0, 250),\n",
" 17: (100, 250),\n",
" 18: (0, 300),\n",
" 19: (50, 300),\n",
" 20: (100, 300),\n",
" 21: (0, 350),\n",
" 22: (50, 350),\n",
" 23: (100, 350),\n",
" 24: (0, 400),\n",
" 25: (50, 400),\n",
" 26: (100, 400),\n",
" 27: (0, 450),\n",
" 28: (50, 450),\n",
" 29: (100, 450),\n",
" 30: (0, 500),\n",
" 31: (100, 500)}}}\n"
]
}
],
"source": [
"channel_groups = {\n",
" # Shank index.\n",
" 0: { \n",
" # List of channels to keep for spike detection.\n",
" 'channels': s.values(),\n",
"\n",
" # 2D positions of the channels\n",
" # channel: (x,y)\n",
" 'geometry': {\n",
" s[3]: (0,500), # column 0\n",
" s[2]: (0,450),\n",
" s[1]: (0,400),\n",
" s[4]: (0,350),\n",
" s[5]: (0,300),\n",
" s[6]: (0,250),\n",
" s[7]: (0,200),\n",
" s[8]: (0,150),\n",
" s[9]: (0,100),\n",
" s[10]: (0,50),\n",
" s[11]: (0,0), \n",
" s[17]: (50,450), # column 1\n",
" s[16]: (50,400), \n",
" s[18]: (50,350), \n",
" s[15]: (50,300), \n",
" s[19]: (50,250), \n",
" s[14]: (50,200), \n",
" s[20]: (50,150), \n",
" s[13]: (50,100), \n",
" s[21]: (50,50), \n",
" s[12]: (50,0), \n",
" s[30]: (100,500), # column 2\n",
" s[31]: (100,450),\n",
" s[32]: (100,400),\n",
" s[29]: (100,350),\n",
" s[28]: (100,300),\n",
" s[27]: (100,250),\n",
" s[26]: (100,200),\n",
" s[25]: (100,150),\n",
" s[23]: (100,100),\n",
" s[24]: (100,50), \n",
" s[22]: (100,0), \n",
" }\n",
" }\n",
"}\n",
"pprint(channel_groups)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Clean up the dead channels\n",
"\n",
"We have a lot of `None`s in there, indicating dead channels. Let's strip those out."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{0: {'channels': [24,\n",
" 27,\n",
" 30,\n",
" 21,\n",
" 18,\n",
" 15,\n",
" 12,\n",
" 9,\n",
" 6,\n",
" 3,\n",
" 7,\n",
" 13,\n",
" 19,\n",
" 25,\n",
" 28,\n",
" 22,\n",
" 10,\n",
" 2,\n",
" 8,\n",
" 11,\n",
" 14,\n",
" 17,\n",
" 20,\n",
" 23,\n",
" 31,\n",
" 29,\n",
" 26],\n",
" 'geometry': {2: (100, 0),\n",
" 3: (0, 50),\n",
" 6: (0, 100),\n",
" 7: (50, 100),\n",
" 8: (100, 50),\n",
" 9: (0, 150),\n",
" 10: (50, 150),\n",
" 11: (100, 150),\n",
" 12: (0, 200),\n",
" 13: (50, 200),\n",
" 14: (100, 200),\n",
" 15: (0, 250),\n",
" 17: (100, 250),\n",
" 18: (0, 300),\n",
" 19: (50, 300),\n",
" 20: (100, 300),\n",
" 21: (0, 350),\n",
" 22: (50, 350),\n",
" 23: (100, 350),\n",
" 24: (0, 400),\n",
" 25: (50, 400),\n",
" 26: (100, 400),\n",
" 27: (0, 450),\n",
" 28: (50, 450),\n",
" 29: (100, 450),\n",
" 30: (0, 500),\n",
" 31: (100, 500)}}}\n"
]
}
],
"source": [
"new_group = {}\n",
"for gr, group in channel_groups.iteritems():\n",
" new_group[gr] = {\n",
" 'channels': [],\n",
" 'geometry': {}\n",
" }\n",
" new_group[gr]['channels'] = [ch for ch in group['channels'] if ch is not None]\n",
" new_group[gr]['geometry'] = {ch:xy for (ch,xy) in group['geometry'].iteritems() if ch is not None}\n",
" \n",
"channel_groups = new_group\n",
"pprint(channel_groups)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Build the adjacency graph from the probe geometry\n",
"\n",
"Next, we'll use SciPy's built-in Delaunay Triangulation to construct a graph from the probe geometry"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[(2, 8), (2, 3), (3, 2), (3, 8), (3, 7), (3, 6), (6, 7), (6, 3), (6, 10), (6, 9), (7, 8), (7, 11), (7, 3), (7, 6), (7, 10), (8, 2), (8, 3), (8, 7), (8, 11), (9, 6), (9, 10), (9, 12), (10, 7), (10, 11), (10, 6), (10, 9), (10, 14), (10, 13), (10, 12), (11, 7), (11, 8), (11, 10), (11, 14), (12, 13), (12, 15), (12, 10), (12, 9), (13, 14), (13, 17), (13, 12), (13, 15), (13, 19), (13, 10), (14, 13), (14, 17), (14, 10), (14, 11), (15, 12), (15, 13), (15, 19), (15, 18), (17, 13), (17, 14), (17, 20), (17, 19), (18, 19), (18, 15), (18, 22), (18, 21), (19, 18), (19, 15), (19, 20), (19, 17), (19, 13), (19, 22), (19, 23), (20, 19), (20, 17), (20, 23), (21, 18), (21, 22), (21, 25), (21, 24), (22, 18), (22, 19), (22, 21), (22, 25), (22, 23), (23, 25), (23, 26), (23, 22), (23, 19), (23, 20), (24, 25), (24, 21), (24, 27), (25, 29), (25, 26), (25, 28), (25, 21), (25, 22), (25, 24), (25, 27), (25, 23), (26, 29), (26, 25), (26, 23), (27, 28), (27, 30), (27, 25), (27, 24), (28, 31), (28, 30), (28, 29), (28, 27), (28, 25), (29, 28), (29, 31), (29, 25), (29, 26), (30, 28), (30, 31), (30, 27), (31, 28), (31, 30), (31, 29)]\n"
]
}
],
"source": [
"from scipy import spatial\n",
"from scipy.spatial.qhull import QhullError\n",
"def get_graph_from_geometry(geometry):\n",
" \n",
" # let's transform the geometry into lists of channel names and coordinates\n",
" chans,coords = zip(*[(ch,xy) for ch,xy in geometry.iteritems()])\n",
" \n",
" # we'll perform the triangulation and extract the \n",
" try:\n",
" tri = spatial.Delaunay(coords)\n",
" except QhullError:\n",
" # oh no! we probably have a linear geometry.\n",
" chans,coords = list(chans),list(coords)\n",
" x,y = zip(*coords)\n",
" # let's add a dummy channel and try again\n",
" coords.append((max(x)+1,max(y)+1))\n",
" tri = spatial.Delaunay(coords)\n",
" \n",
" # then build the list of edges from the triangulation\n",
" indices, indptr = tri.vertex_neighbor_vertices\n",
" edges = []\n",
" for k in range(indices.shape[0]-1):\n",
" for j in indptr[indices[k]:indices[k+1]]:\n",
" try:\n",
" edges.append((chans[k],chans[j]))\n",
" except IndexError:\n",
" # let's ignore anything connected to the dummy channel\n",
" pass\n",
" return edges\n",
"\n",
"\n",
"def build_geometries(channel_groups):\n",
" for gr, group in channel_groups.iteritems():\n",
" group['graph'] = get_graph_from_geometry(group['geometry'])\n",
" return channel_groups\n",
"\n",
"channel_groups = build_geometries(channel_groups)\n",
"print(channel_groups[0]['graph'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## That was way easier than doing it by hand\n",
"Let's see what it looks like..."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Populating the interactive namespace from numpy and matplotlib\n"
]
},
{
"data": {
"image/png": "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\ngg==\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x7f0cd8160310>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%pylab inline\n",
"def plot_channel_groups(channel_groups):\n",
"\n",
" n_shanks = len(channel_groups)\n",
"\n",
" f,ax = plt.subplots(1,n_shanks,squeeze=False)\n",
" for sh in range(n_shanks):\n",
" coords = [xy for ch,xy in channel_groups[sh]['geometry'].iteritems()]\n",
" x,y = zip(*coords)\n",
" ax[sh,0].scatter(x,y,color='0.2')\n",
"\n",
" for pr in channel_groups[sh]['graph']:\n",
" points = [channel_groups[sh]['geometry'][p] for p in pr]\n",
" ax[sh,0].plot(*zip(*points),color='k',alpha=0.2)\n",
"\n",
" ax[sh,0].set_xlim(min(x)-10,max(x)+10)\n",
" ax[sh,0].set_ylim(min(y)-10,max(y)+10)\n",
" ax[sh,0].set_xticks([])\n",
" ax[sh,0].set_yticks([])\n",
" ax[sh,0].set_title('group %i'%sh)\n",
" \n",
" axis('equal')\n",
" \n",
"plot_channel_groups(channel_groups)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Fantastic\n",
"\n",
"If we had just dropped our missing sites, we'd have huge gaping holes in our graph. Instead, we now have a bunch of edges linking past the dead sites, keeping our graph in tact."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment