\n",
"# Part II. Spike-triggered voltage\n",
"\n",
"[toc](#toc)\n",
"\n",
"You can use the event times (spike times) to extract the pre-synaptic and/or post-synaptic voltage signal following each event. This is a helpful way to determine if you have recorded any synaptic pairs (a connected pair of pre and post-synaptic cells). "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e2fb9c5b-5f47-4bfc-b088-7d1af2eddb22",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode:\"form\"}\n",
"\n",
"#@markdown Type in the window duration that you want to analyze following each spiking event. \n",
"#@markdown Then run this cell to plot the average spike-triggered post-synaptic potential for each cluster\n",
"\n",
"window = 0.1 #@param\n",
"\n",
"# No need to edit below this line\n",
"#################################\n",
"windur = window\n",
"winsamps = int(windur * fs)\n",
"x = np.linspace(0,windur,winsamps)*1000 #transform time to milliseconds\n",
"hfig,ax = plt.subplots(1)\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",
"for k in np.unique(df_props['cluster']):\n",
" spkt = df_props.loc[df_props['cluster']==k]['spikeT'].values\n",
" spkt = spkt[(spkt<((len(post)/fs)-windur))]\n",
" synwav = np.asarray([post[(int(t*sampling_rate)):(int(t*sampling_rate)+winsamps)] for t in spkt])\n",
" wav_u = np.mean(synwav,0)\n",
" wav_std = np.std(synwav,0)\n",
" ax.plot(x,wav_u,linewidth = 3,color = pal[k],label='cluster '+str(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,1], fontsize=14);"
]
},
{
"cell_type": "markdown",
"id": "bf5c4ebd-1796-4ed9-a4d4-0d93a7f08497",
"metadata": {},
"source": [
"It is helpful to look at individual trials (events) to get a sense of the variance/reliability in the pre and post-synaptic signal associated with each spike time."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e8815ce9-b00a-4237-90ce-03096e0fa5ff",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode:\"form\"}\n",
"\n",
"#@markdown Type in the window duration that you want to analyze following each spiking event. \n",
"\n",
"window = 0.1 #@param\n",
"\n",
"#@markdown Then, run this code cell to plot a random set of 10 spike time triggered voltage signals\n",
"#@markdown (pre and post synaptic) for each cluster (or maximum number of trials for that cluster).\n",
"#@markdown Every time you select a new cluster, a new random sample of trials from that cluster will be plotted.\n",
"#@markdown
The average waveform is plotted overlaid in black.\n",
"windur = window\n",
"winoffset = 0.002 # in milliseconds\n",
"winsamps = int(windur * fs)\n",
"xtime = ((np.linspace(0,windur,winsamps))- winoffset)*1000 #subtract pre-spike offset\n",
"\n",
"k = df_props['cluster'][0] #seed it to start\n",
"\n",
"f = go.FigureWidget(make_subplots(rows=2,cols=1,shared_xaxes=True))\n",
"\n",
"spkt = df_props.loc[df_props['cluster']==k]['spikeT'].values\n",
"spkt = spkt[(spkt>winoffset) & (spkt<(len(post)/fs)-windur)] - winoffset\n",
"\n",
"spkt_ = spkt[random.sample(range(0,len(spkt)),np.min([10,len(spkt)]))]\n",
" \n",
"spkwav = np.asarray([pre[(int(t*fs)):(int(t*fs)+winsamps)] - pre[int(t*fs)] for t in spkt_]).T\n",
"spk_u = np.mean(spkwav,1)\n",
"spk_std = np.std(spkwav,1)\n",
"\n",
"for i in spkwav.T:\n",
" f.add_trace(go.Scatter(x = xtime, y = i, opacity = 0.5,line_color = pal[k]),row=1,col=1);\n",
"f.add_trace(go.Scatter(x=xtime,y = spk_u,line_color = 'black'),row=1,col=1)\n",
"\n",
"synwav = np.asarray([post[(int(t*fs)):(int(t*fs)+winsamps)] - post[int(t*fs)] for t in spkt_]).T\n",
"syn_u = np.mean(synwav,1)\n",
"syn_std = np.std(synwav,1)\n",
"for i in synwav.T:\n",
" f.add_trace(go.Scatter(x = xtime, y = i, opacity = 0.5,line_color = pal[k]),row=2,col=1);\n",
"f.add_trace(go.Scatter(x=xtime,y = syn_u,line_color = 'black'),row=2,col=1)\n",
"\n",
" \n",
"f.update_layout(height=600, width=800,\n",
" showlegend=False,\n",
" xaxis2_title=\"time(milliseconds)\", \n",
" yaxis_title='amplitude (volts)',yaxis2_title='amplitude (volts)')\n",
"\n",
"cluster_select = widgets.Dropdown(\n",
" options=np.unique(df_props['cluster']),\n",
" value=k,\n",
" description='Cluster ID:',\n",
" disabled=False,\n",
" )\n",
"\n",
"\n",
"# our function that will modify the xaxis range\n",
"def response(k):\n",
" with f.batch_update():\n",
" spkt = df_props.loc[df_props['cluster']==k]['spikeT'].values\n",
" spkt = spkt[(spkt>winoffset) & (spkt<(len(post)/fs)-windur)] - winoffset\n",
" \n",
" spkt_ = spkt[random.sample(range(0,len(spkt)),np.min([10,len(spkt)]))]\n",
" \n",
" spkwav = np.asarray([pre[(int(t*fs)):(int(t*fs)+winsamps)] - pre[int(t*fs)] for t in spkt_]).T\n",
" spk_u = np.mean(spkwav,1)\n",
" spk_std = np.std(spkwav,1)\n",
"\n",
" synwav = np.asarray([post[(int(t*fs)):(int(t*fs)+winsamps)] - post[int(t*fs)] for t in spkt_]).T\n",
" syn_u = np.mean(synwav,1)\n",
" syn_std = np.std(synwav,1)\n",
"\n",
" trace_ = 0\n",
"\n",
" for i in spkwav.T:\n",
" f.data[trace_].y = i\n",
" trace_+=1\n",
" f.data[trace_].y = spk_u\n",
" trace_+=1\n",
"\n",
" for i in synwav.T:\n",
" f.data[trace_].y = i\n",
" trace_+=1\n",
" f.data[trace_].y = syn_u\n",
" trace_+=1\n",
"\n",
"\n",
"vb = VBox((f, interactive(response, k=cluster_select)))\n",
"vb.layout.align_items = 'center'\n",
"vb"
]
},
{
"cell_type": "markdown",
"id": "dab050ce-1e4c-48a2-8e02-4cf1d76dea42",
"metadata": {},
"source": [
"Finally, you can plot the pre and post synaptic signal associated with each individual event time in each cluster. This visualization enables you to extract more exact quantitative measurements from the signals associated with each event."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d686d1b0-3723-41ae-8aa7-8647e83fe65f",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode:\"form\"}\n",
"\n",
"#@markdown Type in the window duration that you want to analyze following each spiking event. \n",
"\n",
"window = 0.1 #@param\n",
"\n",
"#@markdown Then, run this code cell to plot individual spike-triggered voltage signals\n",
"#@markdown (pre and post synaptic) for each cluster. Select the cluster from the dropdown menu.\n",
"#@markdown Select the spike event number using the slider.\n",
"\n",
"windur=window\n",
"\n",
"winoffset = 2 # in milliseconds\n",
"winsamps = int(windur * fs)\n",
"xtime = ((np.linspace(0,windur,winsamps))*1000)- winoffset #subtract pre-spike offset\n",
"\n",
"k = df_props['cluster'][0] #seed it to start\n",
"\n",
"f = go.FigureWidget(make_subplots(rows=2,cols=1,shared_xaxes=True))\n",
"\n",
"spkt = df_props.loc[df_props['cluster']==k]['spikeT'].values\n",
"spkt = spkt[(spkt>winoffset) & (spkt<(len(post)/fs)-windur)] - winoffset/1000\n",
"\n",
"spkt_ = spkt[0]\n",
" \n",
"spkwav = pre[(int(spkt_*fs)):(int(spkt_*fs)+winsamps)] - pre[int(spkt_*fs)]\n",
"f.add_trace(go.Scatter(x=xtime,y = spkwav,line_color = 'black',name='pre synaptic'),row=1,col=1)\n",
"\n",
"synwav = post[(int(spkt_*fs)):(int(spkt_*fs)+winsamps)]# - post[int(spkt_*fs)]\n",
"f.add_trace(go.Scatter(x=xtime,y = synwav,line_color = 'black',name='post synaptic'),row=2,col=1)\n",
"\n",
"f.update_layout(height=600, width=800,\n",
" showlegend=False,\n",
" xaxis2_title=\"time(milliseconds)\", \n",
" yaxis_title='pre-synaptic voltage',yaxis2_title='post-synaptic voltage')\n",
"\n",
"cluster_select = widgets.Dropdown(\n",
" options=np.unique(df_props['cluster']),\n",
" value=k,\n",
" description='Cluster ID:',\n",
" disabled=False,\n",
")\n",
"\n",
"event_select = widgets.IntSlider(\n",
" value=0,\n",
" min=0,\n",
" max=len(spkt),\n",
" step=1,\n",
" description='Spike Event Number:',\n",
" disabled=False,\n",
" continuous_update=False,\n",
" orientation='horizontal',\n",
" readout=True,\n",
" readout_format='d'\n",
")\n",
"event_select.layout.width = '600px'\n",
"\n",
"# our function that will modify the xaxis range\n",
"def response(k,t):\n",
" with f.batch_update():\n",
" spkt = df_props.loc[df_props['cluster']==k]['spikeT'].values\n",
" spkt = spkt[(spkt>winoffset) & (spkt<(len(post)/fs)-windur)] - winoffset/1000\n",
" \n",
" event_select.max=len(spkt)-1\n",
"\n",
" spkt_ = spkt[event_select.value] # shoulod be able to use \"t\"\n",
" spkwav = pre[(int(spkt_*fs)):(int(spkt_*fs)+winsamps)] - pre[int(spkt_*fs)]\n",
" synwav = post[(int(spkt_*fs)):(int(spkt_*fs)+winsamps)]# - post[int(spkt_*fs)]\n",
" \n",
" f.data[0].y = spkwav\n",
" f.data[1].y = synwav\n",
" \n",
" f.update_layout(yaxis1_range=[np.min(pre),np.max(pre)])\n",
"\n",
"\n",
"vb = VBox((f, interactive(response, k=cluster_select, t=event_select)))\n",
"vb.layout.align_items = 'center'\n",
"vb\n",
"\n"
]
},
{
"cell_type": "markdown",
"id": "c224f763-fc10-4dc7-8a2f-8b6be3ce0dba",
"metadata": {},
"source": [
"\n",
"# Part III. PSP amplitudes\n",
"\n",
"[toc](#toc)\n",
"\n",
"To detect post-synaptic potentials (psps), you can use the same method you used to find events (signal peaks) in the pre-synaptic signal. Did you record distinct classes of PSPs or was the distribution of PSPs continuous?"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6406021c-d2b7-401a-9601-cf9eb3e3d5cc",
"metadata": {
"tags": [
"hide-input"
]
},
"outputs": [],
"source": [
"#@title {display-mode:'form'}\n",
"\n",
"#@markdown Type in an appropriate event threshold amplitude for detection of psps.\n",
"threshold = 0.02 #@param {type: \"number\"}\n",
"\n",
"#@markdown Then, run this cell to detect psp peaks and calculate their height.\n",
"\n",
"#@markdown You will see a scatter plot of peaks (red) and bases (green) overlaid on the raw trace\n",
"#@markdown and a histogram of event peak amplitudes.\n",
"\n",
"min_isi = 0.002 #seconds\n",
"\n",
"# samples_inwin = samples[int(start_time/sample_rate):int(stop_time/sample_rate)]\n",
"peaks,props = find_peaks(post,prominence = threshold, distance=int(min_isi*fs))\n",
"peaks_,props_ = find_peaks(-post,prominence = threshold, distance=int(min_isi*fs))\n",
"\n",
"plt.figure(figsize=(15,3))\n",
"plt.plot(post)\n",
"plt.scatter(peaks_,post[peaks_],color='green')\n",
"plt.scatter(peaks,post[peaks],color='red')\n",
"plt.xlim(0,2*fs)\n",
"plt.ylim(-0.8,-0.5)\n",
"\n",
"psp_amp = []\n",
"for p in peaks:\n",
" b = np.max(peaks_[peaks_ \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
}