{
"cells": [
{
"cell_type": "markdown",
"id": "32647951-7dcf-4553-b42a-5d8508d95cf9",
"metadata": {},
"source": [
"
"
]
},
{
"cell_type": "markdown",
"id": "fb67b59b-817a-4769-b406-8d274d60aba9",
"metadata": {},
"source": [
"# Data Explorer"
]
},
{
"cell_type": "markdown",
"id": "f07451ff-cf56-4763-b4ac-bd099ba1f145",
"metadata": {},
"source": [
"\n",
"# Table of Contents\n",
"\n",
"- [Introduction](#intro)\n",
"- [Setup](#setup)\n",
"- [Part I. Process Data](#one)\n",
"- [Part II. Motor Neuron Activity](#two)\n",
"- [Part III. Gaussian-filtered Spikes](#two)"
]
},
{
"cell_type": "markdown",
"id": "38b445ff-7cf7-442e-bb25-83afe6d5a3fb",
"metadata": {},
"source": [
"\n",
"# Neural Circuits\n",
"\n",
"The anterior and posterior roots of N1 control swimmeret movement. How is the motor neuron activity in these two nerves coordinated?\n",
"\n",
"Gaussian-filtered activity (like did for EODs) to plot analyze coordination (correlation) and plot phase-plane plot of output.\n"
]
},
{
"cell_type": "markdown",
"id": "488d476b-9387-43d7-b4bf-0528d9f4950a",
"metadata": {},
"source": [
"\n",
"# Setup\n",
"\n",
"[toc](#toc)"
]
},
{
"cell_type": "markdown",
"id": "b94f344a-8c2a-4080-b972-8e7a30c91e66",
"metadata": {},
"source": [
"Import and define functions"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be70dacf-b862-4afa-8fc0-dce8c363fdf9",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Run this code cell to import packages and define functions \n",
"from pathlib import Path\n",
"import random\n",
"import numpy as np\n",
"import pandas as pd\n",
"import plotly.express as px\n",
"import plotly.graph_objects as go\n",
"from plotly.subplots import make_subplots\n",
"from scipy import ndimage\n",
"from scipy.signal import hilbert,medfilt,resample, find_peaks\n",
"import seaborn as sns\n",
"import matplotlib.pyplot as plt\n",
"from sklearn.decomposition import PCA\n",
"from sklearn.cluster import KMeans\n",
"from datetime import datetime,timezone,timedelta\n",
"pal = sns.color_palette(n_colors=15)\n",
"pal = pal.as_hex()\n",
"\n",
"from ipywidgets import interactive, HBox, VBox, widgets, interact\n",
"\n",
"print('Task completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "markdown",
"id": "f91a09e1-c305-4809-8302-c1fad4140efa",
"metadata": {},
"source": [
"Mount Google Drive"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2b3a4500-470b-44e3-8572-a0d55f3bc1cb",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Run this cell to mount your Google Drive.\n",
"\n",
"from google.colab import drive\n",
"drive.mount('/content/drive')\n",
"\n",
"print('Task completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "markdown",
"id": "61aa1776-ad44-4487-ad14-9dfe050de305",
"metadata": {},
"source": [
"Import data digitized with *Nidaq USB6211* and recorded using *Bonsai-rx* as a *.bin* file"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b4e462b0-3e79-4f68-ac93-4a5ed2a83696",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode: \"form\"}\n",
"\n",
"#@markdown Specify the file path \n",
"#@markdown to your recorded data on Drive (find the filepath in the colab file manager:\n",
"\n",
"filepath = \"full filepath goes here\" #@param \n",
"filepath = '/Users/kperks/mnt/OneDrive - wesleyan.edu/Teaching/Neurophysiology_FA22/data/20220609/A4-bilateral-spont2022-06-09T14_39_25.bin'\n",
"\n",
"#@markdown Specify the sampling rate and number of channels recorded.\n",
"\n",
"sampling_rate = None #@param\n",
"number_channels = None #@param\n",
"muscle_channel = None #@param\n",
"nerve_channel = None #@param\n",
"\n",
"sampling_rate = 30000 #@param\n",
"number_channels = 2 #@param\n",
"muscle_channel = None #@param\n",
"nerve_channel = None #@param\n",
"\n",
"downsample = False #@param\n",
"newfs = 2500 #@param\n",
"\n",
"#@markdown After you have filled out all form fields, \n",
"#@markdown run this code cell to load the data. \n",
"\n",
"filepath = Path(filepath)\n",
"\n",
"# No need to edit below this line\n",
"#################################\n",
"data = np.fromfile(Path(filepath), dtype = np.float64)\n",
"if number_channels>1:\n",
" data = data.reshape(-1,number_channels)\n",
"dur = np.shape(data)[0]/sampling_rate\n",
"print('duration of recording was %0.2f seconds' %dur)\n",
"\n",
"fs = sampling_rate\n",
"if downsample:\n",
" # newfs = 2500 #downsample data\n",
" chunksize = int(sampling_rate/newfs)\n",
" if number_channels>1:\n",
" data = data[0::chunksize,:]\n",
" if number_channels==1:\n",
" data = data[0::chunksize]\n",
" fs = int(np.shape(data)[0]/dur)\n",
"\n",
"time = np.linspace(0,dur,np.shape(data)[0])\n",
"\n",
"print('Now be a bit patient while it plots.')\n",
"\n",
"f = go.FigureWidget(make_subplots(rows=2, cols=1, shared_xaxes= True)) #,layout=go.Layout(height=500, width=800))\n",
"f.add_trace(go.Scatter(x = time[0:fs], y = data[0:fs,0],\n",
" name='channel 0',opacity=1),row=1,col=1)\n",
"f.add_trace(go.Scatter(x = time[0:fs], y = data[0:fs,1],\n",
" name='channel 1',opacity=1),row=2,col=1)\n",
"f.update_layout(height=600, width=800,\n",
" showlegend=False,\n",
" xaxis2_title=\"time(seconds)\", \n",
" yaxis_title='channel 0 voltage', yaxis2_title='channel 1 voltage')\n",
"\n",
"slider = widgets.FloatRangeSlider(\n",
" min=0,\n",
" max=dur,\n",
" value=(0,1),\n",
" step= 1,\n",
" readout=False,\n",
" description='Time')\n",
"slider.layout.width = '600px'\n",
"\n",
"# our function that will modify the xaxis range\n",
"def response(x):\n",
" with f.batch_update():\n",
" starti = int(x[0]*fs)\n",
" stopi = int(x[1]*fs)\n",
" f.data[0].x = time[starti:stopi]\n",
" f.data[0].y = data[starti:stopi,0]\n",
" f.data[1].x = time[starti:stopi]\n",
" f.data[1].y = data[starti:stopi,1]\n",
"\n",
"vb = VBox((f, interactive(response, x=slider)))\n",
"vb.layout.align_items = 'center'\n",
"vb\n"
]
},
{
"cell_type": "markdown",
"id": "b7b40cda-55dd-4604-afb7-89e5d0731983",
"metadata": {},
"source": [
"\n",
"# Part I. Process Data\n",
"\n",
"[toc](#toc)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "142b84be-c0ba-43ae-a16b-03fcdf54dca1",
"metadata": {
"cellView": "form",
"id": "iE2MylaxTGpL",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title { display-mode: \"form\" }\n",
"\n",
"#@markdown Select one channel to analyze at a time.\n",
"channel = 0 #@param\n",
"\n",
"#@markdown Type in the start and stop time (in seconds) \n",
"#@markdown that specifies the section of your recording you want to focus on for analysis.\n",
"start_time = 20 #@param {type: \"number\"}\n",
"stop_time = 60 #@param {type: \"number\"}\n",
"\n",
"#@markdown Also type in an appropriate threshold amplitude for event detection.\n",
"spike_detection_threshold = 0.01 #@param {type: \"number\"}\n",
"\n",
"#@markdown Then from the dropdown, select a polarity (whether peaks are up or down)\n",
"peaks = \"select peak direction\" #@param ['select peak direction','up', 'down']\n",
"peaks = \"down\" \n",
"\n",
"#@markdown Finally, run this cell to set these values and plot a histogram of peak amplitudes.\n",
"\n",
"if peaks=='up': polarity = 1\n",
"if peaks=='down': polarity=-1\n",
"\n",
"min_isi = 0.001 #seconds\n",
"\n",
"peaks,props = find_peaks(polarity * data[:,channel],height=spike_detection_threshold, \n",
" prominence = spike_detection_threshold, distance=int(min_isi*fs))\n",
"peaks_t = peaks/fs\n",
"inwin_inds = ((peaks_t>start_time) & (peaks_t\n",
"\n",
"The histogram plot can give you a sense for how many distinct motor neurons might be in your recording. \n",
"\n",
"We can cluster events based on peak height and waveform shape using [\"Kmeans\"](https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html) clustering. \n",
"This will provide us with \"putative single units\" for further analysis."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f3a1f5d0-54ab-4cab-9076-5b2d872580b4",
"metadata": {
"cellView": "form",
"id": "4e574ce9-d314-4a20-918b-f2496260c9a8",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title Cluster Detected Events { display-mode: \"form\" }\n",
"\n",
"#@markdown Choose the number of clusters you want to split the event-based data into and type that number below.
\n",
"#@markdown >Note: It can sometimes help to \"over-split\" the events into more clusters \n",
"#@markdown than you think will be necessary. You can try both strategies and assess the results.\n",
"number_of_clusters = 7 #@param {type: \"number\"}\n",
"#@markdown Then run this cell to run the Kmeans algorithm. \n",
"\n",
"# No need to edit below this line\n",
"#################################\n",
"\n",
"kmeans = KMeans(n_clusters=number_of_clusters).fit(df_data)\n",
"# df_props['peaks_t'] = peaks_t\n",
"df_props['cluster'] = kmeans.labels_"
]
},
{
"cell_type": "markdown",
"id": "29851d8d-403c-46b4-884d-45b923686fa1",
"metadata": {},
"source": [
"\n",
"\n",
"Now that the events are clustered, you can visualize the mean spike waveform associated with each cluster (putative motor neuron)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4f188f71-cf6c-4fc4-ad24-b43b9ec5544c",
"metadata": {
"cellView": "form",
"id": "4e574ce9-d314-4a20-918b-f2496260c9a8",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode:'form'}\n",
"\n",
"#@markdown Run this cell to display the mean (and std) waveform for each cluster.\n",
"\n",
"windur = 0.001\n",
"winsamps = int(windur * fs)\n",
"x = np.linspace(-windur,windur,winsamps*2)*1000\n",
"hfig,ax = plt.subplots(1,figsize=(8,6))\n",
"ax.set_ylabel('Volts recorded',fontsize=14)\n",
"ax.set_xlabel('milliseconds',fontsize=14)\n",
"plt.xticks(fontsize=14)\n",
"plt.yticks(fontsize=14)\n",
"\n",
"for k in np.unique(df_props['cluster']):\n",
" spkt = df_props.loc[df_props['cluster']==k]['spikeT'].values #['peaks_t'].values\n",
" spkt = spkt[(spkt>windur) & (spkt<((np.shape(data)[0]/fs)-windur))]\n",
" print(str(len(spkt)) + \" spikes in cluster number \" + str(k))\n",
" spkwav = np.asarray([data[(int(t*fs)-winsamps):(int(t*fs)+winsamps),channel] for t in spkt])\n",
" wav_u = np.mean(spkwav,0)\n",
" wav_std = np.std(spkwav,0)\n",
" ax.plot(x,wav_u,linewidth = 3,label='cluster '+ str(k),color=pal[k])\n",
" ax.fill_between(x, wav_u-wav_std, wav_u+wav_std, alpha = 0.25,color=pal[k])\n",
"plt.legend(bbox_to_anchor=[1.25,1],fontsize=14);\n",
"\n",
"print('Tasks completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))"
]
},
{
"cell_type": "markdown",
"id": "0a37aa64-ee1b-4248-8901-982f326e0cb0",
"metadata": {
"id": "9iyWsThmXdI_"
},
"source": [
"\n",
"If there are multiple spike clusters you want to merge into a single cell class, *edit and run* the cell below.\n",
"\n",
"> **merge_cluster_list** = a list of the clusters (identified by numbers associated with the colors specified in the legend above).\n",
" - **For example**, the folowing list would merge clusters 0 and 2 together and 1, 4, and 3 together:
\n",
" **merge_cluster_list = [[0,2],[1,4,3]]**\n",
" - For each merge group, the first cluster number listed will be the re-asigned cluster number for that group (for example, in this case you would end up with a cluster number 0 and a cluster number 1). \n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7ae4811-90b0-4c5d-8f7b-39d48fb2590b",
"metadata": {
"cellView": "form",
"id": "EDJgd8DAXRba",
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title Merge Clusters { display-mode: \"form\" }\n",
"\n",
"#@markdown ONLY USE THIS CODE CELL IF YOU WANT TO MERGE CLUSTERS. \n",
"#@markdown OTHERWISE, MOVE ON. \n",
"#@markdown
Below, create your list (of sublists) of clusters to merge.\n",
"#@markdown >Just leave out from the list any clusters that you want unmerged.\n",
"merge_cluster_list = [[4,7]] #@param\n",
"#@markdown Then, run this cell to merge clusters as specified.\n",
"\n",
"for k_group in merge_cluster_list:\n",
" for k in k_group:\n",
" df_props.loc[df_props['cluster']==k,'cluster'] = k_group[0]\n",
"print('you now have the following clusters: ' + str(np.unique(df_props['cluster'])))\n",
"\n",
"print('Tasks completed at ' + str(datetime.now(timezone(-timedelta(hours=5)))))\n"
]
},
{
"cell_type": "markdown",
"id": "27ba23b1-7643-4138-9b70-8478910de0c4",
"metadata": {},
"source": [
"After merging, return to the [display clusters](#display-clusters) code cell to plot the mean waveform of each new cluster (and determine if you need to [merge more](#merge-clusters))."
]
},
{
"cell_type": "markdown",
"id": "bc5a51f2-a02c-4e03-a227-6079e348bce6",
"metadata": {},
"source": [
"\n",
"\n",
"Once you are happy with the clustering results based on the waveform shapes, check back with the raw data. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c68ccb8b-9d2b-4c66-8939-59c0b6f51df1",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode:\"form\"}\n",
"\n",
"#@markdown Run this cell to overlay spike times by cluster identity on the raw signal\n",
"\n",
"f = go.FigureWidget()\n",
"f.add_trace(go.Scatter(x = time[0:fs], y = data[0:fs,channel],opacity=1,line_color='black'))\n",
"for k in np.unique(df_props['cluster']):\n",
" start = 0 \n",
" stop = 1\n",
" inwin_inds = np.asarray([(df_props['spikeT'].values>start) & (df_props['spikeT'].valuesx[0]) & (df_props['spikeT'].values\n",
"# Part II. Motor Neuron activity\n",
"\n",
"[toc](#toc)\n",
"\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d651d42a-4e78-406e-8bed-55b63c14f99e",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "d0d5c963-3787-4a99-9067-d6f5e90a1ac1",
"metadata": {},
"source": [
"\n",
"# Part III. Gaussian-filtered Spikes\n",
"\n",
"[toc](#toc)\n",
"\n",
"Smooth the signal to be able to perform continuous analysis... correlation and phase-plane trajectory plot."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c00a5f09-b737-46f9-a191-9ebd5ad397ac",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "d451b557-6777-4492-955e-a41130ad39b9",
"metadata": {},
"source": [
"
\n",
"Written by Dr. Krista Perks for courses taught at Wesleyan University."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5c707588-d5f6-4142-8289-da7ba6a97dbc",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "57a8a68c-055e-4af0-ba4d-67d0d67148f1",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"id": "a350712e-e145-4475-9588-e3f19469b5ce",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"id": "86fd2b4a-b890-465f-bf33-c2057738789e",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"id": "b05de192-2baf-4d46-8848-6db1e3b50bff",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"id": "e6519277-fc86-44fd-a75b-f74cad0efcf1",
"metadata": {},
"source": [
""
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.13"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
"nbformat_minor": 5
}