Esempio n. 1
0
 def _parse_cutargs(self, *args):
     """Takes set of args as passed to cut or cutrel and returns (tstart, tend)"""
     print("WARNING: Neuron._parse_cutargs should be deprecated, use spikes.searchsorted "
           "directly instead")
     tstart = None
     tend = None
     if len(args) == 0: # passed nothing
         pass
     elif len(args) == 1: # passed None, or just tstart, or a (tstart, tend) sequence
         if not iterable(args[0]):
             if args[0] == None:
                 pass
             else: # just tstart was passed
                 tstart = args[0]
         elif len(args[0]) == 2: # it's a sequence
             tstart = args[0][0]
             tend = args[0][1]
         else:
             raise ValueError('sequence is too long')
     elif len(args) == 2: # passed tstart and tend as separate args
         tstart = args[0]
         tend = args[1]
     else:
         raise ValueError('too many arguments')
     if tstart in [None, 0]:
         # shorthand for "from first spike" - would be problematic if a spike existed at t=0
         tstart = self.spikes[0]
     if tend in [None, -1]:
         # shorthand for "to last spike" - would be problematic if a spike existed at t=-1
         tend = self.spikes[-1]
     return (tstart, tend)
Esempio n. 2
0
File: surf.py Progetto: nhazar/spyke
 def _verifyParsing(self):
     """Make sure timestamps of all records are in causal (increasing)
     order. If not, sort them"""
     for attrname, attr in self.__dict__.items():
         if attrname.endswith('records') and iterable(attr):
             ts = get_record_timestamps(attr)
             if not issorted(ts):
                 print('sorting %s' % attrname)
                 if type(attr) == list:
                     attr = list(np.asarray(attr)[ts.argsort()])
                 else:
                     attr = attr[ts.argsort()]
                 ts = get_record_timestamps(attr)
                 assert issorted(ts)
                 self.__dict__[attrname] = attr  # update
Esempio n. 3
0
File: surf.py Progetto: nhazar/spyke
 def _verifyParsing(self):
     """Make sure timestamps of all records are in causal (increasing)
     order. If not, sort them"""
     for attrname, attr in self.__dict__.items():
         if attrname.endswith('records') and iterable(attr):
             ts = get_record_timestamps(attr)
             if not issorted(ts):
                 print('sorting %s' % attrname)
                 if type(attr) == list:
                     attr = list(np.asarray(attr)[ts.argsort()])
                 else:
                     attr = attr[ts.argsort()]
                 ts = get_record_timestamps(attr)
                 assert issorted(ts)
                 self.__dict__[attrname] = attr # update
Esempio n. 4
0
    def specgram(self, t0=None, t1=None, f0=0.1, f1=100, p0=-60, p1=None, chanis=-1,
                 width=None, tres=None, cm='jet', colorbar=False,
                 showstates=False, lw=4, alpha=1, relative2t0=False, lim2stim=False,
                 title=True, reclabel=True, swapaxes=False, figsize=None):
        """Plot a spectrogram from t0 to t1 in sec, from f0 to f1 in Hz, and clip power values
        from p0 to p1 in dB, based on channel index chani of LFP data. chanis=0 uses most
        superficial channel, chanis=-1 uses deepest channel. If len(chanis) > 1, take mean of
        specified chanis. width and tres are in sec. As an alternative to cm.jet (the
        default), cm.gray, cm.hsv cm.terrain, and cm.cubehelix_r colormaps seem to bring out
        the most structure in the spectrogram. showstates controls whether to plot lines
        demarcating desynchronized and synchronized periods. relative2t0 controls whether to
        plot relative to t0, or relative to start of ADC clock. lim2stim limits the time range
        only to when a stimulus was on screen, i.e. to the outermost times of non-NULL din"""
        uns = get_ipython().user_ns
        self.get_data()
        ts = self.get_tssec() # full set of timestamps, in sec
        if t0 == None:
            t0, t1 = ts[0], ts[-1] # full duration
        if t1 == None:
            t1 = t0 + 10 # 10 sec window
        if lim2stim:
            t0, t1 = self.apply_lim2stim(t0, t1)
        dt = t1 - t0
        if width == None:
            width = uns['LFPSPECGRAMWIDTH'] # sec
        if tres == None:
            tres = uns['LFPSPECGRAMTRES'] # sec
        assert tres <= width
        NFFT = intround(width * self.sampfreq)
        noverlap = intround(NFFT - tres * self.sampfreq)
        t0i, t1i = ts.searchsorted((t0, t1))
        #ts = ts[t0i:t1i] # constrained set of timestamps, in sec
        data = self.data[:, t0i:t1i] # slice data
        if figsize == None:
            # convert from recording duration time to width in inches, 0.87 accommodates
            # padding around the specgram:
            figwidth = (dt / 1000) * 5 + 0.87
            figheight = 2.5 # inches
            figsize = figwidth, figheight
        f = pl.figure(figsize=figsize)
        a = f.add_subplot(111)
        if iterable(chanis):
            data = data[chanis].mean(axis=0) # take mean of data on chanis
        else:
            data = data[chanis] # get single row of data at chanis
        #data = filter.notch(data)[0] # remove 60 Hz mains noise
        # convert data from uV to mV, returned t is midpoints of time bins in sec from
        # start of data. I think P is in mV^2?:
        P, freqs, t = mpl.mlab.specgram(data/1e3, NFFT=NFFT, Fs=self.sampfreq,
                                        noverlap=noverlap)
        if not relative2t0:
            t += t0 # convert t to time from start of ADC clock:
        # keep only freqs between f0 and f1:
        if f0 == None:
            f0 = freqs[0]
        if f1 == None:
            f1 = freqs[-1]
        df = f1 - f0
        lo, hi = freqs.searchsorted([f0, f1])
        P, freqs = P[lo:hi], freqs[lo:hi]
        # check for and replace zero power values (ostensibly due to gaps in recording)
        # before attempting to convert to dB:
        zis = np.where(P == 0.0) # row and column indices where P has zero power
        if len(zis[0]) > 0: # at least one hit
            P[zis] = np.finfo(np.float64).max # temporarily replace zeros with max float
            minnzval = P.min() # get minimum nonzero value
            P[zis] = minnzval # replace with min nonzero values
        P = 10. * np.log10(P) # convert power to dB wrt 1 mV^2?
        # for better visualization, clip power values to within (p0, p1) dB
        if p0 != None:
            P[P < p0] = p0
        if p1 != None:
            P[P > p1] = p1
        #self.P = P

        # plot horizontal bars over time demarcating different ranges of SI values,
        # or manually defined desynched and synched periods:
        statelinepos = f0 - df*0.015 # plot horizontal bars just below x axis
        if showstates:
            if showstates in [True, 'auto']:
                print("TODO: there's an offset plotting bug for 'auto', compare with 'manual'")
                si, t = self.si(plot=False)
                stranges, states = self.si_split(si, t) # sec
                STATECOLOURS = uns['LFPPRBINCOLOURS']
            elif showstates == 'manual':
                stranges, states = [], []
                for state in uns['MANUALSTATES']:
                    for strange in uns['REC2STATE2TRANGES'][self.r.absname][state]:
                        stranges.append(strange)
                        states.append(state)
                stranges = np.vstack(stranges) # 2D array
                STATECOLOURS = uns['MANUALSTATECOLOURS']
            else:
                raise ValueError('invalid value showstates=%r' % showstates)
            # clip stranges to t0, t1:
            stranges[0, 0] = max(stranges[0, 0], t0)
            stranges[-1, 1] = min(stranges[-1, 1], t1)
            if swapaxes:
                lines = a.vlines
            else:
                lines = a.hlines
            for strange, state in zip(stranges, states):
                clr = STATECOLOURS[state]
                lines(statelinepos, strange[0], strange[1], colors=clr, lw=lw, alpha=alpha,
                      clip_on=False)

        # Label far left, right, top and bottom edges of imshow image. imshow interpolates
        # between these to place the axes ticks. Time limits are
        # set from midpoints of specgram time bins
        extent = t[0], t[-1], freqs[0], freqs[-1]
        #print('specgram extent: %r' % (extent,))
        # flip P vertically for compatibility with imshow:
        im = a.imshow(P[::-1], extent=extent, cmap=cm)
        a.autoscale(enable=True, tight=True)
        a.axis('tight')
        # depending on relative2t0 above, x=0 represents either t0 or time ADC clock started:
        a.set_xlim(xmin=0, xmax=t[-1])
        a.set_ylim(ymin=freqs[0], ymax=freqs[-1])
        # turn off annoying "+2.41e3" type offset on x axis:
        formatter = mpl.ticker.ScalarFormatter(useOffset=False)
        a.xaxis.set_major_formatter(formatter)
        a.set_xlabel("time (s)")
        a.set_ylabel("frequency (Hz)")
        titlestr = lastcmd()
        gcfm().window.setWindowTitle(titlestr)
        if title:
            a.set_title(titlestr)
        if reclabel:
            a.text(0.994, 0.95, '%s' % self.r.absname, color='w', transform=a.transAxes,
                   horizontalalignment='right', verticalalignment='top')
        f.tight_layout(pad=0.3) # crop figure to contents
        if colorbar:
            f.colorbar(im, pad=0) # creates big whitespace to the right for some reason
        self.f = f
        return P, freqs, t
Esempio n. 5
0
 def psd(self, t0=None, t1=None, f0=0.2, f1=110, p0=None, p1=None, chanis=-1,
         width=None, tres=None, xscale='log', figsize=(5, 5)):
     """Plot power spectral density from t0 to t1 in sec, from f0 to f1 in Hz, and clip
     power values from p0 to p1 in dB, based on channel index chani of LFP data. chanis=0
     uses most superficial channel, chanis=-1 uses deepest channel. If len(chanis) > 1,
     take mean of specified chanis. width and tres are in sec."""
     uns = get_ipython().user_ns
     self.get_data()
     ts = self.get_tssec() # full set of timestamps, in sec
     if t0 == None:
         t0, t1 = ts[0], ts[-1] # full duration
     if t1 == None:
         t1 = t0 + 10 # 10 sec window
     if width == None:
         width = uns['LFPSPECGRAMWIDTH'] # sec
     if tres == None:
         tres = uns['LFPSPECGRAMTRES'] # sec
     assert tres <= width
     NFFT = intround(width * self.sampfreq)
     noverlap = intround(NFFT - tres * self.sampfreq)
     t0i, t1i = ts.searchsorted((t0, t1))
     #ts = ts[t0i:t1i] # constrained set of timestamps, in sec
     data = self.data[:, t0i:t1i] # slice data
     f = pl.figure(figsize=figsize)
     a = f.add_subplot(111)
     if iterable(chanis):
         data = data[chanis].mean(axis=0) # take mean of data on chanis
     else:
         data = data[chanis] # get single row of data at chanis
     #data = filter.notch(data)[0] # remove 60 Hz mains noise
     # convert data from uV to mV. I think P is in mV^2?:
     P, freqs = mpl.mlab.psd(data/1e3, NFFT=NFFT, Fs=self.sampfreq, noverlap=noverlap)
     # keep only freqs between f0 and f1:
     if f0 == None:
         f0 = freqs[0]
     if f1 == None:
         f1 = freqs[-1]
     lo, hi = freqs.searchsorted([f0, f1])
     P, freqs = P[lo:hi], freqs[lo:hi]
     # check for and replace zero power values (ostensibly due to gaps in recording)
     # before attempting to convert to dB:
     zis = np.where(P == 0.0) # row and column indices where P has zero power
     if len(zis[0]) > 0: # at least one hit
         P[zis] = np.finfo(np.float64).max # temporarily replace zeros with max float
         minnzval = P.min() # get minimum nonzero value
         P[zis] = minnzval # replace with min nonzero values
     P = 10. * np.log10(P) # convert power to dB wrt 1 mV^2?
     # for better visualization, clip power values to within (p0, p1) dB
     if p0 != None:
         P[P < p0] = p0
     if p1 != None:
         P[P > p1] = p1
     #self.P = P
     a.plot(freqs, P, 'k-')
     # add SI frequency band limits:
     LFPPRLOBAND, LFPPRHIBAND = uns['LFPPRLOBAND'], uns['LFPPRHIBAND']
     a.axvline(x=LFPPRLOBAND[0], c='r', ls='--')
     a.axvline(x=LFPPRLOBAND[1], c='r', ls='--')
     a.axvline(x=LFPPRHIBAND[0], c='b', ls='--')
     a.axvline(x=LFPPRHIBAND[1], c='b', ls='--')
     a.axis('tight')
     a.set_xscale(xscale)
     a.set_xlabel("frequency (Hz)")
     a.set_ylabel("power (dB)")
     titlestr = lastcmd()
     gcfm().window.setWindowTitle(titlestr)
     a.set_title(titlestr)
     a.text(0.998, 0.99, '%s' % self.r.name, color='k', transform=a.transAxes,
            horizontalalignment='right', verticalalignment='top')
     f.tight_layout(pad=0.3) # crop figure to contents
     self.f = f
     return P, freqs
Esempio n. 6
0
    def specgram(self,
                 t0=None,
                 t1=None,
                 f0=0.1,
                 f1=100,
                 p0=-60,
                 p1=None,
                 chanis=-1,
                 width=None,
                 tres=None,
                 cm='jet',
                 colorbar=False,
                 showstates=False,
                 lw=4,
                 alpha=1,
                 relative2t0=False,
                 lim2stim=False,
                 title=True,
                 reclabel=True,
                 swapaxes=False,
                 figsize=None):
        """Plot a spectrogram from t0 to t1 in sec, from f0 to f1 in Hz, and clip power values
        from p0 to p1 in dB, based on channel index chani of LFP data. chanis=0 uses most
        superficial channel, chanis=-1 uses deepest channel. If len(chanis) > 1, take mean of
        specified chanis. width and tres are in sec. As an alternative to cm.jet (the
        default), cm.gray, cm.hsv cm.terrain, and cm.cubehelix_r colormaps seem to bring out
        the most structure in the spectrogram. showstates controls whether to plot lines
        demarcating desynchronized and synchronized periods. relative2t0 controls whether to
        plot relative to t0, or relative to start of ADC clock. lim2stim limits the time range
        only to when a stimulus was on screen, i.e. to the outermost times of non-NULL din"""
        uns = get_ipython().user_ns
        self.get_data()
        ts = self.get_tssec()  # full set of timestamps, in sec
        if t0 == None:
            t0, t1 = ts[0], ts[-1]  # full duration
        if t1 == None:
            t1 = t0 + 10  # 10 sec window
        if lim2stim:
            t0, t1 = self.apply_lim2stim(t0, t1)
        dt = t1 - t0
        if width == None:
            width = uns['LFPSPECGRAMWIDTH']  # sec
        if tres == None:
            tres = uns['LFPSPECGRAMTRES']  # sec
        assert tres <= width
        NFFT = intround(width * self.sampfreq)
        noverlap = intround(NFFT - tres * self.sampfreq)
        t0i, t1i = ts.searchsorted((t0, t1))
        #ts = ts[t0i:t1i] # constrained set of timestamps, in sec
        data = self.data[:, t0i:t1i]  # slice data
        if figsize == None:
            # convert from recording duration time to width in inches, 0.87 accommodates
            # padding around the specgram:
            figwidth = (dt / 1000) * 5 + 0.87
            figheight = 2.5  # inches
            figsize = figwidth, figheight
        f = pl.figure(figsize=figsize)
        a = f.add_subplot(111)
        if iterable(chanis):
            data = data[chanis].mean(axis=0)  # take mean of data on chanis
        else:
            data = data[chanis]  # get single row of data at chanis
        #data = filter.notch(data)[0] # remove 60 Hz mains noise
        # convert data from uV to mV, returned t is midpoints of time bins in sec from
        # start of data. I think P is in mV^2?:
        P, freqs, t = mpl.mlab.specgram(data / 1e3,
                                        NFFT=NFFT,
                                        Fs=self.sampfreq,
                                        noverlap=noverlap)
        if not relative2t0:
            t += t0  # convert t to time from start of ADC clock:
        # keep only freqs between f0 and f1:
        if f0 == None:
            f0 = freqs[0]
        if f1 == None:
            f1 = freqs[-1]
        df = f1 - f0
        lo, hi = freqs.searchsorted([f0, f1])
        P, freqs = P[lo:hi], freqs[lo:hi]
        # check for and replace zero power values (ostensibly due to gaps in recording)
        # before attempting to convert to dB:
        zis = np.where(
            P == 0.0)  # row and column indices where P has zero power
        if len(zis[0]) > 0:  # at least one hit
            P[zis] = np.finfo(
                np.float64).max  # temporarily replace zeros with max float
            minnzval = P.min()  # get minimum nonzero value
            P[zis] = minnzval  # replace with min nonzero values
        P = 10. * np.log10(P)  # convert power to dB wrt 1 mV^2?
        # for better visualization, clip power values to within (p0, p1) dB
        if p0 != None:
            P[P < p0] = p0
        if p1 != None:
            P[P > p1] = p1
        #self.P = P

        # plot horizontal bars over time demarcating different ranges of SI values,
        # or manually defined desynched and synched periods:
        statelinepos = f0 - df * 0.015  # plot horizontal bars just below x axis
        if showstates:
            if showstates in [True, 'auto']:
                print(
                    "TODO: there's an offset plotting bug for 'auto', compare with 'manual'"
                )
                si, t = self.si(plot=False)
                stranges, states = self.si_split(si, t)  # sec
                STATECOLOURS = uns['LFPPRBINCOLOURS']
            elif showstates == 'manual':
                stranges, states = [], []
                for state in uns['MANUALSTATES']:
                    for strange in uns['REC2STATE2TRANGES'][
                            self.r.absname][state]:
                        stranges.append(strange)
                        states.append(state)
                stranges = np.vstack(stranges)  # 2D array
                STATECOLOURS = uns['MANUALSTATECOLOURS']
            else:
                raise ValueError('invalid value showstates=%r' % showstates)
            # clip stranges to t0, t1:
            stranges[0, 0] = max(stranges[0, 0], t0)
            stranges[-1, 1] = min(stranges[-1, 1], t1)
            if swapaxes:
                lines = a.vlines
            else:
                lines = a.hlines
            for strange, state in zip(stranges, states):
                clr = STATECOLOURS[state]
                lines(statelinepos,
                      strange[0],
                      strange[1],
                      colors=clr,
                      lw=lw,
                      alpha=alpha,
                      clip_on=False)

        # Label far left, right, top and bottom edges of imshow image. imshow interpolates
        # between these to place the axes ticks. Time limits are
        # set from midpoints of specgram time bins
        extent = t[0], t[-1], freqs[0], freqs[-1]
        #print('specgram extent: %r' % (extent,))
        # flip P vertically for compatibility with imshow:
        im = a.imshow(P[::-1], extent=extent, cmap=cm)
        a.autoscale(enable=True, tight=True)
        a.axis('tight')
        # depending on relative2t0 above, x=0 represents either t0 or time ADC clock started:
        a.set_xlim(xmin=0, xmax=t[-1])
        a.set_ylim(ymin=freqs[0], ymax=freqs[-1])
        # turn off annoying "+2.41e3" type offset on x axis:
        formatter = mpl.ticker.ScalarFormatter(useOffset=False)
        a.xaxis.set_major_formatter(formatter)
        a.set_xlabel("time (s)")
        a.set_ylabel("frequency (Hz)")
        titlestr = lastcmd()
        gcfm().window.setWindowTitle(titlestr)
        if title:
            a.set_title(titlestr)
        if reclabel:
            a.text(0.994,
                   0.95,
                   '%s' % self.r.absname,
                   color='w',
                   transform=a.transAxes,
                   horizontalalignment='right',
                   verticalalignment='top')
        f.tight_layout(pad=0.3)  # crop figure to contents
        if colorbar:
            f.colorbar(
                im,
                pad=0)  # creates big whitespace to the right for some reason
        self.f = f
        return P, freqs, t
Esempio n. 7
0
 def psd(self,
         t0=None,
         t1=None,
         f0=0.2,
         f1=110,
         p0=None,
         p1=None,
         chanis=-1,
         width=None,
         tres=None,
         xscale='log',
         figsize=(5, 5)):
     """Plot power spectral density from t0 to t1 in sec, from f0 to f1 in Hz, and clip
     power values from p0 to p1 in dB, based on channel index chani of LFP data. chanis=0
     uses most superficial channel, chanis=-1 uses deepest channel. If len(chanis) > 1,
     take mean of specified chanis. width and tres are in sec."""
     uns = get_ipython().user_ns
     self.get_data()
     ts = self.get_tssec()  # full set of timestamps, in sec
     if t0 == None:
         t0, t1 = ts[0], ts[-1]  # full duration
     if t1 == None:
         t1 = t0 + 10  # 10 sec window
     if width == None:
         width = uns['LFPSPECGRAMWIDTH']  # sec
     if tres == None:
         tres = uns['LFPSPECGRAMTRES']  # sec
     assert tres <= width
     NFFT = intround(width * self.sampfreq)
     noverlap = intround(NFFT - tres * self.sampfreq)
     t0i, t1i = ts.searchsorted((t0, t1))
     #ts = ts[t0i:t1i] # constrained set of timestamps, in sec
     data = self.data[:, t0i:t1i]  # slice data
     f = pl.figure(figsize=figsize)
     a = f.add_subplot(111)
     if iterable(chanis):
         data = data[chanis].mean(axis=0)  # take mean of data on chanis
     else:
         data = data[chanis]  # get single row of data at chanis
     #data = filter.notch(data)[0] # remove 60 Hz mains noise
     # convert data from uV to mV. I think P is in mV^2?:
     P, freqs = mpl.mlab.psd(data / 1e3,
                             NFFT=NFFT,
                             Fs=self.sampfreq,
                             noverlap=noverlap)
     # keep only freqs between f0 and f1:
     if f0 == None:
         f0 = freqs[0]
     if f1 == None:
         f1 = freqs[-1]
     lo, hi = freqs.searchsorted([f0, f1])
     P, freqs = P[lo:hi], freqs[lo:hi]
     # check for and replace zero power values (ostensibly due to gaps in recording)
     # before attempting to convert to dB:
     zis = np.where(
         P == 0.0)  # row and column indices where P has zero power
     if len(zis[0]) > 0:  # at least one hit
         P[zis] = np.finfo(
             np.float64).max  # temporarily replace zeros with max float
         minnzval = P.min()  # get minimum nonzero value
         P[zis] = minnzval  # replace with min nonzero values
     P = 10. * np.log10(P)  # convert power to dB wrt 1 mV^2?
     # for better visualization, clip power values to within (p0, p1) dB
     if p0 != None:
         P[P < p0] = p0
     if p1 != None:
         P[P > p1] = p1
     #self.P = P
     a.plot(freqs, P, 'k-')
     # add SI frequency band limits:
     LFPPRLOBAND, LFPPRHIBAND = uns['LFPPRLOBAND'], uns['LFPPRHIBAND']
     a.axvline(x=LFPPRLOBAND[0], c='r', ls='--')
     a.axvline(x=LFPPRLOBAND[1], c='r', ls='--')
     a.axvline(x=LFPPRHIBAND[0], c='b', ls='--')
     a.axvline(x=LFPPRHIBAND[1], c='b', ls='--')
     a.axis('tight')
     a.set_xscale(xscale)
     a.set_xlabel("frequency (Hz)")
     a.set_ylabel("power (dB)")
     titlestr = lastcmd()
     gcfm().window.setWindowTitle(titlestr)
     a.set_title(titlestr)
     a.text(0.998,
            0.99,
            '%s' % self.r.name,
            color='k',
            transform=a.transAxes,
            horizontalalignment='right',
            verticalalignment='top')
     f.tight_layout(pad=0.3)  # crop figure to contents
     self.f = f
     return P, freqs