def setUp(self): xx_ele, yy_ele = utils.generate_electrodes(dim=2, res=9, xlims=[0.05, 0.95], ylims=[0.05, 0.95]) self.ele_pos = np.vstack((xx_ele, yy_ele)).T self.csd_profile = utils.large_source_2D pots = CSD.generate_lfp( self.csd_profile, xx_ele, yy_ele, resolution=100) self.pots = np.reshape(pots, (-1, 1)) self.test_method = 'KCSD2D' self.test_params = {'gdx': 0.25, 'gdy': 0.25, 'R_init': 0.08, 'h': 50., 'xmin': 0., 'xmax': 1., 'ymin': 0., 'ymax': 1.} temp_signals = [] for ii in range(len(self.pots)): temp_signals.append(self.pots[ii]) self.an_sigs = neo.AnalogSignal(np.array(temp_signals).T * pq.mV, sampling_rate=1000 * pq.Hz) chidx = neo.ChannelIndex(range(len(self.pots))) chidx.analogsignals.append(self.an_sigs) chidx.coordinates = self.ele_pos * pq.mm chidx.create_relationship()
def setUp(self): xx_ele, yy_ele, zz_ele = utils.generate_electrodes(dim=3, res=5, xlims=[0.15, 0.85], ylims=[0.15, 0.85], zlims=[0.15, 0.85]) self.ele_pos = np.vstack((xx_ele, yy_ele, zz_ele)).T self.csd_profile = utils.gauss_3d_dipole pots = CSD.generate_lfp(self.csd_profile, xx_ele, yy_ele, zz_ele) self.pots = np.reshape(pots, (-1, 1)) self.test_method = 'KCSD3D' self.test_params = {'gdx': 0.05, 'gdy': 0.05, 'gdz': 0.05, 'lambd': 5.10896977451e-19, 'src_type': 'step', 'R_init': 0.31, 'xmin': 0., 'xmax': 1., 'ymin': 0., 'ymax': 1., 'zmin': 0., 'zmax': 1.} temp_signals = [] for ii in range(len(self.pots)): temp_signals.append(self.pots[ii]) self.an_sigs = neo.AnalogSignal(np.array(temp_signals).T * pq.mV, sampling_rate=1000 * pq.Hz) chidx = neo.ChannelIndex(range(len(self.pots))) chidx.analogsignals.append(self.an_sigs) chidx.coordinates = self.ele_pos * pq.mm chidx.create_relationship()
def _get_current_segment(self, filter_ids=None, variables='all', clear=False): segment = neo.Segment( name="segment%03d" % self._simulator.state.segment_counter, description=self.population.describe(), rec_datetime=datetime.now() ) # would be nice to get the time at the start of the recording, not the end variables_to_include = set(self.recorded.keys()) if variables is not 'all': variables_to_include = variables_to_include.intersection( set(variables)) for variable in variables_to_include: if variable == 'spikes': t_stop = self._simulator.state.t * pq.ms # must run on all MPI nodes sids = sorted(self.filter_recorded('spikes', filter_ids)) data = self._get_spiketimes(sids) segment.spiketrains = [ neo.SpikeTrain(data.get(int(id), []), t_start=self._recording_start_time, t_stop=t_stop, units='ms', source_population=self.population.label, source_id=int(id), source_index=self.population.id_to_index( int(id))) for id in sids ] else: ids = sorted(self.filter_recorded(variable, filter_ids)) signal_array = self._get_all_signals(variable, ids, clear=clear) t_start = self._recording_start_time sampling_period = self.sampling_interval * pq.ms current_time = self._simulator.state.t * pq.ms mpi_node = self._simulator.state.mpi_rank # for debugging if signal_array.size > 0: # may be empty if none of the recorded cells are on this MPI node units = self.population.find_units(variable) source_ids = numpy.fromiter(ids, dtype=int) signal = neo.AnalogSignal( signal_array, units=units, t_start=t_start, sampling_period=sampling_period, name=variable, source_population=self.population.label, source_ids=source_ids) signal.channel_index = neo.ChannelIndex( index=numpy.arange(source_ids.size), channel_ids=numpy.array( [self.population.id_to_index(id) for id in ids])) segment.analogsignals.append(signal) logger.debug("%d **** ids=%s, channels=%s", mpi_node, source_ids, signal.channel_index) assert segment.analogsignals[ 0].t_stop - current_time - 2 * sampling_period < 1e-10 # need to add `Unit` and `RecordingChannelGroup` objects return segment
def get_weights(self): signal = neo.AnalogSignal(self._weights, units='nA', sampling_period=self.interval * ms, name="weight") signal.channel_index = neo.ChannelIndex( numpy.arange(len(self._weights[0]))) return signal
def get_channel_idx(channel_id): if channel_id not in channel_idxs: channel_idxs[channel_id] = neo.ChannelIndex( name='Channel {}'.format(channel_id), index=None, channel_id=channel_id) block.channel_indexes.append(channel_idxs[channel_id]) return channel_idxs[channel_id]
def _get_channel_index(ids, block): # Note this code is only called if not pynn8_syntax for channel_index in block.channel_indexes: if numpy.array_equal(channel_index.index, ids): return channel_index count = len(block.channel_indexes) channel_index = neo.ChannelIndex(name="Index {}".format(count), index=ids) block.channel_indexes.append(channel_index) return channel_index
def __get_channel_index(ids, block): for channel_index in block.channel_indexes: if numpy.array_equal(channel_index.index, ids): return channel_index count = len(block.channel_indexes) channel_index = neo.ChannelIndex(name="Index {}".format(count), index=ids) block.channel_indexes.append(channel_index) return channel_index
def generate_block(n_segments=3, n_channels=8, n_units=3, data_samples=1000, feature_samples=100): """ Generate a block with a single recording channel group and a number of segments, recording channels and units with associated analog signals and spike trains. """ feature_len = feature_samples / data_samples # Create container and grouping objects segments = [neo.Segment(index=i) for i in range(n_segments)] chx = neo.ChannelIndex(index=1, name='T0') for i in range(n_channels): rc = neo.RecordingChannel(name='C%d' % i, index=i) rc.channelindexes = [chx] chx.recordingchannels.append(rc) units = [neo.Unit('U%d' % i) for i in range(n_units)] chx.units = units block = neo.Block() block.segments = segments block.channel_indexes = [chx] # Create synthetic data for seg in segments: feature_pos = np.random.randint(0, data_samples - feature_samples) # Analog signals: Noise with a single sinewave feature wave = 3 * np.sin(np.linspace(0, 2 * np.pi, feature_samples)) for rc in chx.recordingchannels: sig = np.random.randn(data_samples) sig[feature_pos:feature_pos + feature_samples] += wave signal = neo.AnalogSignal(sig * pq.mV, sampling_rate=1 * pq.kHz) seg.analogsignals.append(signal) rc.analogsignals.append(signal) # Spike trains: Random spike times with elevated rate in short period feature_time = feature_pos / data_samples for u in units: random_spikes = np.random.rand(20) feature_spikes = np.random.rand(5) * feature_len + feature_time spikes = np.hstack([random_spikes, feature_spikes]) train = neo.SpikeTrain(spikes * pq.s, 1 * pq.s) seg.spiketrains.append(train) u.spiketrains.append(train) block.create_many_to_one_relationship() return block
def setUp(self): self.ele_pos = utils.generate_electrodes(dim=1).reshape(5, 1) self.csd_profile = utils.gauss_1d_dipole pots = CSD.generate_lfp(self.csd_profile, self.ele_pos) self.pots = np.reshape(pots, (-1, 1)) self.test_method = 'KCSD1D' self.test_params = {'h': 50.} temp_signals = [] for ii in range(len(self.pots)): temp_signals.append(self.pots[ii]) self.an_sigs = neo.AnalogSignal(np.array(temp_signals).T * pq.mV, sampling_rate=1000 * pq.Hz) chidx = neo.ChannelIndex(range(len(self.pots))) chidx.analogsignals.append(self.an_sigs) chidx.coordinates = self.ele_pos * pq.mm chidx.create_relationship()
def ImageSequence2AnalogSignal(block): # ToDo: map potentially 2D array annotations to 1D and update for seg_count, segment in enumerate(block.segments): for imgseq in segment.imagesequences: dim_t, dim_x, dim_y = imgseq.as_array().shape # coords = np.zeros((dim_x, dim_y, 2), dtype=int) # for x, row in enumerate(coords): # for y, cell in enumerate(row): # coords[x][y][0] = x # coords[x][y][1] = y # coords = coords.reshape((dim_x * dim_y, 2)) coords = np.array( list(itertools.product(np.arange(dim_x), np.arange(dim_y)))) imgseq_flat = imgseq.as_array().reshape((dim_t, dim_x * dim_y)) asig = neo.AnalogSignal(signal=imgseq_flat, units=imgseq.units, sampling_rate=imgseq.sampling_rate, file_origin=imgseq.file_origin, description=imgseq.description, name=imgseq.name, array_annotations={ 'x_coords': coords[:, 0], 'y_coords': coords[:, 1] }, grid_size=(dim_x, dim_y), spatial_scale=imgseq.spatial_scale, **imgseq.annotations) chidx = neo.ChannelIndex(name=asig.name, channel_ids=np.arange(dim_x * dim_y), index=np.arange(dim_x * dim_y), coordinates=coords * imgseq.spatial_scale) chidx.annotations.update(asig.array_annotations) # asig.channel_index = chidx chidx.analogsignals = [asig] + chidx.analogsignals # block.channel_indexes.append(chidx) block.segments[seg_count].analogsignals.append(asig) return block
def test_load_save(): n_channels = 5 n_samples = 20 n_spikes = 50 fname = '/tmp/test_phy.exdir' if os.path.exists(fname): shutil.rmtree(fname) wf = np.random.random((n_spikes, n_channels, n_samples)) ts = np.sort(np.random.random(n_spikes)) t_stop = np.ceil(ts[-1]) sptr = neo.SpikeTrain(times=ts, units='s', waveforms=wf * pq.V, t_stop=t_stop, **{'group_id': 0}) blk = neo.Block() seg = neo.Segment() seg.duration = t_stop blk.segments.append(seg) chx = neo.ChannelIndex(index=range(n_channels), **{'group_id': 0}) blk.channel_indexes.append(chx) sptr.channel_index = chx unit = neo.Unit() unit.spiketrains.append(sptr) chx.units.append(unit) seg.spiketrains.append(sptr) epo = neo.Epoch() if os.path.exists(fname): shutil.rmtree(fname) io = neo.ExdirIO(fname) io.write_block(blk) wfswap = wf.swapaxes(1, 2) m = NeoModel(fname, overwrite=True) assert np.array_equal(m.spike_times, ts) assert np.array_equal(m.waveforms, wfswap) m.save() m2 = NeoModel(fname, overwrite=True) assert np.array_equal(m2.spike_times, ts) assert np.array_equal(m2.waveforms, wfswap) assert np.array_equal(m2.features, m.features) assert np.array_equal(m2.amplitudes, m.amplitudes) assert np.array_equal(m2.spike_clusters, m.spike_clusters)
def build_block(data_file): """(plaintextfile_path) -> neo.core.block.Block Thie function reads a plain text file (data_file) with the data exported (per waveform) from Plexon Offline Sorter after sorting and returns a neo.Block with spiketrains ordered in any number of 10 minute segments. For practicality and Plexon management of channels names, units and channels have been ignored in the block structure.""" raw_data = pd.read_csv(data_file, sep=',', header=0, usecols=[0, 1, 2]) ord_times = raw_data.groupby(['Channel Name', 'Unit'])['Timestamp'] new_block = neo.Block() chx = neo.ChannelIndex(index=None, name='MEA_60') new_block.channel_indexes.append(chx) # Next line will not work properly if last spike happens # exactly at the end of the recording num_segments = range(int(raw_data['Timestamp'].max() // 600 + 1)) for ind in num_segments: seg = neo.Segment(name='segment {}'.format(ind), index=ind) new_block.segments.append(seg) for name, group in ord_times: time_stamps = ord_times.get_group(name).values inter = 600 # Number of seconds in 10 minutes first_seg = neo.SpikeTrain( time_stamps[time_stamps < inter], units='sec', t_start=0, t_stop=inter) new_block.segments[0].spiketrains.append(first_seg) new_unit = neo.Unit(name=name) sptrs = [first_seg] for seg in num_segments[1:-1]: seg_train = neo.SpikeTrain(time_stamps[(time_stamps > seg * inter) & (time_stamps < ((seg + 1) * inter))], units='sec', t_start=(seg * inter), t_stop=((seg + 1) * inter)) new_block.segments[seg].spiketrains.append(seg_train) sptrs.append(seg_train) last_seg = neo.SpikeTrain(time_stamps[time_stamps > (num_segments[-1] * inter)], units='sec', t_start=(num_segments[-1]) * inter, t_stop=((num_segments[-1] + 1) * inter)) new_block.segments[num_segments[-1]].spiketrains.append(last_seg) sptrs.append(last_seg) new_unit.spiketrains = sptrs chx.units.append(new_unit) return new_block
def generate_spike_trains(exdir_path, openephys_rec, source='klusta'): import neo if source == 'klusta': # TODO acquire features and masks print('Generating spike trains from KWIK file') exdir_file = exdir.File(exdir_path) acquisition = exdir_file["acquisition"] openephys_session = acquisition.attrs["openephys_session"] klusta_directory = op.join(str(acquisition.directory), openephys_session, 'klusta') n = 0 for root, dirs, files in os.walk(klusta_directory): for f in files: if not f.endswith('_klusta.kwik'): continue n += 1 kwikfile = op.join(root, f) kwikio = neo.io.KwikIO(filename=kwikfile, ) blk = kwikio.read_block(raw_data_units='uV') seg = blk.segments[0] try: exdirio = neo.io.ExdirIO(exdir_path) exdirio.write_block(blk) except Exception: print('WARNING: unable to convert\n', kwikfile) if n == 0: raise IOError('.kwik file cannot be found in ' + klusta_directory) elif source == 'openephys': exdirio = neo.io.ExdirIO(exdir_path) for oe_group in openephys_rec.channel_groups: channel_ids = [ch.id for ch in oe_group.channels] channel_index = [ch.index for ch in oe_group.channels] chx = neo.ChannelIndex(name='channel group {}'.format(oe_group.id), channel_ids=channel_ids, index=channel_index, group_id=oe_group.id) for sptr in oe_group.spiketrains: unit = neo.Unit(cluster_group='unsorted', cluster_id=sptr.attrs['cluster_id'], name=sptr.attrs['name']) unit.spiketrains.append( neo.SpikeTrain(times=sptr.times, waveforms=sptr.waveforms, sampling_rate=sptr.sample_rate, t_stop=sptr.t_stop, **sptr.attrs)) chx.units.append(unit) exdirio.write_channelindex(chx, start_time=0 * pq.s, stop_time=openephys_rec.duration) elif source == 'kilosort': print('Generating spike trains from KiloSort') exdirio = neo.io.ExdirIO(exdir_path) exdir_file = exdir.File(exdir_path) openephys_directory = op.join( str(exdir_file["acquisition"].directory), exdir_file["acquisition"].attrs["openephys_session"]) # iterate over channel groups. As there are no channel associated with # any spiking unit (TO BE IMPLEMENTED), everything is written to # channel_group 0 for oe_group in openephys_rec.channel_groups: channel_ids = [ch.id for ch in oe_group.channels] channel_index = [ch.index for ch in oe_group.channels] chx = neo.ChannelIndex(name='channel group {}'.format(oe_group.id), channel_ids=channel_ids, index=channel_index, group_id=oe_group.id) # load output spt = np.load(op.join(openephys_directory, 'spike_times.npy')).flatten() spc = np.load(op.join(openephys_directory, 'spike_clusters.npy')).flatten() try: cgroup = np.loadtxt(op.join(openephys_directory, 'cluster_group.tsv'), dtype=[('cluster_id', 'i4'), ('group', 'U8')], skiprows=1) if cgroup.shape == (0, ): raise FileNotFoundError except FileNotFoundError: # manual corrections didn't happen; cgroup = np.array(list( zip(np.unique(spc), ['unsorted'] * np.unique(spc).size)), dtype=[('cluster_id', 'i4'), ('group', 'U8')]) for id, grp in cgroup: unit = neo.Unit(cluster_group=str(grp), cluster_id=id, name=id) unit.spiketrains.append( neo.SpikeTrain( times=(spt[spc == id].astype(float) / openephys_rec.sample_rate).simplified, t_stop=openephys_rec.duration, )) chx.units.append(unit) exdirio.write_channelindex(chx, start_time=0 * pq.s, stop_time=openephys_rec.duration) break else: raise ValueError(source + ' not supported')
import neo import quantities as pq import numpy as np import nixio as nix from neo.io import NixIO block = neo.Block() chn_index = neo.ChannelIndex([0, 1, 2], channel_names=["a", "b", "c"], channel_ids=[1, 2, 3]) block.channel_indexes.append(chn_index) unit = neo.Unit(name="x", description="contain1st") chn_index.units.append(unit) seg = neo.Segment() asig = neo.AnalogSignal(name="signal", signal=[1.1, 1.2, 1.5], units="mV", sampling_rate=1 * pq.Hz) seg.analogsignals.append(asig) asig2 = neo.AnalogSignal(name="signal2", signal=[1.1, 1.2, 2.5], units="mV", sampling_rate=1 * pq.Hz) seg.analogsignals.append(asig2) irasig = neo.IrregularlySampledSignal(name="irsignal", signal=np.random.random((100, 2)), units="mV", times=np.cumsum( np.random.random(100) * pq.s)) seg.irregularlysampledsignals.append(irasig)
if options.plot_figure: from pyNN.utility.plotting import Figure, Panel #figure_filename = normalized_filename("Results", "stochastic_comparison", # "png", options.simulator) figure_filename = "Results/stochastic_comparison_{}.png".format( options.simulator) data = {} for label in synapse_types: data[label] = populations[label].get_data().segments[0] if 'stochastic' in label: gsyn = data[label].filter(name='gsyn_inh')[0] gsyn_mean = neo.AnalogSignal(gsyn.mean(axis=1).reshape(-1, 1), sampling_rate=gsyn.sampling_rate) gsyn_mean.channel_index = neo.ChannelIndex(np.array([0])) gsyn_mean.name = 'gsyn_inh_mean' data[label].analogsignals.append(gsyn_mean) def make_panel(population, label): return Panel( population.get_data().segments[0].filter(name='gsyn_inh')[0], data_labels=[label], yticks=True) panels = [ Panel(data['depressing, deterministic'].filter(name='gsyn_inh')[0][:, 0], data_labels=['depressing, deterministic'], yticks=True, ylim=[0, 0.008]),
def save(self, spike_clusters=None, groups=None, *labels): if spike_clusters is None: spike_clusters = self.spike_clusters assert spike_clusters.shape == self.spike_clusters.shape # assert spike_clusters.dtype == self.spike_clusters.dtype # TODO check if this is necessary self.spike_clusters = spike_clusters blk = neo.Block() seg = neo.Segment(name='Segment_{}'.format(self.segment_num), index=self.segment_num) # seg.duration = self.duration blk.segments.append(seg) metadata = self.chx.annotations if labels: metadata.update({name: values for name, values in labels}) chx = neo.ChannelIndex(index=self.chx.index, name=self.chx.name, **metadata) blk.channel_indexes.append(chx) try: wf_units = self.sptrs[0].waveforms.units except AttributeError: wf_units = pq.dimensionless clusters = np.unique(spike_clusters) self.cluster_groups = groups or self.cluster_groups for sc in clusters: mask = self.spike_clusters == sc waveforms = np.swapaxes(self.waveforms[mask], 1, 2) * wf_units sptr = neo.SpikeTrain(times=self.spike_times[mask] * pq.s, waveforms=waveforms, sampling_rate=self.sample_rate * pq.Hz, name='cluster #%i' % sc, t_stop=self.duration, t_start=self.start_time, **{'cluster_id': sc, 'cluster_group': self.cluster_groups[sc].lower(), 'kk2_metadata': self.kk2_metadata}) sptr.channel_index = chx unt = neo.Unit(name='Unit #{}'.format(sc), **{'cluster_id': sc, 'cluster_group': self.cluster_groups[sc].lower()}) unt.spiketrains.append(sptr) chx.units.append(unt) seg.spiketrains.append(sptr) # save block to file try: io = neo.get_io(self.save_path, mode=self.mode) except Exception: io = neo.get_io(self.save_path) io.write_block(blk) if hasattr(io, 'close'): io.close() if self.output_ext == '.exdir': # save features and masks group = exdir.File(directory=self.save_path) self._exdir_save_group = self._find_exdir_channel_group( group["processing"]['electrophysiology']) # TODO not use elphys name if self._exdir_save_group is None: raise IOError('Can not find a dirctory corresponding to ' + 'channel_group {}'.format(self.channel_group)) self.save_features_masks(spike_clusters)
def load_data(self, channel_group=None, segment_num=None): io = neo.get_io(self.data_path) print(self.data_path) assert io.is_readable self.channel_group = channel_group or self.channel_group self.segment_num = segment_num or self.segment_num if self.segment_num is None: self.segment_num = 0 # TODO find the right seg num if neo.Block in io.readable_objects: logger.info('Loading block') blk = io.read_block() # TODO params to select what to read try: io.close() except: pass if not all(['group_id' in chx.annotations for chx in blk.channel_indexes]): logger.warn('"group_id" is not in channel_index.annotations ' + 'counts channel_group as appended to ' + 'Block.channel_indexes') self._chxs = {i: chx for i, chx in enumerate(blk.channel_indexes)} else: self._chxs = {int(chx.annotations['group_id']): chx for chx in blk.channel_indexes} self.channel_groups = list(self._chxs.keys()) self.seg = blk.segments[self.segment_num] if self.channel_group is None: self.channel_group = self.channel_groups[0] if self.channel_group not in self.channel_groups: raise ValueError('channel group not available,' + ' see available channel groups in neo-describe') self.chx = self._chxs[self.channel_group] self.sptrs = [st for st in self.seg.spiketrains if st.channel_index == self.chx] elif neo.Segment in io.readable_objects: logger.info('Loading segment') self.seg = io.read_segment() self.segment_num = 0 self.sptrs = self.seg.spiketrains self.chx = neo.ChannelIndex( index=[range(self.sptrs[0].waveforms.shape[1])], **{'group_id': 0} ) self.duration = self.seg.t_stop - self.seg.t_start self.start_time = self.seg.t_start self.channel_ids = self.chx.index self.n_chans = len(self.chx.index) self.sample_rate = self.sptrs[0].sampling_rate.rescale('Hz').magnitude self.spike_times = self._load_spike_times() sorted_idxs = np.argsort(self.spike_times) self.spike_times = self.spike_times[sorted_idxs] ns, = self.n_spikes, = self.spike_times.shape self.spike_clusters = self._load_spike_clusters()[sorted_idxs] assert self.spike_clusters.shape == (ns,) self.cluster_groups = self._load_cluster_groups() self.waveforms = self._load_waveforms()[sorted_idxs, :, :] assert self.waveforms.shape[::2] == (ns, self.n_chans), '{} != {}'.format(self.waveforms.shape[::2], (ns, self.n_chans)) self.features, self.masks = self._load_features_masks() # loads from waveforms which is already sorted self.amplitudes = self._load_amplitudes() assert self.amplitudes.shape == (ns, self.n_chans) # TODO load positino from params ch_pos = np.zeros((self.n_chans, 2)) ch_pos[:, 1] = np.arange(self.n_chans) self.channel_positions = ch_pos
def generate_lfp(csd_profile, x_positions, y_positions=None, z_positions=None, x_limits=[0., 1.], y_limits=[0., 1.], z_limits=[0., 1.], resolution=50): """ Forward modelling for getting the potentials for testing Current Source Density (CSD). Parameters ---------- csd_profile : callable A function that computes true CSD profile. Available options are (see ./csd/utility_functions.py) 1D : gauss_1d_dipole 2D : large_source_2D and small_source_2D 3D : gauss_3d_dipole x_positions : np.ndarray Positions of the x coordinates of the electrodes y_positions : np.ndarray, optional Positions of the y coordinates of the electrodes Defaults to None, use in 2D or 3D cases only z_positions : np.ndarray, optional Positions of the z coordinates of the electrodes Defaults to None, use in 3D case only x_limits : list, optional A list of [start, end]. The starting spatial coordinate and the ending for integration Defaults to [0.,1.] y_limits : list, optional A list of [start, end]. The starting spatial coordinate and the ending for integration Defaults to [0.,1.], use only in 2D and 3D case z_limits : list, optional A list of [start, end]. The starting spatial coordinate and the ending for integration Defaults to [0.,1.], use only in 3D case resolution : int, optional The resolution of the integration Defaults to 50 Returns ------- LFP : neo.AnalogSignal The potentials created by the csd profile at the electrode positions. The electrode positions are attached as RecordingChannel's coordinate. """ def integrate_1D(x0, csd_x, csd, h): m = np.sqrt((csd_x - x0)**2 + h**2) - abs(csd_x - x0) y = csd * m I = simps(y, csd_x) return I def integrate_2D(x, y, xlin, ylin, csd, h, X, Y): x = np.reshape(x, (1, 1, len(x))) y = np.reshape(y, (1, 1, len(y))) X = np.expand_dims(X, axis=2) Y = np.expand_dims(Y, axis=2) csd = np.expand_dims(csd, axis=2) m = np.sqrt((x - X)**2 + (y - Y)**2) np.clip(m, a_min=0.0000001, a_max=None, out=m) y = np.arcsinh(2 * h / m) * csd I = simps(y.T, ylin) F = simps(I, xlin) return F def integrate_3D(x, y, z, csd, xlin, ylin, zlin, X, Y, Z): m = np.sqrt((x - X)**2 + (y - Y)**2 + (z - Z)**2) np.clip(m, a_min=0.0000001, a_max=None, out=m) z = csd / m Iy = simps(np.transpose(z, (1, 0, 2)), zlin) Iy = simps(Iy, ylin) F = simps(Iy, xlin) return F dim = 1 if z_positions is not None: dim = 3 elif y_positions is not None: dim = 2 x = np.linspace(x_limits[0], x_limits[1], resolution) sigma = 1.0 h = 50. if dim == 1: chrg_x = x csd = csd_profile(chrg_x) pots = integrate_1D(x_positions, chrg_x, csd, h) pots /= 2. * sigma # eq.: 26 from Potworowski et al ele_pos = x_positions elif dim == 2: y = np.linspace(y_limits[0], y_limits[1], resolution) chrg_x = np.expand_dims(x, axis=1) chrg_y = np.expand_dims(y, axis=0) csd = csd_profile(chrg_x, chrg_y) pots = integrate_2D(x_positions, y_positions, x, y, csd, h, chrg_x, chrg_y) pots /= 2 * np.pi * sigma ele_pos = np.vstack((x_positions, y_positions)).T elif dim == 3: y = np.linspace(y_limits[0], y_limits[1], resolution) z = np.linspace(z_limits[0], z_limits[1], resolution) chrg_x, chrg_y, chrg_z = np.mgrid[ x_limits[0]:x_limits[1]:np.complex(0, resolution), y_limits[0]:y_limits[1]:np.complex(0, resolution), z_limits[0]:z_limits[1]:np.complex(0, resolution)] csd = csd_profile(chrg_x, chrg_y, chrg_z) pots = np.zeros(len(x_positions)) for ii in range(len(x_positions)): pots[ii] = integrate_3D(x_positions[ii], y_positions[ii], z_positions[ii], csd, x, y, z, chrg_x, chrg_y, chrg_z) pots /= 4 * np.pi * sigma ele_pos = np.vstack((x_positions, y_positions, z_positions)).T ele_pos = ele_pos * pq.mm ch = neo.ChannelIndex(index=range(len(pots))) asig = neo.AnalogSignal(np.expand_dims(pots, axis=0), sampling_rate=pq.kHz, units='mV') ch.coordinates = ele_pos ch.analogsignals.append(asig) ch.create_relationship() return asig
def save_spikesorting(sorting_file, block, sorting_hash=None, parameter_dict=None): """ :param savedir: :param block: :return: """ if parameter_dict is None and sorting_hash is None: raise ValueError('Please provide either a parameter dictionary or a ' 'sorting hash to specify the sorting you want to ' 'load.') elif parameter_dict is not None: sorting_hash = elephant.spike_sorting.SpikeSorter.get_sorting_hash( parameter_dict) filename = sorting_file + '_spikesorting.hdf5' print('Saving sorted spikes (sorting hash {}) at {}' ''.format(sorting_hash, filename)) sorting_chidx = [ i for i in block.channel_indexes if ('sorting_hash' in i.annotations and i.annotations['sorting_hash'] == sorting_hash) ] if len(sorting_chidx) == 0: warnings.warn('No channel_index selected for saving spikesorting with ' 'hash "{}". Not saving anything.'.format(sorting_hash)) return elif len(sorting_chidx) > 1: warnings.warn('Multiple channels with sorting hash "{}" exist. Not ' 'saving anything.'.format(sorting_hash)) return sorting_chidx = sorting_chidx[0] with neo.nixio.NixIO(filename, 'rw') as nix_file: # check if there is already a block with the same channel_index annotation nix_blocks = nix_file.read_all_blocks() new_block = False if len(nix_blocks) is 0: new_block = True nix_blocks = [neo.Block(name='spike sorting block')] nix_blocks[0].segments.append( neo.Segment(name='spike sorting ' 'segment')) assert len(nix_blocks[0].segments) == 1 nix_block = nix_blocks[0] seg = nix_blocks[0].segments[0] # check if channel_index for this sorting already exists chidx = None for chidx_i in nix_block.channel_indexes: if chidx_i.annotations['sorting_hash'] == sorting_hash: chidx = chidx_i break if chidx is None: chidx = neo.ChannelIndex([-1], name='spike sorting', sorting_hash=sorting_hash) for anno in ['sorter', 'sorting_parameters']: if anno in sorting_chidx.annotations: chidx.annotations[anno] = sorting_chidx.annotations[anno] chidx.block = nix_block nix_block.channel_indexes.append(chidx) duplicated_units = [copy.deepcopy(u) for u in sorting_chidx.units] for uid, unit in enumerate(duplicated_units): chidx.units.append(unit) unit.channel_index = chidx # spiketrain is automatically duplicated when rescaled original_sts = sorting_chidx.units[uid].spiketrains unit.spiketrains = [] for o_st in original_sts: # TODO: Remove this rescaling quickfix once nixpy is fixed # but duplicate spiketrain instead duplicated_st = o_st.rescale('s') unit.spiketrains.append(duplicated_st) # duplicating and decoupling of spiketrains from original neo structure for st in unit.spiketrains: ###################### # # TODO: Remove this quickfix once nixpy is fixed # # Quickfix # st_new = st.rescale('s') # unit.spiketrains.remove(st) # unit.spiketrains.append(st_new) # st_new.segment = seg # TODO: Remove this quickfix once neo nixio is fixed if st.left_sweep: st.left_sweep = [st.left_sweep.rescale('s').magnitude ] * pq.s st.sampling_rate = st.sampling_rate.rescale('Hz') if ('invalid_waveforms' in st.annotations and st.annotations['invalid_waveforms'] == []): st.annotations['invalid_waveforms'] = 0 ######################## st.unit = unit st.segment = seg seg.spiketrains.extend(unit.spiketrains) nix_block.create_relationship() nix_file.write_block(nix_block)
def generate_lfp(csd_profile, ele_xx, ele_yy=None, ele_zz=None, xlims=[0., 1.], ylims=[0., 1.], zlims=[0., 1.], res=50): """Forward modelling for the getting the potentials for testing CSD Parameters ---------- csd_profile : fuction that computes True CSD profile Available options are (see ./csd/utility_functions.py) 1D : gauss_1d_dipole 2D : large_source_2D and small_source_2D 3D : gauss_3d_dipole ele_xx : np.array Positions of the x coordinates of the electrodes ele_yy : np.array Positions of the y coordinates of the electrodes Defaults ot None, use in 2D or 3D cases only ele_zz : np.array Positions of the z coordinates of the electrodes Defaults ot None, use in 3D case only x_lims : [start, end] The starting spatial coordinate and the ending for integration Defaults to [0.,1.] y_lims : [start, end] The starting spatial coordinate and the ending for integration Defaults to [0.,1.], use only in 2D and 3D case z_lims : [start, end] The starting spatial coordinate and the ending for integration Defaults to [0.,1.], use only in 3D case res : int The resolution of the integration Defaults to 50 Returns ------- LFP : neo.AnalogSignal object The potentials created by the csd profile at the electrode positions The electrode postions are attached as RecordingChannel's coordinate """ def integrate_1D(x0, csd_x, csd, h): m = np.sqrt((csd_x - x0)**2 + h**2) - abs(csd_x - x0) y = csd * m I = simps(y, csd_x) return I def integrate_2D(x, y, xlin, ylin, csd, h, X, Y): Ny = ylin.shape[0] m = np.sqrt((x - X)**2 + (y - Y)**2) m[m < 0.0000001] = 0.0000001 y = np.arcsinh(2 * h / m) * csd I = np.zeros(Ny) for i in range(Ny): I[i] = simps(y[:, i], ylin) F = simps(I, xlin) return F def integrate_3D(x, y, z, xlim, ylim, zlim, csd, xlin, ylin, zlin, X, Y, Z): Nz = zlin.shape[0] Ny = ylin.shape[0] m = np.sqrt((x - X)**2 + (y - Y)**2 + (z - Z)**2) m[m < 0.0000001] = 0.0000001 z = csd / m Iy = np.zeros(Ny) for j in range(Ny): Iz = np.zeros(Nz) for i in range(Nz): Iz[i] = simps(z[:, j, i], zlin) Iy[j] = simps(Iz, ylin) F = simps(Iy, xlin) return F dim = 1 if ele_zz is not None: dim = 3 elif ele_yy is not None: dim = 2 x = np.linspace(xlims[0], xlims[1], res) if dim >= 2: y = np.linspace(ylims[0], ylims[1], res) if dim == 3: z = np.linspace(zlims[0], zlims[1], res) sigma = 1.0 h = 50. pots = np.zeros(len(ele_xx)) if dim == 1: chrg_x = np.linspace(xlims[0], xlims[1], res) csd = csd_profile(chrg_x) for ii in range(len(ele_xx)): pots[ii] = integrate_1D(ele_xx[ii], chrg_x, csd, h) pots /= 2. * sigma # eq.: 26 from Potworowski et al ele_pos = ele_xx elif dim == 2: chrg_x, chrg_y = np.mgrid[xlims[0]:xlims[1]:np.complex(0, res), ylims[0]:ylims[1]:np.complex(0, res)] csd = csd_profile(chrg_x, chrg_y) for ii in range(len(ele_xx)): pots[ii] = integrate_2D(ele_xx[ii], ele_yy[ii], x, y, csd, h, chrg_x, chrg_y) pots /= 2 * np.pi * sigma ele_pos = np.vstack((ele_xx, ele_yy)).T elif dim == 3: chrg_x, chrg_y, chrg_z = np.mgrid[xlims[0]:xlims[1]:np.complex(0, res), ylims[0]:ylims[1]:np.complex(0, res), zlims[0]:zlims[1]:np.complex(0, res)] csd = csd_profile(chrg_x, chrg_y, chrg_z) xlin = chrg_x[:, 0, 0] ylin = chrg_y[0, :, 0] zlin = chrg_z[0, 0, :] for ii in range(len(ele_xx)): pots[ii] = integrate_3D(ele_xx[ii], ele_yy[ii], ele_zz[ii], xlims, ylims, zlims, csd, xlin, ylin, zlin, chrg_x, chrg_y, chrg_z) pots /= 4 * np.pi * sigma ele_pos = np.vstack((ele_xx, ele_yy, ele_zz)).T pots = np.reshape(pots, (-1, 1)) * pq.mV ele_pos = ele_pos * pq.mm lfp = [] ch = neo.ChannelIndex(index=range(len(pots))) for ii in range(len(pots)): lfp.append(pots[ii]) asig = neo.AnalogSignal(np.array(lfp).T, sampling_rate=pq.kHz, units='mV') ch.coordinates = ele_pos ch.analogsignals.append(asig) ch.create_relationship() return asig
][0]) asig.array_annotations.update(electrode_location=locations) colors = [ kwargs['ELECTRODE_COLOR'][loc] for loc in asig.array_annotations['electrode_location'] ] asig.array_annotations.update(electrode_color=colors) asig.annotations.update(parse_string2dict(args.annotations)) asig.annotations.update(spatial_scale=args.spatial_scale * pq.mm) dim_t, channel_num = asig.as_array().shape chidx = neo.ChannelIndex(name=asig.name, channel_ids=np.arange(channel_num), index=np.arange(channel_num), coordinates=coords * args.spatial_scale * pq.mm) chidx.annotations.update(asig.array_annotations) # Save data block.name = args.data_name block.segments[0].name = 'Segment 1' block.segments[0].description = 'Loaded with neo.Spike2IO (neo version {})'\ .format(neo.__version__) if asig.description is None: asig.description = '' asig.description += 'ECoG signal. ' # Save data if len(block.segments[0].analogsignals) > 1: raise Warning('Additional AnalogSignal found. The pipeline can yet \