pynkowski.data.array

  1import numpy as np
  2from .base_da import DataField
  3
  4
  5
  6def _eval_field(field, pix):
  7    return field[tuple(pix[:,ii] for ii in np.arange(field.ndim))]
  8
  9def _hotspot_array(field):
 10    """Find the local maxima of the input field.
 11
 12    Arguments
 13    ---------
 14    field : np.array
 15        Array of the pixelized field values.
 16
 17    Returns
 18    -------
 19    pixels : np.array
 20        Indices of the pixels which are local maxima.
 21
 22    values : np.array
 23        Values of input map which are local maxima.
 24
 25    """
 26    ndim = len(field.shape)
 27    # First we shift the field in each direction by 1 pixel, and check that the original pixel is larger than all the `2**ndim` neighbours.
 28    max_mask = np.all(field > np.array([np.roll(field, shift, axis=ii) for shift in [-1,1] for ii in range(ndim)]), axis=0)
 29
 30    # We then remove all the pixels in the border to remove the edge effects.
 31    for dim in range(max_mask.ndim):
 32        # Make current dimension the first dimension
 33        array_moved = np.moveaxis(max_mask, dim, 0)
 34        # Set border values to `False` in the current dimension
 35        array_moved[0] = False
 36        array_moved[-1] = False
 37        # No need to reorder the dimensions as moveaxis returns a view
 38    max_mask = np.pad(max_mask, pad_width=1, mode='constant', constant_values=False)
 39
 40    pixels = np.argwhere(max_mask)
 41    values = _eval_field(field, pixels)
 42    return(pixels, values)
 43
 44class DataArray(DataField):
 45    """Class for a pixelized Euclidean data cube.
 46    
 47    Parameters
 48    ----------
 49    field : np.array
 50        Pixelized field as a 3D array. All pixels (or voxels) are expected to be the same size. The pixel shape is not necessarily a cube (see `spacing`).
 51        The field can have different sizes in each dimension.
 52        
 53    normalise : bool, optional
 54        If `True`, the map is normalised to have unit variance. Note: the mean of the map is not forced to be 0.
 55        Default: `True`.
 56        
 57    mask : np.array or None, optional
 58        Mask where the field if considered. It is a bool array of the same shape that `field`.
 59        Default: all data is included.
 60
 61    spacing : float or np.array, optional
 62        Spacing between pixels (centres) in each dimension. If a float, the spacing is the same in all dimensions. 
 63        If an array, it must have the same length as the number of dimensions of the field.
 64        
 65    Attributes
 66    ----------
 67    field : np.array
 68        Data of the field as a 3D array.
 69        
 70    dim : int
 71        Dimension of the space where the field is defined. In this case, the space is the 3D space and this is 3.
 72        
 73    name : str
 74        Name of the field. In this case, "data cube"
 75        
 76    first_der : np.array or None
 77        First **covariant** derivatives of the field in an orthonormal basis of the space. Same structure as `field`, and shape `(dim, field.shape)`.
 78        
 79    second_der : np.array or None
 80        Second **covariant** derivatives of the field in an orthonormal basis of the space. Same structure as `field`, and shape `(dim*(dim+1)/2, field.shape)`.
 81
 82    spacing : float or np.array
 83        Spacing between pixels (centres) in each dimension. If a float, the spacing is the same in all dimensions.
 84        If an array, it must have the same length as the number of dimensions of the field.
 85        
 86    """
 87    def __init__(self, field, normalise=True, mask=None, spacing=1.):
 88        dim = len(field.shape)
 89        super().__init__(field, dim=dim, name='DataArray', mask=mask)
 90        if self.mask.shape != self.field.shape:
 91            raise ValueError('The map and the mask have different shapes')
 92        if normalise:
 93            self.field /= np.sqrt(self.get_variance())
 94        self.first_der = None
 95        self.second_der = None
 96        self.spacing = spacing
 97            
 98    def get_variance(self):
 99        """Compute the variance of the field within the mask. 
100
101        Returns
102        -------
103        var : float
104            The variance of the input field within the mask.
105
106        """    
107        return (np.var(self.field[self.mask]))
108
109    def get_first_der(self):
110        """Compute the covariant first derivatives of the field. 
111        It stores the derivatives in order. E.g., in a 3D array:
112        
113        - first covariant derivative wrt e₁ in self.first_der[0]
114        - first covariant derivative wrt e₂ in self.first_der[1]
115        - first covariant derivative wrt e₃ in self.first_der[2]
116
117        """
118        self.first_der = np.array(np.gradient(self.field, self.spacing, edge_order=2))
119
120    def get_second_der(self):
121        """Compute the covariant second derivatives of the field. 
122        It stores the second derivatives in the following order: first, all the diagonal derivatives, then the cross derivatives with e₁, then with e₂, and so on.
123        E.g., in a 3D array:
124        
125        - second covariant derivative wrt e₁e₁ in self.second_der[0]
126        - second covariant derivative wrt e₂e₂ in self.second_der[1]
127        - second covariant derivative wrt e₃e₃ in self.second_der[2]
128        - second covariant derivative wrt e₁e₂ in self.second_der[3]
129        - second covariant derivative wrt e₁e₃ in self.second_der[4]
130        - second covariant derivative wrt e₂e₃ in self.second_der[5]
131
132        """
133        if self.first_der is None:
134            self.get_first_der()
135        self.second_der = np.zeros((self.dim*(self.dim+1)//2, *self.field.shape))
136        d = self.dim
137        for i in np.arange(self.dim, dtype=int):
138            indeces = [i]
139            for j in np.arange(i+1, self.dim, dtype=int):
140                indeces.append((d*(d-1)/2) - (d-i)*((d-i)-1)/2 + j - i - 1 + d)
141            indeces = np.array(indeces, dtype=int)
142            self.second_der[indeces] = np.gradient(self.first_der[i], self.spacing, edge_order=2)[i:d]
143
144    def maxima_list(self):
145        """Find the local maxima of the field.
146
147        Returns
148        -------
149        values : np.array
150            Values of input map which are local maxima.
151
152        """
153        pixels, values = _hotspot_array(self.field)
154        return values[_eval_field(self.mask, pixels)]
155    
156    def minima_list(self):
157        """Find the local minima of the field.
158
159        Returns
160        -------
161        values : np.array
162            Values of input map which are local minima.
163
164        """
165        pixels, values = _hotspot_array(-self.field)
166        return -values[_eval_field(self.mask, pixels)]
167
168
169
170__all__ = ["DataArray"]
171__docformat__ = "numpy"
class DataArray(pynkowski.data.base_da.DataField):
 45class DataArray(DataField):
 46    """Class for a pixelized Euclidean data cube.
 47    
 48    Parameters
 49    ----------
 50    field : np.array
 51        Pixelized field as a 3D array. All pixels (or voxels) are expected to be the same size. The pixel shape is not necessarily a cube (see `spacing`).
 52        The field can have different sizes in each dimension.
 53        
 54    normalise : bool, optional
 55        If `True`, the map is normalised to have unit variance. Note: the mean of the map is not forced to be 0.
 56        Default: `True`.
 57        
 58    mask : np.array or None, optional
 59        Mask where the field if considered. It is a bool array of the same shape that `field`.
 60        Default: all data is included.
 61
 62    spacing : float or np.array, optional
 63        Spacing between pixels (centres) in each dimension. If a float, the spacing is the same in all dimensions. 
 64        If an array, it must have the same length as the number of dimensions of the field.
 65        
 66    Attributes
 67    ----------
 68    field : np.array
 69        Data of the field as a 3D array.
 70        
 71    dim : int
 72        Dimension of the space where the field is defined. In this case, the space is the 3D space and this is 3.
 73        
 74    name : str
 75        Name of the field. In this case, "data cube"
 76        
 77    first_der : np.array or None
 78        First **covariant** derivatives of the field in an orthonormal basis of the space. Same structure as `field`, and shape `(dim, field.shape)`.
 79        
 80    second_der : np.array or None
 81        Second **covariant** derivatives of the field in an orthonormal basis of the space. Same structure as `field`, and shape `(dim*(dim+1)/2, field.shape)`.
 82
 83    spacing : float or np.array
 84        Spacing between pixels (centres) in each dimension. If a float, the spacing is the same in all dimensions.
 85        If an array, it must have the same length as the number of dimensions of the field.
 86        
 87    """
 88    def __init__(self, field, normalise=True, mask=None, spacing=1.):
 89        dim = len(field.shape)
 90        super().__init__(field, dim=dim, name='DataArray', mask=mask)
 91        if self.mask.shape != self.field.shape:
 92            raise ValueError('The map and the mask have different shapes')
 93        if normalise:
 94            self.field /= np.sqrt(self.get_variance())
 95        self.first_der = None
 96        self.second_der = None
 97        self.spacing = spacing
 98            
 99    def get_variance(self):
100        """Compute the variance of the field within the mask. 
101
102        Returns
103        -------
104        var : float
105            The variance of the input field within the mask.
106
107        """    
108        return (np.var(self.field[self.mask]))
109
110    def get_first_der(self):
111        """Compute the covariant first derivatives of the field. 
112        It stores the derivatives in order. E.g., in a 3D array:
113        
114        - first covariant derivative wrt e₁ in self.first_der[0]
115        - first covariant derivative wrt e₂ in self.first_der[1]
116        - first covariant derivative wrt e₃ in self.first_der[2]
117
118        """
119        self.first_der = np.array(np.gradient(self.field, self.spacing, edge_order=2))
120
121    def get_second_der(self):
122        """Compute the covariant second derivatives of the field. 
123        It stores the second derivatives in the following order: first, all the diagonal derivatives, then the cross derivatives with e₁, then with e₂, and so on.
124        E.g., in a 3D array:
125        
126        - second covariant derivative wrt e₁e₁ in self.second_der[0]
127        - second covariant derivative wrt e₂e₂ in self.second_der[1]
128        - second covariant derivative wrt e₃e₃ in self.second_der[2]
129        - second covariant derivative wrt e₁e₂ in self.second_der[3]
130        - second covariant derivative wrt e₁e₃ in self.second_der[4]
131        - second covariant derivative wrt e₂e₃ in self.second_der[5]
132
133        """
134        if self.first_der is None:
135            self.get_first_der()
136        self.second_der = np.zeros((self.dim*(self.dim+1)//2, *self.field.shape))
137        d = self.dim
138        for i in np.arange(self.dim, dtype=int):
139            indeces = [i]
140            for j in np.arange(i+1, self.dim, dtype=int):
141                indeces.append((d*(d-1)/2) - (d-i)*((d-i)-1)/2 + j - i - 1 + d)
142            indeces = np.array(indeces, dtype=int)
143            self.second_der[indeces] = np.gradient(self.first_der[i], self.spacing, edge_order=2)[i:d]
144
145    def maxima_list(self):
146        """Find the local maxima of the field.
147
148        Returns
149        -------
150        values : np.array
151            Values of input map which are local maxima.
152
153        """
154        pixels, values = _hotspot_array(self.field)
155        return values[_eval_field(self.mask, pixels)]
156    
157    def minima_list(self):
158        """Find the local minima of the field.
159
160        Returns
161        -------
162        values : np.array
163            Values of input map which are local minima.
164
165        """
166        pixels, values = _hotspot_array(-self.field)
167        return -values[_eval_field(self.mask, pixels)]

Class for a pixelized Euclidean data cube.

Parameters
  • field (np.array): Pixelized field as a 3D array. All pixels (or voxels) are expected to be the same size. The pixel shape is not necessarily a cube (see spacing). The field can have different sizes in each dimension.
  • normalise (bool, optional): If True, the map is normalised to have unit variance. Note: the mean of the map is not forced to be 0. Default: True.
  • mask (np.array or None, optional): Mask where the field if considered. It is a bool array of the same shape that field. Default: all data is included.
  • spacing (float or np.array, optional): Spacing between pixels (centres) in each dimension. If a float, the spacing is the same in all dimensions. If an array, it must have the same length as the number of dimensions of the field.
Attributes
  • field (np.array): Data of the field as a 3D array.
  • dim (int): Dimension of the space where the field is defined. In this case, the space is the 3D space and this is 3.
  • name (str): Name of the field. In this case, "data cube"
  • first_der (np.array or None): First covariant derivatives of the field in an orthonormal basis of the space. Same structure as field, and shape (dim, field.shape).
  • second_der (np.array or None): Second covariant derivatives of the field in an orthonormal basis of the space. Same structure as field, and shape (dim*(dim+1)/2, field.shape).
  • spacing (float or np.array): Spacing between pixels (centres) in each dimension. If a float, the spacing is the same in all dimensions. If an array, it must have the same length as the number of dimensions of the field.
DataArray(field, normalise=True, mask=None, spacing=1.0)
88    def __init__(self, field, normalise=True, mask=None, spacing=1.):
89        dim = len(field.shape)
90        super().__init__(field, dim=dim, name='DataArray', mask=mask)
91        if self.mask.shape != self.field.shape:
92            raise ValueError('The map and the mask have different shapes')
93        if normalise:
94            self.field /= np.sqrt(self.get_variance())
95        self.first_der = None
96        self.second_der = None
97        self.spacing = spacing
def get_variance(self):
 99    def get_variance(self):
100        """Compute the variance of the field within the mask. 
101
102        Returns
103        -------
104        var : float
105            The variance of the input field within the mask.
106
107        """    
108        return (np.var(self.field[self.mask]))

Compute the variance of the field within the mask.

Returns
  • var (float): The variance of the input field within the mask.
def get_first_der(self):
110    def get_first_der(self):
111        """Compute the covariant first derivatives of the field. 
112        It stores the derivatives in order. E.g., in a 3D array:
113        
114        - first covariant derivative wrt e₁ in self.first_der[0]
115        - first covariant derivative wrt e₂ in self.first_der[1]
116        - first covariant derivative wrt e₃ in self.first_der[2]
117
118        """
119        self.first_der = np.array(np.gradient(self.field, self.spacing, edge_order=2))

Compute the covariant first derivatives of the field. It stores the derivatives in order. E.g., in a 3D array:

  • first covariant derivative wrt e₁ in self.first_der[0]
  • first covariant derivative wrt e₂ in self.first_der[1]
  • first covariant derivative wrt e₃ in self.first_der[2]
def get_second_der(self):
121    def get_second_der(self):
122        """Compute the covariant second derivatives of the field. 
123        It stores the second derivatives in the following order: first, all the diagonal derivatives, then the cross derivatives with e₁, then with e₂, and so on.
124        E.g., in a 3D array:
125        
126        - second covariant derivative wrt e₁e₁ in self.second_der[0]
127        - second covariant derivative wrt e₂e₂ in self.second_der[1]
128        - second covariant derivative wrt e₃e₃ in self.second_der[2]
129        - second covariant derivative wrt e₁e₂ in self.second_der[3]
130        - second covariant derivative wrt e₁e₃ in self.second_der[4]
131        - second covariant derivative wrt e₂e₃ in self.second_der[5]
132
133        """
134        if self.first_der is None:
135            self.get_first_der()
136        self.second_der = np.zeros((self.dim*(self.dim+1)//2, *self.field.shape))
137        d = self.dim
138        for i in np.arange(self.dim, dtype=int):
139            indeces = [i]
140            for j in np.arange(i+1, self.dim, dtype=int):
141                indeces.append((d*(d-1)/2) - (d-i)*((d-i)-1)/2 + j - i - 1 + d)
142            indeces = np.array(indeces, dtype=int)
143            self.second_der[indeces] = np.gradient(self.first_der[i], self.spacing, edge_order=2)[i:d]

Compute the covariant second derivatives of the field. It stores the second derivatives in the following order: first, all the diagonal derivatives, then the cross derivatives with e₁, then with e₂, and so on. E.g., in a 3D array:

  • second covariant derivative wrt e₁e₁ in self.second_der[0]
  • second covariant derivative wrt e₂e₂ in self.second_der[1]
  • second covariant derivative wrt e₃e₃ in self.second_der[2]
  • second covariant derivative wrt e₁e₂ in self.second_der[3]
  • second covariant derivative wrt e₁e₃ in self.second_der[4]
  • second covariant derivative wrt e₂e₃ in self.second_der[5]
def maxima_list(self):
145    def maxima_list(self):
146        """Find the local maxima of the field.
147
148        Returns
149        -------
150        values : np.array
151            Values of input map which are local maxima.
152
153        """
154        pixels, values = _hotspot_array(self.field)
155        return values[_eval_field(self.mask, pixels)]

Find the local maxima of the field.

Returns
  • values (np.array): Values of input map which are local maxima.
def minima_list(self):
157    def minima_list(self):
158        """Find the local minima of the field.
159
160        Returns
161        -------
162        values : np.array
163            Values of input map which are local minima.
164
165        """
166        pixels, values = _hotspot_array(-self.field)
167        return -values[_eval_field(self.mask, pixels)]

Find the local minima of the field.

Returns
  • values (np.array): Values of input map which are local minima.