Example #1
0
def interp_filt_position(x, y, tm, box_xlen=1 * pq.m, box_ylen=1 * pq.m,
                         pos_fs=100 * pq.Hz, f_cut=10 * pq.Hz):
    """
    Calculeate head direction in angles or radians for time t

    Parameters
    ----------
    x : quantities.Quantity array in m
        1d vector of x positions
    y : quantities.Quantity array in m
        1d vector of y positions
    tm : quantities.Quantity array in s
        1d vector of times at x, y positions
    pos_fs : quantities scalar in Hz
        return radians

    Returns
    -------
    out : angles, resized t
    """
    import scipy.signal as ss
    assert len(x) == len(y) == len(tm), 'x, y, t must have same length'
    is_quantities([x, y, tm], 'vector')
    is_quantities([pos_fs, box_xlen, box_ylen, f_cut], 'scalar')
    spat_dim = x.units
    t = np.arange(tm.min(), tm.max() + 1. / pos_fs, 1. / pos_fs) * tm.units
    x = np.interp(t, tm, x)
    y = np.interp(t, tm, y)
    # rapid head movements will contribute to velocity artifacts,
    # these can be removed by low-pass filtering
    # see http://www.ncbi.nlm.nih.gov/pmc/articles/PMC1876586/
    # code addapted from Espen Hagen
    b, a = ss.butter(N=1, Wn=f_cut * 2 / pos_fs)
    # zero phase shift filter
    x = ss.filtfilt(b, a, x) * spat_dim
    y = ss.filtfilt(b, a, y) * spat_dim
    # we tolerate small interpolation errors
    x[(x > -1e-3) & (x < 0.0)] = 0.0 * spat_dim
    y[(y > -1e-3) & (y < 0.0)] = 0.0 * spat_dim
    if np.isnan(x).any() and np.isnan(y).any():
        raise ValueError('nans found in  position, ' +
            'x nans = %i, y nans = %i' % (sum(np.isnan(x)), sum(np.isnan(y))))
    if (x.min() < 0 or x.max() > box_xlen or y.min() < 0 or y.max() > box_ylen):
        raise ValueError(
            "Interpolation produces path values " +
            "outside box: min [x, y] = [{}, {}], ".format(x.min(), y.min()) +
            "max [x, y] = [{}, {}]".format(x.max(), y.max()))

    R = np.sqrt(np.diff(x)**2 + np.diff(y)**2)
    V = R / np.diff(t)
    print('Maximum speed {}'.format(V.max()))
    return x, y, t
Example #2
0
def rescale_linear_track_2d_to_1d(x, y, end_0=[], end_1=[]):
    """ Take x, y coordinates of linear track data, rescale to 1-d.

    Parameters
    ----------
    x : quantities.Quantity array in m
        1d vector of x positions
    y : quantities.Quantity array in m
        1d vector of x positions
    t : quantities.Quantity array in s
        1d vector of times at x, y positions
    end_0: quantities.Quantity array in m
        linear track endpoint 1, in x, y
    end_1: quantities.Quantity array in m
        linear track endpoint 2, in x, y

    Returns
    -------
    out : 1d vector
    """
    from exana.misc.tools import is_quantities
    if not all([len(var) == len(var2) for var in
                [x, y] for var2 in [x, y]]):
        raise ValueError('x, y, t must have same number of elements')
    is_quantities([x, y], 'vector')
    x = x.rescale('m').magnitude
    y = y.rescale('m').magnitude
    if len(end_0) != 2 or len(end_1) != 2:
        raise ValueError('end_0 and end_1 must be 2d vectors')
    end_0 = end_0.rescale('m').magnitude
    end_1 = end_1.rescale('m').magnitude
    # shift coordinate system to have end_0 as origin
    x -= end_0[0]
    y -= end_0[1]

    # calculate angle of track
    v_x_axis = np.array([1, 0])
    theta = angle_between_vectors(end_1-end_0, v_x_axis)
    # rotate clockwise
    rot_mat = np.array([[np.cos(-theta), -np.sin(-theta)],
                        [np.sin(-theta),  np.cos(-theta)]])
    x_rot = []
    for x_i, y_i in zip(x, y):
        [x_rot_i, _] = np.dot(rot_mat,
                              np.array([[x_i],
                                        [y_i]]))
        x_rot.append(x_rot_i.item())
    # shift x_rot so that np.min(x_rot) == 0
    x_rot -= np.min(x_rot)
    # only consider x_rot in output
    return x_rot*pq.m
Example #3
0
def select_best_position(x1, y1, t1, x2, y2, t2, speed_filter=5 * pq.m / pq.s):
    """
    selects position data with least nan after speed filtering

    Parameters
    ----------
    x1 : quantities.Quantity array in m
        1d vector of x positions from LED 1
    y1 : quantities.Quantity array in m
        1d vector of x positions from LED 1
    t1 : quantities.Quantity array in s
        1d vector of times from LED 1 at x, y positions
    x2 : quantities.Quantity array in m
        1d vector of x positions from LED 2
    y2 : quantities.Quantity array in m
        1d vector of x positions from LED 2
    t2 : quantities.Quantity array in s
        1d vector of times from LED 2 at x, y positions
    speed_filter : None or quantities in m/s
        threshold filter for translational speed
    """
    is_quantities([x1, y1, t1, x2, y2, t2], 'vector')
    x1, y1, t1, x2, y2, t2 = _cut_to_same_len(x1, y1, t1, x2, y2, t2)
    is_quantities(speed_filter, 'scalar')
    measurements1 = len(x1)
    measurements2 = len(x2)
    x1, y1, t1 = rm_nans(x1, y1, t1)
    x2, y2, t2 = rm_nans(x2, y2, t2)
    if speed_filter is not None:
        x1, y1, t1 = velocity_threshold(x1, y1, t1, speed_filter)
        x2, y2, t2 = velocity_threshold(x2, y2, t2, speed_filter)

    if len(x1) > len(x2):
        print('Removed %.2f %% invalid measurements in path' %
              ((1. - len(x1) / float(measurements1)) * 100.))
        x = x1
        y = y1
        t = t1
    else:
        print('Removed %.2f %% invalid measurements in path' %
              ((1. - len(x2) / float(measurements2)) * 100.))
        x = x2
        y = y2
        t = t2
    return x, y, t
Example #4
0
def velocity_threshold(x, y, t, threshold):
    """
    Removes values above threshold

    Parameters
    ----------
    x : quantities.Quantity array in m
        1d vector of x positions
    y : quantities.Quantity array in m
        1d vector of y positions
    t : quantities.Quantity array in s
        1d vector of times at x, y positions
    threshold : float
    """
    assert len(x) == len(y) == len(t), 'x, y, t must have same length'
    is_quantities([x, y, t], 'vector')
    is_quantities(threshold, 'scalar')
    r = np.sqrt(np.diff(x)**2 + np.diff(y)**2)
    v = np.divide(r, np.diff(t))
    speed_lim = np.concatenate(([False], v > threshold), axis=0)
    x[speed_lim] = np.nan * x.units
    y[speed_lim] = np.nan * y.units
    x, y, t = rm_nans(x, y, t)
    return x, y, t
Example #5
0
def gridness(rate_map, box_xlen, box_ylen, return_acorr=False,
             step_size=0.1*pq.m):
    '''Calculates gridness of a rate map. Calculates the normalized
    autocorrelation (A) of a rate map B where A is given as
    A = 1/n\Sum_{x,y}(B - \bar{B})^{2}/\sigma_{B}^{2}. Further, the Pearsson's
    product-moment correlation coefficients is calculated between A and A_{rot}
    rotated 30 and 60 degrees. Finally the gridness is calculated as the
    difference between the minimum of coefficients at 60 degrees and the
    maximum of coefficients at 30 degrees i.e. gridness = min(r60) - max(r30).
    In order to focus the analysis on symmetry of A the the central and the
    outer part of the gridness is maximized by increasingly mask A at steps of
    ``step_size``. This function is inspired by Lukas Solankas gridcells
    package from Matt Nolans lab.

    Parameters
    ----------
    rate_map : numpy.ndarray
    box_xlen : quantities scalar in m
        side length of quadratic box
    step_size : quantities scalar in m
        step size in masking
    return_acorr : bool
        return autocorrelation map or not

    Returns
    -------
    out : gridness, (autocorrelation map)
    '''
    from scipy.ndimage.interpolation import rotate
    import numpy.ma as ma
    from exana.misc.tools import (is_quantities, fftcorrelate2d,
                                            masked_corrcoef2d)
    is_quantities([box_xlen, box_ylen, step_size], 'scalar')
    box_xlen = box_xlen.rescale('m').magnitude
    box_ylen = box_ylen.rescale('m').magnitude
    step_size = step_size.rescale('m').magnitude
    tmp_map = rate_map.copy()
    tmp_map[~np.isfinite(tmp_map)] = 0
    acorr = fftcorrelate2d(tmp_map, tmp_map, mode='full', normalize=True)
    rows, cols = acorr.shape
    b_x = np.linspace(-box_xlen/2., box_xlen/2., rows)
    b_y = np.linspace(-box_ylen/2., box_ylen/2., cols)
    B_x, B_y = np.meshgrid(b_x, b_y)
    grids = []
    acorrs = []
    # TODO find size of middle gaussian and exclude
    for outer in np.arange(box_xlen/4, box_xlen/2, step_size):
        m_acorr = ma.masked_array(acorr, mask=np.sqrt(B_x**2 + B_y**2) > outer)
        for inner in np.arange(0, box_xlen/4, step_size):
            m_acorr = \
                ma.masked_array(m_acorr, mask=np.sqrt(B_x**2 + B_y**2) < inner)
            angles = range(30, 180+30, 30)
            corr = []
            # Rotate and compute correlation coefficient
            for angle in angles:
                rot_acorr = rotate(m_acorr, angle, reshape=False)
                corr.append(masked_corrcoef2d(rot_acorr, m_acorr)[0, 1])
            r60 = corr[1::2]
            r30 = corr[::2]
            grids.append(np.min(r60) - np.max(r30))
            acorrs.append(m_acorr)
    if return_acorr:
        return max(grids), acorr,  # acorrs[grids.index(max(grids))]
    else:
        return max(grids)
Example #6
0
def spatial_rate_map(x, y, t, sptr, binsize=0.01*pq.m, box_xlen=1*pq.m,
                     box_ylen=1*pq.m, mask_unvisited=True, convolve=True,
                     return_bins=False, smoothing=0.02):
    """Divide a 2D space in bins of size binsize**2, count the number of spikes
    in each bin and divide by the time spent in respective bins. The map can
    then be convolved with a gaussian kernel of size csize determined by the
    smoothing factor, binsize and box_xlen.

    Parameters
    ----------
    sptr : neo.SpikeTrain
    x : quantities.Quantity array in m
        1d vector of x positions
    y : quantities.Quantity array in m
        1d vector of y positions
    t : quantities.Quantity array in s
        1d vector of times at x, y positions
    binsize : float
        spatial binsize
    box_xlen : quantities scalar in m
        side length of quadratic box
    mask_unvisited: bool
        mask bins which has not been visited by nans
    convolve : bool
        convolve the rate map with a 2D Gaussian kernel

    Returns
    -------
    out : rate map
    if return_bins = True
    out : rate map, xbins, ybins
    """
    from exana.misc.tools import is_quantities
    if not all([len(var) == len(var2) for var in [x,y,t] for var2 in [x,y,t]]):
        raise ValueError('x, y, t must have same number of elements')
    if box_xlen < x.max() or box_ylen < y.max():
        raise ValueError('box length must be larger or equal to max path length')
    from decimal import Decimal as dec
    decimals = 1e10
    remainderx = dec(float(box_xlen)*decimals) % dec(float(binsize)*decimals)
    remaindery = dec(float(box_ylen)*decimals) % dec(float(binsize)*decimals)
    if remainderx != 0 or remaindery != 0:
        raise ValueError('the remainder should be zero i.e. the ' +
                                     'box length should be an exact multiple ' +
                                     'of the binsize')
    is_quantities([x, y, t], 'vector')
    is_quantities(binsize, 'scalar')
    t = t.rescale('s')
    box_xlen = box_xlen.rescale('m').magnitude
    box_ylen = box_ylen.rescale('m').magnitude
    binsize = binsize.rescale('m').magnitude
    x = x.rescale('m').magnitude
    y = y.rescale('m').magnitude

    # interpolate one extra timepoint
    t_ = np.array(t.tolist() + [t.max() + np.median(np.diff(t))]) * pq.s
    spikes_in_bin, _ = np.histogram(sptr.times, t_)
    time_in_bin = np.diff(t_.magnitude)
    xbins = np.arange(0, box_xlen + binsize, binsize)
    ybins = np.arange(0, box_ylen + binsize, binsize)
    ix = np.digitize(x, xbins, right=True)
    iy = np.digitize(y, ybins, right=True)
    spike_pos = np.zeros((xbins.size, ybins.size))
    time_pos = np.zeros((xbins.size, ybins.size))
    for n in range(len(x)):
        spike_pos[ix[n], iy[n]] += spikes_in_bin[n]
        time_pos[ix[n], iy[n]] += time_in_bin[n]
    # correct for shifting of map since digitize returns values at right edges
    spike_pos = spike_pos[1:, 1:]
    time_pos = time_pos[1:, 1:]
    with np.errstate(divide='ignore', invalid='ignore'):
        rate = np.divide(spike_pos, time_pos)
    if convolve:
        rate[np.isnan(rate)] = 0.  # for convolution
        from astropy.convolution import Gaussian2DKernel, convolve_fft
        csize = (box_xlen / binsize) * smoothing
        kernel = Gaussian2DKernel(csize)
        rate = convolve_fft(rate, kernel)  # TODO edge correction
    if mask_unvisited:
        was_in_bin = np.asarray(time_pos, dtype=bool)
        rate[np.invert(was_in_bin)] = np.nan
    if return_bins:
        return rate.T, xbins, ybins
    else:
        return rate.T
Example #7
0
def nvisits_map(x, y, t,
                binsize=0.01*pq.m,
                box_xlen=1*pq.m,
                box_ylen=1*pq.m,
                return_bins=False):
    '''Divide a 2D space in bins of size binsize**2, count the
    number of visits in each bin. The map can  be convolved with
    a gaussian kernel of size  determined by the smoothing factor,
    binsize and box_xlen.

    Parameters
    ----------
    x : quantities.Quantity array in m
        1d vector of x positions
    y : quantities.Quantity array in m
        1d vector of y positions
    t : quantities.Quantity array in s
        1d vector of times at x, y positions
    binsize : float
        spatial binsize
    box_xlen : quantities scalar in m
        side length of quadratic box


    Returns
    -------
    nvisits_map : numpy.ndarray
    if return_bins = True
    out : nvisits_map, xbins, ybins
    '''

    from exana.misc.tools import is_quantities
    if not all([len(var) == len(var2) for var in [
            x, y, t] for var2 in [x, y, t]]):
        raise ValueError('x, y, t must have same number of elements')
    if box_xlen < x.max() or box_ylen < y.max():
        raise ValueError(
            'box length must be larger or equal to max path length')
    from decimal import Decimal as dec
    decimals = 1e10
    remainderx = dec(float(box_xlen)*decimals) % dec(float(binsize)*decimals)
    remaindery = dec(float(box_ylen)*decimals) % dec(float(binsize)*decimals)
    if remainderx != 0 or remaindery != 0:
        raise ValueError('the remainder should be zero i.e. the ' +
                         'box length should be an exact multiple ' +
                         'of the binsize')
    is_quantities([x, y, t], 'vector')
    is_quantities(binsize, 'scalar')
    t = t.rescale('s')
    box_xlen = box_xlen.rescale('m').magnitude
    box_ylen = box_ylen.rescale('m').magnitude
    binsize = binsize.rescale('m').magnitude
    x = x.rescale('m').magnitude
    y = y.rescale('m').magnitude

    xbins = np.arange(0, box_xlen + binsize, binsize)
    ybins = np.arange(0, box_ylen + binsize, binsize)
    ix = np.digitize(x, xbins, right=True)
    iy = np.digitize(y, ybins, right=True)
    nvisits_map = np.zeros((xbins.size, ybins.size))
    for n in range(len(x)):
        if n == 0:
            nvisits_map[ix[n], iy[n]] = 1
        else:
            if ix[n-1] != ix[n] or iy[n-1] != iy[n]:
                nvisits_map[ix[n], iy[n]] += 1
    # correct for shifting of map since digitize returns values at right edges
    nvisits_map = nvisits_map[1:, 1:]
    if return_bins:
        return nvisits_map.T, xbins, ybins
    else:
        return nvisits_map.T