예제 #1
0
    def compute_spharm(self,
                       grid_size=None,
                       normalize=False,
                       normalization_method='zero-component',
                       ri=False):
        """
        Compute the spherical harmonics spectrum of the current surface.
        
        Parameters
        ----------
        grid_size : int, optional
            Dimension of the square grid to interpolate the surface points.
            Will be used to interpolate the surface coordinates if self.Rgrid is None 
             (in this case it is a mandatory parameter).
            Default is None.
        normalize : bool, optional
            If True, the values of the spectrum will be normalized according to the `normalization_method`.
            Default is False.            
        normalization_method : str, optional
            If 'mean-radius', the grid values will be divided by the mean grid value prior to the SPHARM transform.
            If 'zero-component', all spectral components will be divided by the value of the first component (m=0, n=0).
            Default is 'zero-component'.
        ri : bool, optional
            If True, rotation-invariant spectrum based on Cartesian coordinates is computed.
            Default is False.

        Returns
        -------
        Spectrum : spherical harmonics spectrum of the surface.
        """
        self.spharm = Spectrum()

        if ri:
            data = []
            for r in [self.x, self.y, self.z]:
                grid = self.interpolate(grid_size=grid_size, r=r)
                self.spharm.from_surface(surface=grid, normalize=normalize)
                data.append(self.spharm.harmonics_csv)
            for c in ['value', 'amplitude', 'power', 'real', 'imag']:
                self.spharm.harmonics_csv[c] = np.sqrt(data[0][c]**2 +
                                                       data[1][c]**2 +
                                                       data[2][c]**2)
            self.spharm.harmonics_csv['amplitude2'] = np.abs(
                self.spharm.harmonics_csv['value'])
            self.spharm.convert_to_shtools_array()

        else:
            if self.Rgrid is None:
                if grid_size is None:
                    raise TypeError(
                        'Grid size for interpolation must be provided')
                else:
                    self.Rgrid = self.interpolate(grid_size=grid_size)
            if self.Rgrid is None:
                print(len(self.x), grid_size)
            self.spharm.from_surface(surface=self.Rgrid,
                                     normalize=normalize,
                                     normalization_method=normalization_method)
        self.spharm.metadata = self.metadata
        return self.spharm
예제 #2
0
 def test_heatmap(self):
     sp = Spectrum()
     sp.from_surface(surface=np.ones([10, 10]))
     pl = sp.heatmap()
     os.makedirs('data/test_data')
     pl.savefig('data/test_data/heatmap.png')
     self.assertEqual(os.path.exists('data/test_data/heatmap.png'), True)
     shutil.rmtree('data/test_data/')
예제 #3
0
def plot_mean_abs_derivative(inputfile, outputfolder, group='Group', cutoff=None, id_col='TrackID'):
    """
    Plot the derivative of each spectral component of different groups over time.

    Parameters
    ----------
    inputfile : str
        Path to the file with spectral data.
    outputfolder : str
        Directory to save the plotted derivative.
    group : str, optional
        Column in the input data sheet to use for grouping.
        Default is 'Group'.
    cutoff : int, optional
        The number of degrees to display.
        If None, all degrees will be displayed.
    id_col : str
        Column in the input data sheet to group connected time points.
        Default is 'TrackID'
    """
    filelib.make_folders([os.path.dirname(outputfolder)])
    if not os.path.exists(inputfile[:-4] + '_mean_abs_derivative.csv'):
        stat = pd.read_csv(inputfile, sep='\t', index_col=0)
        if id_col == 'CellID':
            stat.loc[:, 'Time'] = np.int_(np.round_(stat['Time'] / 10.)) * 10
        nstat = pd.DataFrame()
        if cutoff is not None:
            stat = stat[stat['degree'] <= cutoff]
        for gr in stat[group].unique():
            substat = stat[stat[group] == gr]
            for id in substat[id_col].unique():
                subsubstat = substat[substat[id_col] == id]
                subsubstat = subsubstat.sort_values('Time')
                time_spectrum = TimeSpectrum()
                for t in subsubstat['Time'].unique():
                    sp = Spectrum()
                    sp.harmonics_csv = subsubstat[subsubstat['Time'] == t]
                    time_spectrum.add_spectrum(sp, timepoint=t)

                time_spectrum.compute_derivative()
                meanderivstat = time_spectrum.mean_abs_derivative
                meanderivstat['Group'] = gr
                meanderivstat['TrackID'] = id
                nstat = pd.concat([nstat, meanderivstat], ignore_index=True)

        nstat.to_csv(inputfile[:-4] + '_mean_abs_derivative.csv', sep='\t')
    nstat = pd.read_csv(inputfile[:-4] + '_mean_abs_derivative.csv', sep='\t', index_col=0)
    nstat = nstat.sort_values(['harmonic', group])
    plt.clf()
    plt.figure(figsize=(20, 5))
    sns.barplot(x='harmonic', y='absolute amplitude', data=nstat, hue=group)
    plt.ylabel('Mean absolute derivative of amplitude')
    labels = nstat['harmonic'].unique()
    plt.xticks(np.arange(len(labels)) + 0.6, labels, rotation='vertical')
    margins = {'left': 0.07, 'right': 0.98, 'top': 0.93, 'bottom': 0.25}
    plt.subplots_adjust(**margins)
    plt.savefig(outputfolder + 'mean_abs_derivative.png')
    plt.close()
예제 #4
0
 def test_frequency_plot(self):
     sp = Spectrum(name='Example 1')
     sp.from_surface(surface=np.ones([10, 10]))
     pl = sp.frequency_plot()
     os.makedirs('data/test_data')
     pl.savefig('data/test_data/frequency_plot.png')
     self.assertEqual(os.path.exists('data/test_data/frequency_plot.png'),
                      True)
     shutil.rmtree('data/test_data/')
예제 #5
0
def plot_average_frequency_heatmaps(inputfile, outputfolder, group='Group', cutoff=None,
                                    logscale=False, id_col='TrackID'):
    """
    Plot the Fourier frequencies of spectral components of different groups as a heatmap.

    Parameters
    ----------
    inputfile : str
        Path to the file with spectral data.
    outputfolder : str
        Directory to save the plotted heat maps.
    group : str, optional
        Column in the input data sheet to use for grouping.
        Default is 'Group'.
    cutoff : int, optional
        The number of degrees to display.
        If None, all degrees will be displayed.
    logscale : bool, optional
        If True, the natural logarithm of the value will be displayed.
        Default is False.
    id_col : str
        Column in the input data sheet to group connected time points.
        Default is 'TrackID'
    """
    filelib.make_folders([os.path.dirname(outputfolder)])
    stat = pd.read_csv(inputfile, sep='\t', index_col=0)
    stat.loc[:, 'Time'] = np.int_(np.round_(stat['Time'] / 10.)) * 10
    if cutoff is not None:
        stat = stat[stat['degree'] <= cutoff]
    frequency_stat = pd.DataFrame()
    for gr in stat[group].unique():
        substat = stat[stat[group] == gr]
        for id in substat[id_col].unique():
            subsubstat = substat[substat[id_col] == id]
            time_spectrum = TimeSpectrum()
            for t in subsubstat['Time'].unique():
                sp = Spectrum()
                sp.harmonics_csv = subsubstat[subsubstat['Time'] == t]
                time_spectrum.add_spectrum(sp, timepoint=t)

            time_spectrum.fourier_analysis(value='amplitude')
            time_spectrum.frequencies['Group'] = gr
            frequency_stat = pd.concat([frequency_stat, time_spectrum.frequencies], ignore_index=True)

    frequency_stat = frequency_stat.groupby(['Group', 'frequency', 'harmonic']).mean().reset_index()
    for gr in stat[group].unique():
        time_spectrum = TimeSpectrum()
        time_spectrum.frequencies = frequency_stat[frequency_stat['Group'] == gr]

        pl = time_spectrum.frequency_heatmap(value='amplitude', logscale=logscale)
        if pl is not None:
            pl.savefig(outputfolder + gr + '.png')
예제 #6
0
 def test_saving(self):
     sp = Spectrum()
     sp.from_surface(surface=np.ones([10, 10]))
     sp.convert_to_csv()
     sp.save_to_csv(filename='data/test_data/spectrum.csv')
     sp2 = Spectrum(filename='data/test_data/spectrum.csv')
     self.assertAlmostEqual(
         np.sum(abs(sp.harmonics_shtools - sp2.harmonics_shtools)), 0, 15)
     shutil.rmtree('data/test_data/')
예제 #7
0
def plot_individual_time_heatmaps(inputfile, outputfolder, group='Group', cutoff=None,
                                  logscale=False, id_col='TrackID'):
    """
    Plot the amplitude of spectral components over time for different groups as a heatmap.

    Parameters
    ----------
    inputfile : str
        Path to the file with spectral data.
    outputfolder : str
        Directory to save the plotted heat maps.
    group : str, optional
        Column in the input data sheet to use for grouping.
        Default is 'Group'.
    cutoff : int, optional
        The number of degrees to display.
        If None, all degrees will be displayed.
    logscale : bool, optional
        If True, the natural logarithm of the value will be displayed.
        Default is False.
    id_col : str
        Column in the input data sheet to group connected time points.
        Default is 'TrackID'
    """
    filelib.make_folders([os.path.dirname(outputfolder)])
    stat = pd.read_csv(inputfile, sep='\t', index_col=0)
    stat['Time'] = np.int_(np.round_(stat['Time'] / 10.)) * 10
    if cutoff is not None:
        stat = stat[stat['degree'] <= cutoff]

    for gr in stat[group].unique():
        substat = stat[stat[group] == gr]
        for id in substat[id_col].unique():
            subsubstat = substat[substat[id_col] == id]
            subsubstat = subsubstat.sort_values('Time').reset_index()
            time_spectrum = TimeSpectrum()
            for t in subsubstat['Time'].unique():
                sp = Spectrum()
                sp.harmonics_csv = subsubstat[subsubstat['Time'] == t]
                time_spectrum.add_spectrum(sp, timepoint=t)
            pl = time_spectrum.time_heatmap(value='amplitude', logscale=logscale)
            if pl is not None:
                pl.savefig(outputfolder + '_' + gr + '_' + 'track_' + str(id) + '.png')
예제 #8
0
def plot_inverse_shapes(inputfile, outputfolder, group='Group'):
    """
    Plot average cells shapes obtained by inverse SPHARM.

    Parameters
    ----------
    inputfile : str
        Path to the file with spectral data.
    outputfolder : str
        Directory to save the plotted distributions.
    group : str, optional
        Column in the input data sheet to use for grouping.
        Default is 'Group'.
    """

    filelib.make_folders([os.path.dirname(outputfolder)])
    stat = pd.read_csv(inputfile, sep='\t', index_col=0)
    stat['value'] = stat['real'] + stat['imag']*1j
    if 'Group' not in stat.columns:
        for name in stat['Name'].unique():
            group = name.split('/')[0]
            stat = stat.set_value(stat[stat['Name'] == name].index, 'Group', group)

    data = stat.groupby(['degree', 'order', 'Group']).mean().reset_index()
    groups = data[group].unique()
    for gr in groups:
        curdata = data[data[group] == gr]
        sp = Spectrum()
        sp.harmonics_csv = curdata
        sp.convert_to_shtools_array()
        surf = Surface()
        surf.spharm = sp
        maxdegree = np.max(sp.harmonics_csv['degree'])
        for lmax in np.arange(5, maxdegree + 1, 5):
            surf.inverse_spharm(lmax=lmax)
            surf.plot_surface(points=False).save(outputfolder + '_' + gr + '_inverse_lmax=' + str(lmax) + '.png',
                                                 size=(200, 200))

        surf.inverse_spharm(lmax=None)
        surf.plot_surface(points=False).save(outputfolder + '_' + gr + '_inverse_full.png',
                                             size=(200, 200))
예제 #9
0
def plot_spectra(inputfolder, outputfolder, **kwargs):
    """
    Plot bar plots for individual frequency spectra in a given directory. 

    Parameters
    ----------
    inputfolder : str
        Input directory with spectra to plot.
    outputfolder : str
        Output directory to save the bar plots.
    kwargs : key, value pairings
            Arbitrary keyword arguments to pass to the Spectrum.frequency_plot function.
    """
    files = filelib.list_subfolders(inputfolder, extensions=['csv'])

    for fn in files:
        s = Spectrum(filename=inputfolder + fn)
        pl = s.frequency_plot(title=fn[:-4], **kwargs)
        filelib.make_folders([os.path.dirname(outputfolder + fn[:-4])])
        pl.savefig(outputfolder + fn[:-4] + '.png')
        pl.clf()
예제 #10
0
def plot_average_heatmaps(inputfile, outputfolder, **kwargs):
    """
    Plot heatmaps for group-averaged SPHARM spectra.

    Parameters
    ----------
    inputfile : str
        Path to the file with spectral data.    
    outputfolder : str
        Output directory to save the heatmaps.
    kwargs : key, value pairings
            Arbitrary keyword arguments to pass to the Spectrum.heatmap function.
    """
    filelib.make_folders([os.path.dirname(outputfolder), outputfolder + 'timepoints/'])
    stat = pd.read_csv(inputfile, sep='\t', index_col=0)
    stat.loc[:, 'Time'] = np.int_(np.round_(stat['Time'] / 10.)) * 10
    if 'Group' not in stat.columns:
        for name in stat['Name'].unique():
            group = name.split('/')[0]
            stat = stat.set_value(stat[stat['Name'] == name].index, 'Group', group)

    data = stat.groupby(['degree', 'order', 'Group']).mean().reset_index()
    for gr in data['Group'].unique():
        curdata = data[data['Group'] == gr]
        s = Spectrum()
        s.harmonics_csv = curdata
        pl = s.heatmap(title=gr + ' average', **kwargs)
        pl.savefig(outputfolder + gr + '.png')
        pl.clf()

    # plot separate time points
    stat = stat.groupby(['Time', 'Group', 'degree', 'order']).mean().reset_index()

    for t in stat['Time'].unique():
        for gr in stat['Group'].unique():
            curdata = stat[(stat['Group'] == gr) & (stat['Time'] == t)]
            if len(curdata) > 0:
                s = Spectrum()
                s.harmonics_csv = curdata
                pl = s.heatmap(title=gr + ' average, time point ' + str(t), **kwargs)
                pl.savefig(outputfolder + 'timepoints/' + gr + '_time=' + str(t) + '.png')
                pl.clf()
 def test_fourier(self):
     sp = Spectrum()
     sp.from_surface(surface=np.ones([10, 10]))
     sp.convert_to_csv()
     tsp = TimeSpectrum()
     for i in range(10):
         tsp.add_spectrum(sp)
     tsp.fourier_analysis()
     tsp.save_frequencies_to_csv('data/test_data/time_spectrum_freq.csv')
     self.assertEqual(os.path.exists('data/test_data/time_spectrum_freq.csv'), True)
     pl = tsp.frequency_heatmap()
     pl.savefig('data/test_data/frequency_heatmap.png')
     self.assertEqual(os.path.exists('data/test_data/frequency_heatmap.png'), True)
     shutil.rmtree('data/test_data/')
예제 #12
0
 def test_from_surface(self, case):
     sp = Spectrum()
     harm = sp.from_surface(surface=case)
     self.assertEqual(tuple(harm.shape),
                      (2, case.shape[0] / 2, case.shape[0] / 2))
    def test_feature_vector(self):
        surf = np.ones([10, 10])
        sp = Spectrum()
        sp.from_surface(surface=surf)
        sp.convert_to_csv()

        surf[3:4, 4:8] = 10
        sp2 = Spectrum()
        sp2.from_surface(surface=surf)
        sp2.convert_to_csv()

        tsp = TimeSpectrum()
        tsp.add_spectrum(sp)
        tsp.add_spectrum(sp2)
        tsp.add_spectrum(sp)
        tsp.compute_derivative()
        self.assertEqual(len(tsp.return_feature_vector(cutoff=2)), 3)
예제 #14
0
 def test_frequency_spectrum(self):
     sp = Spectrum()
     sp.from_surface(surface=np.ones([10, 10]))
     sp.compute_frequency_spectrum()
     self.assertEqual(len(sp.frequency_spectrum), 5)
예제 #15
0
 def test_convertion(self):
     sp = Spectrum()
     harm_shtools = sp.from_surface(surface=np.ones([10, 10]))
     sp.convert_to_csv()
     harm_shtools2 = sp.convert_to_shtools_array()
     self.assertEqual(np.sum(abs(harm_shtools - harm_shtools2)), 0)
예제 #16
0
 def test_from_surface_norm2(self, case):
     sp = Spectrum()
     harm = sp.from_surface(surface=case,
                            normalize=True,
                            normalization_method='mean-radius')
     self.assertAlmostEqual(abs(np.max(harm)), 1, 8)
예제 #17
0
class Surface(object):
    """
    Class for a surface grid of an object
    """
    def __init__(self, filename=None, grid=None, data=None, **kwargs):
        """
        Initialize a surface from file or grid.
        
        Parameters
        ----------
        filename : str, optional
            Path to a surface file to read the surface data.
            If None, an empty surface will be initialized.
            Default is None.
        grid : numpy.ndarray, dimension (n, n) or (n, 2*n), n is even, optional
            A 2D equally sampled (default) or equally spaced complex grid 
            that conforms to the sampling theorem of Driscoll and Healy (1994). 
            The first latitudinal band corresponds to 90 N, the latitudinal band for 90 S is not included, 
            and the latitudinal sampling interval is 180/n degrees. 
            The first longitudinal band is 0 E, the longitude band for 360 E is not included, 
            and the longitudinal sampling interval is 360/n for an equally 
            and 180/n for an equally spaced grid, respectively.
        data : pandas DataFrame, optional
            DataFrame with surface coordinates.
            If None, an empty surface will be initialized.
            Default is None.
        kwargs : key, value pairings
            Arbitrary keyword arguments to pass to the self.read_from_file function.
        """

        self.X = None  # grid
        self.Y = None  # grid
        self.Z = None  # grid
        self.Phi = None  # grid
        self.Theta = None  # grid
        self.Rgrid = grid  # grid

        self.x = None  # list of points
        self.y = None  # list of points
        self.z = None  # list of points
        self.R = None  # list of points
        self.phi = None  # list of points
        self.theta = None  # list of points

        self.filename = filename
        self.spharm = None
        self.metadata = pd.Series()
        self.center = None
        self.migration_angles = None

        if grid is None:
            if filename is not None:
                self.read_from_file(filename,
                                    voxel_size=kwargs.get('voxel_size', 1))
            elif data is not None:
                self.from_dataframe(data)

        if self.Rgrid is not None:
            self.R = self.Rgrid.flatten()
            self.Theta = np.linspace(0,
                                     np.pi,
                                     self.Rgrid.shape[0],
                                     endpoint=False)
            self.Phi = np.linspace(0,
                                   2 * np.pi,
                                   self.Rgrid.shape[0],
                                   endpoint=False)
            self.Phi, self.Theta = np.meshgrid(self.Phi, self.Theta)
            self.phi = self.Phi.flatten()
            self.theta = self.Theta.flatten()
            self.x, self.y, self.z = tr.spherical_to_cart(
                self.R, self.theta, self.phi)

    def read_from_file(self, filename, voxel_size=1):
        """
        Read surface coordinates from file.
        
        Parameters
        ----------
        filename : str, optional
            Path to a surface file to read the surface data.
        voxel_size : scalar or sequence of scalars, optional
            Voxel size of the image. 
            Specified either by individual value for each axis, or by one value for all axes.
            Default is 1.
        """
        if os.path.exists(filename):
            f = open(filename)
            st = f.readlines()
            f.close()
            if len(st) > 0:
                stat = pd.read_csv(filename, sep='\t', index_col=0)

                if 'X' in stat.columns and 'Y' in stat.columns and 'Z' in stat.columns:
                    self.x = np.array(stat.X)
                    self.y = np.array(stat.Y)
                    self.z = np.array(stat.Z)

                else:
                    stat = pd.read_csv(filename, sep=',', header=None)
                    px = voxel_size
                    if 0 in stat.columns and 1 in stat.columns and 2 in stat.columns:
                        self.x = np.array(stat[0]) * px
                        self.y = np.array(stat[1]) * px
                        self.z = np.array(stat[2]) * px
                self.to_spherical()
                self.metadata['Name'] = filename
                p = re.compile('[-+]?\d*\.*\d+')
                if 'Time' in stat.columns:
                    self.metadata['Time'] = stat['Time'].iloc[0]
                elif len(filename.split('Time')) > 1:
                    self.metadata['Time'] = float(
                        p.findall(filename.split('Time')[-1])[0])
                else:
                    num = p.findall(filename)
                    if len(num) > 0:
                        self.metadata['Time'] = float(num[-1])
                    else:
                        self.metadata['Time'] = 0

                if 'TrackID' in stat.columns:
                    self.metadata['TrackID'] = stat['TrackID'].iloc[0]
                elif len(filename.split('Cell')) > 1:
                    self.metadata['TrackID'] = float(
                        p.findall(filename.split('cells')[-1])[0])
                else:
                    self.metadata['TrackID'] = 0

    def from_dataframe(self, stat):
        """
        Get surface coordinates from a pandas DataFrame.

        Parameters
        ----------
        stat : pandas DataFrame, optional
            DataFrame with surface coordinates.
        """
        self.x = np.array(stat.X)
        self.y = np.array(stat.Y)
        self.z = np.array(stat.Z)
        self.metadata['Time'] = stat['Time'].iloc[0]

    def save(self, filename):
        """
        Save the surface coordinates to a csv file.
        
        Parameters
        ----------
        filename : str
            Output file name.
        """
        if self.x is not None:
            filelib.make_folders([os.path.dirname(filename)])
            stat = pd.DataFrame({'X': self.x, 'Y': self.y, 'Z': self.z})
            stat['Name'] = self.filename

            stat.to_csv(filename, sep='\t')

    def save_as_stack(self, filename, voxel_size):
        """
        Save the surface as a 3D stack.
        
        Parameters
        ----------
        filename : str
            Output file name.
        voxel_size : scalar or sequence of scalars
            Voxel size of the image. 
            Specified either by individual value for each axis, or by one value for all axes.
        """
        if self.x is not None:
            voxel_size = np.array([voxel_size]).flatten()
            if len(voxel_size) == 1:
                voxel_size = np.ones(3) * voxel_size
            filelib.make_folders([os.path.dirname(filename)])
            img = self.as_stack(voxel_size)
            with warnings.catch_warnings():
                warnings.simplefilter("ignore")
                io.imsave(filename, img.astype(np.uint8))
            metadata = pd.Series({
                'voxel_size_xy': voxel_size[2],
                'voxel_size_z': voxel_size[0]
            })
            metadata['min_x'] = self.x.min() - 1
            metadata['min_y'] = self.y.min() - 1
            metadata['min_z'] = self.z.min() - 1
            metadata.to_csv(filename[:-4] + '.txt', sep='\t', header=False)

    def as_stack(self, voxel_size, minmax=None):
        """
        Convert the surface to a 3D image stack.
        
        Parameters
        ----------
        voxel_size : scalar or sequence of scalars
            Voxel size of the image. 
            Specified either by individual value for each axis, or by one value for all axes.
        minmax : ndarray, optional
            Boundaries to crop the image stack of the form [[z_min, z_max], [y_min, y_max], [x_min, x_max]].
            If None, set to the minimal and maximal given coordinates.
            Default is None.

        Returns
        -------
        ndimage : 3D binary image with surface point as foreground.

        """
        if minmax is not None:
            minmax = [
                minmax[0] / voxel_size[0], minmax[1] / voxel_size[1],
                minmax[2] / voxel_size[2]
            ]
        img = tr.as_stack(np.array(self.x) / voxel_size[2],
                          np.array(self.y) / voxel_size[1],
                          np.array(self.z) / voxel_size[0],
                          minmax=minmax)
        return img

    def centrate(self):
        """
        Substract the mean coordinate form each coordinate value.
        """
        self.center = np.array(
            [np.mean(self.x),
             np.mean(self.y),
             np.mean(self.z)])
        self.x = self.x - np.mean(self.x)
        self.y = self.y - np.mean(self.y)
        self.z = self.z - np.mean(self.z)

    def to_spherical(self):
        """
        Convert coordinates from Cartesian to spherical.
        """
        self.R, self.theta, self.phi = tr.cart_to_spherical(
            self.x, self.y, self.z)

    def rotate(self, theta, phi):
        """
        Rotate the coordinates of the surface by given asimuthal and polar angles.

        Parameters
        ----------
        theta : float
            Polar angle.
        phi : float
            Azimuthal angle.
        """
        self.x, self.y, self.z = tr.rotate_spherical(self.x, self.y, self.z,
                                                     theta, phi)

    def interpolate(self, grid_size, r=None):
        """
        Interpolate the surface points on a regular grid.
        
        Parameters
        ----------
        grid_size : int
            Dimension of the square grid to interpolate the surface points.
        r : array, optional
            The array of values to interpolate.
            If None, the self.R (radius) will be interpolated.
            Default is None.

        Returns
        -------
        ndarray of size (grid_size x grid_size) : interpolated grid.

        """

        R, theta, phi = (self.R, self.theta, self.phi)
        if r is None:
            r = R

        if len(r) > 4:

            # make a lattice
            I = np.linspace(0, np.pi, grid_size, endpoint=False)
            J = np.linspace(0, 2 * np.pi, grid_size, endpoint=False)
            J, I = np.meshgrid(J, I)

            # make a list of shape points (theta and phi angles) and values (radius)
            values = r

            points = np.array([theta, phi]).transpose()

            # add 0 and pi to theta
            points = np.concatenate(
                (points,
                 np.array([[0, 0], [0, 2 * np.pi], [np.pi, 0],
                           [np.pi, 2 * np.pi]])),
                axis=0)
            rmin = np.mean(r[np.where(theta == theta.min())])
            rmax = np.mean(r[np.where(theta == theta.max())])
            values = np.concatenate(
                (values, np.array([rmin, rmin, rmax, rmax])), axis=0)

            # add shape points shifted to the left and right in the longitude dimension, to fill the edges
            points = np.concatenate(
                (points, points - [0, 2 * np.pi], points + [0, 2 * np.pi]),
                axis=0)
            values = np.concatenate((values, values, values), axis=0)

            # make list of lattice points
            xi = np.asarray([[I[i, j], J[i, j]] for i in range(len(I))
                             for j in range(len(I[0]))])

            # interpolate the shape points on the lattice
            grid = griddata(points, values, xi, method='linear')
            grid = grid.reshape((grid_size, grid_size))

        else:
            grid = None

        return grid

    def plot_points(self, scale_factor=0.1):
        """
        Plot a 3D view of the surface points with mayavi.
        
        Returns
        -------
        mayavi scene
        """

        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            mesh = mlab.points3d(self.x,
                                 self.y,
                                 self.z,
                                 self.z,
                                 scale_mode='none',
                                 scale_factor=scale_factor,
                                 mode='sphere',
                                 colormap='gray').scene
            mesh.background = (1, 1, 1)
            mesh.magnification = 10
        return mesh

    def plot_surface(self, points=False, extent=None):
        """
        Plot a 3D view of the surface grid with mayavi.
        
        Parameters
        ----------
        points : bool, optional
            If True, surface points will be displayed.
            Default is False.
        extent : [xmin, xmax, ymin, ymax, zmin, zmax], optional
            Minimal and maximal coordinates to display.
            Default is the x, y, z arrays extent. 

        Returns
        -------
        mayavi scene
        """
        if self.Rgrid is not None:
            grid_size = self.Rgrid.shape[0]
            I = np.linspace(0, np.pi, grid_size + 1, endpoint=True)
            J = np.linspace(0, 2 * np.pi, grid_size + 1, endpoint=True)
            J, I = np.meshgrid(J, I)
            theta = I
            phi = J
            grid = np.zeros((grid_size + 1, grid_size + 1))
            grid[:-1, :-1] = self.Rgrid
            grid[-1] = grid[0]
            grid[:, -1] = grid[:, 0]

            x, y, z = tr.spherical_to_cart(1, theta, phi)
            mlab.clf()
            if extent is not None:
                mesh = mlab.mesh(x * np.abs(grid),
                                 y * np.abs(grid),
                                 z * np.abs(grid),
                                 scalars=grid,
                                 colormap='jet',
                                 extent=extent)
            else:
                mesh = mlab.mesh(x * np.abs(grid),
                                 y * np.abs(grid),
                                 z * np.abs(grid),
                                 scalars=grid,
                                 colormap='jet')
            if points:
                mesh = mlab.points3d(self.x,
                                     self.y,
                                     self.z,
                                     self.z,
                                     scale_mode='none',
                                     scale_factor=0.05)

            mesh.scene.background = (1, 1, 1)
            mesh.scene.magnification = 10
            return mesh.scene

    def compute_spharm(self,
                       grid_size=None,
                       normalize=False,
                       normalization_method='zero-component',
                       ri=False):
        """
        Compute the spherical harmonics spectrum of the current surface.
        
        Parameters
        ----------
        grid_size : int, optional
            Dimension of the square grid to interpolate the surface points.
            Will be used to interpolate the surface coordinates if self.Rgrid is None 
             (in this case it is a mandatory parameter).
            Default is None.
        normalize : bool, optional
            If True, the values of the spectrum will be normalized according to the `normalization_method`.
            Default is False.            
        normalization_method : str, optional
            If 'mean-radius', the grid values will be divided by the mean grid value prior to the SPHARM transform.
            If 'zero-component', all spectral components will be divided by the value of the first component (m=0, n=0).
            Default is 'zero-component'.
        ri : bool, optional
            If True, rotation-invariant spectrum based on Cartesian coordinates is computed.
            Default is False.

        Returns
        -------
        Spectrum : spherical harmonics spectrum of the surface.
        """
        self.spharm = Spectrum()

        if ri:
            data = []
            for r in [self.x, self.y, self.z]:
                grid = self.interpolate(grid_size=grid_size, r=r)
                self.spharm.from_surface(surface=grid, normalize=normalize)
                data.append(self.spharm.harmonics_csv)
            for c in ['value', 'amplitude', 'power', 'real', 'imag']:
                self.spharm.harmonics_csv[c] = np.sqrt(data[0][c]**2 +
                                                       data[1][c]**2 +
                                                       data[2][c]**2)
            self.spharm.harmonics_csv['amplitude2'] = np.abs(
                self.spharm.harmonics_csv['value'])
            self.spharm.convert_to_shtools_array()

        else:
            if self.Rgrid is None:
                if grid_size is None:
                    raise TypeError(
                        'Grid size for interpolation must be provided')
                else:
                    self.Rgrid = self.interpolate(grid_size=grid_size)
            if self.Rgrid is None:
                print(len(self.x), grid_size)
            self.spharm.from_surface(surface=self.Rgrid,
                                     normalize=normalize,
                                     normalization_method=normalization_method)
        self.spharm.metadata = self.metadata
        return self.spharm

    def inverse_spharm(self, lmax=None):
        """
        Inverse transform the SPHARM spectrum to surface using the given number of components.
        
        Parameters
        ----------
        lmax : int, optional
            The maximum spherical harmonic degree to be used in the inverse transform.
            If None, all degrees will be used.
            Default is None.

        Returns
        -------
        ndarray : reconstructed surface grid.
        """
        self.Rgrid = self.spharm.spharm_to_surface(lmax=lmax)
        return self.Rgrid
예제 #18
0
 def test_inverse_transform(self):
     sp = Spectrum()
     surf = np.ones([10, 10])
     sp.from_surface(surface=surf)
     grid = sp.spharm_to_surface()
     self.assertAlmostEqual(np.sum(np.abs(surf - grid)), 0, 7)
예제 #19
0
 def test_feature_vector(self):
     sp = Spectrum()
     surf = np.ones([10, 10])
     sp.from_surface(surface=surf)
     self.assertEqual(len(sp.return_feature_vector(cutoff=3)), 4)
예제 #20
0
 def test_from_surface_errors(self, case):
     sp = Spectrum()
     self.assertRaises(ValueError, sp.from_surface, surface=case)
예제 #21
0
 def test_from_surface_norm(self, case):
     sp = Spectrum()
     harm = sp.from_surface(surface=case,
                            normalize=True,
                            normalization_method='zero-component')
     self.assertEqual(np.max(harm), 1)
    def test_add_spectrum_and_plotting(self):
        tsp = TimeSpectrum()

        sp = Spectrum()
        sp.from_surface(surface=np.ones([10, 10]))
        sp.convert_to_csv()
        for i in range(3):
            tsp.add_spectrum(sp, timepoint=i*20)
        sp = Spectrum()
        surf = np.ones([10, 10])
        surf[3:4, 4:8] = 10
        sp.from_surface(surface=surf)
        sp.convert_to_csv()
        for i in range(2):
            tsp.add_spectrum(sp, timepoint=i*20+60)

        sp = Spectrum()
        sp.from_surface(surface=np.ones([10, 10]))
        sp.convert_to_csv()
        for i in range(3):
            tsp.add_spectrum(sp, timepoint=i*20+100)

        self.assertEqual(len(tsp.spectra), 8)
        tsp.save_to_csv('data/test_data/time_spectrum.csv')
        self.assertEqual(os.path.exists('data/test_data/time_spectrum.csv'), True)
        pl = tsp.time_heatmap()
        pl.savefig('data/test_data/time_heatmap.png')
        self.assertEqual(os.path.exists('data/test_data/time_heatmap.png'), True)

        tsp.compute_derivative()
        pl = tsp.derivative_heatmap()
        pl.savefig('data/test_data/derivative_heatmap.png')
        self.assertEqual(os.path.exists('data/test_data/derivative_heatmap.png'), True)

        pl = tsp.plot_mean_abs_derivative()
        pl.savefig('data/test_data/mean_abs_derivative.png')
        self.assertEqual(os.path.exists('data/test_data/mean_abs_derivative.png'), True)
        shutil.rmtree('data/test_data/')