From 3543510c2056ede375d682051342b09e287cc961 Mon Sep 17 00:00:00 2001 From: Matthias H Hennig Date: Wed, 7 Oct 2020 20:35:24 +0100 Subject: [PATCH 01/10] The reviewer wanted it... --- .../widgets/mapswidget/activitymapwidget.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 0814480..520ea28 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -1,12 +1,15 @@ import numpy as np import spiketoolkit as st import matplotlib.pylab as plt +import matplotlib as mpl +from mpl_toolkits.axes_grid1 import make_axes_locatable from ..utils import LabeledRectangle from spikewidgets.widgets.basewidget import BaseWidget +from mpl_toolkits.axes_grid1.inset_locator import inset_axes def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', background='on', label_color='r', - transpose=False, frame=False, ax=None, figure=None): + transpose=False, frame=False, ax=None, figure=None, colorbar=False): """ Plots spike rate (estimated using simple threshold detector) as 2D activity map. @@ -45,6 +48,7 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', frame=frame, figure=figure, ax=ax, + colorbar=colorbar ) W.plot() return W @@ -53,7 +57,7 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', class ActivityMapWidget(BaseWidget): def __init__(self, recording, channel_ids, trange, cmap, background, label_color='r', transpose=False, frame=False, - figure=None, ax=None): + figure=None, ax=None, colorbar=False): BaseWidget.__init__(self, figure, ax) self._recording = recording self._channel_ids = channel_ids @@ -63,6 +67,8 @@ def __init__(self, recording, channel_ids, trange, cmap, background, label_color self._frame = frame self._bg = background self._label_color = label_color + self._show_colorbar = colorbar + self.colorbar = None self.name = 'ActivityMap' assert 'location' in self._recording.get_shared_channel_property_names(), "Activity map requires 'location'" \ "property" @@ -80,7 +86,9 @@ def _do_plot(self): locations = self._recording.get_channel_locations(channel_ids=self._channel_ids) activity = st.postprocessing.compute_channel_spiking_activity(self._recording, start_frame=self._trange[0], - end_frame=self._trange[1]) + end_frame=self._trange[1], + normalize=False, + recompute_info=False) if self._transpose: locations = np.roll(locations, 1, axis=1) @@ -110,6 +118,9 @@ def _do_plot(self): self._drs = [] elec_x = 0.9 * pitch_x elec_y = 0.9 * pitch_y + activity = activity/(self._trange[1]-self._trange[0]) # spikes/s + max_activity = np.ceil(np.max(activity)*100)/100 # normalise to 1 for colours + activity = activity/max_activity for (loc, act, ch) in zip(locations, activity, self._recording.get_channel_ids()): color = cm(act) rect = plt.Rectangle((loc[0] - elec_x / 2, loc[1] - elec_y / 2), elec_x, elec_y, @@ -127,3 +138,10 @@ def _do_plot(self): self.ax.add_patch(rect) self.ax.axis('equal') self.ax.axis('off') + if self._show_colorbar: + cax = inset_axes(self.ax, width="0.5%", height="50%", loc='upper left', bbox_to_anchor=(0.02, 0., 1, 1),bbox_transform=self.ax.transAxes) + self.colorbar = plt.gcf().colorbar(mpl.collections.PatchCollection(self.ax.patches), cax=cax, orientation='vertical', shrink=0.5) + cax.yaxis.set_ticks_position('left') + cax.yaxis.set_label_position('left') + self.colorbar.set_ticks((0,1)) + self.colorbar.set_ticklabels((0,max_activity)) \ No newline at end of file From 15f0953baea2320e94c42b5f1da4f84c40bee375 Mon Sep 17 00:00:00 2001 From: Matthias H Hennig Date: Fri, 9 Oct 2020 11:38:12 +0100 Subject: [PATCH 02/10] little improvements --- .../widgets/mapswidget/activitymapwidget.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 520ea28..ccbcdb2 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -9,7 +9,7 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', background='on', label_color='r', - transpose=False, frame=False, ax=None, figure=None, colorbar=False): + transpose=False, frame=False, ax=None, figure=None, colorbar=False, recompute_info=False): """ Plots spike rate (estimated using simple threshold detector) as 2D activity map. @@ -48,7 +48,8 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', frame=frame, figure=figure, ax=ax, - colorbar=colorbar + colorbar=colorbar, + recompute_info=recompute_info ) W.plot() return W @@ -57,7 +58,7 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', class ActivityMapWidget(BaseWidget): def __init__(self, recording, channel_ids, trange, cmap, background, label_color='r', transpose=False, frame=False, - figure=None, ax=None, colorbar=False): + figure=None, ax=None, colorbar=False, recompute_info=False): BaseWidget.__init__(self, figure, ax) self._recording = recording self._channel_ids = channel_ids @@ -68,6 +69,7 @@ def __init__(self, recording, channel_ids, trange, cmap, background, label_color self._bg = background self._label_color = label_color self._show_colorbar = colorbar + self._recompute_info = recompute_info self.colorbar = None self.name = 'ActivityMap' assert 'location' in self._recording.get_shared_channel_property_names(), "Activity map requires 'location'" \ @@ -88,7 +90,8 @@ def _do_plot(self): start_frame=self._trange[0], end_frame=self._trange[1], normalize=False, - recompute_info=False) + recompute_info=self._recompute_info, + method='detection') if self._transpose: locations = np.roll(locations, 1, axis=1) @@ -140,7 +143,7 @@ def _do_plot(self): self.ax.axis('off') if self._show_colorbar: cax = inset_axes(self.ax, width="0.5%", height="50%", loc='upper left', bbox_to_anchor=(0.02, 0., 1, 1),bbox_transform=self.ax.transAxes) - self.colorbar = plt.gcf().colorbar(mpl.collections.PatchCollection(self.ax.patches), cax=cax, orientation='vertical', shrink=0.5) + self.colorbar = plt.gcf().colorbar(mpl.collections.PatchCollection(self.ax.patches), cax=cax, orientation='vertical', shrink=0.2) cax.yaxis.set_ticks_position('left') cax.yaxis.set_label_position('left') self.colorbar.set_ticks((0,1)) From 689af02b52c5473ae27ade23ea7a14cb005ab3e2 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Sun, 11 Oct 2020 11:55:07 +0200 Subject: [PATCH 03/10] Improve colorbar handling --- .../widgets/mapswidget/activitymapwidget.py | 94 ++++++++++++++++--- 1 file changed, 79 insertions(+), 15 deletions(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 520ea28..79e6166 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -9,7 +9,8 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', background='on', label_color='r', - transpose=False, frame=False, ax=None, figure=None, colorbar=False): + transpose=False, frame=False, colorbar=False, colorbar_bbox=None, + colorbar_orientation='vertical', colorbar_width=0.02, ax=None, figure=None): """ Plots spike rate (estimated using simple threshold detector) as 2D activity map. @@ -23,10 +24,20 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', List with start time and end time cmap: matplotlib colormap The colormap to be used (default 'viridis') + background: bool + If True, a background is added in between electrodes transpose: bool, optional, default: False Swap x and y channel coordinates if True. frame: bool, optional, default: False Draw a frame around the array if True. + colorbar: bool + If True, a colorbar is displayed + colorbar_bbox: bbox + Bbox (x,y,w,h) in figure coordinates to plot colorbar + colorbar_orientation: str + 'vertical' or 'horizontal' + colorbar_width: float + Width of colorbar in figure coordinates (default 0.02) figure: matplotlib figure The figure to be used. If not given a figure is created ax: matplotlib axis @@ -48,16 +59,20 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', frame=frame, figure=figure, ax=ax, - colorbar=colorbar + colorbar=colorbar, + colorbar_bbox=colorbar_bbox, + colorbar_orientation=colorbar_orientation, + colorbar_width=colorbar_width ) W.plot() + return W class ActivityMapWidget(BaseWidget): - def __init__(self, recording, channel_ids, trange, cmap, background, label_color='r', transpose=False, frame=False, - figure=None, ax=None, colorbar=False): + colorbar=False, colorbar_bbox=None, colorbar_orientation='vertical', colorbar_width=0.02, + figure=None, ax=None): BaseWidget.__init__(self, figure, ax) self._recording = recording self._channel_ids = channel_ids @@ -68,6 +83,9 @@ def __init__(self, recording, channel_ids, trange, cmap, background, label_color self._bg = background self._label_color = label_color self._show_colorbar = colorbar + self._colorbar_bbox = colorbar_bbox + self._colorbar_orient = colorbar_orientation + self._colorbar_width = colorbar_width self.colorbar = None self.name = 'ActivityMap' assert 'location' in self._recording.get_shared_channel_property_names(), "Activity map requires 'location'" \ @@ -87,8 +105,11 @@ def _do_plot(self): activity = st.postprocessing.compute_channel_spiking_activity(self._recording, start_frame=self._trange[0], end_frame=self._trange[1], - normalize=False, - recompute_info=False) + method='detection', + align=False, + normalize=False, + recompute_info=False, + verbose=True) if self._transpose: locations = np.roll(locations, 1, axis=1) @@ -118,9 +139,13 @@ def _do_plot(self): self._drs = [] elec_x = 0.9 * pitch_x elec_y = 0.9 * pitch_y - activity = activity/(self._trange[1]-self._trange[0]) # spikes/s - max_activity = np.ceil(np.max(activity)*100)/100 # normalise to 1 for colours - activity = activity/max_activity + + self._max_activity = np.round(np.max(activity), 2) + + # normalize + activity -= np.min(activity) + activity /= np.ptp(activity) + for (loc, act, ch) in zip(locations, activity, self._recording.get_channel_ids()): color = cm(act) rect = plt.Rectangle((loc[0] - elec_x / 2, loc[1] - elec_y / 2), elec_x, elec_y, @@ -130,18 +155,57 @@ def _do_plot(self): dr.connect() self._drs.append(dr) - self.ax.set_xlim(np.min(x) - pitch_x, np.max(x) + pitch_x) - self.ax.set_ylim(np.min(y) - pitch_y, np.max(y) + pitch_y) if self._frame: rect = plt.Rectangle((np.min(x) - pitch_x, np.min(y) - pitch_y), np.max(x) - np.min(x) + 2 * pitch_x, np.max(y) - np.min(y) + 2 * pitch_y, fill=None, edgecolor='k') self.ax.add_patch(rect) + self.ax.axis('equal') self.ax.axis('off') + self.ax.set_xlim(np.min(x) - pitch_x, np.max(x) + pitch_x) + self.ax.set_ylim(np.min(y) - pitch_y, np.max(y) + pitch_y) + if self._show_colorbar: - cax = inset_axes(self.ax, width="0.5%", height="50%", loc='upper left', bbox_to_anchor=(0.02, 0., 1, 1),bbox_transform=self.ax.transAxes) - self.colorbar = plt.gcf().colorbar(mpl.collections.PatchCollection(self.ax.patches), cax=cax, orientation='vertical', shrink=0.5) + # The canvas need to be drawn to get the right transforms + self.figure.canvas.draw() + + if self._colorbar_bbox is None: + if self._colorbar_orient == 'vertical': + colorbar_width = self._colorbar_width + bottom_left = (np.min(x) - pitch_x, np.min(y) - pitch_y) + top_left = (np.min(x) - pitch_x, np.max(y) + pitch_y) + figure_to_data = self.figure.transFigure + self.ax.transData.inverted() + data_to_figure = figure_to_data.inverted() + corners_figure = data_to_figure.transform([bottom_left, top_left]) + + bottom_left_fig = corners_figure[0] + colorbar_height = (corners_figure[1] - corners_figure[0])[1] + bbox = (bottom_left_fig[0] - 1.5 * colorbar_width, bottom_left_fig[1], + colorbar_width, colorbar_height) + else: + colorbar_height = self._colorbar_width + bottom_left = (np.min(x) - pitch_x, np.min(y) - pitch_y) + bottom_right = (np.max(x) + pitch_x, np.min(y) - pitch_y) + figure_to_data = self.figure.transFigure + self.ax.transData.inverted() + data_to_figure = figure_to_data.inverted() + corners_figure = data_to_figure.transform([bottom_left, bottom_right]) + + bottom_left_fig = corners_figure[0] + colorbar_width = (corners_figure[1] - corners_figure[0])[0] + + bbox = (bottom_left_fig[0], bottom_left_fig[1] - 1.5*colorbar_height, + colorbar_width, colorbar_height) + + else: + bbox = self._colorbar_bbox + + cax = self.figure.add_axes(bbox) + scalable = mpl.cm.ScalarMappable(norm=mpl.colors.Normalize(vmin=0, vmax=1), cmap=self._cmap) + self.colorbar = self.figure.colorbar(scalable, cax=cax, + orientation=self._colorbar_orient)#, shrink=0.5) cax.yaxis.set_ticks_position('left') cax.yaxis.set_label_position('left') - self.colorbar.set_ticks((0,1)) - self.colorbar.set_ticklabels((0,max_activity)) \ No newline at end of file + self.colorbar.set_ticks((0, 1)) + self.colorbar.set_ticklabels((0, self._max_activity)) + + From 1e9b1b02f95cf1927af0dd7901ee7bbb160e88ea Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Tue, 13 Oct 2020 09:46:54 +0200 Subject: [PATCH 04/10] verbose=False --- spikewidgets/widgets/mapswidget/activitymapwidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 9da82ef..237b776 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -112,7 +112,7 @@ def _do_plot(self): method='detection', align=False, recompute_info=self._recompute_info, - verbose=True) + verbose=False) if self._transpose: locations = np.roll(locations, 1, axis=1) From 4756e5cd8aabb83b9d31a75f7dd315e61ec06c2f Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Tue, 13 Oct 2020 11:39:57 +0200 Subject: [PATCH 05/10] Use transData instead of transAxes --- .../widgets/mapswidget/activitymapwidget.py | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 237b776..f572cbf 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -133,7 +133,7 @@ def _do_plot(self): cm = plt.get_cmap(self._cmap) - if self._bg == 'on': + if self._bg: rect = plt.Rectangle((np.min(x) - pitch_x / 2, np.min(y) - pitch_y / 2), float(np.ptp(x)) + pitch_x, float(np.ptp(y)) + pitch_y, color=cm(0), edgecolor=None, alpha=0.9) @@ -159,8 +159,8 @@ def _do_plot(self): self._drs.append(dr) if self._frame: - rect = plt.Rectangle((np.min(x) - pitch_x, np.min(y) - pitch_y), np.max(x) - np.min(x) + 2 * pitch_x, - np.max(y) - np.min(y) + 2 * pitch_y, fill=None, edgecolor='k') + rect = plt.Rectangle((np.min(x) - elec_x / 2, np.min(y) - elec_y / 2), np.max(x) - np.min(x) + elec_x, + np.max(y) - np.min(y) + elec_y, fill=None, edgecolor='k') self.ax.add_patch(rect) self.ax.axis('equal') @@ -174,32 +174,28 @@ def _do_plot(self): colorbar_width = self._colorbar_width bottom_left = (np.min(x) - pitch_x, np.min(y) - pitch_y) top_left = (np.min(x) - pitch_x, np.max(y) + pitch_y) - figure_to_data = self.figure.transFigure + self.ax.transData.inverted() - data_to_figure = figure_to_data.inverted() - corners_figure = data_to_figure.transform([bottom_left, top_left]) - - bottom_left_fig = corners_figure[0] - colorbar_height = (corners_figure[1] - corners_figure[0])[1] - bbox = (bottom_left_fig[0] - 1.5 * colorbar_width, bottom_left_fig[1], - colorbar_width, colorbar_height) + + axes_to_data = self.ax.transAxes + self.ax.transData.inverted() + width_in_data = (axes_to_data.transform((colorbar_width, 0)) - axes_to_data.transform((0, 0)))[0] + height_in_data = top_left[1] - bottom_left[1] + bbox = (bottom_left[0] - 1.5 * width_in_data, bottom_left[1], + width_in_data, height_in_data) + else: colorbar_height = self._colorbar_width bottom_left = (np.min(x) - pitch_x, np.min(y) - pitch_y) bottom_right = (np.max(x) + pitch_x, np.min(y) - pitch_y) - figure_to_data = self.figure.transFigure + self.ax.transData.inverted() - data_to_figure = figure_to_data.inverted() - corners_figure = data_to_figure.transform([bottom_left, bottom_right]) - - bottom_left_fig = corners_figure[0] - colorbar_width = (corners_figure[1] - corners_figure[0])[0] - - bbox = (bottom_left_fig[0], bottom_left_fig[1] - 1.5*colorbar_height, - colorbar_width, colorbar_height) + axes_to_data = self.ax.transAxes + self.ax.transData.inverted() + height_in_data = (axes_to_data.transform((0, colorbar_height)) - axes_to_data.transform((0, 0)))[1] + width_in_data = bottom_right[0] - bottom_left[0] + bbox = (bottom_left[0], bottom_left[1] - 1.5 * height_in_data, + width_in_data, height_in_data) else: bbox = self._colorbar_bbox - cax = self.figure.add_axes(bbox) + cax = inset_axes(self.ax, width="100%", height="100%", bbox_to_anchor=bbox, + bbox_transform=self.ax.transData) scalable = mpl.cm.ScalarMappable(norm=mpl.colors.Normalize(vmin=0, vmax=1), cmap=self._cmap) self.colorbar = self.figure.colorbar(scalable, cax=cax, orientation=self._colorbar_orient)#, shrink=0.5) From da181c96da488e8ceac8954f3700e6e4272126fa Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Tue, 13 Oct 2020 11:46:58 +0200 Subject: [PATCH 06/10] Add colorbar label --- spikewidgets/widgets/mapswidget/activitymapwidget.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index f572cbf..1566cd4 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -175,6 +175,8 @@ def _do_plot(self): bottom_left = (np.min(x) - pitch_x, np.min(y) - pitch_y) top_left = (np.min(x) - pitch_x, np.max(y) + pitch_y) + print(top_left) + axes_to_data = self.ax.transAxes + self.ax.transData.inverted() width_in_data = (axes_to_data.transform((colorbar_width, 0)) - axes_to_data.transform((0, 0)))[0] height_in_data = top_left[1] - bottom_left[1] @@ -203,5 +205,10 @@ def _do_plot(self): cax.yaxis.set_label_position('left') self.colorbar.set_ticks((0, 1)) self.colorbar.set_ticklabels((0, max_activity)) + if self._colorbar_orient == 'vertical': + rotation = 90 + else: + rotation = 0 + self.colorbar.set_label('Spks/s', rotation=rotation) From 261ec8e94aa1b3e58c3a9c3cbff017ef83773b69 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Tue, 13 Oct 2020 12:07:10 +0200 Subject: [PATCH 07/10] Spks/s --> Sp/s --- spikewidgets/widgets/mapswidget/activitymapwidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 1566cd4..81a2355 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -209,6 +209,6 @@ def _do_plot(self): rotation = 90 else: rotation = 0 - self.colorbar.set_label('Spks/s', rotation=rotation) + self.colorbar.set_label('Sp/s', rotation=rotation) From 4199b9c8479aa9d11dab813499f15a5543d04376 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Wed, 18 Nov 2020 12:51:21 +0100 Subject: [PATCH 08/10] Modified plot_activity_map with new spiketoolkit changes --- spikewidgets/tests/test_widgets.py | 4 +++ .../widgets/mapswidget/activitymapwidget.py | 35 ++++++++++++------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/spikewidgets/tests/test_widgets.py b/spikewidgets/tests/test_widgets.py index d13db58..fe8be29 100644 --- a/spikewidgets/tests/test_widgets.py +++ b/spikewidgets/tests/test_widgets.py @@ -25,6 +25,10 @@ def test_spectrogram(self): def test_geometry(self): sw.plot_electrode_geometry(self._RX) + def test_activitymap(self): + sw.plot_activity_map(self._RX, activity='rate') + sw.plot_activity_map(self._RX, activity='amplitude') + def test_unitwaveforms(self): sw.plot_unit_waveforms(self._RX, self._SX) fig, axes = plt.subplots(self.num_units, 1) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 81a2355..00dcaf1 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -2,13 +2,13 @@ import spiketoolkit as st import matplotlib.pylab as plt import matplotlib as mpl -from mpl_toolkits.axes_grid1 import make_axes_locatable from ..utils import LabeledRectangle from spikewidgets.widgets.basewidget import BaseWidget from mpl_toolkits.axes_grid1.inset_locator import inset_axes -def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', background='on', label_color='r', +def plot_activity_map(recording, channel_ids=None, trange=None, activity='rate', + cmap='viridis', background='on', label_color='r', transpose=False, frame=False, colorbar=False, colorbar_bbox=None, colorbar_orientation='vertical', colorbar_width=0.02, recompute_info=False, ax=None, figure=None): @@ -23,6 +23,8 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', The channel ids to display. trange: list List with start time and end time + activity: str + 'rate' or 'amplitude'. If 'rate' the channel spike rate is used. If 'amplitude' the spike amplitude is used. cmap: matplotlib colormap The colormap to be used (default 'viridis') background: bool @@ -55,6 +57,7 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', recording=recording, channel_ids=channel_ids, trange=trange, + activity=activity, background=background, cmap=cmap, label_color=label_color, @@ -73,12 +76,13 @@ def plot_activity_map(recording, channel_ids=None, trange=None, cmap='viridis', class ActivityMapWidget(BaseWidget): - def __init__(self, recording, channel_ids, trange, cmap, background, label_color='r', transpose=False, frame=False, - colorbar=False, colorbar_bbox=None, colorbar_orientation='vertical', colorbar_width=0.02, - recompute_info=False, figure=None, ax=None): + def __init__(self, recording, channel_ids, activity, trange, cmap, background, label_color='r', + transpose=False, frame=False, colorbar=False, colorbar_bbox=None, colorbar_orientation='vertical', + colorbar_width=0.02, recompute_info=False, figure=None, ax=None): BaseWidget.__init__(self, figure, ax) self._recording = recording self._channel_ids = channel_ids + self._activity = activity self._trange = trange self._transpose = transpose self._cmap = cmap @@ -92,6 +96,7 @@ def __init__(self, recording, channel_ids, trange, cmap, background, label_color self._recompute_info = recompute_info self.colorbar = None self.name = 'ActivityMap' + assert activity in ['rate', 'amplitude'], "'activity' can be either 'rate' or 'amplitude'" assert 'location' in self._recording.get_shared_channel_property_names(), "Activity map requires 'location'" \ "property" @@ -106,13 +111,14 @@ def _do_plot(self): self._trange = [int(t * self._recording.get_sampling_frequency()) for t in self._trange] locations = self._recording.get_channel_locations(channel_ids=self._channel_ids) - activity = st.postprocessing.compute_channel_spiking_activity(self._recording, - start_frame=self._trange[0], - end_frame=self._trange[1], - method='detection', - align=False, - recompute_info=self._recompute_info, - verbose=False) + spike_rates, spike_amplitudes = st.postprocessing.compute_channel_spiking_activity(self._recording, + start_frame=self._trange[0], + end_frame=self._trange[1], + method='detection', + align=False, + recompute_info= + self._recompute_info, + verbose=False) if self._transpose: locations = np.roll(locations, 1, axis=1) @@ -143,6 +149,11 @@ def _do_plot(self): elec_x = 0.9 * pitch_x elec_y = 0.9 * pitch_y + if self._activity == 'rate': + activity = spike_rates + else: # amplitude + activity = spike_amplitudes + max_activity = np.round(np.max(activity), 2) # normalize From 5b23681a4580a6a1258519f41db3d4f40d687393 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Wed, 18 Nov 2020 14:13:05 +0100 Subject: [PATCH 09/10] Added log option and fix spike amplitudes map --- .../widgets/mapswidget/activitymapwidget.py | 34 +++++++++++++------ .../widgets/mapswidget/templatemapswidget.py | 3 ++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 00dcaf1..87c3f2d 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -7,7 +7,7 @@ from mpl_toolkits.axes_grid1.inset_locator import inset_axes -def plot_activity_map(recording, channel_ids=None, trange=None, activity='rate', +def plot_activity_map(recording, channel_ids=None, trange=None, activity='rate', log=False, cmap='viridis', background='on', label_color='r', transpose=False, frame=False, colorbar=False, colorbar_bbox=None, colorbar_orientation='vertical', colorbar_width=0.02, recompute_info=False, @@ -20,19 +20,21 @@ def plot_activity_map(recording, channel_ids=None, trange=None, activity='rate', recording: RecordingExtractor The recordng extractor object channel_ids: list - The channel ids to display. + The channel ids to display trange: list List with start time and end time activity: str - 'rate' or 'amplitude'. If 'rate' the channel spike rate is used. If 'amplitude' the spike amplitude is used. + 'rate' or 'amplitude'. If 'rate' the channel spike rate is used. If 'amplitude' the spike amplitude is used + log: bool + If True, log scale is used cmap: matplotlib colormap The colormap to be used (default 'viridis') background: bool If True, a background is added in between electrodes transpose: bool, optional, default: False - Swap x and y channel coordinates if True. + Swap x and y channel coordinates if True frame: bool, optional, default: False - Draw a frame around the array if True. + Draw a frame around the array if True colorbar: bool If True, a colorbar is displayed colorbar_bbox: bbox @@ -58,6 +60,7 @@ def plot_activity_map(recording, channel_ids=None, trange=None, activity='rate', channel_ids=channel_ids, trange=trange, activity=activity, + log=log, background=background, cmap=cmap, label_color=label_color, @@ -76,7 +79,7 @@ def plot_activity_map(recording, channel_ids=None, trange=None, activity='rate', class ActivityMapWidget(BaseWidget): - def __init__(self, recording, channel_ids, activity, trange, cmap, background, label_color='r', + def __init__(self, recording, channel_ids, activity, log, trange, cmap, background, label_color='r', transpose=False, frame=False, colorbar=False, colorbar_bbox=None, colorbar_orientation='vertical', colorbar_width=0.02, recompute_info=False, figure=None, ax=None): BaseWidget.__init__(self, figure, ax) @@ -84,6 +87,7 @@ def __init__(self, recording, channel_ids, activity, trange, cmap, background, l self._channel_ids = channel_ids self._activity = activity self._trange = trange + self._log = log self._transpose = transpose self._cmap = cmap self._frame = frame @@ -152,12 +156,18 @@ def _do_plot(self): if self._activity == 'rate': activity = spike_rates else: # amplitude - activity = spike_amplitudes + activity = np.abs(spike_amplitudes) max_activity = np.round(np.max(activity), 2) + min_activity = np.round(np.min(activity), 2) + + if self._log: + if np.any(activity < 1): + activity += (1 - np.min(activity)) + activity = np.log(activity) # normalize - activity -= np.min(activity) + activity -= (np.min(activity) + 1e-5) activity /= np.ptp(activity) for (loc, act, ch) in zip(locations, activity, self._recording.get_channel_ids()): @@ -209,13 +219,17 @@ def _do_plot(self): cax = inset_axes(self.ax, width="100%", height="100%", bbox_to_anchor=bbox, bbox_transform=self.ax.transData) - scalable = mpl.cm.ScalarMappable(norm=mpl.colors.Normalize(vmin=0, vmax=1), cmap=self._cmap) + if self._log: + norm = mpl.colors.LogNorm(vmin=1e-5, vmax=1) + else: + norm = mpl.colors.Normalize(vmin=0, vmax=1) + scalable = mpl.cm.ScalarMappable(norm=norm, cmap=self._cmap) self.colorbar = self.figure.colorbar(scalable, cax=cax, orientation=self._colorbar_orient)#, shrink=0.5) cax.yaxis.set_ticks_position('left') cax.yaxis.set_label_position('left') self.colorbar.set_ticks((0, 1)) - self.colorbar.set_ticklabels((0, max_activity)) + self.colorbar.set_ticklabels((min_activity, max_activity)) if self._colorbar_orient == 'vertical': rotation = 90 else: diff --git a/spikewidgets/widgets/mapswidget/templatemapswidget.py b/spikewidgets/widgets/mapswidget/templatemapswidget.py index b479cf8..91f1430 100644 --- a/spikewidgets/widgets/mapswidget/templatemapswidget.py +++ b/spikewidgets/widgets/mapswidget/templatemapswidget.py @@ -150,7 +150,10 @@ def _do_plot(self): for i, (template, unit) in enumerate(zip(templates, unit_ids)): ax = self.get_tiled_ax(i, nrows, ncols) temp_map = np.abs(fun(template, axis=1)) + if self._log: + if np.any(temp_map < 1): + temp_map += (1 - np.min(temp_map)) temp_map = np.log(temp_map) # normalize From cc8bc7d0a7cb81abeab4bd7e306e6cfe6f0cdaad Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Sun, 22 Nov 2020 17:06:49 +0100 Subject: [PATCH 10/10] Colorbar label fix --- spikewidgets/widgets/mapswidget/activitymapwidget.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spikewidgets/widgets/mapswidget/activitymapwidget.py b/spikewidgets/widgets/mapswidget/activitymapwidget.py index 87c3f2d..71f4da1 100644 --- a/spikewidgets/widgets/mapswidget/activitymapwidget.py +++ b/spikewidgets/widgets/mapswidget/activitymapwidget.py @@ -234,6 +234,7 @@ def _do_plot(self): rotation = 90 else: rotation = 0 - self.colorbar.set_label('Sp/s', rotation=rotation) - - + if self._activity == 'rate': + self.colorbar.set_label('Sp/s', rotation=rotation) + else: + self.colorbar.set_label('Amp.', rotation=rotation)