G2PInsight 1.0.2__tar.gz → 1.0.4__tar.gz

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.
Files changed (24) hide show
  1. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/__init__.py +1 -1
  2. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/modeltraining.py +11 -11
  3. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/main.py +4 -4
  4. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight.egg-info/PKG-INFO +24 -15
  5. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight.egg-info/requires.txt +1 -0
  6. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/PKG-INFO +24 -15
  7. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/README.md +22 -14
  8. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/pyproject.toml +4 -1
  9. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/__init__.py +0 -0
  10. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/font_utils.py +0 -0
  11. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/gemma_gwas.py +0 -0
  12. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/plink_ld.py +0 -0
  13. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/preprocess.py +0 -0
  14. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/software/gemma-0.98.5-linux-static-AMD64 +0 -0
  15. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/software/plink +0 -0
  16. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight/bin/visualization.py +0 -0
  17. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight.egg-info/SOURCES.txt +0 -0
  18. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight.egg-info/dependency_links.txt +0 -0
  19. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight.egg-info/entry_points.txt +0 -0
  20. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/G2PInsight.egg-info/top_level.txt +0 -0
  21. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/LICENSE +0 -0
  22. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/MANIFEST.in +0 -0
  23. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/setup.cfg +0 -0
  24. {g2pinsight-1.0.2 → g2pinsight-1.0.4}/setup.py +0 -0
@@ -1,3 +1,3 @@
1
- __version__ = '1.0.2'
1
+ __version__ = '1.0.4'
2
2
  __author__ = 'chenrf'
3
3
  __email__ = '12024128035@stu.ynu.edu.cn'
@@ -124,7 +124,6 @@ SHAP_DEPENDENCE_MAX_FEATURES = 0
124
124
  TRAIN_SNP_WARN_THRESHOLD = 500_000
125
125
  TRAIN_SNP_STRONG_WARN_THRESHOLD = 1_000_000
126
126
  TRAIN_SVM_IMPRACTICAL_THRESHOLD = 10_000
127
- TRAIN_PARALLEL_BLOCK_THRESHOLD = 500_000
128
127
 
129
128
  def _resolve_shap_dependence_max_features(value: Optional[int]) -> int:
130
129
  if value is None:
@@ -303,7 +302,7 @@ def _verify_compressed_train_file(train_file: str, expected_n_samples: Optional[
303
302
  if data_rows != expected_n_samples:
304
303
  raise ValueError(f'Training matrix row count mismatch: file has {data_rows} data row(s), metadata expects {expected_n_samples} ({train_file}).\nRe-run preprocess to regenerate a complete training matrix.')
305
304
 
306
- def check_training_metadata_guardrails(metadata: Optional[Dict], *, model_type: Optional[str]=None, task_type: Optional[str]=None, parallel_models: Optional[int]=None, enable_hyperparameter_search: bool=True, n_folds: int=5, threads: Optional[int]=None) -> None:
305
+ def check_training_metadata_guardrails(metadata: Optional[Dict], *, model_type: Optional[str]=None, task_type: Optional[str]=None, parallel_models: Optional[int]=None, enable_hyperparameter_search: bool=False, n_folds: int=5, threads: Optional[int]=None) -> None:
307
306
  n_features, n_samples = _training_dimensions_from_metadata(metadata)
308
307
  file_format = (metadata or {}).get('train_file_format') or {}
309
308
  is_gzip = file_format.get('compression') == 'gzip' or str((metadata or {}).get('output_train_file', '')).endswith('.gz')
@@ -326,16 +325,14 @@ def check_training_metadata_guardrails(metadata: Optional[Dict], *, model_type:
326
325
  if is_gzip:
327
326
  logger.warning('[TRAIN] Training matrix is gzip-compressed; load is slower than uncompressed .txt. Re-run preprocess without compression if you train repeatedly.')
328
327
  parallel_count = 1 if parallel_models is None else max(1, int(parallel_models))
329
- if parallel_count > 1 and n_features >= TRAIN_PARALLEL_BLOCK_THRESHOLD:
330
- raise ValueError(f'train-all with --parallel_models {parallel_count} is not supported when n_feature_columns={n_features:,} (>= {TRAIN_PARALLEL_BLOCK_THRESHOLD:,}).\nEach worker materializes a full copy of the matrix and commonly OOMs.\nUse --parallel_models 1 and train models serially, or reduce SNPs in preprocess (-f 2 / -f 4).')
331
328
  if parallel_count > 1 and n_features >= HIGH_DIM_FEATURE_RF_THRESHOLD:
332
- logger.warning(f'[TRAIN] parallel_models={parallel_count} with {n_features:,} features: use --parallel_models 1 unless job memory is very large.')
329
+ logger.warning(f'[TRAIN] parallel_models={parallel_count} with {n_features:,} features: each worker holds a full matrix copy; ensure job memory is large enough or use --parallel_models 1.')
333
330
  if model_type == 'SVM' and n_features > TRAIN_SVM_IMPRACTICAL_THRESHOLD:
334
331
  logger.warning(f'[TRAIN] SVM on {n_features:,} features is usually impractical (time/memory). Prefer LightGBM for high-dimensional GWAS matrices.')
335
332
  if model_type == 'RandomForest' and n_features > HIGH_DIM_FEATURE_RF_THRESHOLD:
336
333
  logger.warning(f'[TRAIN] RandomForest on {n_features:,} features is slow at this scale; LightGBM/XGBoost are usually faster.')
337
334
  if enable_hyperparameter_search and n_features >= TRAIN_SNP_WARN_THRESHOLD:
338
- logger.warning('[TRAIN] Hyperparameter search on a very wide matrix is expensive. Consider --no_hyperparameter_search for a first run.')
335
+ logger.warning('[TRAIN] Hyperparameter search on a very wide matrix is expensive; omit --hyperparameter_search for a faster first run.')
339
336
  if n_folds > 3 and n_features >= TRAIN_SNP_WARN_THRESHOLD:
340
337
  logger.warning(f'[TRAIN] n_folds={n_folds} multiplies full-matrix fits. Consider --n_folds 3 on high-dimensional data.')
341
338
  if threads is not None and int(threads) > 4 and n_features >= TRAIN_SNP_WARN_THRESHOLD:
@@ -1643,7 +1640,7 @@ def save_training_results(model: Any, metrics: Dict, selected_snps: List[str], o
1643
1640
  logger.warning(f' Failed to save plotting metadata JSON (ignored): {e}')
1644
1641
  logger.info(f' Results saved successfully: {model_dir}')
1645
1642
 
1646
- def run_single_model(input_path: str, model_type: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, preloaded_data: Optional[Dict[str, Any]]=None, cpu_cores: Optional[int]=None, enable_hyperparameter_search: bool=True, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None, ignore_warnings: bool=False) -> int:
1643
+ def run_single_model(input_path: str, model_type: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, preloaded_data: Optional[Dict[str, Any]]=None, cpu_cores: Optional[int]=None, enable_hyperparameter_search: bool=False, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None, ignore_warnings: bool=False) -> int:
1647
1644
  if not str(input_path).endswith('_metadata.json'):
1648
1645
  raise ValueError(f'Training input must be the preprocess-generated metadata file (*_metadata.json).Current input: {input_path}')
1649
1646
  if preloaded_data is not None and preloaded_data.get('ignore_warnings'):
@@ -1654,7 +1651,7 @@ def run_single_model(input_path: str, model_type: str, output_dir: str, task_typ
1654
1651
  finally:
1655
1652
  _exit_train_ignore_warnings(warn_state)
1656
1653
 
1657
- def _run_single_model_body(input_path: str, model_type: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, preloaded_data: Optional[Dict[str, Any]]=None, cpu_cores: Optional[int]=None, enable_hyperparameter_search: bool=True, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None) -> int:
1654
+ def _run_single_model_body(input_path: str, model_type: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, preloaded_data: Optional[Dict[str, Any]]=None, cpu_cores: Optional[int]=None, enable_hyperparameter_search: bool=False, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None) -> int:
1658
1655
  if not str(input_path).endswith('_metadata.json'):
1659
1656
  raise ValueError(f'Training input must be the preprocess-generated metadata file (*_metadata.json).Current input: {input_path}')
1660
1657
  cpu_cores_value = DEFAULT_N_JOBS if cpu_cores is None else max(1, int(cpu_cores))
@@ -1816,7 +1813,10 @@ def _run_single_model_body(input_path: str, model_type: str, output_dir: str, ta
1816
1813
  cv_fold_metrics, cv_oof_data, avg_metrics = _run_kfold_cv_evaluation(model_type=model_type, task_type=task_type, X_train_cv=X_train_cv, y_train_cv=y_train_cv, best_params=best_params, n_folds=n_folds, random_state=random_state, cpu_cores=cpu_cores)
1817
1814
  logger.info(f'{n_folds}-fold cross-validation average results (training set only):')
1818
1815
  logger.info(f' {avg_metrics}')
1819
- logger.info('Training final model on training set with tuned hyperparameters...')
1816
+ if enable_hyperparameter_search:
1817
+ logger.info('Training final model on training set with tuned hyperparameters...')
1818
+ else:
1819
+ logger.info('Training final model on training set with default hyperparameters...')
1820
1820
  final_model = _fit_model_with_params(model_type=model_type, task_type=task_type, X_train=X_train_cv, y_train=y_train_cv, best_params=best_params, random_state=random_state, cpu_cores=cpu_cores)
1821
1821
  logger.info('Final model training completed; evaluating on held-out test set...')
1822
1822
  y_pred_test, y_prob_test = _predict_with_optional_proba(final_model, X_test_holdout, task_type)
@@ -1993,14 +1993,14 @@ def _run_train_all_models_serial(supported_models: List[str], input_path: str, o
1993
1993
  logger.warning(f'[TRAIN_ALL] {len(failed_models)} model(s) failed and were skipped: {", ".join(failed_models)}')
1994
1994
  return results
1995
1995
 
1996
- def run_all_models(input_path: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, parallel_models: Optional[int]=None, threads: Optional[int]=None, enable_hyperparameter_search: bool=True, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None, ignore_warnings: bool=False) -> int:
1996
+ def run_all_models(input_path: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, parallel_models: Optional[int]=None, threads: Optional[int]=None, enable_hyperparameter_search: bool=False, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None, ignore_warnings: bool=False) -> int:
1997
1997
  warn_state = _enter_train_ignore_warnings(ignore_warnings)
1998
1998
  try:
1999
1999
  return _run_all_models_body(input_path=input_path, output_dir=output_dir, task_type=task_type, n_folds=n_folds, random_state=random_state, parallel_models=parallel_models, threads=threads, enable_hyperparameter_search=enable_hyperparameter_search, group_file=group_file, train_ids_file=train_ids_file, test_ids_file=test_ids_file, shap_dependence_max_features=shap_dependence_max_features, ignore_warnings=ignore_warnings)
2000
2000
  finally:
2001
2001
  _exit_train_ignore_warnings(warn_state)
2002
2002
 
2003
- def _run_all_models_body(input_path: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, parallel_models: Optional[int]=None, threads: Optional[int]=None, enable_hyperparameter_search: bool=True, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None, ignore_warnings: bool=False) -> int:
2003
+ def _run_all_models_body(input_path: str, output_dir: str, task_type: Optional[str]=None, n_folds: int=5, random_state: int=42, parallel_models: Optional[int]=None, threads: Optional[int]=None, enable_hyperparameter_search: bool=False, group_file: Optional[str]=None, train_ids_file: Optional[str]=None, test_ids_file: Optional[str]=None, shap_dependence_max_features: Optional[int]=None, ignore_warnings: bool=False) -> int:
2004
2004
  if not str(input_path).endswith('_metadata.json'):
2005
2005
  raise ValueError(f'Training input must be the preprocess-generated metadata file (*_metadata.json).Current input: {input_path}')
2006
2006
  input_info = parse_train_input_path(input_path)
@@ -209,9 +209,9 @@ def setup_argparse() -> argparse.ArgumentParser:
209
209
  train_parser.add_argument('--test-ids-file', dest='test_ids_file', default=None, help='Optional one-column sample ID file for the test set (must be used with --train-ids-file; mutually exclusive with --group-file)')
210
210
  train_parser.add_argument('--threads', type=int, default=None, help='CPU threads per model (n_jobs/thread_count and BLAS backends; default: 1)')
211
211
  train_parser.add_argument('--shap_dependence_top', type=int, default=None, help='Max SNPs saved in *_shap_dependence.tsv when feature count > 1000 (default: 0 = save all features; set e.g. 50 to cap for disk use; should be >= visualize --top_snps when capped)')
212
- train_parser.add_argument('--no_hyperparameter_search', dest='hyperparameter_search', action='store_false', help='Disable RandomizedSearchCV hyperparameter grid search (default: enabled)')
212
+ train_parser.add_argument('--hyperparameter_search', dest='hyperparameter_search', action='store_true', help='Enable RandomizedSearchCV hyperparameter grid search (default: disabled; uses model defaults)')
213
213
  train_parser.add_argument('--ignore-warnings', dest='ignore_warnings', action='store_true', help='Suppress Python warnings and [WARNING] log messages during training (errors still shown)')
214
- train_parser.set_defaults(hyperparameter_search=True, ignore_warnings=False, func=run_training)
214
+ train_parser.set_defaults(hyperparameter_search=False, ignore_warnings=False, func=run_training)
215
215
  train_all_parser = subparsers.add_parser('train-all', help='Train all models and compare performance (GWAS/LD filtering should be done in preprocess stage)')
216
216
  train_all_parser.add_argument('-j', '--json', required=True, help='Preprocess metadata JSON file (*_metadata.json). Training is metadata-driven; direct .txt input is not supported.')
217
217
  train_all_parser.add_argument('--task_type', required=False, choices=['classification', 'regression'], help="Task type (classification/regression). If not specified, will be automatically read from metadata or default to 'regression'")
@@ -224,9 +224,9 @@ def setup_argparse() -> argparse.ArgumentParser:
224
224
  train_all_parser.add_argument('--parallel_models', type=int, default=1, help='Number of models to train in parallel (default: 1, serial). Increase only when sufficient memory/CPU is available')
225
225
  train_all_parser.add_argument('--threads', type=int, default=None, help='CPU threads per model process (default: 1; use with care when --parallel_models > 1)')
226
226
  train_all_parser.add_argument('--shap_dependence_top', type=int, default=None, help='Max SNPs saved in *_shap_dependence.tsv when feature count > 1000 (default: 0 = save all features; set e.g. 50 to cap for disk use; should be >= visualize --top_snps when capped)')
227
- train_all_parser.add_argument('--no_hyperparameter_search', dest='hyperparameter_search', action='store_false', help='Disable RandomizedSearchCV hyperparameter grid search (default: enabled)')
227
+ train_all_parser.add_argument('--hyperparameter_search', dest='hyperparameter_search', action='store_true', help='Enable RandomizedSearchCV hyperparameter grid search (default: disabled; uses model defaults)')
228
228
  train_all_parser.add_argument('--ignore-warnings', dest='ignore_warnings', action='store_true', help='Suppress Python warnings and [WARNING] log messages during training (errors still shown)')
229
- train_all_parser.set_defaults(hyperparameter_search=True, ignore_warnings=False, func=run_train_all)
229
+ train_all_parser.set_defaults(hyperparameter_search=False, ignore_warnings=False, func=run_train_all)
230
230
  predict_parser = subparsers.add_parser('predict', help='Predict using trained model')
231
231
  predict_parser.add_argument('-i', '--input', required=True, help='Input data for prediction')
232
232
  predict_parser.add_argument('-m', '--model', required=True, help='Path to trained model file (.pkl), e.g. /path/to/LightGBM_model.pkl')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: G2PInsight
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Genome-wide association analysis toolkit
5
5
  Author-email: chenrf <12024128035@stu.ynu.edu.cn>
6
6
  License: MIT
@@ -27,6 +27,7 @@ Requires-Dist: scikit-learn>=1.0
27
27
  Requires-Dist: scipy<2.0,>=1.7
28
28
  Requires-Dist: joblib>=1.0
29
29
  Requires-Dist: matplotlib>=3.5
30
+ Requires-Dist: Pillow<12.3,>=8
30
31
  Requires-Dist: numba>=0.57
31
32
  Requires-Dist: lightgbm<4.0.0,>=3.3
32
33
  Requires-Dist: xgboost<2.1,>=1.6
@@ -95,7 +96,7 @@ Supports **classification** and **regression**, optional **GWAS/LD** SNP filteri
95
96
  - **train** / **train-all** need preprocess `*_metadata.json` (`-j`) — not a raw matrix path alone.
96
97
  - GWAS/LD runs in **preprocess only**; training does not re-filter SNPs.
97
98
  - Wait for preprocess to finish (`Metadata file generated successfully`) before training. In batch jobs, use `set -e`.
98
- - **Large SNP counts:** use preprocess `-f 2`–`4`, **train-all** with `--parallel_models 1`; metadata guardrails **block `--parallel_models > 1` when 500k SNPs**.
99
+ - **Large SNP counts:** use preprocess `-f 2`–`4`; for **train-all**, prefer `--parallel_models 1` unless job memory is very large (each parallel worker copies the full matrix).
99
100
 
100
101
  ---
101
102
 
@@ -121,7 +122,7 @@ G2PInsight visualize \
121
122
  ```bash
122
123
  G2PInsight preprocess --bfile genotype -p pheno.txt -o results/ -f 4
123
124
  G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
124
- --parallel_models 1 --threads 4 --no_hyperparameter_search --n_folds 3
125
+ --parallel_models 1 --threads 4 --n_folds 3
125
126
  ```
126
127
 
127
128
  ---
@@ -226,7 +227,7 @@ G2PInsight preprocess --bfile genotype -p pheno.txt -o results/ -f 4 --no-cache
226
227
 
227
228
  #### What it is
228
229
 
229
- Trains **one** ML model from `*_metadata.json`: load matrix → split samples → tune hyperparameters (optional) → K-fold CV on training split → final fit → test metrics → SHAP.
230
+ Trains **one** ML model from `*_metadata.json`: load matrix → split samples → optional hyperparameter search → K-fold CV on training split → final fit → test metrics → SHAP.
230
231
 
231
232
  #### What you get
232
233
 
@@ -246,17 +247,21 @@ G2PInsight train -j <metadata.json> -m <model> -o <output_dir> [options]
246
247
  ```
247
248
 
248
249
  ```bash
249
- # Default
250
+ # Default (no hyperparameter search)
250
251
  G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/
251
252
 
252
253
  # Large SNP set
253
254
  G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/ \
254
- --no_hyperparameter_search --n_folds 3 --threads 4
255
+ --n_folds 3 --threads 4
256
+
257
+ # Enable hyperparameter search
258
+ G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/ \
259
+ --hyperparameter_search
255
260
  ```
256
261
 
257
- **Evaluation (default):** 80% train / 20% test. Tuning and K-fold CV use the 80% only; `{Model}_metrics.json` reports the 20% test set.
262
+ **Evaluation (default):** 80% train / 20% test. Optional tuning and K-fold CV use the 80% only; `{Model}_metrics.json` reports the 20% test set.
258
263
 
259
- **Flow:** hyperparameter search (internal 80/20 of train) → K-fold CV on full train split → final fit → SHAP.
264
+ **Flow (default):** K-fold CV on train split (model defaults) → final fit → SHAP. With `--hyperparameter_search`: hold-out tuning on the train split → CV → final fit → SHAP.
260
265
 
261
266
  #### Key options
262
267
 
@@ -264,7 +269,7 @@ G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o resu
264
269
  |--------|---------|-------|
265
270
  | `-m` | — | `LightGBM`, `RandomForest`, `XGBoost`, `SVM`, `CatBoost`, `Logistic` |
266
271
  | `--n_folds` | `5` | CV folds on training split |
267
- | `--no_hyperparameter_search` | off | Faster; recommended for huge matrices |
272
+ | `--hyperparameter_search` | off | Opt-in RandomizedSearchCV; off by default for speed |
268
273
  | `--threads` | `1` | Per-model CPU threads |
269
274
  | `--shap_dependence_top` | `0` | Cap SNPs in dependence file (`0` = all) |
270
275
  | `--ignore-warnings` | off | Hide `[WARNING]` logs and Python warnings (errors still shown) |
@@ -305,10 +310,14 @@ G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
305
310
 
306
311
  # Millions of SNPs
307
312
  G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
308
- --parallel_models 1 --threads 4 --no_hyperparameter_search --n_folds 3
313
+ --parallel_models 1 --threads 4 --n_folds 3
314
+
315
+ # Opt-in hyperparameter search
316
+ G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
317
+ --hyperparameter_search --parallel_models 1 --threads 4
309
318
  ```
310
319
 
311
- Shares **train** options except `-m`. Extra: `--parallel_models` (default `1`), `--ignore-warnings`. **≥ 500k SNPs + `parallel_models > 1` is rejected** before load.
320
+ Shares **train** options except `-m`. Extra: `--parallel_models` (default `1`), `--ignore-warnings`. On very wide matrices, high `--parallel_models` needs substantial RAM (each worker copies the full matrix).
312
321
 
313
322
  ---
314
323
 
@@ -421,20 +430,20 @@ By design: best model by **test set** metrics. See `best_model_info.json` and `a
421
430
 
422
431
  ### train-all fails or workers crash
423
432
 
424
- **Symptom:** `Worker process crashed`, all models `failed`, or error about `--parallel_models` and SNP count.
433
+ **Symptom:** `Worker process crashed`, all models `failed`, or OOM under high `--parallel_models`.
425
434
 
426
435
  **What to do:**
427
436
 
428
- 1. **≥ 500k SNPs:** use `--parallel_models 1` only (higher values are blocked).
437
+ 1. **≥ 500k SNPs:** prefer `--parallel_models 1` unless memory is abundant.
429
438
  2. Re-run preprocess with **`-f 2` or `-f 4`** if too many SNPs were kept.
430
- 3. Use `--parallel_models 1 --no_hyperparameter_search --n_folds 3 --threads 4`.
439
+ 3. Use `--parallel_models 1 --n_folds 3 --threads 4` (omit `--hyperparameter_search`).
431
440
  4. Increase job memory; or test one model: `G2PInsight train -m LightGBM ...`.
432
441
 
433
442
  ### Training or load is very slow
434
443
 
435
444
  1. Fewer SNPs in preprocess (`-f 2`–`4`).
436
445
  2. Uncompressed `*_train_data.txt` loads faster than `.gz`.
437
- 3. `--no_hyperparameter_search`, lower `--n_folds`.
446
+ 3. Keep default (no `--hyperparameter_search`), lower `--n_folds`.
438
447
 
439
448
  ### SHAP dependence file is huge
440
449
 
@@ -4,6 +4,7 @@ scikit-learn>=1.0
4
4
  scipy<2.0,>=1.7
5
5
  joblib>=1.0
6
6
  matplotlib>=3.5
7
+ Pillow<12.3,>=8
7
8
  numba>=0.57
8
9
  lightgbm<4.0.0,>=3.3
9
10
  xgboost<2.1,>=1.6
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: G2PInsight
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Genome-wide association analysis toolkit
5
5
  Author-email: chenrf <12024128035@stu.ynu.edu.cn>
6
6
  License: MIT
@@ -27,6 +27,7 @@ Requires-Dist: scikit-learn>=1.0
27
27
  Requires-Dist: scipy<2.0,>=1.7
28
28
  Requires-Dist: joblib>=1.0
29
29
  Requires-Dist: matplotlib>=3.5
30
+ Requires-Dist: Pillow<12.3,>=8
30
31
  Requires-Dist: numba>=0.57
31
32
  Requires-Dist: lightgbm<4.0.0,>=3.3
32
33
  Requires-Dist: xgboost<2.1,>=1.6
@@ -95,7 +96,7 @@ Supports **classification** and **regression**, optional **GWAS/LD** SNP filteri
95
96
  - **train** / **train-all** need preprocess `*_metadata.json` (`-j`) — not a raw matrix path alone.
96
97
  - GWAS/LD runs in **preprocess only**; training does not re-filter SNPs.
97
98
  - Wait for preprocess to finish (`Metadata file generated successfully`) before training. In batch jobs, use `set -e`.
98
- - **Large SNP counts:** use preprocess `-f 2`–`4`, **train-all** with `--parallel_models 1`; metadata guardrails **block `--parallel_models > 1` when 500k SNPs**.
99
+ - **Large SNP counts:** use preprocess `-f 2`–`4`; for **train-all**, prefer `--parallel_models 1` unless job memory is very large (each parallel worker copies the full matrix).
99
100
 
100
101
  ---
101
102
 
@@ -121,7 +122,7 @@ G2PInsight visualize \
121
122
  ```bash
122
123
  G2PInsight preprocess --bfile genotype -p pheno.txt -o results/ -f 4
123
124
  G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
124
- --parallel_models 1 --threads 4 --no_hyperparameter_search --n_folds 3
125
+ --parallel_models 1 --threads 4 --n_folds 3
125
126
  ```
126
127
 
127
128
  ---
@@ -226,7 +227,7 @@ G2PInsight preprocess --bfile genotype -p pheno.txt -o results/ -f 4 --no-cache
226
227
 
227
228
  #### What it is
228
229
 
229
- Trains **one** ML model from `*_metadata.json`: load matrix → split samples → tune hyperparameters (optional) → K-fold CV on training split → final fit → test metrics → SHAP.
230
+ Trains **one** ML model from `*_metadata.json`: load matrix → split samples → optional hyperparameter search → K-fold CV on training split → final fit → test metrics → SHAP.
230
231
 
231
232
  #### What you get
232
233
 
@@ -246,17 +247,21 @@ G2PInsight train -j <metadata.json> -m <model> -o <output_dir> [options]
246
247
  ```
247
248
 
248
249
  ```bash
249
- # Default
250
+ # Default (no hyperparameter search)
250
251
  G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/
251
252
 
252
253
  # Large SNP set
253
254
  G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/ \
254
- --no_hyperparameter_search --n_folds 3 --threads 4
255
+ --n_folds 3 --threads 4
256
+
257
+ # Enable hyperparameter search
258
+ G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/ \
259
+ --hyperparameter_search
255
260
  ```
256
261
 
257
- **Evaluation (default):** 80% train / 20% test. Tuning and K-fold CV use the 80% only; `{Model}_metrics.json` reports the 20% test set.
262
+ **Evaluation (default):** 80% train / 20% test. Optional tuning and K-fold CV use the 80% only; `{Model}_metrics.json` reports the 20% test set.
258
263
 
259
- **Flow:** hyperparameter search (internal 80/20 of train) → K-fold CV on full train split → final fit → SHAP.
264
+ **Flow (default):** K-fold CV on train split (model defaults) → final fit → SHAP. With `--hyperparameter_search`: hold-out tuning on the train split → CV → final fit → SHAP.
260
265
 
261
266
  #### Key options
262
267
 
@@ -264,7 +269,7 @@ G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o resu
264
269
  |--------|---------|-------|
265
270
  | `-m` | — | `LightGBM`, `RandomForest`, `XGBoost`, `SVM`, `CatBoost`, `Logistic` |
266
271
  | `--n_folds` | `5` | CV folds on training split |
267
- | `--no_hyperparameter_search` | off | Faster; recommended for huge matrices |
272
+ | `--hyperparameter_search` | off | Opt-in RandomizedSearchCV; off by default for speed |
268
273
  | `--threads` | `1` | Per-model CPU threads |
269
274
  | `--shap_dependence_top` | `0` | Cap SNPs in dependence file (`0` = all) |
270
275
  | `--ignore-warnings` | off | Hide `[WARNING]` logs and Python warnings (errors still shown) |
@@ -305,10 +310,14 @@ G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
305
310
 
306
311
  # Millions of SNPs
307
312
  G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
308
- --parallel_models 1 --threads 4 --no_hyperparameter_search --n_folds 3
313
+ --parallel_models 1 --threads 4 --n_folds 3
314
+
315
+ # Opt-in hyperparameter search
316
+ G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
317
+ --hyperparameter_search --parallel_models 1 --threads 4
309
318
  ```
310
319
 
311
- Shares **train** options except `-m`. Extra: `--parallel_models` (default `1`), `--ignore-warnings`. **≥ 500k SNPs + `parallel_models > 1` is rejected** before load.
320
+ Shares **train** options except `-m`. Extra: `--parallel_models` (default `1`), `--ignore-warnings`. On very wide matrices, high `--parallel_models` needs substantial RAM (each worker copies the full matrix).
312
321
 
313
322
  ---
314
323
 
@@ -421,20 +430,20 @@ By design: best model by **test set** metrics. See `best_model_info.json` and `a
421
430
 
422
431
  ### train-all fails or workers crash
423
432
 
424
- **Symptom:** `Worker process crashed`, all models `failed`, or error about `--parallel_models` and SNP count.
433
+ **Symptom:** `Worker process crashed`, all models `failed`, or OOM under high `--parallel_models`.
425
434
 
426
435
  **What to do:**
427
436
 
428
- 1. **≥ 500k SNPs:** use `--parallel_models 1` only (higher values are blocked).
437
+ 1. **≥ 500k SNPs:** prefer `--parallel_models 1` unless memory is abundant.
429
438
  2. Re-run preprocess with **`-f 2` or `-f 4`** if too many SNPs were kept.
430
- 3. Use `--parallel_models 1 --no_hyperparameter_search --n_folds 3 --threads 4`.
439
+ 3. Use `--parallel_models 1 --n_folds 3 --threads 4` (omit `--hyperparameter_search`).
431
440
  4. Increase job memory; or test one model: `G2PInsight train -m LightGBM ...`.
432
441
 
433
442
  ### Training or load is very slow
434
443
 
435
444
  1. Fewer SNPs in preprocess (`-f 2`–`4`).
436
445
  2. Uncompressed `*_train_data.txt` loads faster than `.gz`.
437
- 3. `--no_hyperparameter_search`, lower `--n_folds`.
446
+ 3. Keep default (no `--hyperparameter_search`), lower `--n_folds`.
438
447
 
439
448
  ### SHAP dependence file is huge
440
449
 
@@ -50,7 +50,7 @@ Supports **classification** and **regression**, optional **GWAS/LD** SNP filteri
50
50
  - **train** / **train-all** need preprocess `*_metadata.json` (`-j`) — not a raw matrix path alone.
51
51
  - GWAS/LD runs in **preprocess only**; training does not re-filter SNPs.
52
52
  - Wait for preprocess to finish (`Metadata file generated successfully`) before training. In batch jobs, use `set -e`.
53
- - **Large SNP counts:** use preprocess `-f 2`–`4`, **train-all** with `--parallel_models 1`; metadata guardrails **block `--parallel_models > 1` when 500k SNPs**.
53
+ - **Large SNP counts:** use preprocess `-f 2`–`4`; for **train-all**, prefer `--parallel_models 1` unless job memory is very large (each parallel worker copies the full matrix).
54
54
 
55
55
  ---
56
56
 
@@ -76,7 +76,7 @@ G2PInsight visualize \
76
76
  ```bash
77
77
  G2PInsight preprocess --bfile genotype -p pheno.txt -o results/ -f 4
78
78
  G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
79
- --parallel_models 1 --threads 4 --no_hyperparameter_search --n_folds 3
79
+ --parallel_models 1 --threads 4 --n_folds 3
80
80
  ```
81
81
 
82
82
  ---
@@ -181,7 +181,7 @@ G2PInsight preprocess --bfile genotype -p pheno.txt -o results/ -f 4 --no-cache
181
181
 
182
182
  #### What it is
183
183
 
184
- Trains **one** ML model from `*_metadata.json`: load matrix → split samples → tune hyperparameters (optional) → K-fold CV on training split → final fit → test metrics → SHAP.
184
+ Trains **one** ML model from `*_metadata.json`: load matrix → split samples → optional hyperparameter search → K-fold CV on training split → final fit → test metrics → SHAP.
185
185
 
186
186
  #### What you get
187
187
 
@@ -201,17 +201,21 @@ G2PInsight train -j <metadata.json> -m <model> -o <output_dir> [options]
201
201
  ```
202
202
 
203
203
  ```bash
204
- # Default
204
+ # Default (no hyperparameter search)
205
205
  G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/
206
206
 
207
207
  # Large SNP set
208
208
  G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/ \
209
- --no_hyperparameter_search --n_folds 3 --threads 4
209
+ --n_folds 3 --threads 4
210
+
211
+ # Enable hyperparameter search
212
+ G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o results/ \
213
+ --hyperparameter_search
210
214
  ```
211
215
 
212
- **Evaluation (default):** 80% train / 20% test. Tuning and K-fold CV use the 80% only; `{Model}_metrics.json` reports the 20% test set.
216
+ **Evaluation (default):** 80% train / 20% test. Optional tuning and K-fold CV use the 80% only; `{Model}_metrics.json` reports the 20% test set.
213
217
 
214
- **Flow:** hyperparameter search (internal 80/20 of train) → K-fold CV on full train split → final fit → SHAP.
218
+ **Flow (default):** K-fold CV on train split (model defaults) → final fit → SHAP. With `--hyperparameter_search`: hold-out tuning on the train split → CV → final fit → SHAP.
215
219
 
216
220
  #### Key options
217
221
 
@@ -219,7 +223,7 @@ G2PInsight train -j results/preprocess/results_metadata.json -m LightGBM -o resu
219
223
  |--------|---------|-------|
220
224
  | `-m` | — | `LightGBM`, `RandomForest`, `XGBoost`, `SVM`, `CatBoost`, `Logistic` |
221
225
  | `--n_folds` | `5` | CV folds on training split |
222
- | `--no_hyperparameter_search` | off | Faster; recommended for huge matrices |
226
+ | `--hyperparameter_search` | off | Opt-in RandomizedSearchCV; off by default for speed |
223
227
  | `--threads` | `1` | Per-model CPU threads |
224
228
  | `--shap_dependence_top` | `0` | Cap SNPs in dependence file (`0` = all) |
225
229
  | `--ignore-warnings` | off | Hide `[WARNING]` logs and Python warnings (errors still shown) |
@@ -260,10 +264,14 @@ G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
260
264
 
261
265
  # Millions of SNPs
262
266
  G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
263
- --parallel_models 1 --threads 4 --no_hyperparameter_search --n_folds 3
267
+ --parallel_models 1 --threads 4 --n_folds 3
268
+
269
+ # Opt-in hyperparameter search
270
+ G2PInsight train-all -j results/preprocess/results_metadata.json -o results/ \
271
+ --hyperparameter_search --parallel_models 1 --threads 4
264
272
  ```
265
273
 
266
- Shares **train** options except `-m`. Extra: `--parallel_models` (default `1`), `--ignore-warnings`. **≥ 500k SNPs + `parallel_models > 1` is rejected** before load.
274
+ Shares **train** options except `-m`. Extra: `--parallel_models` (default `1`), `--ignore-warnings`. On very wide matrices, high `--parallel_models` needs substantial RAM (each worker copies the full matrix).
267
275
 
268
276
  ---
269
277
 
@@ -376,20 +384,20 @@ By design: best model by **test set** metrics. See `best_model_info.json` and `a
376
384
 
377
385
  ### train-all fails or workers crash
378
386
 
379
- **Symptom:** `Worker process crashed`, all models `failed`, or error about `--parallel_models` and SNP count.
387
+ **Symptom:** `Worker process crashed`, all models `failed`, or OOM under high `--parallel_models`.
380
388
 
381
389
  **What to do:**
382
390
 
383
- 1. **≥ 500k SNPs:** use `--parallel_models 1` only (higher values are blocked).
391
+ 1. **≥ 500k SNPs:** prefer `--parallel_models 1` unless memory is abundant.
384
392
  2. Re-run preprocess with **`-f 2` or `-f 4`** if too many SNPs were kept.
385
- 3. Use `--parallel_models 1 --no_hyperparameter_search --n_folds 3 --threads 4`.
393
+ 3. Use `--parallel_models 1 --n_folds 3 --threads 4` (omit `--hyperparameter_search`).
386
394
  4. Increase job memory; or test one model: `G2PInsight train -m LightGBM ...`.
387
395
 
388
396
  ### Training or load is very slow
389
397
 
390
398
  1. Fewer SNPs in preprocess (`-f 2`–`4`).
391
399
  2. Uncompressed `*_train_data.txt` loads faster than `.gz`.
392
- 3. `--no_hyperparameter_search`, lower `--n_folds`.
400
+ 3. Keep default (no `--hyperparameter_search`), lower `--n_folds`.
393
401
 
394
402
  ### SHAP dependence file is huge
395
403
 
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "G2PInsight"
10
- version = "1.0.2"
10
+ version = "1.0.4"
11
11
  description = "Genome-wide association analysis toolkit"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.8,<3.13"
@@ -43,6 +43,9 @@ dependencies = [
43
43
  "scipy>=1.7,<2.0", # scipy 2.0+ 可能需要更新的编译器
44
44
  "joblib>=1.0",
45
45
  "matplotlib>=3.5",
46
+ # Pillow 12.3+ only ships manylinux_2_28 wheels; CentOS7/glibc2.17 falls back to
47
+ # source builds and fails on old GCC (C99 for-loop error).
48
+ "Pillow>=8,<12.3",
46
49
  "numba>=0.57", # 通常有预编译 wheel,不需要从源码编译
47
50
  "lightgbm>=3.3,<4.0.0",
48
51
  "xgboost>=1.6,<2.1", # xgboost 2.1+ 可能需要更新的编译器
File without changes
File without changes
File without changes
File without changes