예제 #1
0
def rauc(signal, baseline=None, bin_duration=None, t_start=None, t_stop=None):
    '''
    Calculate the rectified area under the curve (RAUC) for an AnalogSignal.

    The signal is optionally divided into bins with duration `bin_duration`,
    and the rectified signal (absolute value) is integrated within each bin to
    find the area under the curve. The mean or median of the signal or an
    arbitrary baseline may optionally be subtracted before rectification. If
    the number of bins is 1 (default), a single value is returned for each
    channel in the input signal. Otherwise, an AnalogSignal containing the
    values for each bin is returned along with the times of the centers of the
    bins.

    Parameters
    ----------
    signal : neo.AnalogSignal
        The signal to integrate. If `signal` contains more than one channel,
        each is integrated separately.
    bin_duration : quantities.Quantity
        The length of time that each integration should span. If None, there
        will be only one bin spanning the entire signal duration. If
        `bin_duration` does not divide evenly into the signal duration, the end
        of the signal is padded with zeros to accomodate the final,
        overextending bin.
        Default: None
    baseline : string or quantities.Quantity
        A factor to subtract from the signal before rectification. If `'mean'`
        or `'median'`, the mean or median value of the entire signal is
        subtracted on a channel-by-channel basis.
        Default: None
    t_start, t_stop : quantities.Quantity
        Times to start and end the algorithm. The signal is cropped using
        `signal.time_slice(t_start, t_stop)` after baseline removal. Useful if
        you want the RAUC for a short section of the signal but want the
        mean or median calculation (`baseline='mean'` or `baseline='median'`)
        to use the entire signal for better baseline estimation.
        Default: None

    Returns
    -------
    quantities.Quantity or neo.AnalogSignal
        If the number of bins is 1, the returned object is a scalar or
        vector Quantity containing a single RAUC value for each channel.
        Otherwise, the returned object is an AnalogSignal containing the
        RAUC(s) for each bin stored as a sample, with times corresponding to
        the center of each bin. The output signal will have the same number
        of channels as the input signal.

    Raises
    ------
    TypeError
        If the input signal is not a neo.AnalogSignal.
    TypeError
        If `bin_duration` is not None or a Quantity.
    TypeError
        If `baseline` is not None, `'mean'`, `'median'`, or a Quantity.
    '''

    if not isinstance(signal, neo.AnalogSignal):
        raise TypeError('Input signal is not a neo.AnalogSignal!')

    if baseline is None:
        pass
    elif baseline is 'mean':
        # subtract mean from each channel
        signal = signal - signal.mean(axis=0)
    elif baseline is 'median':
        # subtract median from each channel
        signal = signal - np.median(signal.as_quantity(), axis=0)
    elif isinstance(baseline, pq.Quantity):
        # subtract arbitrary baseline
        signal = signal - baseline
    else:
        raise TypeError('baseline must be None, \'mean\', \'median\', '
                        'or a Quantity: {}'.format(baseline))

    # slice the signal after subtracting baseline
    signal = signal.time_slice(t_start, t_stop)

    if bin_duration is not None:
        # from bin duration, determine samples per bin and number of bins
        if isinstance(bin_duration, pq.Quantity):
            samples_per_bin = int(
                np.round(
                    bin_duration.rescale('s') /
                    signal.sampling_period.rescale('s')))
            n_bins = int(np.ceil(signal.shape[0] / samples_per_bin))
        else:
            raise TypeError(
                'bin_duration must be a Quantity: {}'.format(bin_duration))
    else:
        # all samples in one bin
        samples_per_bin = signal.shape[0]
        n_bins = 1

    # store the actual bin duration
    bin_duration = samples_per_bin * signal.sampling_period

    # reshape into equal size bins, padding the end with zeros if necessary
    n_channels = signal.shape[1]
    sig_binned = signal.as_quantity().copy()
    sig_binned.resize(n_bins * samples_per_bin, n_channels, refcheck=False)
    sig_binned = sig_binned.reshape(n_bins, samples_per_bin, n_channels)

    # rectify and integrate over each bin
    rauc = np.trapz(np.abs(sig_binned), dx=signal.sampling_period, axis=1)

    if n_bins == 1:
        # return a single value for each channel
        return rauc.squeeze()

    else:
        # return an AnalogSignal with times corresponding to center of each bin
        rauc_sig = neo.AnalogSignal(
            rauc,
            t_start=signal.t_start.rescale(bin_duration.units) +
            bin_duration / 2,
            sampling_period=bin_duration)
        return rauc_sig
예제 #2
0
def phase_histogram(signal,
                    spikes,
                    before=200 * ms,
                    after=200 * ms,
                    bins=72,
                    filter_range=[25, 35],
                    del_zero=True):
    """
    Creates and displays a circular histogram showing the distribution of oscillation
    phases when the spikes occur over the duration of the signal.

    Parameters
    ----------
    signal: Neo Analog Signal object
        Time series of voltage values; should be a down-sampled signal.
    spikes: Array of Neo SpikeTrain objects
        Spike trains, each of which will have its own phase histogram plotted
    before: time quantity
        window of time before spike event to consider in calculating LFP phase
    after: time quantity
        window of time after spike event to consider in calculating LFP phase
    bins: int
        number of bins to use in the circular histogram
    filter_range: List of two integers or floats
        Designates the low and high bounds of the bandpass filter for the LFP
    del_zero: boolean
        If true, deletes cluster #0

    Returns
    -------
    Displays circular histogram using plt.show(); doesn't return any actual values
    """

    cluster_num = 0
    if del_zero:
        spikes = np.delete(spikes, 0)
        cluster_num = 1
    rfs = signal.sampling_rate
    low = filter_range[0]
    high = filter_range[1]

    sig_start = signal.t_start
    #signal = cheby2_bp_filter(signal, low, high, rfs, order=5, axis=0)
    #signal = neo.core.AnalogSignal(signal, units=uV, sampling_rate=rfs, t_start=sig_start)

    pdone = 0
    spikes = spikes[1::]
    for spike_train in spikes:
        spike_train = spike_train[:len(spike_train) -
                                  10:1]  # to make the code run faster

        all_phases = [None] * len(spike_train)
        count = 0
        for n1 in spike_train:
            n = round(n1, 1000. / rfs)
            print(count / len(spike_train))
            spike_start = n - before
            if spike_start < spike_train.t_start:
                spike_start = spike_train.t_start
            spike_finish = n + after
            if spike_finish > spike_train.t_stop:
                spike_finish = spike_train.t_stop

            spike_start, spike_finish = round(spike_start,
                                              1), round(spike_finish, 1)

            lfp = signal.time_slice(spike_start, spike_finish)
            analytic_signal = hilbert(lfp, None, 0)
            instantaneous_phase = np.rad2deg(
                np.unwrap(np.angle(analytic_signal,
                                   deg=False)))  # changed, unwrap uses rads

            n = n - n % (round(lfp.times.rescale(ms), 1)[1] -
                         round(lfp.times.rescale(ms), 1)[0])
            n = round(n.rescale(ms), 1)

            phase_dictionary = dict(
                zip(list(map(str, (round(lfp.times.rescale(ms), 1)))),
                    instantaneous_phase))
            ip_of_spike = phase_dictionary[str(n)]
            all_phases[count] = ip_of_spike
            count = count + 1
        """
        create the histogram, and caculate vector sum
        """

        if len(all_phases) == 0:
            cluster_num = cluster_num + 1
            continue

        number_bins = bins
        bin_size = 360 / number_bins
        print(all_phases)
        counts, theta = np.histogram(np.squeeze(all_phases),
                                     np.arange(-180, 180 + bin_size, bin_size))
        theta = theta[:-1] + bin_size / 2.
        theta = theta * np.pi / 180
        a_deg = map(lambda x: np.ndarray.item(x), all_phases)
        a_rad = map(lambda x: math.radians(x), a_deg)
        a_rad = np.fromiter(a_rad, dtype=np.float)

        a_cos = map(lambda x: math.cos(x), a_rad)
        a_sin = map(lambda x: math.sin(x), a_rad)
        a_cos, a_sin = np.fromiter(a_cos,
                                   dtype=np.float), np.fromiter(a_sin,
                                                                dtype=np.float)

        uv_x = sum(a_cos) / len(a_cos)
        uv_y = sum(a_sin) / len(a_sin)
        uv_radius = np.sqrt((uv_x * uv_x) + (uv_y * uv_y)) * max(counts)
        uv_phase = np.angle(complex(uv_x, uv_y))
        uv_phase_deg = round(np.rad2deg(uv_phase), 1)
        if uv_phase_deg < 0:
            uv_phase_deg = uv_phase_deg + 360
        """
        plot histogram and vector sum
        """
        fig = plt.figure()
        ax1 = fig.add_axes([0.1, 0.16, 0.05, 0.56])
        ax2 = fig.add_axes([0.75, 0.16, 0.05, 0.56],
                           frameon=False,
                           xticks=(),
                           yticks=())

        histo = fig.add_subplot(111, polar=True)
        histo.yaxis.set_ticks(())

        plt.suptitle("Phase distribution for Neuron #" + str(cluster_num),
                     fontsize=15,
                     y=.94)
        plt.subplots_adjust(bottom=0.12, right=0.90, top=0.78, wspace=0.4)
        width = (2 * np.pi) / number_bins
        bars = histo.bar(theta, counts, width=width, bottom=0.000)

        for r, bar in zip(counts, bars):
            bar.set_facecolor(plt.cm.jet(r / max(counts)))
            bar.set_alpha(0.7)

        norm = matplotlib.colors.Normalize(
            vmin=(counts.min()) * len(all_phases) * bin_size,
            vmax=(counts.max()) * len(all_phases) * bin_size)
        cb1 = matplotlib.colorbar.ColorbarBase(
            ax1,
            cmap=plt.cm.jet,
            orientation='vertical',
            norm=norm,
            alpha=0.4,
        )
        # ticks=np.arange(0,(counts.max())*len(all_phases)*bin_size), )
        cb1.ax.tick_params(labelsize=11)
        cb1.solids.set_rasterized(True)
        cb1.set_label("# spikes")
        cb1.ax.yaxis.set_label_position('left')

        histo.annotate('',
                       xy=(uv_phase, uv_radius),
                       xytext=(uv_phase, 0),
                       xycoords='data',
                       arrowprops=dict(width=3,
                                       color='black',
                                       alpha=0.6,
                                       headwidth=7,
                                       headlength=7))
        ax2.annotate(
            "Number of spikes: " + str(len(all_phases)) +
            "\nVector sum length: " + str(round(
                (uv_radius / max(counts)), 3)) + "\nVector sum phase: " +
            str(uv_phase_deg) + degree_sign + "\np = " + str(
                np.round(
                    np.exp(-1 * len(all_phases) *
                           (uv_radius / max(counts))**2), 6)),
            xy=(0, 1),
            fontsize=8)

        plt.show()
        cluster_num = cluster_num + 1
        return 1
예제 #3
0
def rauc(signal, baseline=None, bin_duration=None, t_start=None, t_stop=None):
    """
    Calculate the rectified area under the curve (RAUC) for a
    `neo.AnalogSignal`.

    The signal is optionally divided into bins with duration `bin_duration`,
    and the rectified signal (absolute value) is integrated within each bin to
    find the area under the curve. The mean or median of the signal or an
    arbitrary baseline may optionally be subtracted before rectification.

    Parameters
    ----------
    signal : neo.AnalogSignal
        The signal to integrate. If `signal` contains more than one channel,
        each is integrated separately.
    baseline : pq.Quantity or {'mean', 'median'}, optional
        A factor to subtract from the signal before rectification.
        If 'mean', the mean value of the entire `signal` is subtracted on a
        channel-by-channel basis.
        If 'median', the median value of the entire `signal` is subtracted on
        a channel-by-channel basis.
        Default: None
    bin_duration : pq.Quantity, optional
        The length of time that each integration should span.
        If None, there will be only one bin spanning the entire signal
        duration.
        If `bin_duration` does not divide evenly into the signal duration, the
        end of the signal is padded with zeros to accomodate the final,
        overextending bin.
        Default: None
    t_start : pq.Quantity, optional
        Time to start the algorithm.
        If None, starts at the beginning of `signal`.
        Default: None
    t_stop : pq.Quantity, optional
        Time to end the algorithm.
        If None, ends at the last time of `signal`.
        The signal is cropped using `signal.time_slice(t_start, t_stop)` after
        baseline removal. Useful if you want the RAUC for a short section of
        the signal but want the mean or median calculation (`baseline`='mean'
        or `baseline`='median') to use the entire signal for better baseline
        estimation.
        Default: None

    Returns
    -------
    pq.Quantity or neo.AnalogSignal
        If the number of bins is 1, the returned object is a scalar or
        vector `pq.Quantity` containing a single RAUC value for each channel.
        Otherwise, the returned object is a `neo.AnalogSignal` containing the
        RAUC(s) for each bin stored as a sample, with times corresponding to
        the center of each bin. The output signal will have the same number
        of channels as the input signal.

    Raises
    ------
    ValueError
        If `signal` is not `neo.AnalogSignal`.

        If `bin_duration` is not None or `pq.Quantity`.

        If `baseline` is not None, 'mean', 'median', or `pq.Quantity`.

    See Also
    --------
    neo.AnalogSignal.time_slice : how `t_start` and `t_stop` are used

    Examples
    --------
    >>> import neo
    >>> import numpy as np
    >>> import quantities as pq
    >>> from elephant.signal_processing import rauc
    >>> signal = neo.AnalogSignal(np.arange(10), sampling_rate=20 * pq.Hz,
    ...     units='mV')
    >>> rauc(signal)
    array(2.025) * mV/Hz

    """

    if not isinstance(signal, neo.AnalogSignal):
        raise ValueError('Input signal is not a neo.AnalogSignal!')

    if baseline is None:
        pass
    elif baseline == 'mean':
        # subtract mean from each channel
        signal = signal - signal.mean(axis=0)
    elif baseline == 'median':
        # subtract median from each channel
        signal = signal - np.median(signal.as_quantity(), axis=0)
    elif isinstance(baseline, pq.Quantity):
        # subtract arbitrary baseline
        signal = signal - baseline
    else:
        raise ValueError("baseline must be either None, 'mean', 'median', or "
                         "a Quantity. Got {}".format(baseline))

    # slice the signal after subtracting baseline
    signal = signal.time_slice(t_start, t_stop)

    if bin_duration is not None:
        # from bin duration, determine samples per bin and number of bins
        if isinstance(bin_duration, pq.Quantity):
            samples_per_bin = int(
                np.round(
                    bin_duration.rescale('s') /
                    signal.sampling_period.rescale('s')))
            n_bins = int(np.ceil(signal.shape[0] / samples_per_bin))
        else:
            raise ValueError(
                "bin_duration must be a Quantity. Got {}".format(bin_duration))
    else:
        # all samples in one bin
        samples_per_bin = signal.shape[0]
        n_bins = 1

    # store the actual bin duration
    bin_duration = samples_per_bin * signal.sampling_period

    # reshape into equal size bins, padding the end with zeros if necessary
    n_channels = signal.shape[1]
    sig_binned = signal.as_quantity().copy()
    sig_binned.resize(n_bins * samples_per_bin, n_channels, refcheck=False)
    sig_binned = sig_binned.reshape(n_bins, samples_per_bin, n_channels)

    # rectify and integrate over each bin
    rauc = np.trapz(np.abs(sig_binned), dx=signal.sampling_period, axis=1)

    if n_bins == 1:
        # return a single value for each channel
        return rauc.squeeze()

    else:
        # return an AnalogSignal with times corresponding to center of each bin
        t_start = signal.t_start.rescale(bin_duration.units) + bin_duration / 2
        rauc_sig = neo.AnalogSignal(rauc,
                                    t_start=t_start,
                                    sampling_period=bin_duration)
        return rauc_sig
예제 #4
0
def rauc(signal, baseline=None, bin_duration=None, t_start=None, t_stop=None):
    '''
    Calculate the rectified area under the curve (RAUC) for an AnalogSignal.

    The signal is optionally divided into bins with duration `bin_duration`,
    and the rectified signal (absolute value) is integrated within each bin to
    find the area under the curve. The mean or median of the signal or an
    arbitrary baseline may optionally be subtracted before rectification. If
    the number of bins is 1 (default), a single value is returned for each
    channel in the input signal. Otherwise, an AnalogSignal containing the
    values for each bin is returned along with the times of the centers of the
    bins.

    Parameters
    ----------
    signal : neo.AnalogSignal
        The signal to integrate. If `signal` contains more than one channel,
        each is integrated separately.
    bin_duration : quantities.Quantity
        The length of time that each integration should span. If None, there
        will be only one bin spanning the entire signal duration. If
        `bin_duration` does not divide evenly into the signal duration, the end
        of the signal is padded with zeros to accomodate the final,
        overextending bin.
        Default: None
    baseline : string or quantities.Quantity
        A factor to subtract from the signal before rectification. If `'mean'`
        or `'median'`, the mean or median value of the entire signal is
        subtracted on a channel-by-channel basis.
        Default: None
    t_start, t_stop : quantities.Quantity
        Times to start and end the algorithm. The signal is cropped using
        `signal.time_slice(t_start, t_stop)` after baseline removal. Useful if
        you want the RAUC for a short section of the signal but want the
        mean or median calculation (`baseline='mean'` or `baseline='median'`)
        to use the entire signal for better baseline estimation.
        Default: None

    Returns
    -------
    quantities.Quantity or neo.AnalogSignal
        If the number of bins is 1, the returned object is a scalar or
        vector Quantity containing a single RAUC value for each channel.
        Otherwise, the returned object is an AnalogSignal containing the
        RAUC(s) for each bin stored as a sample, with times corresponding to
        the center of each bin. The output signal will have the same number
        of channels as the input signal.

    Raises
    ------
    TypeError
        If the input signal is not a neo.AnalogSignal.
    TypeError
        If `bin_duration` is not None or a Quantity.
    TypeError
        If `baseline` is not None, `'mean'`, `'median'`, or a Quantity.
    '''

    if not isinstance(signal, neo.AnalogSignal):
        raise TypeError('Input signal is not a neo.AnalogSignal!')

    if baseline is None:
        pass
    elif baseline is 'mean':
        # subtract mean from each channel
        signal = signal - signal.mean(axis=0)
    elif baseline is 'median':
        # subtract median from each channel
        signal = signal - np.median(signal.as_quantity(), axis=0)
    elif isinstance(baseline, pq.Quantity):
        # subtract arbitrary baseline
        signal = signal - baseline
    else:
        raise TypeError(
            'baseline must be None, \'mean\', \'median\', '
            'or a Quantity: {}'.format(baseline))

    # slice the signal after subtracting baseline
    signal = signal.time_slice(t_start, t_stop)

    if bin_duration is not None:
        # from bin duration, determine samples per bin and number of bins
        if isinstance(bin_duration, pq.Quantity):
            samples_per_bin = int(np.round(
                bin_duration.rescale('s')/signal.sampling_period.rescale('s')))
            n_bins = int(np.ceil(signal.shape[0]/samples_per_bin))
        else:
            raise TypeError(
                'bin_duration must be a Quantity: {}'.format(bin_duration))
    else:
        # all samples in one bin
        samples_per_bin = signal.shape[0]
        n_bins = 1

    # store the actual bin duration
    bin_duration = samples_per_bin * signal.sampling_period

    # reshape into equal size bins, padding the end with zeros if necessary
    n_channels = signal.shape[1]
    sig_binned = signal.as_quantity().copy()
    sig_binned.resize(n_bins * samples_per_bin, n_channels)
    sig_binned = sig_binned.reshape(n_bins, samples_per_bin, n_channels)

    # rectify and integrate over each bin
    rauc = np.trapz(np.abs(sig_binned), dx=signal.sampling_period, axis=1)

    if n_bins == 1:
        # return a single value for each channel
        return rauc.squeeze()

    else:
        # return an AnalogSignal with times corresponding to center of each bin
        rauc_sig = neo.AnalogSignal(
            rauc,
            t_start=signal.t_start.rescale(bin_duration.units)+bin_duration/2,
            sampling_period=bin_duration)
        return rauc_sig