gsMap 1.70__py3-none-any.whl → 1.71.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gsMap/GNN/__init__.py +0 -0
- gsMap/GNN/adjacency_matrix.py +75 -75
- gsMap/GNN/model.py +90 -89
- gsMap/GNN/train.py +0 -0
- gsMap/__init__.py +5 -5
- gsMap/__main__.py +2 -2
- gsMap/cauchy_combination_test.py +141 -141
- gsMap/config.py +805 -805
- gsMap/diagnosis.py +273 -273
- gsMap/find_latent_representation.py +133 -133
- gsMap/format_sumstats.py +407 -407
- gsMap/generate_ldscore.py +618 -618
- gsMap/latent_to_gene.py +234 -234
- gsMap/main.py +31 -31
- gsMap/report.py +160 -160
- gsMap/run_all_mode.py +194 -194
- gsMap/setup.py +0 -0
- gsMap/spatial_ldsc_multiple_sumstats.py +380 -380
- gsMap/templates/report_template.html +198 -198
- gsMap/utils/__init__.py +0 -0
- gsMap/utils/generate_r2_matrix.py +735 -735
- gsMap/utils/jackknife.py +514 -514
- gsMap/utils/make_annotations.py +518 -518
- gsMap/utils/manhattan_plot.py +639 -639
- gsMap/utils/regression_read.py +294 -294
- gsMap/visualize.py +198 -198
- {gsmap-1.70.dist-info → gsmap-1.71.1.dist-info}/LICENSE +21 -21
- {gsmap-1.70.dist-info → gsmap-1.71.1.dist-info}/METADATA +2 -2
- gsmap-1.71.1.dist-info/RECORD +31 -0
- gsmap-1.70.dist-info/RECORD +0 -31
- {gsmap-1.70.dist-info → gsmap-1.71.1.dist-info}/WHEEL +0 -0
- {gsmap-1.70.dist-info → gsmap-1.71.1.dist-info}/entry_points.txt +0 -0
gsMap/config.py
CHANGED
@@ -1,805 +1,805 @@
|
|
1
|
-
import sys
|
2
|
-
import argparse
|
3
|
-
import logging
|
4
|
-
from collections import OrderedDict, namedtuple
|
5
|
-
from dataclasses import dataclass
|
6
|
-
from pathlib import Path
|
7
|
-
from pprint import pprint
|
8
|
-
from typing import Callable
|
9
|
-
from typing import Union, Literal, Tuple, Optional, List
|
10
|
-
from functools import wraps
|
11
|
-
import pyfiglet
|
12
|
-
|
13
|
-
from gsMap.__init__ import __version__
|
14
|
-
|
15
|
-
# Global registry to hold functions
|
16
|
-
cli_function_registry = OrderedDict()
|
17
|
-
subcommand = namedtuple('subcommand', ['name', 'func', 'add_args_function', 'description'])
|
18
|
-
|
19
|
-
|
20
|
-
def get_gsMap_logger(logger_name):
|
21
|
-
logger = logging.getLogger(logger_name)
|
22
|
-
logger.setLevel(logging.DEBUG)
|
23
|
-
handler = logging.StreamHandler()
|
24
|
-
handler.setFormatter(logging.Formatter(
|
25
|
-
'[{asctime}] {levelname:.5s} | {name} - {message}', style='{'))
|
26
|
-
logger.addHandler(handler)
|
27
|
-
return logger
|
28
|
-
|
29
|
-
logger = get_gsMap_logger('gsMap')
|
30
|
-
|
31
|
-
# Decorator to register functions for cli parsing
|
32
|
-
def register_cli(name: str, description: str, add_args_function: Callable) -> Callable:
|
33
|
-
def decorator(func: Callable) -> Callable:
|
34
|
-
def wrapper(*args, **kwargs):
|
35
|
-
name.replace('_', ' ')
|
36
|
-
gsMap_main_logo = pyfiglet.figlet_format("gsMap", font='doom', width=80, justify='center', ).rstrip()
|
37
|
-
print(gsMap_main_logo, flush=True)
|
38
|
-
version_number = 'Version: ' + __version__
|
39
|
-
print(version_number.center(80), flush=True)
|
40
|
-
print('=' * 80, flush=True)
|
41
|
-
logger.info(f"Running {name}...")
|
42
|
-
func(*args, **kwargs)
|
43
|
-
logger.info(f"Finished running {name}.")
|
44
|
-
|
45
|
-
cli_function_registry[name] = subcommand(name=name, func=wrapper, add_args_function=add_args_function,
|
46
|
-
description=description)
|
47
|
-
return wrapper
|
48
|
-
|
49
|
-
return decorator
|
50
|
-
|
51
|
-
def add_shared_args(parser):
|
52
|
-
parser.add_argument('--workdir', type=str, required=True, help='Path to the working directory.')
|
53
|
-
parser.add_argument('--sample_name', type=str, required=True, help='Name of the sample.')
|
54
|
-
|
55
|
-
def add_find_latent_representations_args(parser):
|
56
|
-
add_shared_args(parser)
|
57
|
-
parser.add_argument('--input_hdf5_path', required=True, type=str, help='Path to the input HDF5 file.')
|
58
|
-
parser.add_argument('--annotation', required=True, type=str, help='Name of the annotation in adata.obs to use.')
|
59
|
-
parser.add_argument('--data_layer', type=str, default='counts', required=True,
|
60
|
-
help='Data layer for gene expression (e.g., "count", "counts", "log1p").')
|
61
|
-
parser.add_argument('--epochs', type=int, default=300, help='Number of training epochs.')
|
62
|
-
parser.add_argument('--feat_hidden1', type=int, default=256, help='Neurons in the first hidden layer.')
|
63
|
-
parser.add_argument('--feat_hidden2', type=int, default=128, help='Neurons in the second hidden layer.')
|
64
|
-
parser.add_argument('--gat_hidden1', type=int, default=64, help='Units in the first GAT hidden layer.')
|
65
|
-
parser.add_argument('--gat_hidden2', type=int, default=30, help='Units in the second GAT hidden layer.')
|
66
|
-
parser.add_argument('--p_drop', type=float, default=0.1, help='Dropout rate.')
|
67
|
-
parser.add_argument('--gat_lr', type=float, default=0.001, help='Learning rate for the GAT.')
|
68
|
-
parser.add_argument('--n_neighbors', type=int, default=11, help='Number of neighbors for GAT.')
|
69
|
-
parser.add_argument('--n_comps', type=int, default=300, help='Number of principal components for PCA.')
|
70
|
-
parser.add_argument('--weighted_adj', action='store_true', help='Use weighted adjacency in GAT.')
|
71
|
-
parser.add_argument('--convergence_threshold', type=float, default=1e-4, help='Threshold for convergence.')
|
72
|
-
parser.add_argument('--hierarchically', action='store_true', help='Enable hierarchical latent representation finding.')
|
73
|
-
|
74
|
-
|
75
|
-
def chrom_choice(value):
|
76
|
-
if value.isdigit():
|
77
|
-
ivalue = int(value)
|
78
|
-
if 1 <= ivalue <= 22:
|
79
|
-
return ivalue
|
80
|
-
elif value.lower() == 'all':
|
81
|
-
return value
|
82
|
-
else:
|
83
|
-
raise argparse.ArgumentTypeError(f"'{value}' is an invalid chromosome choice. Choose from 1-22 or 'all'.")
|
84
|
-
|
85
|
-
|
86
|
-
def filter_args_for_dataclass(args_dict, data_class: dataclass):
|
87
|
-
return {k: v for k, v in args_dict.items() if k in data_class.__dataclass_fields__}
|
88
|
-
|
89
|
-
|
90
|
-
def get_dataclass_from_parser(args: argparse.Namespace, data_class: dataclass):
|
91
|
-
remain_kwargs = filter_args_for_dataclass(vars(args), data_class)
|
92
|
-
print(f'Using the following arguments for {data_class.__name__}:', flush=True)
|
93
|
-
pprint(remain_kwargs, indent=4)
|
94
|
-
sys.stdout.flush()
|
95
|
-
return data_class(**remain_kwargs)
|
96
|
-
|
97
|
-
|
98
|
-
def add_latent_to_gene_args(parser):
|
99
|
-
add_shared_args(parser)
|
100
|
-
parser.add_argument('--annotation', type=str, help='Name of the annotation in adata.obs to use. (optional).')
|
101
|
-
parser.add_argument('--no_expression_fraction', action='store_true', help='Skip expression fraction filtering.')
|
102
|
-
parser.add_argument('--latent_representation', type=str, choices=['latent_GVAE', 'latent_PCA'], default='latent_GVAE',
|
103
|
-
help='Type of latent representation.')
|
104
|
-
parser.add_argument('--num_neighbour', type=int, default=21, help='Number of neighbors.')
|
105
|
-
parser.add_argument('--num_neighbour_spatial', type=int, default=101, help='Number of spatial neighbors.')
|
106
|
-
# parser.add_argument('--species', type=str, help='Species name for homolog gene mapping (optional).')
|
107
|
-
parser.add_argument('--homolog_file', type=str, help='Path to homologous gene conversion file (optional).')
|
108
|
-
|
109
|
-
|
110
|
-
def add_generate_ldscore_args(parser):
|
111
|
-
add_shared_args(parser)
|
112
|
-
parser.add_argument('--chrom', type=str, required=True, help='Chromosome id (1-22) or "all".')
|
113
|
-
parser.add_argument('--bfile_root', type=str, required=True, help='Root path for genotype plink bfiles (.bim, .bed, .fam).')
|
114
|
-
parser.add_argument('--keep_snp_root', type=str, required=True, help='Root path for SNP files.')
|
115
|
-
parser.add_argument('--gtf_annotation_file', type=str, required=True, help='Path to GTF annotation file.')
|
116
|
-
parser.add_argument('--gene_window_size', type=int, default=50000, help='Gene window size in base pairs.')
|
117
|
-
parser.add_argument('--enhancer_annotation_file', type=str, help='Path to enhancer annotation file (optional).')
|
118
|
-
parser.add_argument('--snp_multiple_enhancer_strategy', type=str, choices=['max_mkscore', 'nearest_TSS'], default='max_mkscore',
|
119
|
-
help='Strategy for handling multiple enhancers per SNP.')
|
120
|
-
parser.add_argument('--gene_window_enhancer_priority', type=str, choices=['gene_window_first', 'enhancer_first', 'enhancer_only'],
|
121
|
-
help='Priority between gene window and enhancer annotations.')
|
122
|
-
parser.add_argument('--spots_per_chunk', type=int, default=1000, help='Number of spots per chunk.')
|
123
|
-
parser.add_argument('--ld_wind', type=int, default=1, help='LD window size.')
|
124
|
-
parser.add_argument('--ld_unit', type=str, choices=['SNP', 'KB', 'CM'], default='CM', help='Unit for LD window.')
|
125
|
-
parser.add_argument('--additional_baseline_annotation', type=str, default=None, help='Path of additional baseline annotations')
|
126
|
-
|
127
|
-
|
128
|
-
def add_latent_to_gene_args(parser):
|
129
|
-
add_shared_args(parser)
|
130
|
-
parser.add_argument('--annotation', type=str, required=True, help='Name of the annotation layer.')
|
131
|
-
parser.add_argument('--no_expression_fraction', action='store_true', help='Skip expression fraction filtering.')
|
132
|
-
parser.add_argument('--latent_representation', type=str, choices=['latent_GVAE', 'latent_PCA'], default='latent_GVAE',
|
133
|
-
help='Type of latent representation.')
|
134
|
-
parser.add_argument('--num_neighbour', type=int, default=21, help='Number of neighbors.')
|
135
|
-
parser.add_argument('--num_neighbour_spatial', type=int, default=101, help='Number of spatial neighbors.')
|
136
|
-
# parser.add_argument('--species', type=str, help='Species name for homolog gene mapping (optional).')
|
137
|
-
parser.add_argument('--homolog_file', type=str, help='Path to homologous gene conversion file (optional).')
|
138
|
-
|
139
|
-
|
140
|
-
def add_spatial_ldsc_args(parser):
|
141
|
-
add_shared_args(parser)
|
142
|
-
parser.add_argument('--sumstats_file', type=str, required=True, help='Path to GWAS summary statistics file.')
|
143
|
-
parser.add_argument('--w_file', type=str, required=True, help='Path to regression weight file.')
|
144
|
-
parser.add_argument('--trait_name', type=str, required=True, help='Name of the trait being analyzed.')
|
145
|
-
parser.add_argument('--n_blocks', type=int, default=200, help='Number of blocks for jackknife resampling.')
|
146
|
-
parser.add_argument('--chisq_max', type=int, help='Maximum chi-square value for filtering SNPs.')
|
147
|
-
parser.add_argument('--num_processes', type=int, default=4, help='Number of processes for parallel computing.')
|
148
|
-
parser.add_argument('--use_additional_baseline_annotation', type=bool, nargs='?', const=True, default=True, help='Use additional baseline annotations when provided')
|
149
|
-
|
150
|
-
|
151
|
-
def add_Cauchy_combination_args(parser):
|
152
|
-
add_shared_args(parser)
|
153
|
-
parser.add_argument('--trait_name', type=str, required=True, help='Name of the trait being analyzed.')
|
154
|
-
parser.add_argument('--annotation', type=str, required=True, help='Name of the annotation in adata.obs to use.')
|
155
|
-
parser.add_argument('--meta', type=str, help='Optional meta information.')
|
156
|
-
parser.add_argument('--slide', type=str, help='Optional slide information.')
|
157
|
-
|
158
|
-
|
159
|
-
def add_report_args(parser):
|
160
|
-
add_shared_args(parser)
|
161
|
-
parser.add_argument('--trait_name', type=str, required=True, help='Name of the trait to generate the report for.')
|
162
|
-
parser.add_argument('--annotation', type=str, required=True, help='Annotation layer name.')
|
163
|
-
# parser.add_argument('--plot_type', type=str, choices=['manhattan', 'GSS', 'gsMap', 'all'], default='all',
|
164
|
-
# help="Type of diagnostic plot to generate. Choose from 'manhattan', 'GSS', 'gsMap', or 'all'.")
|
165
|
-
parser.add_argument('--top_corr_genes', type=int, default=50,
|
166
|
-
help='Number of top correlated genes to display.')
|
167
|
-
parser.add_argument('--selected_genes', type=str, nargs='*',
|
168
|
-
help='List of specific genes to include in the report (optional).')
|
169
|
-
parser.add_argument('--sumstats_file', type=str, required=True, help='Path to GWAS summary statistics file.')
|
170
|
-
|
171
|
-
# Optional arguments for customization
|
172
|
-
parser.add_argument('--fig_width', type=int, default=None, help='Width of the generated figures in pixels.')
|
173
|
-
parser.add_argument('--fig_height', type=int, default=None, help='Height of the generated figures in pixels.')
|
174
|
-
parser.add_argument('--point_size', type=int, default=None, help='Point size for the figures.')
|
175
|
-
parser.add_argument('--fig_style', type=str, default='light', choices=['dark', 'light'],
|
176
|
-
help='Style of the generated figures.')
|
177
|
-
|
178
|
-
def add_format_sumstats_args(parser):
|
179
|
-
# Required arguments
|
180
|
-
parser.add_argument('--sumstats', required=True, type=str,
|
181
|
-
help='Path to gwas summary data')
|
182
|
-
parser.add_argument('--out', required=True, type=str,
|
183
|
-
help='Path to save the formatted gwas data')
|
184
|
-
|
185
|
-
# Arguments for specify column name
|
186
|
-
parser.add_argument('--snp', default=None, type=str,
|
187
|
-
help="Name of snp column (if not a name that gsMap understands)")
|
188
|
-
parser.add_argument('--a1', default=None, type=str,
|
189
|
-
help="Name of effect allele column (if not a name that gsMap understands)")
|
190
|
-
parser.add_argument('--a2', default=None, type=str,
|
191
|
-
help="Name of none-effect allele column (if not a name that gsMap understands)")
|
192
|
-
parser.add_argument('--info', default=None, type=str,
|
193
|
-
help="Name of info column (if not a name that gsMap understands)")
|
194
|
-
parser.add_argument('--beta', default=None, type=str,
|
195
|
-
help="Name of gwas beta column (if not a name that gsMap understands).")
|
196
|
-
parser.add_argument('--se', default=None, type=str,
|
197
|
-
help="Name of gwas standar error of beta column (if not a name that gsMap understands)")
|
198
|
-
parser.add_argument('--p', default=None, type=str,
|
199
|
-
help="Name of p-value column (if not a name that gsMap understands)")
|
200
|
-
parser.add_argument('--frq', default=None, type=str,
|
201
|
-
help="Name of A1 ferquency column (if not a name that gsMap understands)")
|
202
|
-
parser.add_argument('--n', default=None, type=str,
|
203
|
-
help="Name of sample size column (if not a name that gsMap understands)")
|
204
|
-
parser.add_argument('--z', default=None, type=str,
|
205
|
-
help="Name of gwas Z-statistics column (if not a name that gsMap understands)")
|
206
|
-
parser.add_argument('--OR', default=None, type=str,
|
207
|
-
help="Name of gwas OR column (if not a name that gsMap understands)")
|
208
|
-
parser.add_argument('--se_OR', default=None, type=str,
|
209
|
-
help="Name of standar error of OR column (if not a name that gsMap understands)")
|
210
|
-
|
211
|
-
# Arguments for convert SNP (chr, pos) to rsid
|
212
|
-
parser.add_argument('--chr', default="Chr", type=str,
|
213
|
-
help="Name of SNP chromosome column (if not a name that gsMap understands)")
|
214
|
-
parser.add_argument('--pos', default="Pos", type=str,
|
215
|
-
help="Name of SNP positions column (if not a name that gsMap understands)")
|
216
|
-
parser.add_argument('--dbsnp', default=None, type=str,
|
217
|
-
help='Path to reference dnsnp file')
|
218
|
-
parser.add_argument('--chunksize', default=1e+6, type=int,
|
219
|
-
help='Chunk size for loading dbsnp file')
|
220
|
-
|
221
|
-
# Arguments for output format and quality
|
222
|
-
parser.add_argument('--format', default='gsMap', type=str,
|
223
|
-
help='Format of output data', choices=['gsMap', 'COJO'])
|
224
|
-
parser.add_argument('--info_min', default=0.9, type=float,
|
225
|
-
help='Minimum INFO score.')
|
226
|
-
parser.add_argument('--maf_min', default=0.01, type=float,
|
227
|
-
help='Minimum MAF.')
|
228
|
-
parser.add_argument('--keep_chr_pos', action='store_true', default=False,
|
229
|
-
help='Keep SNP chromosome and position columns in the output data')
|
230
|
-
|
231
|
-
def add_run_all_mode_args(parser):
|
232
|
-
add_shared_args(parser)
|
233
|
-
|
234
|
-
# Required paths and configurations
|
235
|
-
parser.add_argument('--gsMap_resource_dir', type=str, required=True,
|
236
|
-
help='Directory containing gsMap resources (e.g., genome annotations, LD reference panel, etc.).')
|
237
|
-
parser.add_argument('--hdf5_path', type=str, required=True,
|
238
|
-
help='Path to the input spatial transcriptomics data (H5AD format).')
|
239
|
-
parser.add_argument('--annotation', type=str, required=True,
|
240
|
-
help='Name of the annotation in adata.obs to use.')
|
241
|
-
parser.add_argument('--data_layer', type=str, default='counts', required=True,
|
242
|
-
help='Data layer for gene expression (e.g., "count", "counts", "log1p").')
|
243
|
-
|
244
|
-
# GWAS Data Parameters
|
245
|
-
parser.add_argument('--trait_name', type=str, help='Name of the trait for GWAS analysis (required if sumstats_file is provided).')
|
246
|
-
parser.add_argument('--sumstats_file', type=str,
|
247
|
-
help='Path to GWAS summary statistics file. Either sumstats_file or sumstats_config_file is required.')
|
248
|
-
parser.add_argument('--sumstats_config_file', type=str,
|
249
|
-
help='Path to GWAS summary statistics config file. Either sumstats_file or sumstats_config_file is required.')
|
250
|
-
|
251
|
-
# Homolog Data Parameters
|
252
|
-
parser.add_argument('--homolog_file', type=str,
|
253
|
-
help='Path to homologous gene for converting gene names from different species to human (optional, used for cross-species analysis).')
|
254
|
-
|
255
|
-
# Maximum number of processes
|
256
|
-
parser.add_argument('--max_processes', type=int, default=10,
|
257
|
-
help='Maximum number of processes for parallel execution.')
|
258
|
-
|
259
|
-
# # Optional paths for customization
|
260
|
-
# parser.add_argument('--bfile_root', type=str,
|
261
|
-
# help='Root path to PLINK bfiles (LD reference panel). If not provided, it will use the default in gsMap_resource_dir.')
|
262
|
-
# parser.add_argument('--keep_snp_root', type=str,
|
263
|
-
# help='Root path for SNP filtering. If not provided, it will use the default in gsMap_resource_dir.')
|
264
|
-
# parser.add_argument('--w_file', type=str,
|
265
|
-
# help='Path to the regression weight file. If not provided, it will use the default in gsMap_resource_dir.')
|
266
|
-
# parser.add_argument('--snp_gene_weight_adata_path', type=str,
|
267
|
-
# help='Path to the SNP-gene weight matrix file. If not provided, it will use the default in gsMap_resource_dir.')
|
268
|
-
# parser.add_argument('--baseline_annotation_dir', type=str,
|
269
|
-
# help='Directory containing the baseline annotations for quick mode. If not provided, it will use the default in gsMap_resource_dir.')
|
270
|
-
# parser.add_argument('--SNP_gene_pair_dir', type=str,
|
271
|
-
# help='Directory for SNP-gene pair data. If not provided, it will use the default in gsMap_resource_dir.')
|
272
|
-
|
273
|
-
|
274
|
-
def ensure_path_exists(func):
|
275
|
-
@wraps(func)
|
276
|
-
def wrapper(*args, **kwargs):
|
277
|
-
result = func(*args, **kwargs)
|
278
|
-
if isinstance(result, Path):
|
279
|
-
if result.suffix:
|
280
|
-
result.parent.mkdir(parents=True, exist_ok=True, mode=0o755)
|
281
|
-
else: # It's a directory path
|
282
|
-
result.mkdir(parents=True, exist_ok=True, mode=0o755)
|
283
|
-
return result
|
284
|
-
|
285
|
-
return wrapper
|
286
|
-
|
287
|
-
|
288
|
-
@dataclass
|
289
|
-
class ConfigWithAutoPaths:
|
290
|
-
workdir: str
|
291
|
-
sample_name: str
|
292
|
-
|
293
|
-
def __post_init__(self):
|
294
|
-
if self.workdir is None:
|
295
|
-
raise ValueError('workdir must be provided.')
|
296
|
-
|
297
|
-
@property
|
298
|
-
@ensure_path_exists
|
299
|
-
def hdf5_with_latent_path(self) -> Path:
|
300
|
-
return Path(f'{self.workdir}/{self.sample_name}/find_latent_representations/{self.sample_name}_add_latent.h5ad')
|
301
|
-
|
302
|
-
@property
|
303
|
-
@ensure_path_exists
|
304
|
-
def mkscore_feather_path(self) -> Path:
|
305
|
-
return Path(f'{self.workdir}/{self.sample_name}/latent_to_gene/{self.sample_name}_gene_marker_score.feather')
|
306
|
-
|
307
|
-
@property
|
308
|
-
@ensure_path_exists
|
309
|
-
def ldscore_save_dir(self) -> Path:
|
310
|
-
return Path(f'{self.workdir}/{self.sample_name}/generate_ldscore')
|
311
|
-
|
312
|
-
@property
|
313
|
-
@ensure_path_exists
|
314
|
-
def ldsc_save_dir(self) -> Path:
|
315
|
-
return Path(f'{self.workdir}/{self.sample_name}/spatial_ldsc')
|
316
|
-
|
317
|
-
@property
|
318
|
-
@ensure_path_exists
|
319
|
-
def cauchy_save_dir(self) -> Path:
|
320
|
-
return Path(f'{self.workdir}/{self.sample_name}/cauchy_combination')
|
321
|
-
|
322
|
-
@ensure_path_exists
|
323
|
-
def get_report_dir(self, trait_name: str) -> Path:
|
324
|
-
return Path(f'{self.workdir}/{self.sample_name}/report/{trait_name}')
|
325
|
-
|
326
|
-
def get_gsMap_report_file(self, trait_name: str) -> Path:
|
327
|
-
return self.get_report_dir(trait_name) / f'{self.sample_name}_{trait_name}_gsMap_Report.html'
|
328
|
-
|
329
|
-
@ensure_path_exists
|
330
|
-
def get_manhattan_html_plot_path(self, trait_name: str) -> Path:
|
331
|
-
return Path(
|
332
|
-
f'{self.workdir}/{self.sample_name}/report/{trait_name}/manhattan_plot/{self.sample_name}_{trait_name}_Diagnostic_Manhattan_Plot.html')
|
333
|
-
|
334
|
-
@ensure_path_exists
|
335
|
-
def get_GSS_plot_dir(self, trait_name: str) -> Path:
|
336
|
-
return Path(f'{self.workdir}/{self.sample_name}/report/{trait_name}/GSS_plot')
|
337
|
-
|
338
|
-
def get_GSS_plot_select_gene_file(self, trait_name: str) -> Path:
|
339
|
-
return self.get_GSS_plot_dir(trait_name) / 'plot_genes.csv'
|
340
|
-
|
341
|
-
@ensure_path_exists
|
342
|
-
def get_ldsc_result_file(self, trait_name: str) -> Path:
|
343
|
-
return Path(f'{self.ldsc_save_dir}/{self.sample_name}_{trait_name}.csv.gz')
|
344
|
-
|
345
|
-
@ensure_path_exists
|
346
|
-
def get_cauchy_result_file(self, trait_name: str) -> Path:
|
347
|
-
return Path(f'{self.cauchy_save_dir}/{self.sample_name}_{trait_name}.Cauchy.csv.gz')
|
348
|
-
|
349
|
-
@ensure_path_exists
|
350
|
-
def get_gene_diagnostic_info_save_path(self, trait_name: str) -> Path:
|
351
|
-
return Path(
|
352
|
-
f'{self.workdir}/{self.sample_name}/report/{trait_name}/{self.sample_name}_{trait_name}_Gene_Diagnostic_Info.csv')
|
353
|
-
|
354
|
-
@ensure_path_exists
|
355
|
-
def get_gsMap_plot_save_dir(self, trait_name: str) -> Path:
|
356
|
-
return Path(f'{self.workdir}/{self.sample_name}/report/{trait_name}/gsMap_plot')
|
357
|
-
|
358
|
-
def get_gsMap_html_plot_save_path(self, trait_name: str) -> Path:
|
359
|
-
return self.get_gsMap_plot_save_dir(trait_name) / f'{self.sample_name}_{trait_name}_gsMap_plot.html'
|
360
|
-
|
361
|
-
@dataclass
|
362
|
-
class FindLatentRepresentationsConfig(ConfigWithAutoPaths):
|
363
|
-
input_hdf5_path: str
|
364
|
-
# output_hdf5_path: str
|
365
|
-
annotation: str = None
|
366
|
-
data_layer: str = None
|
367
|
-
|
368
|
-
epochs: int = 300
|
369
|
-
feat_hidden1: int = 256
|
370
|
-
feat_hidden2: int = 128
|
371
|
-
feat_cell: int = 3000
|
372
|
-
gat_hidden1: int = 64
|
373
|
-
gat_hidden2: int = 30
|
374
|
-
p_drop: float = 0.1
|
375
|
-
gat_lr: float = 0.001
|
376
|
-
gcn_decay: float = 0.01
|
377
|
-
n_neighbors: int = 11
|
378
|
-
label_w: float = 1
|
379
|
-
rec_w: float = 1
|
380
|
-
input_pca: bool = True
|
381
|
-
n_comps: int = 300
|
382
|
-
weighted_adj: bool = False
|
383
|
-
nheads: int = 3
|
384
|
-
var: bool = False
|
385
|
-
convergence_threshold: float = 1e-4
|
386
|
-
hierarchically: bool = False
|
387
|
-
|
388
|
-
def __post_init__(self):
|
389
|
-
# self.output_hdf5_path = self.hdf5_with_latent_path
|
390
|
-
if self.hierarchically:
|
391
|
-
if self.annotation is None:
|
392
|
-
raise ValueError('annotation must be provided if hierarchically is True.')
|
393
|
-
logger.info(
|
394
|
-
f'------Hierarchical mode is enabled. This will find the latent representations within each annotation.')
|
395
|
-
|
396
|
-
# remind for not providing annotation
|
397
|
-
if self.annotation is None:
|
398
|
-
logger.warning(
|
399
|
-
'annotation is not provided. This will find the latent representations for the whole dataset.')
|
400
|
-
else:
|
401
|
-
logger.info(f'------Find latent representations for {self.annotation}...')
|
402
|
-
|
403
|
-
|
404
|
-
@dataclass
|
405
|
-
class LatentToGeneConfig(ConfigWithAutoPaths):
|
406
|
-
# input_hdf5_with_latent_path: str
|
407
|
-
# output_feather_path: str
|
408
|
-
no_expression_fraction: bool = False
|
409
|
-
latent_representation: str = 'latent_GVAE'
|
410
|
-
num_neighbour: int = 21
|
411
|
-
num_neighbour_spatial: int = 101
|
412
|
-
homolog_file: str = None
|
413
|
-
gM_slices: str = None
|
414
|
-
annotation: str = None
|
415
|
-
|
416
|
-
def __post_init__(self):
|
417
|
-
if self.homolog_file is not None:
|
418
|
-
logger.info(f"User provided homolog file to map gene names to human: {self.homolog_file}")
|
419
|
-
# check the format of the homolog file
|
420
|
-
with open(self.homolog_file, 'r') as f:
|
421
|
-
first_line = f.readline().strip()
|
422
|
-
_n_col = len(first_line.split())
|
423
|
-
if _n_col != 2:
|
424
|
-
raise ValueError(
|
425
|
-
f"Invalid homolog file format. Expected 2 columns, first column should be other species gene name, second column should be human gene name. "
|
426
|
-
f"Got {_n_col} columns in the first line.")
|
427
|
-
else:
|
428
|
-
first_col_name, second_col_name = first_line.split()
|
429
|
-
self.species = first_col_name
|
430
|
-
logger.info(
|
431
|
-
f"Homolog file provided and will map gene name from column1:{first_col_name} to column2:{second_col_name}")
|
432
|
-
else:
|
433
|
-
logger.info("No homolog file provided. Run in human mode.")
|
434
|
-
|
435
|
-
|
436
|
-
@dataclass
|
437
|
-
class GenerateLDScoreConfig(ConfigWithAutoPaths):
|
438
|
-
chrom: Union[int, str]
|
439
|
-
|
440
|
-
bfile_root: str
|
441
|
-
keep_snp_root: Optional[str]
|
442
|
-
|
443
|
-
# annotation by gene distance
|
444
|
-
gtf_annotation_file: str
|
445
|
-
gene_window_size: int = 50000
|
446
|
-
|
447
|
-
# annotation by enhancer
|
448
|
-
enhancer_annotation_file: str = None
|
449
|
-
snp_multiple_enhancer_strategy: Literal['max_mkscore', 'nearest_TSS'] = 'max_mkscore'
|
450
|
-
gene_window_enhancer_priority: Optional[Literal['gene_window_first', 'enhancer_first', 'enhancer_only',]] = None
|
451
|
-
|
452
|
-
# for calculating ld score
|
453
|
-
additional_baseline_annotation: str = None
|
454
|
-
spots_per_chunk: int = 1_000
|
455
|
-
ld_wind: int = 1
|
456
|
-
ld_unit: str = 'CM'
|
457
|
-
|
458
|
-
# zarr config
|
459
|
-
ldscore_save_format: Literal['feather', 'zarr', 'quick_mode'] = 'feather'
|
460
|
-
|
461
|
-
zarr_chunk_size: Tuple[int, int] = None
|
462
|
-
|
463
|
-
# for pre calculating the SNP Gene ldscore Weight
|
464
|
-
save_pre_calculate_snp_gene_weight_matrix: bool = False
|
465
|
-
|
466
|
-
baseline_annotation_dir: Optional[str] = None
|
467
|
-
SNP_gene_pair_dir: Optional[str] = None
|
468
|
-
def __post_init__(self):
|
469
|
-
# if self.mkscore_feather_file is None:
|
470
|
-
# self.mkscore_feather_file = self._get_mkscore_feather_path()
|
471
|
-
|
472
|
-
if self.enhancer_annotation_file is not None and self.gene_window_enhancer_priority is None:
|
473
|
-
logger.warning("enhancer_annotation_file is provided but gene_window_enhancer_priority is not provided. "
|
474
|
-
"by default, gene_window_enhancer_priority is set to 'enhancer_only', when enhancer_annotation_file is provided.")
|
475
|
-
self.gene_window_enhancer_priority = 'enhancer_only'
|
476
|
-
if self.enhancer_annotation_file is None and self.gene_window_enhancer_priority is not None:
|
477
|
-
logger.warning("gene_window_enhancer_priority is provided but enhancer_annotation_file is not provided. "
|
478
|
-
"by default, gene_window_enhancer_priority is set to None, when enhancer_annotation_file is not provided.")
|
479
|
-
self.gene_window_enhancer_priority = None
|
480
|
-
assert self.gene_window_enhancer_priority in [None, 'gene_window_first', 'enhancer_first', 'enhancer_only', ], \
|
481
|
-
f"gene_window_enhancer_priority must be one of None, 'gene_window_first', 'enhancer_first', 'enhancer_only', but got {self.gene_window_enhancer_priority}."
|
482
|
-
if self.gene_window_enhancer_priority in ['gene_window_first', 'enhancer_first']:
|
483
|
-
logger.info(f'Both gene_window and enhancer annotation will be used to calculate LD score. ')
|
484
|
-
logger.info(
|
485
|
-
f'SNP within +-{self.gene_window_size} bp of gene body will be used and enhancer annotation will be used to calculate LD score. If a snp maps to multiple enhancers, the strategy to choose by your select strategy: {self.snp_multiple_enhancer_strategy}.')
|
486
|
-
elif self.gene_window_enhancer_priority == 'enhancer_only':
|
487
|
-
logger.info(f'Only enhancer annotation will be used to calculate LD score. ')
|
488
|
-
else:
|
489
|
-
logger.info(
|
490
|
-
f'Only gene window annotation will be used to calculate LD score. SNP within +-{self.gene_window_size} bp of gene body will be used. ')
|
491
|
-
|
492
|
-
# remind for baseline annotation
|
493
|
-
if self.additional_baseline_annotation is None:
|
494
|
-
logger.info(f'------Baseline annotation is not provided. Default baseline annotation will be used.')
|
495
|
-
else:
|
496
|
-
logger.info(
|
497
|
-
f'------Baseline annotation is provided. Additional baseline annotation will be used with the default baseline annotation.')
|
498
|
-
logger.info(f'------Baseline annotation directory: {self.additional_baseline_annotation}')
|
499
|
-
# check the existence of baseline annotation
|
500
|
-
if self.chrom == 'all':
|
501
|
-
for chrom in range(1, 23):
|
502
|
-
chrom = str(chrom)
|
503
|
-
baseline_annotation_path = Path(
|
504
|
-
self.additional_baseline_annotation) / f'baseline.{chrom}.annot.gz'
|
505
|
-
if not baseline_annotation_path.exists():
|
506
|
-
raise FileNotFoundError(
|
507
|
-
f'baseline.{chrom}.annot.gz is not found in {self.additional_baseline_annotation}.')
|
508
|
-
else:
|
509
|
-
baseline_annotation_path = Path(
|
510
|
-
self.additional_baseline_annotation) / f'baseline.{self.chrom}.annot.gz'
|
511
|
-
if not baseline_annotation_path.exists():
|
512
|
-
raise FileNotFoundError(
|
513
|
-
f'baseline.{self.chrom}.annot.gz is not found in {self.additional_baseline_annotation}.')
|
514
|
-
|
515
|
-
# set the default zarr chunk size
|
516
|
-
if self.ldscore_save_format == 'zarr' and self.zarr_chunk_size is None:
|
517
|
-
self.zarr_chunk_size = (10_000, self.spots_per_chunk)
|
518
|
-
|
519
|
-
|
520
|
-
@dataclass
|
521
|
-
class SpatialLDSCConfig(ConfigWithAutoPaths):
|
522
|
-
w_file: str
|
523
|
-
# ldscore_save_dir: str
|
524
|
-
use_additional_baseline_annotation: bool = True
|
525
|
-
trait_name: Optional[str] = None
|
526
|
-
sumstats_file: Optional[str] = None
|
527
|
-
sumstats_config_file: Optional[str] = None
|
528
|
-
num_processes: int = 4
|
529
|
-
not_M_5_50: bool = False
|
530
|
-
n_blocks: int = 200
|
531
|
-
chisq_max: Optional[int] = None
|
532
|
-
all_chunk: Optional[int] = None
|
533
|
-
chunk_range: Optional[Tuple[int, int]] = None
|
534
|
-
|
535
|
-
ldscore_save_format: Literal['feather', 'zarr', 'quick_mode'] = 'feather'
|
536
|
-
|
537
|
-
spots_per_chunk_quick_mode: int = 1_000
|
538
|
-
snp_gene_weight_adata_path: Optional[str] = None
|
539
|
-
|
540
|
-
def __post_init__(self):
|
541
|
-
super().__post_init__()
|
542
|
-
if self.sumstats_file is None and self.sumstats_config_file is None:
|
543
|
-
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
544
|
-
if self.sumstats_file is not None and self.sumstats_config_file is not None:
|
545
|
-
raise ValueError('Only one of sumstats_file and sumstats_config_file must be provided.')
|
546
|
-
if self.sumstats_file is not None and self.trait_name is None:
|
547
|
-
raise ValueError('trait_name must be provided if sumstats_file is provided.')
|
548
|
-
if self.sumstats_config_file is not None and self.trait_name is not None:
|
549
|
-
raise ValueError('trait_name must not be provided if sumstats_config_file is provided.')
|
550
|
-
self.sumstats_config_dict = {}
|
551
|
-
# load the sumstats config file
|
552
|
-
if self.sumstats_config_file is not None:
|
553
|
-
import yaml
|
554
|
-
with open(self.sumstats_config_file) as f:
|
555
|
-
config = yaml.load(f, Loader=yaml.FullLoader)
|
556
|
-
for trait_name, sumstats_file in config.items():
|
557
|
-
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
558
|
-
# load the sumstats file
|
559
|
-
elif self.sumstats_file is not None:
|
560
|
-
self.sumstats_config_dict[self.trait_name] = self.sumstats_file
|
561
|
-
else:
|
562
|
-
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
563
|
-
|
564
|
-
for sumstats_file in self.sumstats_config_dict.values():
|
565
|
-
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
566
|
-
|
567
|
-
# check if additional baseline annotation is exist
|
568
|
-
# self.use_additional_baseline_annotation = False
|
569
|
-
|
570
|
-
if self.use_additional_baseline_annotation:
|
571
|
-
self.process_additional_baseline_annotation()
|
572
|
-
|
573
|
-
def process_additional_baseline_annotation(self):
|
574
|
-
additional_baseline_annotation = Path(self.ldscore_save_dir) / 'additional_baseline'
|
575
|
-
dir_exists = additional_baseline_annotation.exists()
|
576
|
-
|
577
|
-
if not dir_exists:
|
578
|
-
self.use_additional_baseline_annotation = False
|
579
|
-
# if self.use_additional_baseline_annotation:
|
580
|
-
# logger.warning(f"additional_baseline directory is not found in {self.ldscore_save_dir}.")
|
581
|
-
# print('''\
|
582
|
-
# if you want to use additional baseline annotation,
|
583
|
-
# please provide additional baseline annotation when calculating ld score.
|
584
|
-
# ''')
|
585
|
-
# raise FileNotFoundError(
|
586
|
-
# f'additional_baseline directory is not found.')
|
587
|
-
# return
|
588
|
-
# self.use_additional_baseline_annotation = self.use_additional_baseline_annotation or True
|
589
|
-
else:
|
590
|
-
logger.info(
|
591
|
-
f'------Additional baseline annotation is provided. It will be used with the default baseline annotation.')
|
592
|
-
logger.info(f'------Additional baseline annotation directory: {additional_baseline_annotation}')
|
593
|
-
|
594
|
-
chrom_list = range(1, 23)
|
595
|
-
for chrom in chrom_list:
|
596
|
-
baseline_annotation_path = additional_baseline_annotation / f'baseline.{chrom}.l2.ldscore.feather'
|
597
|
-
if not baseline_annotation_path.exists():
|
598
|
-
raise FileNotFoundError(
|
599
|
-
f'baseline.{chrom}.annot.gz is not found in {additional_baseline_annotation}.')
|
600
|
-
return None
|
601
|
-
|
602
|
-
|
603
|
-
@dataclass
|
604
|
-
class CauchyCombinationConfig(ConfigWithAutoPaths):
|
605
|
-
trait_name: str
|
606
|
-
annotation: str
|
607
|
-
meta: str = None
|
608
|
-
slide: str = None
|
609
|
-
|
610
|
-
|
611
|
-
@dataclass
|
612
|
-
class VisualizeConfig(ConfigWithAutoPaths):
|
613
|
-
trait_name: str
|
614
|
-
|
615
|
-
annotation: str = None
|
616
|
-
fig_title: str = None
|
617
|
-
fig_height: int = 600
|
618
|
-
fig_width: int = 800
|
619
|
-
point_size: int = None
|
620
|
-
fig_style: Literal['dark', 'light'] = 'light'
|
621
|
-
|
622
|
-
|
623
|
-
@dataclass
|
624
|
-
class DiagnosisConfig(ConfigWithAutoPaths):
|
625
|
-
annotation: str
|
626
|
-
# mkscore_feather_file: str
|
627
|
-
|
628
|
-
trait_name: str
|
629
|
-
sumstats_file: str
|
630
|
-
plot_type: Literal['manhattan', 'GSS', 'gsMap', 'all'] = 'all'
|
631
|
-
top_corr_genes: int = 50
|
632
|
-
selected_genes: Optional[List[str]] = None
|
633
|
-
|
634
|
-
fig_width: Optional[int] = None
|
635
|
-
fig_height: Optional[int] = None
|
636
|
-
point_size: Optional[int] = None
|
637
|
-
fig_style: Literal['dark', 'light'] = 'light'
|
638
|
-
|
639
|
-
def __post_init__(self):
|
640
|
-
if any([self.fig_width, self.fig_height, self.point_size]):
|
641
|
-
logger.info('Customizing the figure size and point size.')
|
642
|
-
assert all([self.fig_width, self.fig_height, self.point_size]), 'All of fig_width, fig_height, and point_size must be provided.'
|
643
|
-
self.customize_fig = True
|
644
|
-
else:
|
645
|
-
self.customize_fig = False
|
646
|
-
@dataclass
|
647
|
-
class ReportConfig(DiagnosisConfig):
|
648
|
-
pass
|
649
|
-
|
650
|
-
|
651
|
-
@dataclass
|
652
|
-
class RunAllModeConfig(ConfigWithAutoPaths):
|
653
|
-
gsMap_resource_dir: str
|
654
|
-
|
655
|
-
# == ST DATA PARAMETERS ==
|
656
|
-
hdf5_path: str
|
657
|
-
annotation: str
|
658
|
-
data_layer: str = 'X'
|
659
|
-
|
660
|
-
# ==GWAS DATA PARAMETERS==
|
661
|
-
trait_name: Optional[str] = None
|
662
|
-
sumstats_file: Optional[str] = None
|
663
|
-
sumstats_config_file: Optional[str] = None
|
664
|
-
|
665
|
-
# === homolog PARAMETERS ===
|
666
|
-
homolog_file: Optional[str] = None
|
667
|
-
|
668
|
-
max_processes: int = 10
|
669
|
-
|
670
|
-
def __post_init__(self):
|
671
|
-
super().__post_init__()
|
672
|
-
self.gtffile = f"{self.gsMap_resource_dir}/genome_annotation/gtf/gencode.v39lift37.annotation.gtf"
|
673
|
-
self.bfile_root = f"{self.gsMap_resource_dir}/LD_Reference_Panel/1000G_EUR_Phase3_plink/1000G.EUR.QC"
|
674
|
-
self.keep_snp_root = f"{self.gsMap_resource_dir}/LDSC_resource/hapmap3_snps/hm"
|
675
|
-
self.w_file = f"{self.gsMap_resource_dir}/LDSC_resource/weights_hm3_no_hla/weights."
|
676
|
-
self.snp_gene_weight_adata_path = f"{self.gsMap_resource_dir}/quick_mode/snp_gene_weight_matrix.h5ad"
|
677
|
-
self.baseline_annotation_dir = Path(f"{self.gsMap_resource_dir}/quick_mode/baseline").resolve()
|
678
|
-
self.SNP_gene_pair_dir = Path(f"{self.gsMap_resource_dir}/quick_mode/SNP_gene_pair").resolve()
|
679
|
-
# check the existence of the input files and resources files
|
680
|
-
for file in [self.hdf5_path, self.gtffile]:
|
681
|
-
if not Path(file).exists():
|
682
|
-
raise FileNotFoundError(f"File {file} does not exist.")
|
683
|
-
|
684
|
-
if self.sumstats_file is None and self.sumstats_config_file is None:
|
685
|
-
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
686
|
-
if self.sumstats_file is not None and self.sumstats_config_file is not None:
|
687
|
-
raise ValueError('Only one of sumstats_file and sumstats_config_file must be provided.')
|
688
|
-
if self.sumstats_file is not None and self.trait_name is None:
|
689
|
-
raise ValueError('trait_name must be provided if sumstats_file is provided.')
|
690
|
-
if self.sumstats_config_file is not None and self.trait_name is not None:
|
691
|
-
raise ValueError('trait_name must not be provided if sumstats_config_file is provided.')
|
692
|
-
self.sumstats_config_dict = {}
|
693
|
-
# load the sumstats config file
|
694
|
-
if self.sumstats_config_file is not None:
|
695
|
-
import yaml
|
696
|
-
with open(self.sumstats_config_file) as f:
|
697
|
-
config = yaml.load(f, Loader=yaml.FullLoader)
|
698
|
-
for trait_name, sumstats_file in config.items():
|
699
|
-
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
700
|
-
self.sumstats_config_dict[trait_name] = sumstats_file
|
701
|
-
# load the sumstats file
|
702
|
-
elif self.sumstats_file is not None and self.trait_name is not None:
|
703
|
-
self.sumstats_config_dict[self.trait_name] = self.sumstats_file
|
704
|
-
else:
|
705
|
-
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
706
|
-
|
707
|
-
for sumstats_file in self.sumstats_config_dict.values():
|
708
|
-
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
709
|
-
|
710
|
-
|
711
|
-
@dataclass
|
712
|
-
class FormatSumstatsConfig:
|
713
|
-
sumstats: str
|
714
|
-
out: str
|
715
|
-
dbsnp: str
|
716
|
-
snp: str = None
|
717
|
-
a1: str = None
|
718
|
-
a2: str = None
|
719
|
-
info: str = None
|
720
|
-
beta: str = None
|
721
|
-
se: str = None
|
722
|
-
p: str = None
|
723
|
-
frq: str = None
|
724
|
-
n: str = None
|
725
|
-
z: str = None
|
726
|
-
OR: str = None
|
727
|
-
se_OR: str = None
|
728
|
-
format: str = None
|
729
|
-
chr: str = None
|
730
|
-
pos: str = None
|
731
|
-
chunksize: int = 1e+7
|
732
|
-
info_min: float = 0.9
|
733
|
-
maf_min: float = 0.01
|
734
|
-
keep_chr_pos: bool = False
|
735
|
-
|
736
|
-
|
737
|
-
@register_cli(name='run_find_latent_representations',
|
738
|
-
description='Run Find_latent_representations \nFind the latent representations of each spot by running GNN-VAE',
|
739
|
-
add_args_function=add_find_latent_representations_args)
|
740
|
-
def run_find_latent_representation_from_cli(args: argparse.Namespace):
|
741
|
-
from gsMap.find_latent_representation import run_find_latent_representation
|
742
|
-
config = get_dataclass_from_parser(args, FindLatentRepresentationsConfig)
|
743
|
-
run_find_latent_representation(config)
|
744
|
-
|
745
|
-
|
746
|
-
@register_cli(name='run_latent_to_gene',
|
747
|
-
description='Run Latent_to_gene \nEstimate gene marker gene scores for each spot by using latent representations from nearby spots',
|
748
|
-
add_args_function=add_latent_to_gene_args)
|
749
|
-
def run_latent_to_gene_from_cli(args: argparse.Namespace):
|
750
|
-
from gsMap.latent_to_gene import run_latent_to_gene
|
751
|
-
config = get_dataclass_from_parser(args, LatentToGeneConfig)
|
752
|
-
run_latent_to_gene(config)
|
753
|
-
|
754
|
-
|
755
|
-
@register_cli(name='run_generate_ldscore',
|
756
|
-
description='Run Generate_ldscore \nGenerate LD scores for each spot',
|
757
|
-
add_args_function=add_generate_ldscore_args)
|
758
|
-
def run_generate_ldscore_from_cli(args: argparse.Namespace):
|
759
|
-
from gsMap.generate_ldscore import run_generate_ldscore
|
760
|
-
config = get_dataclass_from_parser(args, GenerateLDScoreConfig)
|
761
|
-
run_generate_ldscore(config)
|
762
|
-
|
763
|
-
|
764
|
-
@register_cli(name='run_spatial_ldsc',
|
765
|
-
description='Run Spatial_ldsc \nRun spatial LDSC for each spot',
|
766
|
-
add_args_function=add_spatial_ldsc_args)
|
767
|
-
def run_spatial_ldsc_from_cli(args: argparse.Namespace):
|
768
|
-
from gsMap.spatial_ldsc_multiple_sumstats import run_spatial_ldsc
|
769
|
-
config = get_dataclass_from_parser(args, SpatialLDSCConfig)
|
770
|
-
run_spatial_ldsc(config)
|
771
|
-
|
772
|
-
|
773
|
-
@register_cli(name='run_cauchy_combination',
|
774
|
-
description='Run Cauchy_combination for each annotation',
|
775
|
-
add_args_function=add_Cauchy_combination_args)
|
776
|
-
def run_Cauchy_combination_from_cli(args: argparse.Namespace):
|
777
|
-
from gsMap.cauchy_combination_test import run_Cauchy_combination
|
778
|
-
config = get_dataclass_from_parser(args, CauchyCombinationConfig)
|
779
|
-
run_Cauchy_combination(config)
|
780
|
-
|
781
|
-
|
782
|
-
@register_cli(name='run_report',
|
783
|
-
description='Run Report to generate diagnostic plots and tables',
|
784
|
-
add_args_function=add_report_args)
|
785
|
-
def run_Report_from_cli(args: argparse.Namespace):
|
786
|
-
from gsMap.report import run_report
|
787
|
-
config = get_dataclass_from_parser(args, ReportConfig)
|
788
|
-
run_report(config)
|
789
|
-
|
790
|
-
|
791
|
-
@register_cli(name='format_sumstats',
|
792
|
-
description='Format gwas summary statistics',
|
793
|
-
add_args_function=add_format_sumstats_args)
|
794
|
-
def gwas_format_from_cli(args: argparse.Namespace):
|
795
|
-
from gsMap.format_sumstats import gwas_format
|
796
|
-
config = get_dataclass_from_parser(args, FormatSumstatsConfig)
|
797
|
-
gwas_format(config)
|
798
|
-
|
799
|
-
@register_cli(name='quick_mode',
|
800
|
-
description='Run all the gsMap pipeline in quick mode',
|
801
|
-
add_args_function=add_run_all_mode_args)
|
802
|
-
def run_all_mode_from_cli(args: argparse.Namespace):
|
803
|
-
from gsMap.run_all_mode import run_pipeline
|
804
|
-
config = get_dataclass_from_parser(args, RunAllModeConfig)
|
805
|
-
run_pipeline(config)
|
1
|
+
import sys
|
2
|
+
import argparse
|
3
|
+
import logging
|
4
|
+
from collections import OrderedDict, namedtuple
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from pathlib import Path
|
7
|
+
from pprint import pprint
|
8
|
+
from typing import Callable
|
9
|
+
from typing import Union, Literal, Tuple, Optional, List
|
10
|
+
from functools import wraps
|
11
|
+
import pyfiglet
|
12
|
+
|
13
|
+
from gsMap.__init__ import __version__
|
14
|
+
|
15
|
+
# Global registry to hold functions
|
16
|
+
cli_function_registry = OrderedDict()
|
17
|
+
subcommand = namedtuple('subcommand', ['name', 'func', 'add_args_function', 'description'])
|
18
|
+
|
19
|
+
|
20
|
+
def get_gsMap_logger(logger_name):
|
21
|
+
logger = logging.getLogger(logger_name)
|
22
|
+
logger.setLevel(logging.DEBUG)
|
23
|
+
handler = logging.StreamHandler()
|
24
|
+
handler.setFormatter(logging.Formatter(
|
25
|
+
'[{asctime}] {levelname:.5s} | {name} - {message}', style='{'))
|
26
|
+
logger.addHandler(handler)
|
27
|
+
return logger
|
28
|
+
|
29
|
+
logger = get_gsMap_logger('gsMap')
|
30
|
+
|
31
|
+
# Decorator to register functions for cli parsing
|
32
|
+
def register_cli(name: str, description: str, add_args_function: Callable) -> Callable:
|
33
|
+
def decorator(func: Callable) -> Callable:
|
34
|
+
def wrapper(*args, **kwargs):
|
35
|
+
name.replace('_', ' ')
|
36
|
+
gsMap_main_logo = pyfiglet.figlet_format("gsMap", font='doom', width=80, justify='center', ).rstrip()
|
37
|
+
print(gsMap_main_logo, flush=True)
|
38
|
+
version_number = 'Version: ' + __version__
|
39
|
+
print(version_number.center(80), flush=True)
|
40
|
+
print('=' * 80, flush=True)
|
41
|
+
logger.info(f"Running {name}...")
|
42
|
+
func(*args, **kwargs)
|
43
|
+
logger.info(f"Finished running {name}.")
|
44
|
+
|
45
|
+
cli_function_registry[name] = subcommand(name=name, func=wrapper, add_args_function=add_args_function,
|
46
|
+
description=description)
|
47
|
+
return wrapper
|
48
|
+
|
49
|
+
return decorator
|
50
|
+
|
51
|
+
def add_shared_args(parser):
|
52
|
+
parser.add_argument('--workdir', type=str, required=True, help='Path to the working directory.')
|
53
|
+
parser.add_argument('--sample_name', type=str, required=True, help='Name of the sample.')
|
54
|
+
|
55
|
+
def add_find_latent_representations_args(parser):
|
56
|
+
add_shared_args(parser)
|
57
|
+
parser.add_argument('--input_hdf5_path', required=True, type=str, help='Path to the input HDF5 file.')
|
58
|
+
parser.add_argument('--annotation', required=True, type=str, help='Name of the annotation in adata.obs to use.')
|
59
|
+
parser.add_argument('--data_layer', type=str, default='counts', required=True,
|
60
|
+
help='Data layer for gene expression (e.g., "count", "counts", "log1p").')
|
61
|
+
parser.add_argument('--epochs', type=int, default=300, help='Number of training epochs.')
|
62
|
+
parser.add_argument('--feat_hidden1', type=int, default=256, help='Neurons in the first hidden layer.')
|
63
|
+
parser.add_argument('--feat_hidden2', type=int, default=128, help='Neurons in the second hidden layer.')
|
64
|
+
parser.add_argument('--gat_hidden1', type=int, default=64, help='Units in the first GAT hidden layer.')
|
65
|
+
parser.add_argument('--gat_hidden2', type=int, default=30, help='Units in the second GAT hidden layer.')
|
66
|
+
parser.add_argument('--p_drop', type=float, default=0.1, help='Dropout rate.')
|
67
|
+
parser.add_argument('--gat_lr', type=float, default=0.001, help='Learning rate for the GAT.')
|
68
|
+
parser.add_argument('--n_neighbors', type=int, default=11, help='Number of neighbors for GAT.')
|
69
|
+
parser.add_argument('--n_comps', type=int, default=300, help='Number of principal components for PCA.')
|
70
|
+
parser.add_argument('--weighted_adj', action='store_true', help='Use weighted adjacency in GAT.')
|
71
|
+
parser.add_argument('--convergence_threshold', type=float, default=1e-4, help='Threshold for convergence.')
|
72
|
+
parser.add_argument('--hierarchically', action='store_true', help='Enable hierarchical latent representation finding.')
|
73
|
+
|
74
|
+
|
75
|
+
def chrom_choice(value):
|
76
|
+
if value.isdigit():
|
77
|
+
ivalue = int(value)
|
78
|
+
if 1 <= ivalue <= 22:
|
79
|
+
return ivalue
|
80
|
+
elif value.lower() == 'all':
|
81
|
+
return value
|
82
|
+
else:
|
83
|
+
raise argparse.ArgumentTypeError(f"'{value}' is an invalid chromosome choice. Choose from 1-22 or 'all'.")
|
84
|
+
|
85
|
+
|
86
|
+
def filter_args_for_dataclass(args_dict, data_class: dataclass):
|
87
|
+
return {k: v for k, v in args_dict.items() if k in data_class.__dataclass_fields__}
|
88
|
+
|
89
|
+
|
90
|
+
def get_dataclass_from_parser(args: argparse.Namespace, data_class: dataclass):
|
91
|
+
remain_kwargs = filter_args_for_dataclass(vars(args), data_class)
|
92
|
+
print(f'Using the following arguments for {data_class.__name__}:', flush=True)
|
93
|
+
pprint(remain_kwargs, indent=4)
|
94
|
+
sys.stdout.flush()
|
95
|
+
return data_class(**remain_kwargs)
|
96
|
+
|
97
|
+
|
98
|
+
def add_latent_to_gene_args(parser):
|
99
|
+
add_shared_args(parser)
|
100
|
+
parser.add_argument('--annotation', type=str, help='Name of the annotation in adata.obs to use. (optional).')
|
101
|
+
parser.add_argument('--no_expression_fraction', action='store_true', help='Skip expression fraction filtering.')
|
102
|
+
parser.add_argument('--latent_representation', type=str, choices=['latent_GVAE', 'latent_PCA'], default='latent_GVAE',
|
103
|
+
help='Type of latent representation.')
|
104
|
+
parser.add_argument('--num_neighbour', type=int, default=21, help='Number of neighbors.')
|
105
|
+
parser.add_argument('--num_neighbour_spatial', type=int, default=101, help='Number of spatial neighbors.')
|
106
|
+
# parser.add_argument('--species', type=str, help='Species name for homolog gene mapping (optional).')
|
107
|
+
parser.add_argument('--homolog_file', type=str, help='Path to homologous gene conversion file (optional).')
|
108
|
+
|
109
|
+
|
110
|
+
def add_generate_ldscore_args(parser):
|
111
|
+
add_shared_args(parser)
|
112
|
+
parser.add_argument('--chrom', type=str, required=True, help='Chromosome id (1-22) or "all".')
|
113
|
+
parser.add_argument('--bfile_root', type=str, required=True, help='Root path for genotype plink bfiles (.bim, .bed, .fam).')
|
114
|
+
parser.add_argument('--keep_snp_root', type=str, required=True, help='Root path for SNP files.')
|
115
|
+
parser.add_argument('--gtf_annotation_file', type=str, required=True, help='Path to GTF annotation file.')
|
116
|
+
parser.add_argument('--gene_window_size', type=int, default=50000, help='Gene window size in base pairs.')
|
117
|
+
parser.add_argument('--enhancer_annotation_file', type=str, help='Path to enhancer annotation file (optional).')
|
118
|
+
parser.add_argument('--snp_multiple_enhancer_strategy', type=str, choices=['max_mkscore', 'nearest_TSS'], default='max_mkscore',
|
119
|
+
help='Strategy for handling multiple enhancers per SNP.')
|
120
|
+
parser.add_argument('--gene_window_enhancer_priority', type=str, choices=['gene_window_first', 'enhancer_first', 'enhancer_only'],
|
121
|
+
help='Priority between gene window and enhancer annotations.')
|
122
|
+
parser.add_argument('--spots_per_chunk', type=int, default=1000, help='Number of spots per chunk.')
|
123
|
+
parser.add_argument('--ld_wind', type=int, default=1, help='LD window size.')
|
124
|
+
parser.add_argument('--ld_unit', type=str, choices=['SNP', 'KB', 'CM'], default='CM', help='Unit for LD window.')
|
125
|
+
parser.add_argument('--additional_baseline_annotation', type=str, default=None, help='Path of additional baseline annotations')
|
126
|
+
|
127
|
+
|
128
|
+
def add_latent_to_gene_args(parser):
|
129
|
+
add_shared_args(parser)
|
130
|
+
parser.add_argument('--annotation', type=str, required=True, help='Name of the annotation layer.')
|
131
|
+
parser.add_argument('--no_expression_fraction', action='store_true', help='Skip expression fraction filtering.')
|
132
|
+
parser.add_argument('--latent_representation', type=str, choices=['latent_GVAE', 'latent_PCA'], default='latent_GVAE',
|
133
|
+
help='Type of latent representation.')
|
134
|
+
parser.add_argument('--num_neighbour', type=int, default=21, help='Number of neighbors.')
|
135
|
+
parser.add_argument('--num_neighbour_spatial', type=int, default=101, help='Number of spatial neighbors.')
|
136
|
+
# parser.add_argument('--species', type=str, help='Species name for homolog gene mapping (optional).')
|
137
|
+
parser.add_argument('--homolog_file', type=str, help='Path to homologous gene conversion file (optional).')
|
138
|
+
|
139
|
+
|
140
|
+
def add_spatial_ldsc_args(parser):
|
141
|
+
add_shared_args(parser)
|
142
|
+
parser.add_argument('--sumstats_file', type=str, required=True, help='Path to GWAS summary statistics file.')
|
143
|
+
parser.add_argument('--w_file', type=str, required=True, help='Path to regression weight file.')
|
144
|
+
parser.add_argument('--trait_name', type=str, required=True, help='Name of the trait being analyzed.')
|
145
|
+
parser.add_argument('--n_blocks', type=int, default=200, help='Number of blocks for jackknife resampling.')
|
146
|
+
parser.add_argument('--chisq_max', type=int, help='Maximum chi-square value for filtering SNPs.')
|
147
|
+
parser.add_argument('--num_processes', type=int, default=4, help='Number of processes for parallel computing.')
|
148
|
+
parser.add_argument('--use_additional_baseline_annotation', type=bool, nargs='?', const=True, default=True, help='Use additional baseline annotations when provided')
|
149
|
+
|
150
|
+
|
151
|
+
def add_Cauchy_combination_args(parser):
|
152
|
+
add_shared_args(parser)
|
153
|
+
parser.add_argument('--trait_name', type=str, required=True, help='Name of the trait being analyzed.')
|
154
|
+
parser.add_argument('--annotation', type=str, required=True, help='Name of the annotation in adata.obs to use.')
|
155
|
+
parser.add_argument('--meta', type=str, help='Optional meta information.')
|
156
|
+
parser.add_argument('--slide', type=str, help='Optional slide information.')
|
157
|
+
|
158
|
+
|
159
|
+
def add_report_args(parser):
|
160
|
+
add_shared_args(parser)
|
161
|
+
parser.add_argument('--trait_name', type=str, required=True, help='Name of the trait to generate the report for.')
|
162
|
+
parser.add_argument('--annotation', type=str, required=True, help='Annotation layer name.')
|
163
|
+
# parser.add_argument('--plot_type', type=str, choices=['manhattan', 'GSS', 'gsMap', 'all'], default='all',
|
164
|
+
# help="Type of diagnostic plot to generate. Choose from 'manhattan', 'GSS', 'gsMap', or 'all'.")
|
165
|
+
parser.add_argument('--top_corr_genes', type=int, default=50,
|
166
|
+
help='Number of top correlated genes to display.')
|
167
|
+
parser.add_argument('--selected_genes', type=str, nargs='*',
|
168
|
+
help='List of specific genes to include in the report (optional).')
|
169
|
+
parser.add_argument('--sumstats_file', type=str, required=True, help='Path to GWAS summary statistics file.')
|
170
|
+
|
171
|
+
# Optional arguments for customization
|
172
|
+
parser.add_argument('--fig_width', type=int, default=None, help='Width of the generated figures in pixels.')
|
173
|
+
parser.add_argument('--fig_height', type=int, default=None, help='Height of the generated figures in pixels.')
|
174
|
+
parser.add_argument('--point_size', type=int, default=None, help='Point size for the figures.')
|
175
|
+
parser.add_argument('--fig_style', type=str, default='light', choices=['dark', 'light'],
|
176
|
+
help='Style of the generated figures.')
|
177
|
+
|
178
|
+
def add_format_sumstats_args(parser):
|
179
|
+
# Required arguments
|
180
|
+
parser.add_argument('--sumstats', required=True, type=str,
|
181
|
+
help='Path to gwas summary data')
|
182
|
+
parser.add_argument('--out', required=True, type=str,
|
183
|
+
help='Path to save the formatted gwas data')
|
184
|
+
|
185
|
+
# Arguments for specify column name
|
186
|
+
parser.add_argument('--snp', default=None, type=str,
|
187
|
+
help="Name of snp column (if not a name that gsMap understands)")
|
188
|
+
parser.add_argument('--a1', default=None, type=str,
|
189
|
+
help="Name of effect allele column (if not a name that gsMap understands)")
|
190
|
+
parser.add_argument('--a2', default=None, type=str,
|
191
|
+
help="Name of none-effect allele column (if not a name that gsMap understands)")
|
192
|
+
parser.add_argument('--info', default=None, type=str,
|
193
|
+
help="Name of info column (if not a name that gsMap understands)")
|
194
|
+
parser.add_argument('--beta', default=None, type=str,
|
195
|
+
help="Name of gwas beta column (if not a name that gsMap understands).")
|
196
|
+
parser.add_argument('--se', default=None, type=str,
|
197
|
+
help="Name of gwas standar error of beta column (if not a name that gsMap understands)")
|
198
|
+
parser.add_argument('--p', default=None, type=str,
|
199
|
+
help="Name of p-value column (if not a name that gsMap understands)")
|
200
|
+
parser.add_argument('--frq', default=None, type=str,
|
201
|
+
help="Name of A1 ferquency column (if not a name that gsMap understands)")
|
202
|
+
parser.add_argument('--n', default=None, type=str,
|
203
|
+
help="Name of sample size column (if not a name that gsMap understands)")
|
204
|
+
parser.add_argument('--z', default=None, type=str,
|
205
|
+
help="Name of gwas Z-statistics column (if not a name that gsMap understands)")
|
206
|
+
parser.add_argument('--OR', default=None, type=str,
|
207
|
+
help="Name of gwas OR column (if not a name that gsMap understands)")
|
208
|
+
parser.add_argument('--se_OR', default=None, type=str,
|
209
|
+
help="Name of standar error of OR column (if not a name that gsMap understands)")
|
210
|
+
|
211
|
+
# Arguments for convert SNP (chr, pos) to rsid
|
212
|
+
parser.add_argument('--chr', default="Chr", type=str,
|
213
|
+
help="Name of SNP chromosome column (if not a name that gsMap understands)")
|
214
|
+
parser.add_argument('--pos', default="Pos", type=str,
|
215
|
+
help="Name of SNP positions column (if not a name that gsMap understands)")
|
216
|
+
parser.add_argument('--dbsnp', default=None, type=str,
|
217
|
+
help='Path to reference dnsnp file')
|
218
|
+
parser.add_argument('--chunksize', default=1e+6, type=int,
|
219
|
+
help='Chunk size for loading dbsnp file')
|
220
|
+
|
221
|
+
# Arguments for output format and quality
|
222
|
+
parser.add_argument('--format', default='gsMap', type=str,
|
223
|
+
help='Format of output data', choices=['gsMap', 'COJO'])
|
224
|
+
parser.add_argument('--info_min', default=0.9, type=float,
|
225
|
+
help='Minimum INFO score.')
|
226
|
+
parser.add_argument('--maf_min', default=0.01, type=float,
|
227
|
+
help='Minimum MAF.')
|
228
|
+
parser.add_argument('--keep_chr_pos', action='store_true', default=False,
|
229
|
+
help='Keep SNP chromosome and position columns in the output data')
|
230
|
+
|
231
|
+
def add_run_all_mode_args(parser):
|
232
|
+
add_shared_args(parser)
|
233
|
+
|
234
|
+
# Required paths and configurations
|
235
|
+
parser.add_argument('--gsMap_resource_dir', type=str, required=True,
|
236
|
+
help='Directory containing gsMap resources (e.g., genome annotations, LD reference panel, etc.).')
|
237
|
+
parser.add_argument('--hdf5_path', type=str, required=True,
|
238
|
+
help='Path to the input spatial transcriptomics data (H5AD format).')
|
239
|
+
parser.add_argument('--annotation', type=str, required=True,
|
240
|
+
help='Name of the annotation in adata.obs to use.')
|
241
|
+
parser.add_argument('--data_layer', type=str, default='counts', required=True,
|
242
|
+
help='Data layer for gene expression (e.g., "count", "counts", "log1p").')
|
243
|
+
|
244
|
+
# GWAS Data Parameters
|
245
|
+
parser.add_argument('--trait_name', type=str, help='Name of the trait for GWAS analysis (required if sumstats_file is provided).')
|
246
|
+
parser.add_argument('--sumstats_file', type=str,
|
247
|
+
help='Path to GWAS summary statistics file. Either sumstats_file or sumstats_config_file is required.')
|
248
|
+
parser.add_argument('--sumstats_config_file', type=str,
|
249
|
+
help='Path to GWAS summary statistics config file. Either sumstats_file or sumstats_config_file is required.')
|
250
|
+
|
251
|
+
# Homolog Data Parameters
|
252
|
+
parser.add_argument('--homolog_file', type=str,
|
253
|
+
help='Path to homologous gene for converting gene names from different species to human (optional, used for cross-species analysis).')
|
254
|
+
|
255
|
+
# Maximum number of processes
|
256
|
+
parser.add_argument('--max_processes', type=int, default=10,
|
257
|
+
help='Maximum number of processes for parallel execution.')
|
258
|
+
|
259
|
+
# # Optional paths for customization
|
260
|
+
# parser.add_argument('--bfile_root', type=str,
|
261
|
+
# help='Root path to PLINK bfiles (LD reference panel). If not provided, it will use the default in gsMap_resource_dir.')
|
262
|
+
# parser.add_argument('--keep_snp_root', type=str,
|
263
|
+
# help='Root path for SNP filtering. If not provided, it will use the default in gsMap_resource_dir.')
|
264
|
+
# parser.add_argument('--w_file', type=str,
|
265
|
+
# help='Path to the regression weight file. If not provided, it will use the default in gsMap_resource_dir.')
|
266
|
+
# parser.add_argument('--snp_gene_weight_adata_path', type=str,
|
267
|
+
# help='Path to the SNP-gene weight matrix file. If not provided, it will use the default in gsMap_resource_dir.')
|
268
|
+
# parser.add_argument('--baseline_annotation_dir', type=str,
|
269
|
+
# help='Directory containing the baseline annotations for quick mode. If not provided, it will use the default in gsMap_resource_dir.')
|
270
|
+
# parser.add_argument('--SNP_gene_pair_dir', type=str,
|
271
|
+
# help='Directory for SNP-gene pair data. If not provided, it will use the default in gsMap_resource_dir.')
|
272
|
+
|
273
|
+
|
274
|
+
def ensure_path_exists(func):
|
275
|
+
@wraps(func)
|
276
|
+
def wrapper(*args, **kwargs):
|
277
|
+
result = func(*args, **kwargs)
|
278
|
+
if isinstance(result, Path):
|
279
|
+
if result.suffix:
|
280
|
+
result.parent.mkdir(parents=True, exist_ok=True, mode=0o755)
|
281
|
+
else: # It's a directory path
|
282
|
+
result.mkdir(parents=True, exist_ok=True, mode=0o755)
|
283
|
+
return result
|
284
|
+
|
285
|
+
return wrapper
|
286
|
+
|
287
|
+
|
288
|
+
@dataclass
|
289
|
+
class ConfigWithAutoPaths:
|
290
|
+
workdir: str
|
291
|
+
sample_name: str
|
292
|
+
|
293
|
+
def __post_init__(self):
|
294
|
+
if self.workdir is None:
|
295
|
+
raise ValueError('workdir must be provided.')
|
296
|
+
|
297
|
+
@property
|
298
|
+
@ensure_path_exists
|
299
|
+
def hdf5_with_latent_path(self) -> Path:
|
300
|
+
return Path(f'{self.workdir}/{self.sample_name}/find_latent_representations/{self.sample_name}_add_latent.h5ad')
|
301
|
+
|
302
|
+
@property
|
303
|
+
@ensure_path_exists
|
304
|
+
def mkscore_feather_path(self) -> Path:
|
305
|
+
return Path(f'{self.workdir}/{self.sample_name}/latent_to_gene/{self.sample_name}_gene_marker_score.feather')
|
306
|
+
|
307
|
+
@property
|
308
|
+
@ensure_path_exists
|
309
|
+
def ldscore_save_dir(self) -> Path:
|
310
|
+
return Path(f'{self.workdir}/{self.sample_name}/generate_ldscore')
|
311
|
+
|
312
|
+
@property
|
313
|
+
@ensure_path_exists
|
314
|
+
def ldsc_save_dir(self) -> Path:
|
315
|
+
return Path(f'{self.workdir}/{self.sample_name}/spatial_ldsc')
|
316
|
+
|
317
|
+
@property
|
318
|
+
@ensure_path_exists
|
319
|
+
def cauchy_save_dir(self) -> Path:
|
320
|
+
return Path(f'{self.workdir}/{self.sample_name}/cauchy_combination')
|
321
|
+
|
322
|
+
@ensure_path_exists
|
323
|
+
def get_report_dir(self, trait_name: str) -> Path:
|
324
|
+
return Path(f'{self.workdir}/{self.sample_name}/report/{trait_name}')
|
325
|
+
|
326
|
+
def get_gsMap_report_file(self, trait_name: str) -> Path:
|
327
|
+
return self.get_report_dir(trait_name) / f'{self.sample_name}_{trait_name}_gsMap_Report.html'
|
328
|
+
|
329
|
+
@ensure_path_exists
|
330
|
+
def get_manhattan_html_plot_path(self, trait_name: str) -> Path:
|
331
|
+
return Path(
|
332
|
+
f'{self.workdir}/{self.sample_name}/report/{trait_name}/manhattan_plot/{self.sample_name}_{trait_name}_Diagnostic_Manhattan_Plot.html')
|
333
|
+
|
334
|
+
@ensure_path_exists
|
335
|
+
def get_GSS_plot_dir(self, trait_name: str) -> Path:
|
336
|
+
return Path(f'{self.workdir}/{self.sample_name}/report/{trait_name}/GSS_plot')
|
337
|
+
|
338
|
+
def get_GSS_plot_select_gene_file(self, trait_name: str) -> Path:
|
339
|
+
return self.get_GSS_plot_dir(trait_name) / 'plot_genes.csv'
|
340
|
+
|
341
|
+
@ensure_path_exists
|
342
|
+
def get_ldsc_result_file(self, trait_name: str) -> Path:
|
343
|
+
return Path(f'{self.ldsc_save_dir}/{self.sample_name}_{trait_name}.csv.gz')
|
344
|
+
|
345
|
+
@ensure_path_exists
|
346
|
+
def get_cauchy_result_file(self, trait_name: str) -> Path:
|
347
|
+
return Path(f'{self.cauchy_save_dir}/{self.sample_name}_{trait_name}.Cauchy.csv.gz')
|
348
|
+
|
349
|
+
@ensure_path_exists
|
350
|
+
def get_gene_diagnostic_info_save_path(self, trait_name: str) -> Path:
|
351
|
+
return Path(
|
352
|
+
f'{self.workdir}/{self.sample_name}/report/{trait_name}/{self.sample_name}_{trait_name}_Gene_Diagnostic_Info.csv')
|
353
|
+
|
354
|
+
@ensure_path_exists
|
355
|
+
def get_gsMap_plot_save_dir(self, trait_name: str) -> Path:
|
356
|
+
return Path(f'{self.workdir}/{self.sample_name}/report/{trait_name}/gsMap_plot')
|
357
|
+
|
358
|
+
def get_gsMap_html_plot_save_path(self, trait_name: str) -> Path:
|
359
|
+
return self.get_gsMap_plot_save_dir(trait_name) / f'{self.sample_name}_{trait_name}_gsMap_plot.html'
|
360
|
+
|
361
|
+
@dataclass
|
362
|
+
class FindLatentRepresentationsConfig(ConfigWithAutoPaths):
|
363
|
+
input_hdf5_path: str
|
364
|
+
# output_hdf5_path: str
|
365
|
+
annotation: str = None
|
366
|
+
data_layer: str = None
|
367
|
+
|
368
|
+
epochs: int = 300
|
369
|
+
feat_hidden1: int = 256
|
370
|
+
feat_hidden2: int = 128
|
371
|
+
feat_cell: int = 3000
|
372
|
+
gat_hidden1: int = 64
|
373
|
+
gat_hidden2: int = 30
|
374
|
+
p_drop: float = 0.1
|
375
|
+
gat_lr: float = 0.001
|
376
|
+
gcn_decay: float = 0.01
|
377
|
+
n_neighbors: int = 11
|
378
|
+
label_w: float = 1
|
379
|
+
rec_w: float = 1
|
380
|
+
input_pca: bool = True
|
381
|
+
n_comps: int = 300
|
382
|
+
weighted_adj: bool = False
|
383
|
+
nheads: int = 3
|
384
|
+
var: bool = False
|
385
|
+
convergence_threshold: float = 1e-4
|
386
|
+
hierarchically: bool = False
|
387
|
+
|
388
|
+
def __post_init__(self):
|
389
|
+
# self.output_hdf5_path = self.hdf5_with_latent_path
|
390
|
+
if self.hierarchically:
|
391
|
+
if self.annotation is None:
|
392
|
+
raise ValueError('annotation must be provided if hierarchically is True.')
|
393
|
+
logger.info(
|
394
|
+
f'------Hierarchical mode is enabled. This will find the latent representations within each annotation.')
|
395
|
+
|
396
|
+
# remind for not providing annotation
|
397
|
+
if self.annotation is None:
|
398
|
+
logger.warning(
|
399
|
+
'annotation is not provided. This will find the latent representations for the whole dataset.')
|
400
|
+
else:
|
401
|
+
logger.info(f'------Find latent representations for {self.annotation}...')
|
402
|
+
|
403
|
+
|
404
|
+
@dataclass
|
405
|
+
class LatentToGeneConfig(ConfigWithAutoPaths):
|
406
|
+
# input_hdf5_with_latent_path: str
|
407
|
+
# output_feather_path: str
|
408
|
+
no_expression_fraction: bool = False
|
409
|
+
latent_representation: str = 'latent_GVAE'
|
410
|
+
num_neighbour: int = 21
|
411
|
+
num_neighbour_spatial: int = 101
|
412
|
+
homolog_file: str = None
|
413
|
+
gM_slices: str = None
|
414
|
+
annotation: str = None
|
415
|
+
|
416
|
+
def __post_init__(self):
|
417
|
+
if self.homolog_file is not None:
|
418
|
+
logger.info(f"User provided homolog file to map gene names to human: {self.homolog_file}")
|
419
|
+
# check the format of the homolog file
|
420
|
+
with open(self.homolog_file, 'r') as f:
|
421
|
+
first_line = f.readline().strip()
|
422
|
+
_n_col = len(first_line.split())
|
423
|
+
if _n_col != 2:
|
424
|
+
raise ValueError(
|
425
|
+
f"Invalid homolog file format. Expected 2 columns, first column should be other species gene name, second column should be human gene name. "
|
426
|
+
f"Got {_n_col} columns in the first line.")
|
427
|
+
else:
|
428
|
+
first_col_name, second_col_name = first_line.split()
|
429
|
+
self.species = first_col_name
|
430
|
+
logger.info(
|
431
|
+
f"Homolog file provided and will map gene name from column1:{first_col_name} to column2:{second_col_name}")
|
432
|
+
else:
|
433
|
+
logger.info("No homolog file provided. Run in human mode.")
|
434
|
+
|
435
|
+
|
436
|
+
@dataclass
|
437
|
+
class GenerateLDScoreConfig(ConfigWithAutoPaths):
|
438
|
+
chrom: Union[int, str]
|
439
|
+
|
440
|
+
bfile_root: str
|
441
|
+
keep_snp_root: Optional[str]
|
442
|
+
|
443
|
+
# annotation by gene distance
|
444
|
+
gtf_annotation_file: str
|
445
|
+
gene_window_size: int = 50000
|
446
|
+
|
447
|
+
# annotation by enhancer
|
448
|
+
enhancer_annotation_file: str = None
|
449
|
+
snp_multiple_enhancer_strategy: Literal['max_mkscore', 'nearest_TSS'] = 'max_mkscore'
|
450
|
+
gene_window_enhancer_priority: Optional[Literal['gene_window_first', 'enhancer_first', 'enhancer_only',]] = None
|
451
|
+
|
452
|
+
# for calculating ld score
|
453
|
+
additional_baseline_annotation: str = None
|
454
|
+
spots_per_chunk: int = 1_000
|
455
|
+
ld_wind: int = 1
|
456
|
+
ld_unit: str = 'CM'
|
457
|
+
|
458
|
+
# zarr config
|
459
|
+
ldscore_save_format: Literal['feather', 'zarr', 'quick_mode'] = 'feather'
|
460
|
+
|
461
|
+
zarr_chunk_size: Tuple[int, int] = None
|
462
|
+
|
463
|
+
# for pre calculating the SNP Gene ldscore Weight
|
464
|
+
save_pre_calculate_snp_gene_weight_matrix: bool = False
|
465
|
+
|
466
|
+
baseline_annotation_dir: Optional[str] = None
|
467
|
+
SNP_gene_pair_dir: Optional[str] = None
|
468
|
+
def __post_init__(self):
|
469
|
+
# if self.mkscore_feather_file is None:
|
470
|
+
# self.mkscore_feather_file = self._get_mkscore_feather_path()
|
471
|
+
|
472
|
+
if self.enhancer_annotation_file is not None and self.gene_window_enhancer_priority is None:
|
473
|
+
logger.warning("enhancer_annotation_file is provided but gene_window_enhancer_priority is not provided. "
|
474
|
+
"by default, gene_window_enhancer_priority is set to 'enhancer_only', when enhancer_annotation_file is provided.")
|
475
|
+
self.gene_window_enhancer_priority = 'enhancer_only'
|
476
|
+
if self.enhancer_annotation_file is None and self.gene_window_enhancer_priority is not None:
|
477
|
+
logger.warning("gene_window_enhancer_priority is provided but enhancer_annotation_file is not provided. "
|
478
|
+
"by default, gene_window_enhancer_priority is set to None, when enhancer_annotation_file is not provided.")
|
479
|
+
self.gene_window_enhancer_priority = None
|
480
|
+
assert self.gene_window_enhancer_priority in [None, 'gene_window_first', 'enhancer_first', 'enhancer_only', ], \
|
481
|
+
f"gene_window_enhancer_priority must be one of None, 'gene_window_first', 'enhancer_first', 'enhancer_only', but got {self.gene_window_enhancer_priority}."
|
482
|
+
if self.gene_window_enhancer_priority in ['gene_window_first', 'enhancer_first']:
|
483
|
+
logger.info(f'Both gene_window and enhancer annotation will be used to calculate LD score. ')
|
484
|
+
logger.info(
|
485
|
+
f'SNP within +-{self.gene_window_size} bp of gene body will be used and enhancer annotation will be used to calculate LD score. If a snp maps to multiple enhancers, the strategy to choose by your select strategy: {self.snp_multiple_enhancer_strategy}.')
|
486
|
+
elif self.gene_window_enhancer_priority == 'enhancer_only':
|
487
|
+
logger.info(f'Only enhancer annotation will be used to calculate LD score. ')
|
488
|
+
else:
|
489
|
+
logger.info(
|
490
|
+
f'Only gene window annotation will be used to calculate LD score. SNP within +-{self.gene_window_size} bp of gene body will be used. ')
|
491
|
+
|
492
|
+
# remind for baseline annotation
|
493
|
+
if self.additional_baseline_annotation is None:
|
494
|
+
logger.info(f'------Baseline annotation is not provided. Default baseline annotation will be used.')
|
495
|
+
else:
|
496
|
+
logger.info(
|
497
|
+
f'------Baseline annotation is provided. Additional baseline annotation will be used with the default baseline annotation.')
|
498
|
+
logger.info(f'------Baseline annotation directory: {self.additional_baseline_annotation}')
|
499
|
+
# check the existence of baseline annotation
|
500
|
+
if self.chrom == 'all':
|
501
|
+
for chrom in range(1, 23):
|
502
|
+
chrom = str(chrom)
|
503
|
+
baseline_annotation_path = Path(
|
504
|
+
self.additional_baseline_annotation) / f'baseline.{chrom}.annot.gz'
|
505
|
+
if not baseline_annotation_path.exists():
|
506
|
+
raise FileNotFoundError(
|
507
|
+
f'baseline.{chrom}.annot.gz is not found in {self.additional_baseline_annotation}.')
|
508
|
+
else:
|
509
|
+
baseline_annotation_path = Path(
|
510
|
+
self.additional_baseline_annotation) / f'baseline.{self.chrom}.annot.gz'
|
511
|
+
if not baseline_annotation_path.exists():
|
512
|
+
raise FileNotFoundError(
|
513
|
+
f'baseline.{self.chrom}.annot.gz is not found in {self.additional_baseline_annotation}.')
|
514
|
+
|
515
|
+
# set the default zarr chunk size
|
516
|
+
if self.ldscore_save_format == 'zarr' and self.zarr_chunk_size is None:
|
517
|
+
self.zarr_chunk_size = (10_000, self.spots_per_chunk)
|
518
|
+
|
519
|
+
|
520
|
+
@dataclass
|
521
|
+
class SpatialLDSCConfig(ConfigWithAutoPaths):
|
522
|
+
w_file: str
|
523
|
+
# ldscore_save_dir: str
|
524
|
+
use_additional_baseline_annotation: bool = True
|
525
|
+
trait_name: Optional[str] = None
|
526
|
+
sumstats_file: Optional[str] = None
|
527
|
+
sumstats_config_file: Optional[str] = None
|
528
|
+
num_processes: int = 4
|
529
|
+
not_M_5_50: bool = False
|
530
|
+
n_blocks: int = 200
|
531
|
+
chisq_max: Optional[int] = None
|
532
|
+
all_chunk: Optional[int] = None
|
533
|
+
chunk_range: Optional[Tuple[int, int]] = None
|
534
|
+
|
535
|
+
ldscore_save_format: Literal['feather', 'zarr', 'quick_mode'] = 'feather'
|
536
|
+
|
537
|
+
spots_per_chunk_quick_mode: int = 1_000
|
538
|
+
snp_gene_weight_adata_path: Optional[str] = None
|
539
|
+
|
540
|
+
def __post_init__(self):
|
541
|
+
super().__post_init__()
|
542
|
+
if self.sumstats_file is None and self.sumstats_config_file is None:
|
543
|
+
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
544
|
+
if self.sumstats_file is not None and self.sumstats_config_file is not None:
|
545
|
+
raise ValueError('Only one of sumstats_file and sumstats_config_file must be provided.')
|
546
|
+
if self.sumstats_file is not None and self.trait_name is None:
|
547
|
+
raise ValueError('trait_name must be provided if sumstats_file is provided.')
|
548
|
+
if self.sumstats_config_file is not None and self.trait_name is not None:
|
549
|
+
raise ValueError('trait_name must not be provided if sumstats_config_file is provided.')
|
550
|
+
self.sumstats_config_dict = {}
|
551
|
+
# load the sumstats config file
|
552
|
+
if self.sumstats_config_file is not None:
|
553
|
+
import yaml
|
554
|
+
with open(self.sumstats_config_file) as f:
|
555
|
+
config = yaml.load(f, Loader=yaml.FullLoader)
|
556
|
+
for trait_name, sumstats_file in config.items():
|
557
|
+
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
558
|
+
# load the sumstats file
|
559
|
+
elif self.sumstats_file is not None:
|
560
|
+
self.sumstats_config_dict[self.trait_name] = self.sumstats_file
|
561
|
+
else:
|
562
|
+
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
563
|
+
|
564
|
+
for sumstats_file in self.sumstats_config_dict.values():
|
565
|
+
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
566
|
+
|
567
|
+
# check if additional baseline annotation is exist
|
568
|
+
# self.use_additional_baseline_annotation = False
|
569
|
+
|
570
|
+
if self.use_additional_baseline_annotation:
|
571
|
+
self.process_additional_baseline_annotation()
|
572
|
+
|
573
|
+
def process_additional_baseline_annotation(self):
|
574
|
+
additional_baseline_annotation = Path(self.ldscore_save_dir) / 'additional_baseline'
|
575
|
+
dir_exists = additional_baseline_annotation.exists()
|
576
|
+
|
577
|
+
if not dir_exists:
|
578
|
+
self.use_additional_baseline_annotation = False
|
579
|
+
# if self.use_additional_baseline_annotation:
|
580
|
+
# logger.warning(f"additional_baseline directory is not found in {self.ldscore_save_dir}.")
|
581
|
+
# print('''\
|
582
|
+
# if you want to use additional baseline annotation,
|
583
|
+
# please provide additional baseline annotation when calculating ld score.
|
584
|
+
# ''')
|
585
|
+
# raise FileNotFoundError(
|
586
|
+
# f'additional_baseline directory is not found.')
|
587
|
+
# return
|
588
|
+
# self.use_additional_baseline_annotation = self.use_additional_baseline_annotation or True
|
589
|
+
else:
|
590
|
+
logger.info(
|
591
|
+
f'------Additional baseline annotation is provided. It will be used with the default baseline annotation.')
|
592
|
+
logger.info(f'------Additional baseline annotation directory: {additional_baseline_annotation}')
|
593
|
+
|
594
|
+
chrom_list = range(1, 23)
|
595
|
+
for chrom in chrom_list:
|
596
|
+
baseline_annotation_path = additional_baseline_annotation / f'baseline.{chrom}.l2.ldscore.feather'
|
597
|
+
if not baseline_annotation_path.exists():
|
598
|
+
raise FileNotFoundError(
|
599
|
+
f'baseline.{chrom}.annot.gz is not found in {additional_baseline_annotation}.')
|
600
|
+
return None
|
601
|
+
|
602
|
+
|
603
|
+
@dataclass
|
604
|
+
class CauchyCombinationConfig(ConfigWithAutoPaths):
|
605
|
+
trait_name: str
|
606
|
+
annotation: str
|
607
|
+
meta: str = None
|
608
|
+
slide: str = None
|
609
|
+
|
610
|
+
|
611
|
+
@dataclass
|
612
|
+
class VisualizeConfig(ConfigWithAutoPaths):
|
613
|
+
trait_name: str
|
614
|
+
|
615
|
+
annotation: str = None
|
616
|
+
fig_title: str = None
|
617
|
+
fig_height: int = 600
|
618
|
+
fig_width: int = 800
|
619
|
+
point_size: int = None
|
620
|
+
fig_style: Literal['dark', 'light'] = 'light'
|
621
|
+
|
622
|
+
|
623
|
+
@dataclass
|
624
|
+
class DiagnosisConfig(ConfigWithAutoPaths):
|
625
|
+
annotation: str
|
626
|
+
# mkscore_feather_file: str
|
627
|
+
|
628
|
+
trait_name: str
|
629
|
+
sumstats_file: str
|
630
|
+
plot_type: Literal['manhattan', 'GSS', 'gsMap', 'all'] = 'all'
|
631
|
+
top_corr_genes: int = 50
|
632
|
+
selected_genes: Optional[List[str]] = None
|
633
|
+
|
634
|
+
fig_width: Optional[int] = None
|
635
|
+
fig_height: Optional[int] = None
|
636
|
+
point_size: Optional[int] = None
|
637
|
+
fig_style: Literal['dark', 'light'] = 'light'
|
638
|
+
|
639
|
+
def __post_init__(self):
|
640
|
+
if any([self.fig_width, self.fig_height, self.point_size]):
|
641
|
+
logger.info('Customizing the figure size and point size.')
|
642
|
+
assert all([self.fig_width, self.fig_height, self.point_size]), 'All of fig_width, fig_height, and point_size must be provided.'
|
643
|
+
self.customize_fig = True
|
644
|
+
else:
|
645
|
+
self.customize_fig = False
|
646
|
+
@dataclass
|
647
|
+
class ReportConfig(DiagnosisConfig):
|
648
|
+
pass
|
649
|
+
|
650
|
+
|
651
|
+
@dataclass
|
652
|
+
class RunAllModeConfig(ConfigWithAutoPaths):
|
653
|
+
gsMap_resource_dir: str
|
654
|
+
|
655
|
+
# == ST DATA PARAMETERS ==
|
656
|
+
hdf5_path: str
|
657
|
+
annotation: str
|
658
|
+
data_layer: str = 'X'
|
659
|
+
|
660
|
+
# ==GWAS DATA PARAMETERS==
|
661
|
+
trait_name: Optional[str] = None
|
662
|
+
sumstats_file: Optional[str] = None
|
663
|
+
sumstats_config_file: Optional[str] = None
|
664
|
+
|
665
|
+
# === homolog PARAMETERS ===
|
666
|
+
homolog_file: Optional[str] = None
|
667
|
+
|
668
|
+
max_processes: int = 10
|
669
|
+
|
670
|
+
def __post_init__(self):
|
671
|
+
super().__post_init__()
|
672
|
+
self.gtffile = f"{self.gsMap_resource_dir}/genome_annotation/gtf/gencode.v39lift37.annotation.gtf"
|
673
|
+
self.bfile_root = f"{self.gsMap_resource_dir}/LD_Reference_Panel/1000G_EUR_Phase3_plink/1000G.EUR.QC"
|
674
|
+
self.keep_snp_root = f"{self.gsMap_resource_dir}/LDSC_resource/hapmap3_snps/hm"
|
675
|
+
self.w_file = f"{self.gsMap_resource_dir}/LDSC_resource/weights_hm3_no_hla/weights."
|
676
|
+
self.snp_gene_weight_adata_path = f"{self.gsMap_resource_dir}/quick_mode/snp_gene_weight_matrix.h5ad"
|
677
|
+
self.baseline_annotation_dir = Path(f"{self.gsMap_resource_dir}/quick_mode/baseline").resolve()
|
678
|
+
self.SNP_gene_pair_dir = Path(f"{self.gsMap_resource_dir}/quick_mode/SNP_gene_pair").resolve()
|
679
|
+
# check the existence of the input files and resources files
|
680
|
+
for file in [self.hdf5_path, self.gtffile]:
|
681
|
+
if not Path(file).exists():
|
682
|
+
raise FileNotFoundError(f"File {file} does not exist.")
|
683
|
+
|
684
|
+
if self.sumstats_file is None and self.sumstats_config_file is None:
|
685
|
+
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
686
|
+
if self.sumstats_file is not None and self.sumstats_config_file is not None:
|
687
|
+
raise ValueError('Only one of sumstats_file and sumstats_config_file must be provided.')
|
688
|
+
if self.sumstats_file is not None and self.trait_name is None:
|
689
|
+
raise ValueError('trait_name must be provided if sumstats_file is provided.')
|
690
|
+
if self.sumstats_config_file is not None and self.trait_name is not None:
|
691
|
+
raise ValueError('trait_name must not be provided if sumstats_config_file is provided.')
|
692
|
+
self.sumstats_config_dict = {}
|
693
|
+
# load the sumstats config file
|
694
|
+
if self.sumstats_config_file is not None:
|
695
|
+
import yaml
|
696
|
+
with open(self.sumstats_config_file) as f:
|
697
|
+
config = yaml.load(f, Loader=yaml.FullLoader)
|
698
|
+
for trait_name, sumstats_file in config.items():
|
699
|
+
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
700
|
+
self.sumstats_config_dict[trait_name] = sumstats_file
|
701
|
+
# load the sumstats file
|
702
|
+
elif self.sumstats_file is not None and self.trait_name is not None:
|
703
|
+
self.sumstats_config_dict[self.trait_name] = self.sumstats_file
|
704
|
+
else:
|
705
|
+
raise ValueError('One of sumstats_file and sumstats_config_file must be provided.')
|
706
|
+
|
707
|
+
for sumstats_file in self.sumstats_config_dict.values():
|
708
|
+
assert Path(sumstats_file).exists(), f'{sumstats_file} does not exist.'
|
709
|
+
|
710
|
+
|
711
|
+
@dataclass
|
712
|
+
class FormatSumstatsConfig:
|
713
|
+
sumstats: str
|
714
|
+
out: str
|
715
|
+
dbsnp: str
|
716
|
+
snp: str = None
|
717
|
+
a1: str = None
|
718
|
+
a2: str = None
|
719
|
+
info: str = None
|
720
|
+
beta: str = None
|
721
|
+
se: str = None
|
722
|
+
p: str = None
|
723
|
+
frq: str = None
|
724
|
+
n: str = None
|
725
|
+
z: str = None
|
726
|
+
OR: str = None
|
727
|
+
se_OR: str = None
|
728
|
+
format: str = None
|
729
|
+
chr: str = None
|
730
|
+
pos: str = None
|
731
|
+
chunksize: int = 1e+7
|
732
|
+
info_min: float = 0.9
|
733
|
+
maf_min: float = 0.01
|
734
|
+
keep_chr_pos: bool = False
|
735
|
+
|
736
|
+
|
737
|
+
@register_cli(name='run_find_latent_representations',
|
738
|
+
description='Run Find_latent_representations \nFind the latent representations of each spot by running GNN-VAE',
|
739
|
+
add_args_function=add_find_latent_representations_args)
|
740
|
+
def run_find_latent_representation_from_cli(args: argparse.Namespace):
|
741
|
+
from gsMap.find_latent_representation import run_find_latent_representation
|
742
|
+
config = get_dataclass_from_parser(args, FindLatentRepresentationsConfig)
|
743
|
+
run_find_latent_representation(config)
|
744
|
+
|
745
|
+
|
746
|
+
@register_cli(name='run_latent_to_gene',
|
747
|
+
description='Run Latent_to_gene \nEstimate gene marker gene scores for each spot by using latent representations from nearby spots',
|
748
|
+
add_args_function=add_latent_to_gene_args)
|
749
|
+
def run_latent_to_gene_from_cli(args: argparse.Namespace):
|
750
|
+
from gsMap.latent_to_gene import run_latent_to_gene
|
751
|
+
config = get_dataclass_from_parser(args, LatentToGeneConfig)
|
752
|
+
run_latent_to_gene(config)
|
753
|
+
|
754
|
+
|
755
|
+
@register_cli(name='run_generate_ldscore',
|
756
|
+
description='Run Generate_ldscore \nGenerate LD scores for each spot',
|
757
|
+
add_args_function=add_generate_ldscore_args)
|
758
|
+
def run_generate_ldscore_from_cli(args: argparse.Namespace):
|
759
|
+
from gsMap.generate_ldscore import run_generate_ldscore
|
760
|
+
config = get_dataclass_from_parser(args, GenerateLDScoreConfig)
|
761
|
+
run_generate_ldscore(config)
|
762
|
+
|
763
|
+
|
764
|
+
@register_cli(name='run_spatial_ldsc',
|
765
|
+
description='Run Spatial_ldsc \nRun spatial LDSC for each spot',
|
766
|
+
add_args_function=add_spatial_ldsc_args)
|
767
|
+
def run_spatial_ldsc_from_cli(args: argparse.Namespace):
|
768
|
+
from gsMap.spatial_ldsc_multiple_sumstats import run_spatial_ldsc
|
769
|
+
config = get_dataclass_from_parser(args, SpatialLDSCConfig)
|
770
|
+
run_spatial_ldsc(config)
|
771
|
+
|
772
|
+
|
773
|
+
@register_cli(name='run_cauchy_combination',
|
774
|
+
description='Run Cauchy_combination for each annotation',
|
775
|
+
add_args_function=add_Cauchy_combination_args)
|
776
|
+
def run_Cauchy_combination_from_cli(args: argparse.Namespace):
|
777
|
+
from gsMap.cauchy_combination_test import run_Cauchy_combination
|
778
|
+
config = get_dataclass_from_parser(args, CauchyCombinationConfig)
|
779
|
+
run_Cauchy_combination(config)
|
780
|
+
|
781
|
+
|
782
|
+
@register_cli(name='run_report',
|
783
|
+
description='Run Report to generate diagnostic plots and tables',
|
784
|
+
add_args_function=add_report_args)
|
785
|
+
def run_Report_from_cli(args: argparse.Namespace):
|
786
|
+
from gsMap.report import run_report
|
787
|
+
config = get_dataclass_from_parser(args, ReportConfig)
|
788
|
+
run_report(config)
|
789
|
+
|
790
|
+
|
791
|
+
@register_cli(name='format_sumstats',
|
792
|
+
description='Format gwas summary statistics',
|
793
|
+
add_args_function=add_format_sumstats_args)
|
794
|
+
def gwas_format_from_cli(args: argparse.Namespace):
|
795
|
+
from gsMap.format_sumstats import gwas_format
|
796
|
+
config = get_dataclass_from_parser(args, FormatSumstatsConfig)
|
797
|
+
gwas_format(config)
|
798
|
+
|
799
|
+
@register_cli(name='quick_mode',
|
800
|
+
description='Run all the gsMap pipeline in quick mode',
|
801
|
+
add_args_function=add_run_all_mode_args)
|
802
|
+
def run_all_mode_from_cli(args: argparse.Namespace):
|
803
|
+
from gsMap.run_all_mode import run_pipeline
|
804
|
+
config = get_dataclass_from_parser(args, RunAllModeConfig)
|
805
|
+
run_pipeline(config)
|