scmkl.calculate_z

  1import numpy as np
  2import scipy
  3import anndata as ad
  4
  5from scmkl.tfidf_normalize import tfidf_train_test
  6from scmkl.estimate_sigma import est_group_sigma, get_batches
  7from scmkl.data_processing import process_data, get_group_mat, sample_cells
  8from scmkl.projections import gaussian_trans, laplacian_trans, cauchy_trans
  9
 10
 11def check_for_nan(adata: ad.AnnData):
 12    """
 13    Ensures only valid values are in training and test matrices.
 14
 15    Parameters
 16    ----------
 17    adata : ad.AnnData
 18        Object with `'Z_train'` and `'Z_test'` keys in `.uns` 
 19        attribute.
 20
 21    Returns
 22    -------
 23    None
 24    """
 25    n_nans = np.sum(np.isnan(adata.uns['Z_train']))
 26    n_nans += np.sum(np.isnan(adata.uns['Z_test']))
 27
 28    if n_nans:
 29        raise ValueError(
 30            "Some values in Z matrix are type `np.nan`. This is likely "
 31            "due to a small kernel width or invalid values in input Z matrix."
 32            )
 33    
 34    return None
 35
 36
 37def check_for_inf(adata: ad.AnnData):
 38    """
 39    Ensures only valid values are in training and test matrices.
 40
 41    Parameters
 42    ----------
 43    adata : ad.AnnData
 44        Object with `'Z_train'` and `'Z_test'` keys in `.uns` 
 45        attribute.
 46
 47    Returns
 48    -------
 49    None
 50    """
 51    n_infs = np.sum(np.isinf(adata.uns['Z_train']))
 52    n_infs += np.sum(np.isinf(adata.uns['Z_test']))
 53
 54    if n_infs:
 55        raise ValueError(
 56            "Some values in Z matrix are type `np.inf`. This is likely "
 57            "due to input matrix containing negative values."
 58            )
 59    
 60    return None
 61
 62
 63def get_z_indices(m, D):
 64    """
 65    Takes the number associated with the group as `m` and returns the 
 66    indices for cos and sin functions to be applied.
 67
 68    Parameters
 69    ----------
 70    m : int
 71        The chronological number of the group being processed.
 72
 73    D : int
 74        The number of dimensions per group.
 75
 76    Returns
 77    -------
 78    cos_idx, sin_idx : np.ndarray, np.ndarray
 79        The indices for cos and sin projections in overall Z matrix.
 80    """
 81    x_idx = np.arange(m*2*D ,(m + 1)*2*D)
 82    cos_idx = x_idx[:len(x_idx)//2]
 83    sin_idx = x_idx[len(x_idx)//2:]
 84
 85    return cos_idx, sin_idx
 86
 87
 88def calc_groupz(X_train, X_test, adata, D, sigma, proj_func, dtype):
 89    """
 90    Calculates the Z matrix for grouping.
 91
 92    Parameters
 93    ----------
 94    X_train : np.ndarray
 95        The filtered data matrix to calculate train Z mat for.
 96    
 97    X_test : np.ndarray
 98        The filtered data matrix to calculate test Z mat for.
 99
100    adata : anndata.AnnData 
101        AnnData object containing `seed_obj` in `.uns` attribute.
102
103    D : int
104        Number of dimensions per grouping.
105
106    sigma : float
107        Kernel width for grouping.
108
109    proj_func : function
110        The projection direction function to be applied to data.
111
112    Returns
113    -------
114    train_projections, test_projections : np.ndarray, np.ndarray
115        Training and testing Z matrices for group.
116    """  
117    if scipy.sparse.issparse(X_train):
118        X_train = X_train.toarray().astype(np.float16)
119        X_test = X_test.toarray().astype(np.float16)
120
121    W = proj_func(X_train, sigma, adata.uns['seed_obj'], D)
122
123    train_projection = np.matmul(X_train, W, dtype=dtype)
124    test_projection = np.matmul(X_test, W, dtype=dtype)
125
126    if np.isnan(train_projection).any() or np.isnan(test_projection).any():
127        train_projection = np.matmul(X_train, W, dtype=np.float64)
128        test_projection = np.matmul(X_test, W, dtype=np.float64)
129
130    return train_projection, test_projection
131
132
133def calculate_z(adata, n_features=5000, batches=10, 
134                batch_size=100) -> ad.AnnData:
135    """
136    Function to calculate Z matrices for all groups in both training 
137    and testing data.
138
139    Parameters
140    ----------
141    adata : ad.AnnData
142        created by `scmkl.create_adata()` with `adata.uns.keys()`: 
143        `'train_indices'`, and `'test_indices'`. 
144
145    n_features : int
146        Number of random feature to use when calculating Z; used for 
147        scalability.
148
149    batches : int
150        The number of batches to use for the distance calculation.
151        This will average the result of `batches` distance calculations
152        of `batch_size` randomly sampled cells. More batches will converge
153        to population distance values at the cost of scalability.
154
155    batch_size : int
156        The number of cells to include per batch for distance
157        calculations. Higher batch size will converge to population
158        distance values at the cost of scalability.
159        If `batches*batch_size > num_training_cells`,
160        `batch_size` will be reduced to 
161        `int(num_training_cells / batches)`.
162
163    Returns
164    -------
165    adata : ad.AnnData
166        `adata` with Z matrices accessible with `adata.uns['Z_train']` 
167        and `adata.uns['Z_test']`.
168
169    Examples
170    --------
171    >>> adata = scmkl.estimate_sigma(adata)
172    >>> adata = scmkl.calculate_z(adata)
173    >>> adata.uns.keys()
174    dict_keys(['Z_train', 'Z_test', 'sigmas', 'train_indices', 
175    'test_indices'])
176    """
177    # Number of groupings taking from group_dict
178    n_pathway = len(adata.uns['group_dict'].keys())
179    D = adata.uns['D']
180
181    sq_i_d = np.sqrt(1/D)
182
183    # Capturing training and testing sizes
184    train_len = len(adata.uns['train_indices'])
185    test_len = len(adata.uns['test_indices'])
186
187    if batch_size * batches > train_len:
188        old_batch_size = batch_size
189        batch_size = int(train_len/batches)
190        print("Specified batch size required too many cells for "
191                "independent batches. Reduced batch size from "
192                f"{old_batch_size} to {batch_size}")
193
194    if 'sigma' not in adata.uns.keys():
195        n_samples = np.min((2000, train_len))
196        sample_range = np.arange(n_samples)
197        batch_idx = get_batches(sample_range, adata.uns['seed_obj'], 
198                                batches=batches, batch_size=batch_size)
199        sigma_indices = sample_cells(adata.uns['train_indices'], n_samples, adata.uns['seed_obj'])
200
201    # Create Arrays to store concatenated group Zs
202    # Each group of features will have a corresponding entry in each array
203    n_cols = 2*adata.uns['D']*n_pathway
204    Z_train = np.zeros((train_len, n_cols), dtype=np.float16)
205    Z_test = np.zeros((test_len, n_cols), dtype=np.float16)
206
207
208    # Setting kernel function 
209    match adata.uns['kernel_type'].lower():
210        case 'gaussian':
211            proj_func = gaussian_trans
212        case 'laplacian':
213            proj_func = laplacian_trans
214        case 'cauchy':
215            proj_func = cauchy_trans
216
217
218    # Loop over each of the groups and creating Z for each
219    z_dtype = np.float16
220    sigma_list = list()
221    for m, group_features in enumerate(adata.uns['group_dict'].values()):
222
223        n_group_features = len(group_features)
224
225        X_train, X_test = get_group_mat(adata, n_features, group_features, 
226                                        n_group_features, process_test=True)
227        
228        if adata.uns['tfidf']:
229            X_train, X_test = tfidf_train_test(X_train, X_test)
230
231        # Data filtering, and transformation according to given data_type
232        # Will remove low variance (< 1e5) features regardless of data_type
233        # If scale/transform data depending on .uns values
234        X_train, X_test = process_data(X_train=X_train, X_test=X_test, 
235                                       scale_data=adata.uns['scale_data'], 
236                                       transform_data=adata.uns['transform_data'],
237                                       return_dense=True, 
238                                       add_ones=adata.uns['add_ones'])    
239
240        # Getting sigma
241        if 'sigma' in adata.uns.keys():
242            sigma = adata.uns['sigma'][m]
243        else:
244            sigma = est_group_sigma(adata, X_train, n_group_features, 
245                                    n_features, batch_idx=batch_idx)
246            sigma_list.append(sigma)
247            
248        assert sigma > 0, "Sigma must be more than 0"
249        train_projection, test_projection = calc_groupz(X_train, X_test, 
250                                                        adata, D, sigma, 
251                                                        proj_func, z_dtype)
252
253        if train_projection.dtype != z_dtype:
254            print("Converting Z dtype from np.float16 to np.float64", 
255                  flush=True)
256            z_dtype = train_projection.dtype
257            Z_train = Z_train.astype(z_dtype)
258            Z_test = Z_test.astype(z_dtype)
259
260        # Store group Z in whole-Z object
261        # Preserves order to be able to extract meaningful groups
262        cos_idx, sin_idx = get_z_indices(m, D)
263
264        Z_train[0:, cos_idx] = np.cos(train_projection, dtype=z_dtype)
265        Z_train[0:, sin_idx] = np.sin(train_projection, dtype=z_dtype)
266
267        Z_test[0:, cos_idx] = np.cos(test_projection, dtype=z_dtype)
268        Z_test[0:, sin_idx] = np.sin(test_projection, dtype=z_dtype)
269        
270    adata.uns['Z_train'] = Z_train*sq_i_d
271    adata.uns['Z_test'] = Z_test*sq_i_d
272
273    if 'sigma' not in adata.uns.keys():
274        adata.uns['sigma'] = np.array(sigma_list)
275
276    check_for_nan(adata)
277    check_for_inf(adata)
278
279    return adata
def check_for_nan(adata: anndata._core.anndata.AnnData):
12def check_for_nan(adata: ad.AnnData):
13    """
14    Ensures only valid values are in training and test matrices.
15
16    Parameters
17    ----------
18    adata : ad.AnnData
19        Object with `'Z_train'` and `'Z_test'` keys in `.uns` 
20        attribute.
21
22    Returns
23    -------
24    None
25    """
26    n_nans = np.sum(np.isnan(adata.uns['Z_train']))
27    n_nans += np.sum(np.isnan(adata.uns['Z_test']))
28
29    if n_nans:
30        raise ValueError(
31            "Some values in Z matrix are type `np.nan`. This is likely "
32            "due to a small kernel width or invalid values in input Z matrix."
33            )
34    
35    return None

Ensures only valid values are in training and test matrices.

Parameters
  • adata (ad.AnnData): Object with 'Z_train' and 'Z_test' keys in .uns attribute.
Returns
  • None
def check_for_inf(adata: anndata._core.anndata.AnnData):
38def check_for_inf(adata: ad.AnnData):
39    """
40    Ensures only valid values are in training and test matrices.
41
42    Parameters
43    ----------
44    adata : ad.AnnData
45        Object with `'Z_train'` and `'Z_test'` keys in `.uns` 
46        attribute.
47
48    Returns
49    -------
50    None
51    """
52    n_infs = np.sum(np.isinf(adata.uns['Z_train']))
53    n_infs += np.sum(np.isinf(adata.uns['Z_test']))
54
55    if n_infs:
56        raise ValueError(
57            "Some values in Z matrix are type `np.inf`. This is likely "
58            "due to input matrix containing negative values."
59            )
60    
61    return None

Ensures only valid values are in training and test matrices.

Parameters
  • adata (ad.AnnData): Object with 'Z_train' and 'Z_test' keys in .uns attribute.
Returns
  • None
def get_z_indices(m, D):
64def get_z_indices(m, D):
65    """
66    Takes the number associated with the group as `m` and returns the 
67    indices for cos and sin functions to be applied.
68
69    Parameters
70    ----------
71    m : int
72        The chronological number of the group being processed.
73
74    D : int
75        The number of dimensions per group.
76
77    Returns
78    -------
79    cos_idx, sin_idx : np.ndarray, np.ndarray
80        The indices for cos and sin projections in overall Z matrix.
81    """
82    x_idx = np.arange(m*2*D ,(m + 1)*2*D)
83    cos_idx = x_idx[:len(x_idx)//2]
84    sin_idx = x_idx[len(x_idx)//2:]
85
86    return cos_idx, sin_idx

Takes the number associated with the group as m and returns the indices for cos and sin functions to be applied.

Parameters
  • m (int): The chronological number of the group being processed.
  • D (int): The number of dimensions per group.
Returns
  • cos_idx, sin_idx (np.ndarray, np.ndarray): The indices for cos and sin projections in overall Z matrix.
def calc_groupz(X_train, X_test, adata, D, sigma, proj_func, dtype):
 89def calc_groupz(X_train, X_test, adata, D, sigma, proj_func, dtype):
 90    """
 91    Calculates the Z matrix for grouping.
 92
 93    Parameters
 94    ----------
 95    X_train : np.ndarray
 96        The filtered data matrix to calculate train Z mat for.
 97    
 98    X_test : np.ndarray
 99        The filtered data matrix to calculate test Z mat for.
100
101    adata : anndata.AnnData 
102        AnnData object containing `seed_obj` in `.uns` attribute.
103
104    D : int
105        Number of dimensions per grouping.
106
107    sigma : float
108        Kernel width for grouping.
109
110    proj_func : function
111        The projection direction function to be applied to data.
112
113    Returns
114    -------
115    train_projections, test_projections : np.ndarray, np.ndarray
116        Training and testing Z matrices for group.
117    """  
118    if scipy.sparse.issparse(X_train):
119        X_train = X_train.toarray().astype(np.float16)
120        X_test = X_test.toarray().astype(np.float16)
121
122    W = proj_func(X_train, sigma, adata.uns['seed_obj'], D)
123
124    train_projection = np.matmul(X_train, W, dtype=dtype)
125    test_projection = np.matmul(X_test, W, dtype=dtype)
126
127    if np.isnan(train_projection).any() or np.isnan(test_projection).any():
128        train_projection = np.matmul(X_train, W, dtype=np.float64)
129        test_projection = np.matmul(X_test, W, dtype=np.float64)
130
131    return train_projection, test_projection

Calculates the Z matrix for grouping.

Parameters
  • X_train (np.ndarray): The filtered data matrix to calculate train Z mat for.
  • X_test (np.ndarray): The filtered data matrix to calculate test Z mat for.
  • adata (anndata.AnnData): AnnData object containing seed_obj in .uns attribute.
  • D (int): Number of dimensions per grouping.
  • sigma (float): Kernel width for grouping.
  • proj_func (function): The projection direction function to be applied to data.
Returns
  • train_projections, test_projections (np.ndarray, np.ndarray): Training and testing Z matrices for group.
def calculate_z( adata, n_features=5000, batches=10, batch_size=100) -> anndata._core.anndata.AnnData:
134def calculate_z(adata, n_features=5000, batches=10, 
135                batch_size=100) -> ad.AnnData:
136    """
137    Function to calculate Z matrices for all groups in both training 
138    and testing data.
139
140    Parameters
141    ----------
142    adata : ad.AnnData
143        created by `scmkl.create_adata()` with `adata.uns.keys()`: 
144        `'train_indices'`, and `'test_indices'`. 
145
146    n_features : int
147        Number of random feature to use when calculating Z; used for 
148        scalability.
149
150    batches : int
151        The number of batches to use for the distance calculation.
152        This will average the result of `batches` distance calculations
153        of `batch_size` randomly sampled cells. More batches will converge
154        to population distance values at the cost of scalability.
155
156    batch_size : int
157        The number of cells to include per batch for distance
158        calculations. Higher batch size will converge to population
159        distance values at the cost of scalability.
160        If `batches*batch_size > num_training_cells`,
161        `batch_size` will be reduced to 
162        `int(num_training_cells / batches)`.
163
164    Returns
165    -------
166    adata : ad.AnnData
167        `adata` with Z matrices accessible with `adata.uns['Z_train']` 
168        and `adata.uns['Z_test']`.
169
170    Examples
171    --------
172    >>> adata = scmkl.estimate_sigma(adata)
173    >>> adata = scmkl.calculate_z(adata)
174    >>> adata.uns.keys()
175    dict_keys(['Z_train', 'Z_test', 'sigmas', 'train_indices', 
176    'test_indices'])
177    """
178    # Number of groupings taking from group_dict
179    n_pathway = len(adata.uns['group_dict'].keys())
180    D = adata.uns['D']
181
182    sq_i_d = np.sqrt(1/D)
183
184    # Capturing training and testing sizes
185    train_len = len(adata.uns['train_indices'])
186    test_len = len(adata.uns['test_indices'])
187
188    if batch_size * batches > train_len:
189        old_batch_size = batch_size
190        batch_size = int(train_len/batches)
191        print("Specified batch size required too many cells for "
192                "independent batches. Reduced batch size from "
193                f"{old_batch_size} to {batch_size}")
194
195    if 'sigma' not in adata.uns.keys():
196        n_samples = np.min((2000, train_len))
197        sample_range = np.arange(n_samples)
198        batch_idx = get_batches(sample_range, adata.uns['seed_obj'], 
199                                batches=batches, batch_size=batch_size)
200        sigma_indices = sample_cells(adata.uns['train_indices'], n_samples, adata.uns['seed_obj'])
201
202    # Create Arrays to store concatenated group Zs
203    # Each group of features will have a corresponding entry in each array
204    n_cols = 2*adata.uns['D']*n_pathway
205    Z_train = np.zeros((train_len, n_cols), dtype=np.float16)
206    Z_test = np.zeros((test_len, n_cols), dtype=np.float16)
207
208
209    # Setting kernel function 
210    match adata.uns['kernel_type'].lower():
211        case 'gaussian':
212            proj_func = gaussian_trans
213        case 'laplacian':
214            proj_func = laplacian_trans
215        case 'cauchy':
216            proj_func = cauchy_trans
217
218
219    # Loop over each of the groups and creating Z for each
220    z_dtype = np.float16
221    sigma_list = list()
222    for m, group_features in enumerate(adata.uns['group_dict'].values()):
223
224        n_group_features = len(group_features)
225
226        X_train, X_test = get_group_mat(adata, n_features, group_features, 
227                                        n_group_features, process_test=True)
228        
229        if adata.uns['tfidf']:
230            X_train, X_test = tfidf_train_test(X_train, X_test)
231
232        # Data filtering, and transformation according to given data_type
233        # Will remove low variance (< 1e5) features regardless of data_type
234        # If scale/transform data depending on .uns values
235        X_train, X_test = process_data(X_train=X_train, X_test=X_test, 
236                                       scale_data=adata.uns['scale_data'], 
237                                       transform_data=adata.uns['transform_data'],
238                                       return_dense=True, 
239                                       add_ones=adata.uns['add_ones'])    
240
241        # Getting sigma
242        if 'sigma' in adata.uns.keys():
243            sigma = adata.uns['sigma'][m]
244        else:
245            sigma = est_group_sigma(adata, X_train, n_group_features, 
246                                    n_features, batch_idx=batch_idx)
247            sigma_list.append(sigma)
248            
249        assert sigma > 0, "Sigma must be more than 0"
250        train_projection, test_projection = calc_groupz(X_train, X_test, 
251                                                        adata, D, sigma, 
252                                                        proj_func, z_dtype)
253
254        if train_projection.dtype != z_dtype:
255            print("Converting Z dtype from np.float16 to np.float64", 
256                  flush=True)
257            z_dtype = train_projection.dtype
258            Z_train = Z_train.astype(z_dtype)
259            Z_test = Z_test.astype(z_dtype)
260
261        # Store group Z in whole-Z object
262        # Preserves order to be able to extract meaningful groups
263        cos_idx, sin_idx = get_z_indices(m, D)
264
265        Z_train[0:, cos_idx] = np.cos(train_projection, dtype=z_dtype)
266        Z_train[0:, sin_idx] = np.sin(train_projection, dtype=z_dtype)
267
268        Z_test[0:, cos_idx] = np.cos(test_projection, dtype=z_dtype)
269        Z_test[0:, sin_idx] = np.sin(test_projection, dtype=z_dtype)
270        
271    adata.uns['Z_train'] = Z_train*sq_i_d
272    adata.uns['Z_test'] = Z_test*sq_i_d
273
274    if 'sigma' not in adata.uns.keys():
275        adata.uns['sigma'] = np.array(sigma_list)
276
277    check_for_nan(adata)
278    check_for_inf(adata)
279
280    return adata

Function to calculate Z matrices for all groups in both training and testing data.

Parameters
  • adata (ad.AnnData): created by scmkl.create_adata with adata.uns.keys(): 'train_indices', and 'test_indices'.
  • n_features (int): Number of random feature to use when calculating Z; used for scalability.
  • batches (int): The number of batches to use for the distance calculation. This will average the result of batches distance calculations of batch_size randomly sampled cells. More batches will converge to population distance values at the cost of scalability.
  • batch_size (int): The number of cells to include per batch for distance calculations. Higher batch size will converge to population distance values at the cost of scalability. If batches*batch_size > num_training_cells, batch_size will be reduced to int(num_training_cells / batches).
Returns
  • adata (ad.AnnData): adata with Z matrices accessible with adata.uns['Z_train'] and adata.uns['Z_test'].
Examples
>>> adata = scmkl.estimate_sigma(adata)
>>> adata = scmkl.calculate_z(adata)
>>> adata.uns.keys()
dict_keys(['Z_train', 'Z_test', 'sigmas', 'train_indices', 
'test_indices'])