Best Python code snippet using grail_python
dr.py
Source:dr.py
1import numpy as np2import scipy3from sklearn.decomposition import FastICA, PCA, FactorAnalysis, KernelPCA, NMF4from sklearn.manifold import Isomap, MDS, LocallyLinearEmbedding, SpectralEmbedding, TSNE5from sklearn.cluster import KMeans67import umap8from openTSNE import TSNEEmbedding9from openTSNE import affinity10from openTSNE import initialization11import phate1213import scipy.spatial14import scipy.stats15from CytofDR.evaluation import EvaluationMetrics, PointClusterDistance1617import seaborn as sns18import matplotlib.pyplot as plt1920import _csv21import csv22import os23import warnings24from typing import Union, Optional, List, Dict, Any, Set2526METHODS: Dict[str, bool] = {"SAUCIE": True, "ZIFA": True, "GrandPrix": True}27 28try:29 import SAUCIE30except ImportError:31 METHODS["SAUCIE"] = False32 print("No 'saucie' implementation.")33 34try:35 from ZIFA import ZIFA36except ImportError:37 METHODS["ZIFA"] = False38 print("No 'ZIFA' implementation.")39 40try:41 from GrandPrix import GrandPrix42except ImportError:43 METHODS["GrandPrix"] = False44 print("No 'Grandprix' implementation.")45 46 47def _verbose(message: str, verbose:bool=True):48 if verbose:49 print(message)50 51 52class Reductions():53 """A class for reductions and their evaluation. 5455 This class is a convenient data class for storing and evaluaqting reductions.5657 :param reductions: A dictionary of reductions as indexed by their names.58 59 :Attributes:60 - **reductions**: A dictionary of reductions as indexed by names.61 - **names**: The names of the reductions.62 - **original_data**: The original space data before DR.63 - **original_labels**: Clusterings based on original space data.64 - **original_cell_types**: Cell types based on original space data. 65 - **embedding_data**: The embedding space reduction.66 - **embedding_labels**: Clusterings based on embedding space reduction.67 - **embedding_cell_types**: Cell types based on embedding space reduction. 68 - **comparison_data**: The comparison data (matched with original data in some way) for concordance analysis.69 - **comparison_cell_types**: Cell types based on comparison data.70 - **comparison_classes**: Common cell types between embedding and comparison data.71 - **evaluations**: The DR evaluation results, which are generated by the default evaluation method.72 - **custom_evaluations**: The custom DR evaluation results, which are added by the ``add_custom_evaluation_results()`` method.73 - **custom_evaluation_weights**: The weights for each metric in the ``custom_evaluations``. They do not distinguish between same metrics on different reductions.74 - **custom_evaluation_reverse_ranking**: Whether each metric in the ``custom_evaluations`` should be reverse ranked (i.e. smaller is better).75 """76 77 def __init__(self, reductions: Optional[Dict[str, "np.ndarray"]]=None):78 """Constructor method for Reductions.79 """80 self._reductions: Dict[str, "np.ndarray"]81 self.names: Set[str]82 if reductions is None:83 self._reductions = {}84 self.names = set()85 else:86 self._reductions = reductions87 self.names = set(self._reductions.keys())88 self.original_data: Optional["np.ndarray"] = None89 self.original_labels: Optional["np.ndarray"] = None90 self.original_cell_types: Optional["np.ndarray"] = None91 self.embedding_labels: Optional[Dict[str, "np.ndarray"]] = None92 self.embedding_cell_types: Optional[Dict[str, "np.ndarray"]] = None93 self.comparison_data: Optional["np.ndarray"] = None94 self.comparison_cell_types: Optional["np.ndarray"] = None95 self.comparison_classes: Optional[Union[str, List[str]]] = None96 97 self.evaluations: Dict[str, Any] = {}98 self.custom_evaluations: Dict[str, Any] = {"custom": {}}99 self.custom_evaluation_weights: Dict[str, Any] = {}100 self.custom_evaluation_reverse_ranking: Dict[str, Any] = {}101 102 103 def add_reduction(self, reduction: "np.ndarray", name: str, replace: bool=False):104 """Add a reduction embedding.105106 This method allows users to add additional embeddings.107108 :param reduction: The reduction array.109 :param name: The name of the reduction.110 :param replace: If the original name exists, whether to replace the original, defaults to False111 112 :raises ValueError: Reduction already exists but users choose not to replace the original.113 """114 if name in self.names and not replace:115 raise ValueError("Reduction already exists. Set 'replace' to True if replacement is intended.")116 self.reductions[name] = reduction117 self.names.add(name)118 119 120 def get_reduction(self, name: str):121 """Retrieve a reduction my name.122123 This method allows users to retrieve a reduction by name. This equivalent to running ``self.reductions[name]``.124125 :param name: The name of the reduction.126 """127 return self.reductions[name]128 129 130 def add_evaluation_metadata(self,131 original_data: Optional["np.ndarray"]=None,132 original_labels: Optional["np.ndarray"]=None,133 original_cell_types: Optional["np.ndarray"]=None,134 embedding_labels: Optional[Dict[str, "np.ndarray"]]=None,135 embedding_cell_types: Optional[Dict[str, "np.ndarray"]]=None,136 comparison_data: Optional["np.ndarray"]=None,137 comparison_cell_types: Optional["np.ndarray"]=None,138 comparison_classes: Optional[Union[str, List[str]]]=None,139 ):140 """Add supporting metadata for DR evaluation.141142 This method allows you to add metadata in the process of DR evaluation. They do not143 override existing metadata unless actual inputs are specified.144 145 :param original_data: The original space data before DR.146 :param original_labels: Clusterings based on original space data.147 :param original_cell_types: Cell types based on original space data. 148 :param embedding_data: The embedding space reduction.149 :param embedding_labels: Clusterings based on embedding space reduction.150 :param embedding_cell_types: Cell types based on embedding space reduction. 151 :param comparison_data: The comparison data (matched with original data in some way) for concordance analysis.152 :param comparison_cell_types: Cell types based on comparison data.153 :param comparison_classes: Common cell types between embedding and comparison data.154 """155 if original_data is not None:156 self.original_data = original_data157 if original_labels is not None:158 self.original_labels = original_labels159 if original_cell_types is not None:160 self.original_cell_types = original_cell_types161 if embedding_labels is not None:162 self.embedding_labels = embedding_labels163 if embedding_cell_types is not None:164 self.embedding_cell_types = embedding_cell_types165 if comparison_data is not None:166 self.comparison_data = comparison_data167 if comparison_cell_types is not None:168 self.comparison_cell_types = comparison_cell_types169 if comparison_classes is not None:170 self.comparison_classes = comparison_classes171 172 173 def cluster(self, n_clusters: int, cluster_data: bool = True, cluster_embedding: bool = True, **kwargs):174 """Cluster original_data and reductions.175176 This provides a convenient method to cluster `original_data` and embeddings using the177 ``KMeans`` method. The number of clusters must be manually specified.178179 :param n_clusters: The number of clusters180 :param cluster_data: Whether to cluster original data, defaults to True181 :param cluster_embedding: Whether to cluster embeddings, defaults to True182 :param kwargs: Keyword only arguments passed into ``sklearn.cluster.KMeans``.183 """184 185 if cluster_data and self.original_data is None:186 raise ValueError("'original_data' is missing: cannot cluster.")187 188 if cluster_embedding and len(self.reductions) == 0:189 raise ValueError("'reductions' is missing: cannot cluster.")190 191 if cluster_data:192 self.original_labels = KMeans(n_clusters=n_clusters, **kwargs).fit(self.original_data).labels_193 194 if cluster_embedding:195 self.embedding_labels = {}196 for e in self.reductions.keys():197 self.embedding_labels[e] = KMeans(n_clusters=n_clusters, **kwargs).fit(self.reductions[e]).labels_198 199 200 def evaluate(self,201 category: Union[str, List[str]],202 pwd_metric: str="PCD",203 k_neighbors: int=5,204 annoy_original_data_path: Optional[str]=None,205 auto_cluster: bool=True,206 n_clusters: int=20,207 verbose: bool=True): 208 """Evaluate DR Methods Using Default DR Evaluation Scheme.209210 This method ranks the DR methods based on any of the four default categories:211 ``global``, ``local``, ``downstream``, or ``concordance``. 212 213 :param category: The major evaluation category: ``global``, ``local``, ``downstream``, or ``concordance``.214 :param pwd_metric: The pairwise distance metric. Two options are "PCD" or "pairwise".215 PCD refers to Point Cluster Distance as implemented in this package; pairwise 216 is the traditional pairwise distance. For large datasets, PCD is recommended. Defaults to "PCD".217 :param k_neighbors: The number of neighbors to use for ``local`` metrics. Defaults to 5.218 :param annoy_original_data_path: The file path to an ANNOY object for original data.219 220 :raises ValueError: No reductions to evalate.221 :raises ValueError: Unsupported 'pwd_metric': 'PCD' or 'Pairwise' only.222 :raises ValueError: Evaluation needs 'original_data', 'original_labels', and 'embedding_labels' attributes.223 224225 .. note::226 227 This method required ``add_evaluation_metadata`` to run first. ``original_cell_types`` and228 ``embedding_cell_types`` are optional for the downstream category. For ``concordance``, if229 you wish to use clustering results for embedding and comparison files, set the appropriate230 clusterings to ``embedding_cell_types`` and ``comparison_cell_types``. 231 """232 233 if len(self.reductions) == 0:234 raise ValueError("No reductions to evalate. Add your reductions first.")235 if pwd_metric.lower() not in ["pcd", "pairwise"]:236 raise ValueError("Unsupported 'pwd_metric': 'PCD' or 'Pairwise' only.")237 238 self.evaluations = {}239 240 if isinstance(category, list):241 category = [c.lower() for c in category]242 else:243 category = [category.lower()]244 245 if self.original_labels is None and auto_cluster:246 self.cluster(n_clusters=n_clusters, cluster_embedding=False)247 if self.embedding_labels is None and auto_cluster:248 self.cluster(n_clusters=n_clusters, cluster_data=False)249 250 if self.original_labels is None or self.embedding_labels is None:251 message: str = "Evaluation needs 'original_labels', and 'embedding_labels' attributes. "252 message += "Use the 'auto_cluster' option or add your own lables with the 'add_evaluation_metadata()' method."253 raise ValueError(message)254 255 if self.original_data is None:256 raise ValueError("Evaluation needs 'original_data'. Please add it with the 'add_evaluation_metadata()' method.")257 258 e: str259 if "global" in category:260 _verbose("Evaluating global...", verbose=verbose)261 self.evaluations["global"] = {"spearman": {}, "emd": {}}262 data_distance: "np.ndarray"263 embedding_distance: Dict[str, "np.ndarray"] = {}264 265 if pwd_metric.lower() == "pcd":266 data_distance = PointClusterDistance(self.original_data, self.original_labels).fit(flatten=True)267 for e in self.reductions.keys():268 embedding_distance[e] = PointClusterDistance(self.reductions[e], self.original_labels).fit(flatten=True)269 270 else:271 data_distance = scipy.spatial.distance.pdist(self.original_data)272 for e in self.reductions.keys():273 embedding_distance[e] = scipy.spatial.distance.pdist(self.reductions[e], metric="euclidean")274 275 for e in self.reductions.keys():276 val: float = EvaluationMetrics.correlation(x=data_distance, y=embedding_distance[e], metric="Spearman")277 self.evaluations["global"]["spearman"][e] = val278 279 val: float = EvaluationMetrics.EMD(x=data_distance, y=embedding_distance[e])280 self.evaluations["global"]["emd"][e] = val281 282 if "local" in category:283 _verbose("Evaluating local...", verbose=verbose)284 assert self.original_labels is not None285 self.evaluations["local"] = {"knn": {}, "npe": {}}286 287 data_neighbors: "np.ndarray" = EvaluationMetrics.build_annoy(self.original_data, annoy_original_data_path, k_neighbors)288 for e in self.reductions.keys():289 embedding_neighbors: "np.ndarray" = EvaluationMetrics.build_annoy(self.reductions[e], None, k_neighbors)290 self.evaluations["local"]["npe"][e] = EvaluationMetrics.NPE(labels = self.original_labels, data_neighbors=data_neighbors, embedding_neighbors=embedding_neighbors)291 self.evaluations["local"]["knn"][e] = EvaluationMetrics.KNN(data_neighbors=data_neighbors, embedding_neighbors=embedding_neighbors)292 293 if "downstream" in category:294 _verbose("Evaluating downstream...", verbose=verbose)295 self.evaluations["downstream"] = {"cluster reconstruction: silhouette": {},296 "cluster reconstruction: DBI": {},297 "cluster reconstruction: CHI": {},298 "cluster reconstruction: RF": {},299 "cluster concordance: ARI": {},300 "cluster concordance: NMI": {},301 "cell type-clustering concordance: ARI": {},302 "cell type-clustering concordance: NMI": {}}303 for e in self.reductions.keys():304 self.evaluations["downstream"]["cluster reconstruction: silhouette"][e] = EvaluationMetrics.silhouette(embedding=self.reductions[e],305 labels=self.original_labels)306 self.evaluations["downstream"]["cluster reconstruction: DBI"][e] = EvaluationMetrics.davies_bouldin(embedding=self.reductions[e],307 labels=self.original_labels)308 self.evaluations["downstream"]["cluster reconstruction: CHI"][e] = EvaluationMetrics.calinski_harabasz(embedding=self.reductions[e],309 labels=self.original_labels)310 self.evaluations["downstream"]["cluster reconstruction: RF"][e] = EvaluationMetrics.random_forest(embedding=self.reductions[e],311 labels=self.original_labels)312 313 self.evaluations["downstream"]["cluster concordance: ARI"][e] = EvaluationMetrics.ARI(x_labels=self.original_labels,314 y_labels=self.embedding_labels[e])315 self.evaluations["downstream"]["cluster concordance: NMI"][e] = EvaluationMetrics.NMI(x_labels=self.original_labels,316 y_labels=self.embedding_labels[e])317 318 if self.original_cell_types is not None:319 self.evaluations["downstream"]["cell type-clustering concordance: ARI"][e] = EvaluationMetrics.ARI(x_labels=self.original_cell_types,320 y_labels=self.embedding_labels[e])321 self.evaluations["downstream"]["cell type-clustering concordance: NMI"][e] = EvaluationMetrics.NMI(x_labels=self.original_cell_types,322 y_labels=self.embedding_labels[e])323 else:324 warnings.warn("No 'original_sell_types': Cell type-clustering concordance is not evaluated.")325 326 if "concordance" in category:327 _verbose("Evaluating concordance...", verbose=verbose)328 assert self.embedding_cell_types is not None329 assert self.comparison_cell_types is not None330 assert self.original_cell_types is not None331 assert self.comparison_data is not None332 self.evaluations["concordance"] = {"cluster distance": {}, "emd": {}, "gating concordance: ARI": {}, "gating concordance: NMI": {}}333 for e in self.reductions.keys():334 self.evaluations["concordance"]["emd"][e] = EvaluationMetrics.embedding_concordance(self.reductions[e],335 self.embedding_cell_types[e],336 self.comparison_data,337 self.comparison_cell_types,338 self.comparison_classes,339 "emd")340 self.evaluations["concordance"]["cluster distance"][e] = EvaluationMetrics.embedding_concordance(self.reductions[e],341 self.embedding_cell_types[e],342 self.comparison_data,343 self.comparison_cell_types,344 self.comparison_classes,345 "cluster_distance")346 self.evaluations["concordance"]["gating concordance: ARI"][e] = EvaluationMetrics.ARI(x_labels=self.embedding_cell_types[e],347 y_labels=self.original_cell_types)348 self.evaluations["concordance"]["gating concordance: NMI"][e] = EvaluationMetrics.NMI(x_labels=self.embedding_cell_types[e],349 y_labels=self.original_cell_types)350 351352 def rank_dr_methods(self, tie_method: str="max"):353 """Rank DR Methods Using Default DR Evaluation.354355 Based on the results from the ``evaluate`` method, this method ranks the DR methods356 based on the categories chosen. All weighting schemes are consistent with the paper.357 Custom evaluation and weighting schemes are not supported in this case.358 359 :param tie_method: The method to deal with ties when ranking, defaults to "max".360 :type tie_method: str, optional361362 :return: A dictionary of DR methods and their final weighted ranks.363 """364 category_counter: int = 0365 overall_rank: np.ndarray = np.zeros(len(self.reductions))366 if "global" in self.evaluations.keys():367 category_counter += 1368 global_eval: np.ndarray = scipy.stats.rankdata(np.array(list(self.evaluations["global"]["spearman"].values())), method=tie_method)/2369 global_eval += scipy.stats.rankdata(-np.array(list(self.evaluations["global"]["emd"].values())), method=tie_method)/2370 overall_rank += global_eval371 372 if "local" in self.evaluations.keys():373 category_counter += 1374 local_eval: np.ndarray = scipy.stats.rankdata(np.array(list(self.evaluations["local"]["knn"].values())), method=tie_method)/2375 local_eval += scipy.stats.rankdata(-np.array(list(self.evaluations["local"]["npe"].values())), method=tie_method)/2376 overall_rank += local_eval377 378 if "downstream" in self.evaluations.keys():379 category_counter += 1380 cluster_reconstruction_eval: np.ndarray = scipy.stats.rankdata(np.array(list(self.evaluations["downstream"]["cluster reconstruction: RF"].values())), method=tie_method)/4381 cluster_reconstruction_eval += scipy.stats.rankdata(np.array(list(self.evaluations["downstream"]["cluster reconstruction: silhouette"].values())), method=tie_method)/4382 cluster_reconstruction_eval += scipy.stats.rankdata(-np.array(list(self.evaluations["downstream"]["cluster reconstruction: DBI"].values())), method=tie_method)/4383 cluster_reconstruction_eval += scipy.stats.rankdata(np.array(list(self.evaluations["downstream"]["cluster reconstruction: CHI"].values())), method=tie_method)/4384 385 cluster_concordance_eval: np.ndarray = scipy.stats.rankdata(np.array(list(self.evaluations["downstream"]["cluster concordance: ARI"].values())), method=tie_method)/2386 cluster_concordance_eval += scipy.stats.rankdata(np.array(list(self.evaluations["downstream"]["cluster concordance: NMI"].values())), method=tie_method)/2387 388 if len(self.evaluations["downstream"]["cell type-clustering concordance: ARI"]) > 0:389 type_cluster_concordance_eval: np.ndarray = scipy.stats.rankdata(np.array(list(self.evaluations["downstream"]["cell type-clustering concordance: ARI"].values())), method=tie_method)/2390 type_cluster_concordance_eval += scipy.stats.rankdata(np.array(list(self.evaluations["downstream"]["cell type-clustering concordance: ARI"].values())), method=tie_method)/2391 downstream_eval: np.ndarray = (cluster_reconstruction_eval + cluster_concordance_eval + type_cluster_concordance_eval)/3392 else:393 downstream_eval: np.ndarray = (cluster_reconstruction_eval + cluster_concordance_eval)/2394 overall_rank += downstream_eval395 396 if "concordance" in self.evaluations.keys():397 category_counter += 1398 concordance_eval: np.ndarray = scipy.stats.rankdata(-np.array(list(self.evaluations["concordance"]["cluster distance"].values())), method=tie_method)/3399 concordance_eval += scipy.stats.rankdata(-np.array(list(self.evaluations["concordance"]["emd"].values())), method=tie_method)/3400 concordance_eval += scipy.stats.rankdata(np.array(list(self.evaluations["concordance"]["gating concordance: ARI"].values())), method=tie_method)/6401 concordance_eval += scipy.stats.rankdata(np.array(list(self.evaluations["concordance"]["gating concordance: NMI"].values())), method=tie_method)/6402 overall_rank += concordance_eval403 404 overall_rank = overall_rank/category_counter405 return dict(zip(self.reductions.keys(), list(overall_rank)))406 407 408 def add_custom_evaluation_result(self, metric_name: str, reduction_name: str, value: float, weight: Optional[float]=None, reverse_ranking: bool=False):409 """Add custom evaluation result.410411 This method allows you work with custom evaluation metrics. Instead of relying on the412 builtin schemes, you can add any metrics you would like. This offers great flexibility413 by allowing you to use metrics not included in the ``CytofDR`` package. However, the414 downside is that you have to run these metrics manually in your workflow. This method415 simply stores the results in the object so that the DR methods can automatically ranked.416 417 .. note::418419 This method does not distinguish between metrics' weight and revese_ranking properties420 across different embeddings. For example, if you add the same metrics for ``umap`` and421 ``tsne``, they should have the same ``weight`` and ``reverse_ranking`` properties, with the422 only difference being ``value``. Otherwise, latter properties will overwrite previous ones.423 424 :param metric_name: The name of the custom metric.425 :type metric_name: str426 :param reduction_name: The name pf the reduction on which the metric is run.427 :type reduction_name: str428 :param value: The value of the evaluation metric.429 :type value: float430 :param weight: The weight of the metric in the overall DR ranking scheme, defaults to None431 :type weight: Optional[float], optional432 :param reverse_ranking: Whether it should be reverse ranked, defaults to False. If ``True``, this means that smaller values are better.433 :type reverse_ranking: bool, optional434 """435 if metric_name not in self.custom_evaluations["custom"].keys():436 self.custom_evaluations["custom"][metric_name] = {}437 self.custom_evaluations["custom"][metric_name][reduction_name] = value438 439 if weight is not None:440 self.custom_evaluation_weights[metric_name] = weight441 self.custom_evaluation_reverse_ranking[metric_name] = reverse_ranking 442443444 def rank_dr_methods_custom(self, tie_method: str="max") -> Dict[str, float]:445 """Rank DR methods according to custom evaluation metrics.446447 This is the custom version the ``rank_dr_methods`` method. It ranks all the448 DR methods using the metrics added through the ``add_custom_evaluation_result``449 method and their corresonding weights.450451 :param tie_method: The method to deal with ties when ranking, defaults to "max"452 :type tie_method: str, optional453 :raises RuntimeError: Missing metrics for certain DR methods.454 :return: A dictionary of methods with their weighted rankings.455 :rtype: Dict[str, float]456 """457 458 custom_evaluations: Dict[str, Any] = {}459 all_dr_methods: np.ndarray = np.array(list(self.names))460 for metric in self.custom_evaluations["custom"].keys():461 method_check: np.ndarray = np.isin(all_dr_methods, np.array(list(self.custom_evaluations["custom"][metric].keys())))462 if not np.all(method_check):463 raise RuntimeError(f"Missing metrics for the following methods: {str(all_dr_methods[np.where(np.invert(method_check))])}")464 custom_evaluations[metric] = dict(sorted(self.custom_evaluations["custom"][metric].items()))465 466 eval_ranks: np.ndarray = np.zeros((all_dr_methods.shape[0],))467 for metric in custom_evaluations.keys():468 rev: int = -1 if self.custom_evaluation_reverse_ranking[metric] else 1469 weight: float = self.custom_evaluation_weights[metric] if len(self.custom_evaluation_weights) != 0 else 1/len(custom_evaluations.keys())470 eval_ranks += scipy.stats.rankdata(rev*np.array(list(custom_evaluations[metric].values())), method=tie_method)*weight471 472 return dict(zip(custom_evaluations[list(custom_evaluations)[0]].keys(), list(eval_ranks)))473 474 475 def plot_reduction(self, name: str, save_path: str, style: str="darkgrid", hue: Optional["np.ndarray"]=None, **kwargs):476 """Draw embedding using a scatter plot.477478 This method generates a scatter plot for reductions in the class. 479 480 :param name: The name of the reduction.481 :param save_path: The path to save the plot.482 :param stype: The plot style, defaults to "darkgrid".483 :param hue: Labels used to color the points.484 :param kwargs: Keyword arguments passed into the ``sns.scatterplot`` method.485 486 .. note:: Live viewing is not supported by this method.487 """488 plt.figure()489 sns.set_style(style)490 sns_plot = sns.scatterplot(x=self.reductions[name][:,0], y=self.reductions[name][:,1], hue=hue, **kwargs)491 fig = sns_plot.get_figure()492 fig.savefig(save_path)493 plt.clf()494 495 496 def save_reduction(self, name: str, path: str, overwrite: bool=False, delimiter: str="\t", **kwargs):497 """Save a DR embeddings to disk.498499 This method saves a specific reduction to a disk. The file is presumed500 to be a plain text file. All additional arguments are passed to ``np.savetxt``.501502 :param name: The name of the embedding to be saved as the key stored in the ``reductions`` dictionary.503 :type name: str504 :param path: The path to save the 505 :type path:506 :param overwrite: Whether to overwrite existing files, defaults to False507 :type overwrite: bool, optional508 :param delimiter: The delimiter used, defaults to "\t"509 :type delimiter: str, optional510 """511 if not overwrite and os.path.exists(path):512 raise FileExistsError()513 np.savetxt(path, self.reductions[name], delimiter=delimiter, **kwargs)514 515 516 def save_all_reductions(self, save_dir: str, overwrite: bool=False, delimiter: str="\t", **kwargs):517 """Save all DR embeddings to a specified directory.518519 This method saves all reductions to a specified directory. All files520 are plain text files with their embedding names as their names. All521 additional arguments are passed to ``np.savetxt``.522523 :param save_dir: The directory path to save all the embeddings.524 :type save_dir: str525 :param overwrite: Whether to overwrite existing files, defaults to False526 :type overwrite: bool, optional527 :param delimiter: The delimiter used, defaults to "\t"528 :type delimiter: str, optional529 """530 for method in self.names:531 path = save_dir + method + ".txt"532 self.save_reduction(method, path, overwrite, delimiter, **kwargs)533 534 535 def save_evaluations(self, path: str, overwrite: bool=False):536 """Save DR evaluation results.537538 This method saves all the results of DR evaluation in a539 csv.540541 :param path: The path to the file.542 :type path: str543 :param overwrite: Whether to overwrite existing file, defaults to False544 :type overwrite: bool, optional545 :raises AttributeError: There is no ``evaluations`` attribute. Need to run ``evaluate`` first.546 :raises FileExistsError: The file already exists and ``overwrite`` is set to false.547 """548 if len(self.evaluations) == 0:549 raise AttributeError("No 'evaluations' values found. Run the 'evaluate()' method first.")550 if os.path.exists(path) and not overwrite:551 raise FileExistsError()552 553 with open(path, "w") as f:554 w: "_csv._writer" = csv.writer(f)555 w.writerow(["Category", "Metric", "Method", "Value"])556 557 category: str558 for category in self.evaluations.keys():559 for metric in self.evaluations[category].keys():560 for method in self.evaluations[category][metric].keys():561 w.writerow([category, metric, method, self.evaluations[category][metric][method]])562 563 564 def __str__(self) -> str:565 return f"A 'Reductions' object with {', '.join(self.names)}."566 567 568 def __getitem__(self, name: str) -> np.ndarray:569 """Get a specific embedding from the ``Reductions`` object.570571 This method implements the bracket notation to access reductions. This is572 equivalent to ``get_reduction()``.573574 :param name: The name of the reduction.575 :type name: str576 :return: A numpy array of the reduction.577 :rtype: np.ndarray578 """579 return self.reductions[name]580 581 582 def __setitem__(self, name: str, reduction: "np.ndarray"):583 """To set reductions to the ``Reductions`` object.584585 This method implements the bracket notation to add reductions586 to the object. This is largely the same as the ``add_reduction()``587 method. The difference is that here there is no overwrite588 protection, but the ``add_reduction()`` method defaults to589 raise an error instead of replacing existing embeddings.590591 :param name: The name of the reduction.592 :type name: str593 :param reduction: A numpy array of the reduction.594 :type reduction: np.ndarray595 """596 self.add_reduction(reduction, name, replace=True)597 598 599 def __delitem__(self, name: str):600 del self.reductions[name]601 self.names = set(self.reductions.keys())602 603 604 @property605 def reductions(self) -> Dict[str, "np.ndarray"]:606 return self._reductions607 608 609 @reductions.setter610 def reductions(self, reductions: Dict[str, "np.ndarray"]):611 self._reductions = reductions612 self.names = set(reductions.keys())613 614 615 616class LinearMethods():617 """Linear DR Methods618 619 This class contains static methods of a group of Linear DR Methods. If620 available, the sklearn implementation is used. All keyword arguments are621 passed directly to the method itself to allow for flexibility.622 """623 624 @staticmethod625 def PCA(data: "np.ndarray",626 out_dims: int=2,627 **kwargs628 ) -> "np.ndarray":629 """Scikit-Learn Principal Component Analysis (PCA)630631 This method uses the Sklearn's standard PCA.632 633 :param data: The input high-dimensional array.634 :param out_dims: The number of dimensions of the output, defaults to 2.635636 :return: The low-dimensional embedding.637 """638 return PCA(n_components=out_dims, **kwargs).fit_transform(data)639 640 641 @staticmethod642 def ICA(data: "np.ndarray",643 out_dims: int=2,644 **kwargs) -> "np.ndarray": 645 """Scikit-Learn Independent Component Analysis (ICA)646647 This method uses the SKlearn's FastICA implementation of ICA.648 649 :param data: The input high-dimensional array.650 :param out_dims: The number of dimensions of the output, defaults to 2.651652 :return: The low-dimensional embedding.653 """654 return FastICA(n_components=out_dims, **kwargs).fit_transform(data) #type: ignore655 656 657 @staticmethod658 def ZIFA(data: "np.ndarray",659 out_dims: int=2,660 **kwargs) -> "np.ndarray":661 """Zero-Inflated Factor Analysis (ZIFA)662663 This method implements ZIFA as developed by Pierson & Yau (2015).664 665 :param data: The input high-dimensional array.666 :param out_dims: The number of dimensions of the output, defaults to 2.667668 :return: The low-dimensional embedding.669 """670 # Fix all-zero columns671 nonzero_col: List[int] = []672 col: int673 for col in range(data.shape[1]):674 if not np.all(data[:,col]==0):675 nonzero_col.append(col)676 data = data[:, nonzero_col]677 678 z: "np.ndarray"679 z, _ = ZIFA.fitModel(data, out_dims, **kwargs)680 681 return z682 683 684 @staticmethod685 def FA(data: "np.ndarray",686 out_dims: int=2,687 **kwargs) -> "np.ndarray":688 """Scikit-Learn Factor Analysis (FA)689690 This method uses the SKlearn's FA implementation.691 692 :param data: The input high-dimensional array.693 :param out_dims: The number of dimensions of the output, defaults to 2.694695 :return: The low-dimensional embedding.696 """697 return FactorAnalysis(n_components=out_dims, **kwargs).fit_transform(data)698 699 700 @staticmethod701 def NMF(data: "np.ndarray",702 out_dims: int=2,703 **kwargs) -> "np.ndarray":704 """Scikit-Learn Nonnegative Matrix Factorization (NMF)705706 This method uses the SKlearn's NMF implementation.707 708 :param data: The input high-dimensional array.709 :param out_dims: The number of dimensions of the output, defaults to 2.710711 :return: The low-dimensional embedding.712 """713 return NMF(n_components=out_dims, init = "nndsvd", **kwargs).fit_transform(data)714 715 716class NonLinearMethods():717 """NonLinear DR Methods.718719 This class contains static methods of a group of NonLinear DR Methods, except for tSNE.720 """721 722 @staticmethod723 def MDS(data: "np.ndarray",724 out_dims: int=2,725 n_jobs: int=-1,726 **kwargs) -> "np.ndarray":727 """Scikit-Learn Multi-Dimensional Scaling (MDS)728729 This method uses the SKlearn's MDS implementation.730 731 :param data: The input high-dimensional array.732 :param out_dims: The number of dimensions of the output, defaults to 2.733 :param n_jobs: The number of jobs to run concurrantly, defaults to -1.734735 :return: The low-dimensional embedding.736 """737 return MDS(n_components=out_dims,738 n_jobs=n_jobs,739 **kwargs740 ).fit_transform(data)741 742 743 @staticmethod744 def UMAP(data: "np.ndarray",745 out_dims: int=2,746 n_jobs: int=-1,747 **kwargs748 ) -> "np.ndarray":749 """UMAP750751 This method uses the UMAP package's UMAP implementation.752 753 :param data: The input high-dimensional array.754 :param out_dims: The number of dimensions of the output, defaults to 2.755 :param n_jobs: The number of jobs to run concurrantly, defaults to -1.756757 :return: The low-dimensional embedding.758 """759 return umap.UMAP(n_components=out_dims,760 n_jobs=n_jobs,761 **kwargs762 ).fit_transform(data) #type: ignore763 764 765 @staticmethod766 def SAUCIE(data: "np.ndarray",767 steps: int=1000,768 batch_size: int=256,769 **kwargs770 ) -> "np.ndarray":771 """SAUCIE772773 This method is a wrapper for SAUCIE package's SAUCIE model. Specifically,774 dimension reduction is of interest. Here, all keyword arguments are passed775 into the ``SAUCIE.SAUCIE`` method. The training parameters ``steps`` and 776 ``batch_size`` are directly exposed in this wrapper.777 778 :param data: The input high-dimensional array.779 :param steps: The number of training steps to use, defaults to 1000.780 :param batch_size: The batch size for training, defaults to 256.781782 :return: The low-dimensional embedding.783 """784 785 saucie: "SAUCIE.model.SAUCIE" = SAUCIE.SAUCIE(data.shape[1], **kwargs)786 train: "SAUCIE.loader.Loader" = SAUCIE.Loader(data, shuffle=True)787 saucie.train(train, steps=steps, batch_size=batch_size)788 789 eval: "SAUCIE.loader.Loader" = SAUCIE.Loader(data, shuffle=False)790 embedding: "np.ndarray" = saucie.get_embedding(eval) #type: ignore791 792 return embedding793 794 795 @staticmethod796 def isomap(data: "np.ndarray",797 out_dims: int=2,798 transform: Optional["np.ndarray"]=None,799 n_jobs: int=-1,800 **kwargs801 ) -> "np.ndarray":802 """Scikit-Learn Isomap803804 This method is a wrapper for sklearn's implementation of Isomap.805 806 :param data: The input high-dimensional array.807 :param out_dims: The number of dimensions of the output, defaults to 2.808 :param transform: The array to transform with the trained model.809 :param n_jobs: The number of threads to use.810811 :return: The low-dimensional embedding.812 """813 814 if transform is None:815 embedding: "np.ndarray" = Isomap(n_components=out_dims,816 n_jobs=n_jobs,817 **kwargs).fit_transform(data)818 else:819 embedding: "np.ndarray" = Isomap(n_components=out_dims,820 n_jobs=n_jobs,821 **kwargs).fit(data).transform(transform)822 823 return embedding824 825 826 @staticmethod827 def LLE(data: "np.ndarray",828 out_dims: int=2,829 transform: Optional["np.ndarray"]=None,830 n_jobs: int=-1,831 **kwargs832 ) -> "np.ndarray": 833 """Scikit-Learn Locally Linear Embedding (LLE)834835 This method is a wrapper for sklearn's implementation of LLE.836 837 :param data: The input high-dimensional array.838 :param out_dims: The number of dimensions of the output, defaults to 2.839 :param transform: The array to transform with the trained model.840841 :return: The low-dimensional embedding.842 """843 844 if transform is None:845 embedding: "np.ndarray" = LocallyLinearEmbedding(n_components=out_dims,846 n_jobs=n_jobs,847 **kwargs).fit_transform(data)848 else:849 embedding: "np.ndarray" = LocallyLinearEmbedding(n_components=out_dims,850 n_jobs=n_jobs,851 **kwargs).fit(data).transform(transform)852 853 return embedding854 855 856 @staticmethod857 def kernelPCA(data: "np.ndarray",858 out_dims: int=2,859 kernel: str="poly",860 n_jobs: int=-1,861 **kwargs862 ) -> "np.ndarray": 863 """Scikit-Learn Kernel PCA864865 This method is a wrapper for sklearn's implementation of kernel PCA.866 867 :param data: The input high-dimensional array.868 :param out_dims: The number of dimensions of the output, defaults to 2.869 :param kernel: The kernel to use: "poly," "linear," "rbf," "sigmoid," or "cosine."870 :param n_jobs: The number of jobs to run concurrantly, defaults to -1.871872 :return: The low-dimensional embedding.873 """874 return KernelPCA(n_components=out_dims,875 kernel=kernel,876 n_jobs=n_jobs,877 **kwargs).fit_transform(data)878879880 @staticmethod881 def spectral(data: "np.ndarray",882 out_dims: int=2,883 n_jobs: int=-1,884 **kwargs):885 """Scikit-Learn Spectral Embedding886887 This method is a wrapper for sklearn's implementation of spectral embedding.888 889 :param data: The input high-dimensional array.890 :param out_dims: The number of dimensions of the output, defaults to 2.891 :param n_jobs: The number of jobs to run concurrantly, defaults to -1.892893 :return: The low-dimensional embedding.894 """895 return SpectralEmbedding(n_components=out_dims,896 n_jobs=n_jobs,897 **kwargs).fit_transform(data)898899 900 @staticmethod901 def phate(data: "np.ndarray",902 out_dims: int=2,903 n_jobs: int=-1,904 **kwargs):905 """PHATE906907 This method is a wrapper for PHATE.908 909 :param data: The input high-dimensional array.910 :param out_dims: The number of dimensions of the output, defaults to 2.911 :param n_jobs: The number of jobs to run concurrantly, defaults to -1.912913 :return: The low-dimensional embedding.914 """915 return phate.PHATE(n_components=out_dims,916 n_jobs=n_jobs,917 **kwargs).fit_transform(data)918 919 920 @staticmethod921 def grandprix(data: "np.ndarray",922 out_dims: int=2,923 **kwargs):924 """GrandPrix925926 This method is a wrapper for GrandPrix.927 928 :param data: The input high-dimensional array.929 :param out_dims: The number of dimensions of the output, defaults to 2.930931 :return: The low-dimensional embedding.932 """933 return GrandPrix.fit_model(data = data, n_latent_dims = out_dims, **kwargs)[0]934 935 936 @staticmethod937 def sklearn_tsne(data: "np.ndarray",938 out_dims: int=2,939 n_jobs: int=-1,940 **kwargs941 )-> "np.ndarray":942 """Scikit-Learn t-SNE943944 This method uses the Scikit-learn implementation of t-SNE. It supports both945 traditional and BH t-SNE with more control of variables.946 947 :param data: The input high-dimensional array.948 :param out_dims: The number of dimensions of the output, defaults to 2.949 :param n_jobs: The number of jobs to run concurrantly, defaults to -1.950951 :return: The low-dimensional embedding.952 """953 return TSNE(n_components=out_dims,954 n_jobs=n_jobs,955 **kwargs956 ).fit_transform(data)957 958 959 @staticmethod960 def open_tsne(data: "np.ndarray",961 out_dims: int=2,962 perp: Union[List[int],int]=30,963 learning_rate: Union[str, float]="auto",964 early_exaggeration_iter: int=250,965 early_exaggeration: float=12,966 max_iter: int=500,967 metric: str="euclidean",968 dof: int=1,969 theta: float=0.5, 970 init: Union["np.ndarray", str]="pca",971 negative_gradient_method: str="fft",972 n_jobs: int=-1973 ) -> "np.ndarray":974 """openTSNE implementation of FIt-SNE975976 This is the Python implementation of FIt-SNE through the ``openTSNE`` package. Its implementation977 is based on research from Linderman et al. (2019). This is the default recommended implementation.978 To allow for flexibility and avoid confusion, common parameters are directly exposed without allowing979 additional keyword arguments. 980 981 :param data: The input high-dimensional array.982 :param out_dims: The number of dimensions of the output, defaults to 2.983 :param perp: Perplexity. The default is set to 30. Tradition is between 30 and 50.984 This also supports multiple perplexities with a list, defaults to 30.985 :param learning_rate: The learning rate used during gradient descent, defaults to "auto".986 :param early_exaggeration_iter: Number of early exaggeration iterations, defaults to 250.987 :param early_exaggeration: Early exaggeration factor, defaults to 12.988 :param max_iter: Maximum number of iterations to optimize, defaults to 500989 :param dof: T-distribution degree of freedom, defaults to "euclidean"990 :param theta: The speed/accuracy trade-off, defaults to 0.5.991 :param init: Method of initialiazation. 'random', 'pca', 'spectral', or array, defaults to "pca"992 :negative_gradient_method: Whether to use "bh" or "fft" tSNE, defaults to "fft".993 :param n_jobs: The number of jobs to run concurrantly, defaults to -1.994 995 :return: The low-dimensional embedding.996 """997 998 n_iter: int = max_iter - early_exaggeration_iter999 affinities_array: Union["affinity.PerplexityBasedNN", "affinity.Multiscale"]1000 init_array: "np.ndarray" = np.empty((data.shape[0], out_dims))1001 1002 if isinstance(perp, list) and len(perp) > 1:1003 affinities_array = affinity.Multiscale(data=data,1004 perplexities=perp,1005 metric=metric,1006 n_jobs=n_jobs,1007 verbose=True)1008 else:1009 perp = perp[0] if isinstance(perp, list) else perp1010 affinities_array = affinity.PerplexityBasedNN(data=data,1011 perplexity=perp,1012 metric=metric,1013 n_jobs=n_jobs,1014 verbose=True)10151016 if isinstance(init, str):1017 if init == "pca":1018 init_array = initialization.pca(data, n_components=out_dims)1019 elif init == "spectral":1020 init_array = initialization.spectral(affinities_array.P, n_components=out_dims)1021 else:1022 init_array = initialization.random(data, n_components=out_dims)1023 else:1024 init_array = init1025 1026 embedding: "np.ndarray" = TSNEEmbedding(embedding=init_array,1027 affinities=affinities_array,1028 negative_gradient_method=negative_gradient_method,1029 learning_rate=learning_rate,1030 theta=theta,1031 dof=dof,1032 n_jobs=n_jobs,1033 verbose=True)1034 # Early exaggeration1035 embedding.optimize(n_iter=early_exaggeration_iter,1036 inplace=True,1037 exaggeration=early_exaggeration,1038 momentum=0.5)1039 # Regular GD1040 embedding.optimize(n_iter=n_iter,1041 inplace=True,1042 momentum=0.8)1043 1044 return embedding 1045 10461047def run_dr_methods(data: "np.ndarray",1048 methods: Union[str, List[str]]="all",1049 out_dims: int=2,1050 transform: Optional["np.ndarray"]=None,1051 n_jobs: int=-1,1052 verbose: bool=True,1053 suppress_error_msg: bool=False1054 ) -> "Reductions":1055 """Run dimension reduction methods.10561057 This is a one-size-fits-all dispatcher that runs all supported methods in the module. It1058 supports running multiple methods at the same time at the sacrifice of some more1059 granular control of parameters. If you would like more customization, run each method1060 indicidually instead.1061 1062 :param data: The input high-dimensional array.1063 :param methods: DR methods to run (not case sensitive).1064 :param out_dims: Output dimension of DR.1065 :param transform: An array to transform after training on the traning set.1066 :param n_jobs: The number of jobs to run when applicable, defaults to -1.1067 :param verbose: Whether to print out progress, defaults to ``True``.1068 :param suppress_error_msg: Whether to suppress error messages print outs, defaults to ``False``.10691070 :return: A Reductions object with all dimension reductions.1071 """1072 1073 if not isinstance(methods, list):1074 methods = [methods]1075 methods = [each_method.lower() for each_method in methods]1076 1077 if "all" in methods:1078 methods = ["pca", "ica", "umap", "sklearn_tsne", "open_tsne", "fa", "isomap", "mds", "lle",1079 "kpca_poly", "kpca_rbf", "phate", "nmf", "spectral"]1080 if METHODS["SAUCIE"]:1081 methods.append("saucie")1082 if METHODS["ZIFA"]:1083 methods.append("zifa")1084 if METHODS["GrandPrix"]:1085 methods.append("grandprix")1086 1087 reductions: Reductions = Reductions()1088 reductions.add_evaluation_metadata(original_data=data)1089 1090 if "pca" in methods:1091 try:1092 _verbose("Running PCA", verbose=verbose)1093 reductions.add_reduction(LinearMethods.PCA(data, out_dims=out_dims), "PCA")1094 except Exception as method_error:1095 _verbose(str(method_error), verbose=not suppress_error_msg)1096 1097 if "ica" in methods:1098 try:1099 _verbose("Running ICA", verbose=verbose)1100 reductions.add_reduction(LinearMethods.ICA(data, out_dims=out_dims), "ICA") 1101 except Exception as method_error:1102 _verbose(str(method_error), verbose=not suppress_error_msg)1103 1104 if "umap" in methods:1105 try:1106 _verbose("Running UMAP", verbose=verbose) 1107 reductions.add_reduction(NonLinearMethods.UMAP(data, out_dims=out_dims, n_jobs=n_jobs), "UMAP")1108 except Exception as method_error:1109 _verbose(str(method_error), verbose=not suppress_error_msg)1110 1111 if "saucie" in methods:1112 try:1113 _verbose("Running SAUCIE", verbose=verbose)1114 reductions.add_reduction(NonLinearMethods.SAUCIE(data), "SAUCIE")1115 except Exception as method_error:1116 _verbose(str(method_error), verbose=not suppress_error_msg)1117 1118 # sklearn BH1119 if "sklearn_tsne" in methods:1120 try:1121 _verbose("Running sklearn_tsne", verbose=verbose)1122 reductions.add_reduction(NonLinearMethods.sklearn_tsne(data, out_dims=out_dims, n_jobs=n_jobs), "sklearn_tsne")1123 except Exception as method_error:1124 _verbose(str(method_error), verbose=not suppress_error_msg)1125 1126 if "open_tsne" in methods:1127 try:1128 _verbose("Running open_tsne", verbose=verbose)1129 reductions.add_reduction(NonLinearMethods.open_tsne(data, out_dims=out_dims, n_jobs=n_jobs), "open_tsne")1130 except Exception as method_error:1131 _verbose(str(method_error), verbose=not suppress_error_msg)1132 1133 if "zifa" in methods:1134 try:1135 _verbose("Running ZIFA", verbose=verbose)1136 reductions.add_reduction(LinearMethods.ZIFA(data, out_dims=out_dims), "ZIFA")1137 except Exception as method_error:1138 _verbose(str(method_error), verbose=not suppress_error_msg)1139 1140 if "fa" in methods:1141 try:1142 _verbose("Running FA", verbose=verbose)1143 reductions.add_reduction(LinearMethods.FA(data, out_dims=out_dims), "FA")1144 except Exception as method_error:1145 _verbose(str(method_error), verbose=not suppress_error_msg)1146 1147 if "isomap" in methods:1148 try:1149 _verbose("Running Isomap", verbose=verbose)1150 reductions.add_reduction(NonLinearMethods.isomap(data, out_dims=out_dims,transform=transform, n_jobs=n_jobs), "Isomap")1151 except Exception as method_error:1152 _verbose(str(method_error), verbose=not suppress_error_msg)1153 1154 if "mds" in methods:1155 try:1156 _verbose("Running MDS", verbose=verbose)1157 reductions.add_reduction(NonLinearMethods.MDS(data, out_dims=out_dims, n_jobs=n_jobs), "MDS")1158 except Exception as method_error:1159 _verbose(str(method_error), verbose=not suppress_error_msg)1160 1161 if "lle" in methods:1162 try:1163 _verbose("Running LLE", verbose=verbose)1164 reductions.add_reduction(NonLinearMethods.LLE(data, out_dims=out_dims, transform=transform, n_jobs=n_jobs), "LLE")1165 except Exception as method_error:1166 _verbose(str(method_error), verbose=not suppress_error_msg)1167 1168 if "spectral" in methods:1169 try:1170 _verbose("Running Spectral", verbose=verbose)1171 reductions.add_reduction(NonLinearMethods.spectral(data, out_dims=out_dims, n_jobs=n_jobs), "Spectral")1172 except Exception as method_error:1173 _verbose(str(method_error), verbose=not suppress_error_msg)1174 1175 if "kpca_poly" in methods:1176 try:1177 _verbose("Running kpca_poly", verbose=verbose)1178 reductions.add_reduction(NonLinearMethods.kernelPCA(data, out_dims=out_dims, kernel="poly", n_jobs=n_jobs), "kpca_poly")1179 except Exception as method_error:1180 _verbose(str(method_error), verbose=not suppress_error_msg)1181 1182 if "kpca_rbf" in methods:1183 try:1184 _verbose("Running kpca_rbf", verbose=verbose)1185 reductions.add_reduction(NonLinearMethods.kernelPCA(data, out_dims=out_dims, kernel="rbf", n_jobs=n_jobs), "kpca_rbf")1186 except Exception as method_error:1187 _verbose(str(method_error), verbose=not suppress_error_msg)1188 1189 if "phate" in methods:1190 try:1191 _verbose("Running PHATE", verbose=verbose)1192 reductions.add_reduction(NonLinearMethods.phate(data, out_dims=out_dims, n_jobs=n_jobs), "PHATE")1193 except Exception as method_error:1194 _verbose(str(method_error), verbose=not suppress_error_msg)1195 1196 if "nmf" in methods:1197 try:1198 _verbose("Running nmf", verbose=verbose)1199 reductions.add_reduction(LinearMethods.NMF(data, out_dims=out_dims), "NMF")1200 except Exception as method_error:1201 _verbose(str(method_error), verbose=not suppress_error_msg)1202 1203 if "grandprix" in methods:1204 try:1205 _verbose("Running grandprix", verbose=verbose)1206 reductions.add_reduction(NonLinearMethods.grandprix(data, out_dims=out_dims), "GrandPrix")1207 except Exception as method_error:1208 _verbose(str(method_error), verbose=not suppress_error_msg)1209
...
restful.py
Source:restful.py
...38 """39 return result(code=HttpCode.un_auth_error, message=message, data=data)404142def method_error(message='', data=None):43 """44 æ¹æ³é误45 """46 return result(code=HttpCode.method_error, message=message, data=data)474849def server_error(message='', data=None):50 """51 æå¡å¨å
é¨é误52 """
...
json_status.py
Source:json_status.py
...16 return result(Code.params_error, message=message, data=data)17def un_auth_error(message='', data=None):18 """æéé误"""19 return result(code=Code.un_auth_error, message=message, data=data)20def method_error(message='', data=None):21 """æ¹æ³é误"""22 return result(code=Code.method_error, message=message, data=data)23def server_error(message='', data=None):24 """æå¡å¨å
é¨é误"""...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!