def _update_cluster_ids(self, to_remove=None, to_add=None): # Update the list of non-empty cluster ids. self._cluster_ids = _unique(self._spike_clusters) # Clusters to remove. if to_remove is not None: for clu in to_remove: self._spikes_per_cluster.pop(clu, None) # Clusters to add. if to_add: for clu, spk in to_add.items(): self._spikes_per_cluster[clu] = spk # If spikes_per_cluster is invalid, recompute the entire # spikes_per_cluster array. coherent = np.all(np.in1d(self._cluster_ids, sorted(self._spikes_per_cluster), )) if not coherent: logger.debug("Recompute spikes_per_cluster manually: " "this is long.") sc = self._spike_clusters self._spikes_per_cluster = _spikes_per_cluster(sc)
def _do_assign(self, spike_ids, new_spike_clusters): """Make spike-cluster assignments after the spike selection has been extended to full clusters.""" # Ensure spike_clusters has the right shape. spike_ids = _as_array(spike_ids) if len(new_spike_clusters) == 1 and len(spike_ids) > 1: new_spike_clusters = (np.ones(len(spike_ids), dtype=np.int64) * new_spike_clusters[0]) old_spike_clusters = self._spike_clusters[spike_ids] assert len(spike_ids) == len(old_spike_clusters) assert len(new_spike_clusters) == len(spike_ids) # Update the spikes per cluster structure. old_clusters = _unique(old_spike_clusters) # NOTE: shortcut to a merge if this assignment is effectively a merge # i.e. if all spikes are assigned to a single cluster. # The fact that spike selection has been previously extended to # whole clusters is critical here. new_clusters = _unique(new_spike_clusters) if len(new_clusters) == 1: return self._do_merge(spike_ids, old_clusters, new_clusters[0]) # We return the UpdateInfo structure. up = _assign_update_info(spike_ids, old_spike_clusters, new_spike_clusters) # We update the new cluster id (strictly increasing during a session). self._new_cluster_id = max(self._new_cluster_id, max(up.added) + 1) # We make the assignments. self._spike_clusters[spike_ids] = new_spike_clusters # OPTIM: we update spikes_per_cluster manually. new_spc = _spikes_per_cluster(new_spike_clusters, spike_ids) self._update_cluster_ids(to_remove=old_clusters, to_add=new_spc) return up
def _update_cluster_ids(self, to_remove=None, to_add=None): # Update the list of non-empty cluster ids. self._cluster_ids = _unique(self._spike_clusters) # Clusters to remove. if to_remove is not None: for clu in to_remove: self._spikes_per_cluster.pop(clu, None) # Clusters to add. if to_add: for clu, spk in to_add.items(): self._spikes_per_cluster[clu] = spk # If spikes_per_cluster is invalid, recompute the entire # spikes_per_cluster array. coherent = np.all( np.in1d( self._cluster_ids, sorted(self._spikes_per_cluster), )) if not coherent: logger.debug("Recompute spikes_per_cluster manually: " "this is long.") sc = self._spike_clusters self._spikes_per_cluster = _spikes_per_cluster(sc)
def test_feature_view(qtbot, tempdir, n_channels): nc = n_channels ns = 500 features = artificial_features(ns, nc, 4) spike_clusters = artificial_spike_clusters(ns, 4) spike_times = np.linspace(0., 1., ns) spc = _spikes_per_cluster(spike_clusters) def get_spike_ids(cluster_id): return (spc[cluster_id] if cluster_id is not None else np.arange(ns)) def get_features(cluster_id=None, channel_ids=None, spike_ids=None, load_all=None): if load_all: spike_ids = spc[cluster_id] else: spike_ids = get_spike_ids(cluster_id) return Bunch( data=features[spike_ids], spike_ids=spike_ids, masks=np.random.rand(ns, nc), channel_ids=(channel_ids if channel_ids is not None else np.arange(nc)[::-1]), ) def get_time(cluster_id=None, load_all=None): return Bunch( data=spike_times[get_spike_ids(cluster_id)], lim=(0., 1.), ) v = FeatureView( features=get_features, attributes={'time': get_time}, ) v.set_state(GUIState(scaling=None)) gui = GUI(config_dir=tempdir) gui.show() v.attach(gui) qtbot.addWidget(gui) v.on_select([]) v.on_select([0]) v.on_select([0, 2, 3]) v.on_select([0, 2]) gui.emit('select', [0, 2]) qtbot.wait(10) v.increase() v.decrease() v.on_channel_click(channel_id=3, button=1, key=2) v.clear_channels() v.toggle_automatic_channel_selection() # Split without selection. spike_ids = v.on_request_split() assert len(spike_ids) == 0 # Draw a lasso. def _click(x, y): qtbot.mouseClick(v.native, Qt.LeftButton, pos=QPoint(x, y), modifier=Qt.ControlModifier) _click(10, 10) _click(10, 100) _click(100, 100) _click(100, 10) # Split lassoed points. spike_ids = v.on_request_split() assert len(spike_ids) > 0 # qtbot.stop() gui.close()
def test_feature_view(qtbot, tempdir, n_channels): nc = n_channels ns = 500 features = artificial_features(ns, nc, 4) spike_clusters = artificial_spike_clusters(ns, 4) spike_times = np.linspace(0., 1., ns) spc = _spikes_per_cluster(spike_clusters) def get_spike_ids(cluster_id): return (spc[cluster_id] if cluster_id is not None else np.arange(ns)) def get_features(cluster_id=None, channel_ids=None, spike_ids=None, load_all=None): if load_all: spike_ids = spc[cluster_id] else: spike_ids = get_spike_ids(cluster_id) return Bunch(data=features[spike_ids], spike_ids=spike_ids, masks=np.random.rand(ns, nc), channel_ids=(channel_ids if channel_ids is not None else np.arange(nc)[::-1]), ) def get_time(cluster_id=None, load_all=None): return Bunch(data=spike_times[get_spike_ids(cluster_id)], lim=(0., 1.), ) v = FeatureView(features=get_features, attributes={'time': get_time}, ) v.set_state(GUIState(scaling=None)) gui = GUI(config_dir=tempdir) gui.show() v.attach(gui) qtbot.addWidget(gui) v.on_select([]) v.on_select([0]) v.on_select([0, 2, 3]) v.on_select([0, 2]) gui.emit('select', [0, 2]) qtbot.wait(10) v.increase() v.decrease() v.on_channel_click(channel_id=3, button=1, key=2) v.clear_channels() v.toggle_automatic_channel_selection() # Split without selection. spike_ids = v.on_request_split() assert len(spike_ids) == 0 # Draw a lasso. def _click(x, y): qtbot.mouseClick(v.native, Qt.LeftButton, pos=QPoint(x, y), modifier=Qt.ControlModifier) _click(10, 10) _click(10, 100) _click(100, 100) _click(100, 10) # Split lassoed points. spike_ids = v.on_request_split() assert len(spike_ids) > 0 # qtbot.stop() gui.close()