gpsea 0.9.2__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.
Files changed (95) hide show
  1. gpsea/__init__.py +10 -0
  2. gpsea/analysis/__init__.py +22 -0
  3. gpsea/analysis/_base.py +507 -0
  4. gpsea/analysis/_partition.py +69 -0
  5. gpsea/analysis/_util.py +29 -0
  6. gpsea/analysis/clf/__init__.py +40 -0
  7. gpsea/analysis/clf/_api.py +350 -0
  8. gpsea/analysis/clf/_counter.py +67 -0
  9. gpsea/analysis/clf/_gt_classifiers.py +702 -0
  10. gpsea/analysis/clf/_pheno.py +185 -0
  11. gpsea/analysis/clf/_test__gt_classifiers.py +145 -0
  12. gpsea/analysis/clf/_util.py +84 -0
  13. gpsea/analysis/mtc_filter/__init__.py +14 -0
  14. gpsea/analysis/mtc_filter/_impl.py +625 -0
  15. gpsea/analysis/pcats/__init__.py +38 -0
  16. gpsea/analysis/pcats/_config.py +29 -0
  17. gpsea/analysis/pcats/_impl.py +457 -0
  18. gpsea/analysis/pcats/stats/__init__.py +5 -0
  19. gpsea/analysis/pcats/stats/_stats.py +231 -0
  20. gpsea/analysis/pcats/stats/_test__stats.py +32 -0
  21. gpsea/analysis/predicate/__init__.py +43 -0
  22. gpsea/analysis/predicate/_api.py +223 -0
  23. gpsea/analysis/predicate/_predicates.py +750 -0
  24. gpsea/analysis/predicate/_variant.py +442 -0
  25. gpsea/analysis/pscore/__init__.py +9 -0
  26. gpsea/analysis/pscore/_api.py +288 -0
  27. gpsea/analysis/pscore/_hpo.py +328 -0
  28. gpsea/analysis/pscore/_measurement.py +112 -0
  29. gpsea/analysis/pscore/stats/__init__.py +7 -0
  30. gpsea/analysis/pscore/stats/_stats.py +122 -0
  31. gpsea/analysis/temporal/__init__.py +16 -0
  32. gpsea/analysis/temporal/_api.py +217 -0
  33. gpsea/analysis/temporal/_base.py +27 -0
  34. gpsea/analysis/temporal/_util.py +21 -0
  35. gpsea/analysis/temporal/endpoint/__init__.py +12 -0
  36. gpsea/analysis/temporal/endpoint/_impl.py +347 -0
  37. gpsea/analysis/temporal/stats/__init__.py +11 -0
  38. gpsea/analysis/temporal/stats/_api.py +35 -0
  39. gpsea/analysis/temporal/stats/_impl.py +54 -0
  40. gpsea/config.py +51 -0
  41. gpsea/io.py +407 -0
  42. gpsea/model/__init__.py +28 -0
  43. gpsea/model/_base.py +110 -0
  44. gpsea/model/_cohort.py +709 -0
  45. gpsea/model/_gt.py +176 -0
  46. gpsea/model/_phenotype.py +295 -0
  47. gpsea/model/_protein.py +551 -0
  48. gpsea/model/_temporal.py +300 -0
  49. gpsea/model/_test_gt.py +37 -0
  50. gpsea/model/_test_temporal.py +51 -0
  51. gpsea/model/_test_tx.py +64 -0
  52. gpsea/model/_tx.py +229 -0
  53. gpsea/model/_variant.py +910 -0
  54. gpsea/model/_variant_effects.py +170 -0
  55. gpsea/model/genome/GCF_000001405.25_GRCh37.p13_assembly_report.tsv +333 -0
  56. gpsea/model/genome/GCF_000001405.39_GRCh38.p13_assembly_report.tsv +703 -0
  57. gpsea/model/genome/__init__.py +22 -0
  58. gpsea/model/genome/_builds.py +37 -0
  59. gpsea/model/genome/_genome.py +553 -0
  60. gpsea/model/genome/_test_builds.py +42 -0
  61. gpsea/model/genome/_test_genome.py +248 -0
  62. gpsea/preprocessing/__init__.py +29 -0
  63. gpsea/preprocessing/_api.py +235 -0
  64. gpsea/preprocessing/_caching.py +223 -0
  65. gpsea/preprocessing/_config.py +530 -0
  66. gpsea/preprocessing/_generic.py +56 -0
  67. gpsea/preprocessing/_patient.py +80 -0
  68. gpsea/preprocessing/_phenopacket.py +953 -0
  69. gpsea/preprocessing/_test__caching.py +72 -0
  70. gpsea/preprocessing/_test__phenopacket.py +44 -0
  71. gpsea/preprocessing/_uniprot.py +167 -0
  72. gpsea/preprocessing/_vep.py +197 -0
  73. gpsea/preprocessing/_vv.py +407 -0
  74. gpsea/py.typed +0 -0
  75. gpsea/util.py +78 -0
  76. gpsea/view/__init__.py +29 -0
  77. gpsea/view/_base.py +89 -0
  78. gpsea/view/_draw_variants.py +405 -0
  79. gpsea/view/_formatter.py +49 -0
  80. gpsea/view/_phenotype_analysis.py +78 -0
  81. gpsea/view/_protein_visualizable.py +167 -0
  82. gpsea/view/_protein_visualizer.py +687 -0
  83. gpsea/view/_txp.py +106 -0
  84. gpsea/view/_viewers.py +683 -0
  85. gpsea/view/templates/all_variants.html +30 -0
  86. gpsea/view/templates/cohort.html +216 -0
  87. gpsea/view/templates/disease.html +106 -0
  88. gpsea/view/templates/minibase.html +136 -0
  89. gpsea/view/templates/protein.html +32 -0
  90. gpsea/view/templates/stats.html +33 -0
  91. gpsea-0.9.2.dist-info/LICENSE +21 -0
  92. gpsea-0.9.2.dist-info/METADATA +79 -0
  93. gpsea-0.9.2.dist-info/RECORD +95 -0
  94. gpsea-0.9.2.dist-info/WHEEL +5 -0
  95. gpsea-0.9.2.dist-info/top_level.txt +1 -0
gpsea/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ """
2
+ GPSEA is a library for finding genotype-phenotype associations.
3
+ """
4
+
5
+ __version__ = "0.9.2"
6
+
7
+ _overwrite = False
8
+ """
9
+ A private "hack" flag for regenerating documentation output (HTML fragments, figures).
10
+ """
@@ -0,0 +1,22 @@
1
+ from ._base import (
2
+ AnalysisException,
3
+ AnalysisResult,
4
+ MonoPhenotypeAnalysisResult,
5
+ MultiPhenotypeAnalysisResult,
6
+ Statistic,
7
+ StatisticResult,
8
+ )
9
+ from ._partition import Partitioning, ContinuousPartitioning
10
+ from ._util import Summarizable
11
+
12
+ __all__ = [
13
+ "AnalysisException",
14
+ "AnalysisResult",
15
+ "MonoPhenotypeAnalysisResult",
16
+ "MultiPhenotypeAnalysisResult",
17
+ "Statistic",
18
+ "StatisticResult",
19
+ "Partitioning",
20
+ "ContinuousPartitioning",
21
+ "Summarizable",
22
+ ]
@@ -0,0 +1,507 @@
1
+ import abc
2
+ import math
3
+ import os
4
+ import typing
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+
9
+ from .clf import GenotypeClassifier, PhenotypeClassifier, P
10
+ from ._partition import Partitioning
11
+
12
+
13
+ class StatisticResult:
14
+ """
15
+ `StatisticResult` reports result of a :class:`~gpsea.analysis.Statistic`.
16
+
17
+ It includes a statistic (optional) and a corresponding p value.
18
+ The p value can be `NaN` if it is impossible to compute for a given dataset.
19
+
20
+ Raises an :class:`AssertionError` for an invalid input.
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ statistic: typing.Optional[typing.Union[int, float]],
26
+ pval: float,
27
+ ):
28
+ if statistic is not None:
29
+ assert isinstance(statistic, (float, int))
30
+ self._statistic = float(statistic)
31
+ else:
32
+ self._statistic = None
33
+
34
+ assert isinstance(pval, float) and (math.isnan(pval) or 0.0 <= pval <= 1.0)
35
+ self._pval = float(pval)
36
+
37
+ @property
38
+ def statistic(self) -> typing.Optional[float]:
39
+ """
40
+ Get a `float` with the test statistic or `None` if not available.
41
+ """
42
+ return self._statistic
43
+
44
+ @property
45
+ def pval(self) -> float:
46
+ """
47
+ Get a p value (a value or a `NaN`).
48
+ """
49
+ return self._pval
50
+
51
+ def __eq__(self, value: object) -> bool:
52
+ return (
53
+ isinstance(value, StatisticResult)
54
+ and self._statistic == value._statistic
55
+ and self._pval == value._pval
56
+ )
57
+
58
+ def __hash__(self) -> int:
59
+ return hash(
60
+ (
61
+ self._statistic,
62
+ self._pval,
63
+ )
64
+ )
65
+
66
+ def __str__(self) -> str:
67
+ return repr(self)
68
+
69
+ def __repr__(self) -> str:
70
+ return f"StatisticResult(statistic={self._statistic}, pval={self._pval})"
71
+
72
+
73
+ class AnalysisException(Exception):
74
+ """
75
+ Reports analysis issues that need user's attention.
76
+
77
+ To aid troubleshooting, the exception includes :attr:`~gpsea.analysis.AnalysisException.data` -
78
+ a mapping with any data that has been computed prior encountering the issues.
79
+ """
80
+
81
+ def __init__(
82
+ self,
83
+ data: typing.Mapping[str, typing.Any],
84
+ *args,
85
+ ):
86
+ super().__init__(*args)
87
+ self._data = data
88
+
89
+ @property
90
+ def data(self) -> typing.Mapping[str, typing.Any]:
91
+ """
92
+ Get a mapping with (partial) data to aid troubleshooting.
93
+ """
94
+ return self._data
95
+
96
+ def __repr__(self) -> str:
97
+ return f"AnalysisException(args={self.args}, data={self._data})"
98
+
99
+
100
+ class Statistic(metaclass=abc.ABCMeta):
101
+ """
102
+ Mixin for classes that are used to compute a nominal p value for a genotype-phenotype association.
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ name: str,
108
+ ):
109
+ self._name = name
110
+
111
+ @property
112
+ def name(self) -> str:
113
+ """
114
+ Get the name of the statistic (e.g. `Fisher Exact Test`, `Logrank test`).
115
+ """
116
+ return self._name
117
+
118
+ def __eq__(self, value: object) -> bool:
119
+ if isinstance(value, Statistic):
120
+ return self._name == value._name
121
+ return NotImplemented
122
+
123
+ def __hash__(self) -> int:
124
+ return hash((self._name,))
125
+
126
+
127
+ class AnalysisResult(metaclass=abc.ABCMeta):
128
+ """
129
+ `AnalysisResult` includes the common parts of results of all analyses.
130
+ """
131
+
132
+ def __init__(
133
+ self,
134
+ gt_clf: GenotypeClassifier,
135
+ statistic: Statistic,
136
+ ):
137
+ assert isinstance(gt_clf, GenotypeClassifier)
138
+ self._gt_clf = gt_clf
139
+
140
+ assert isinstance(statistic, Statistic)
141
+ self._statistic = statistic
142
+
143
+ @property
144
+ def gt_clf(self) -> GenotypeClassifier:
145
+ """
146
+ Get the genotype classifier used in the survival analysis that produced this result.
147
+ """
148
+ return self._gt_clf
149
+
150
+ @property
151
+ def statistic(self) -> Statistic:
152
+ """
153
+ Get the statistic which computed the (nominal) p values for this result.
154
+ """
155
+ return self._statistic
156
+
157
+ def __eq__(self, value: object) -> bool:
158
+ return (
159
+ isinstance(value, AnalysisResult)
160
+ and self._gt_clf == value._gt_clf
161
+ and self._statistic == value._statistic
162
+ )
163
+
164
+ def __hash__(self) -> int:
165
+ return hash(
166
+ (
167
+ self._gt_clf,
168
+ self._statistic,
169
+ )
170
+ )
171
+
172
+
173
+ class MultiPhenotypeAnalysisResult(typing.Generic[P], AnalysisResult):
174
+ """
175
+ `MultiPhenotypeAnalysisResult` reports the outcome of an analysis
176
+ that tested the association of genotype with two or more phenotypes.
177
+ """
178
+
179
+ def __init__(
180
+ self,
181
+ gt_clf: GenotypeClassifier,
182
+ pheno_clfs: typing.Iterable[PhenotypeClassifier[P]],
183
+ statistic: Statistic,
184
+ n_usable: typing.Sequence[int],
185
+ all_counts: typing.Sequence[pd.DataFrame],
186
+ statistic_results: typing.Sequence[typing.Optional[StatisticResult]],
187
+ corrected_pvals: typing.Optional[typing.Sequence[float]],
188
+ mtc_correction: typing.Optional[str],
189
+ ):
190
+ super().__init__(
191
+ gt_clf=gt_clf,
192
+ statistic=statistic,
193
+ )
194
+
195
+ self._pheno_clfs = tuple(pheno_clfs)
196
+
197
+ self._n_usable = tuple(n_usable)
198
+ self._all_counts = tuple(all_counts)
199
+
200
+ self._statistic_results = tuple(statistic_results)
201
+ self._corrected_pvals = (
202
+ None if corrected_pvals is None else tuple(corrected_pvals)
203
+ )
204
+ errors = self._check_sanity()
205
+ if errors:
206
+ raise ValueError(os.linesep.join(errors))
207
+
208
+ if mtc_correction is not None:
209
+ assert isinstance(mtc_correction, str)
210
+ self._mtc_correction = mtc_correction
211
+
212
+ def _check_sanity(self) -> typing.Sequence[str]:
213
+ errors = []
214
+ # All sequences must have the same lengths ...
215
+ for seq, name in (
216
+ (self._n_usable, "n_usable"),
217
+ (self._all_counts, "all_counts"),
218
+ (self._statistic_results, "statistic_results"),
219
+ ):
220
+ if len(self._pheno_clfs) != len(seq):
221
+ errors.append(
222
+ f"`len(pheno_clfs)` must be the same as `len({name})` but "
223
+ f"{len(self._pheno_clfs)}!={len(seq)}"
224
+ )
225
+
226
+ # ... including the optional corrected p values
227
+ if self._corrected_pvals is not None and len(self._pheno_clfs) != len(
228
+ self._corrected_pvals
229
+ ):
230
+ errors.append(
231
+ f"`len(pheno_predicates)` must be the same as `len(corrected_pvals)` but "
232
+ f"{len(self._pheno_clfs)}!={len(self._corrected_pvals)}"
233
+ )
234
+
235
+ if not isinstance(self._gt_clf, GenotypeClassifier):
236
+ errors.append("`gt_clf` must be an instance of `GenotypeClassifier`")
237
+ return errors
238
+
239
+ @property
240
+ def pheno_clfs(
241
+ self,
242
+ ) -> typing.Sequence[PhenotypeClassifier[P]]:
243
+ """
244
+ Get the phenotype classifiers used in the analysis.
245
+ """
246
+ return self._pheno_clfs
247
+
248
+ @property
249
+ def phenotypes(self) -> typing.Sequence[P]:
250
+ """
251
+ Get the phenotypes that were tested for association with genotype in the analysis.
252
+ """
253
+ return tuple(p.phenotype for p in self._pheno_clfs)
254
+
255
+ @property
256
+ def n_usable(self) -> typing.Sequence[int]:
257
+ """
258
+ Get a sequence of numbers of patients where the phenotype was assessable,
259
+ and are, thus, usable for genotype-phenotype correlation analysis.
260
+ """
261
+ return self._n_usable
262
+
263
+ @property
264
+ def all_counts(self) -> typing.Sequence[pd.DataFrame]:
265
+ """
266
+ Get a :class:`~pandas.DataFrame` sequence where each `DataFrame` includes the counts of patients
267
+ in genotype and phenotype groups.
268
+
269
+ An example for a genotype predicate that bins into two categories (`Yes` and `No`) based on presence
270
+ of a missense variant in transcript `NM_123456.7`, and phenotype predicate that checks
271
+ presence/absence of `HP:0001166` (a phenotype term)::
272
+
273
+ Has MISSENSE_VARIANT in NM_123456.7
274
+ No Yes
275
+ Present
276
+ Yes 1 13
277
+ No 7 5
278
+
279
+ The rows correspond to the phenotype categories, and the columns represent the genotype categories.
280
+ """
281
+ return self._all_counts
282
+
283
+ @property
284
+ def statistic_results(self) -> typing.Sequence[typing.Optional[StatisticResult]]:
285
+ """
286
+ Get a sequence of :class:`~gpsea.analysis.StatisticResult` items with nominal p values and the associated statistic values
287
+ for each tested phenotype or `None` for the untested phenotypes.
288
+ """
289
+ return self._statistic_results
290
+
291
+ @property
292
+ def pvals(self) -> typing.Sequence[float]:
293
+ """
294
+ Get a sequence of nominal p values for each tested phenotype.
295
+ The sequence includes a `NaN` value for each phenotype that was *not* tested.
296
+ """
297
+ return tuple(
298
+ float("nan") if r is None else r.pval for r in self._statistic_results
299
+ )
300
+
301
+ @property
302
+ def corrected_pvals(self) -> typing.Optional[typing.Sequence[float]]:
303
+ """
304
+ Get a sequence with p values for each tested phenotype after multiple testing correction
305
+ or `None` if the correction was not applied.
306
+ The sequence includes a `NaN` value for each phenotype that was *not* tested.
307
+ """
308
+ return self._corrected_pvals
309
+
310
+ def n_significant_for_alpha(
311
+ self,
312
+ alpha: float = 0.05,
313
+ ) -> typing.Optional[int]:
314
+ """
315
+ Get the count of the corrected p values with the value being less than or equal to `alpha`.
316
+
317
+ :param alpha: a `float` with significance level.
318
+ """
319
+ if self.corrected_pvals is None:
320
+ return None
321
+ else:
322
+ return sum(p_val <= alpha for p_val in self.corrected_pvals)
323
+
324
+ def significant_phenotype_indices(
325
+ self,
326
+ alpha: float = 0.05,
327
+ pval_kind: typing.Literal["corrected", "nominal"] = "corrected",
328
+ ) -> typing.Optional[typing.Sequence[int]]:
329
+ """
330
+ Get the indices of phenotypes that attain significance for provided `alpha`.
331
+ """
332
+ if pval_kind == "corrected":
333
+ if self.corrected_pvals is None:
334
+ vals = None
335
+ else:
336
+ vals = np.array(self.corrected_pvals)
337
+ elif pval_kind == "nominal":
338
+ vals = np.array(self.pvals)
339
+ else:
340
+ raise ValueError(f"Unsupported `pval_kind` value {pval_kind}")
341
+
342
+ if vals is None:
343
+ return None
344
+
345
+ not_na = ~np.isnan(vals)
346
+ significant = vals <= alpha
347
+ selected = not_na & significant
348
+
349
+ return tuple(int(idx) for idx in np.argsort(vals) if selected[idx])
350
+
351
+ @property
352
+ def total_tests(self) -> int:
353
+ """
354
+ Get total count of genotype-phenotype associations that were tested in this analysis.
355
+ """
356
+ return sum(1 for result in self._statistic_results if result is not None)
357
+
358
+ @property
359
+ def mtc_correction(self) -> typing.Optional[str]:
360
+ """
361
+ Get name/code of the used multiple testing correction
362
+ (e.g. `fdr_bh` for Benjamini-Hochberg) or `None` if no correction was applied.
363
+ """
364
+ return self._mtc_correction
365
+
366
+ def __eq__(self, value: object) -> bool:
367
+ return (
368
+ isinstance(value, MultiPhenotypeAnalysisResult)
369
+ and super(AnalysisResult, self).__eq__(value)
370
+ and self._pheno_clfs == value._pheno_clfs
371
+ and self._n_usable == value._n_usable
372
+ and self._all_counts == value._all_counts
373
+ and self._statistic_results == value._statistic_results
374
+ and self._corrected_pvals == value._corrected_pvals
375
+ and self._mtc_correction == value._mtc_correction
376
+ )
377
+
378
+ def __hash__(self) -> int:
379
+ return hash(
380
+ (
381
+ super(AnalysisResult, self).__hash__(),
382
+ self._pheno_clfs,
383
+ self._n_usable,
384
+ self._all_counts,
385
+ self._statistic_results,
386
+ self._corrected_pvals,
387
+ self._mtc_correction,
388
+ )
389
+ )
390
+
391
+
392
+ class MonoPhenotypeAnalysisResult(AnalysisResult, metaclass=abc.ABCMeta):
393
+ """
394
+ `MonoPhenotypeAnalysisResult` reports the outcome of an analysis
395
+ that tested a single genotype-phenotype association.
396
+ """
397
+
398
+ SAMPLE_ID = "patient_id"
399
+ """
400
+ Name of the data index.
401
+ """
402
+
403
+ GT_COL = "genotype"
404
+ """
405
+ Name of column for storing genotype data.
406
+ """
407
+
408
+ PH_COL = "phenotype"
409
+ """
410
+ Name of column for storing phenotype data.
411
+ """
412
+
413
+ DATA_COLUMNS = (GT_COL, PH_COL)
414
+ """
415
+ The required columns of the `data` data frame.
416
+ """
417
+
418
+ def __init__(
419
+ self,
420
+ gt_clf: GenotypeClassifier,
421
+ phenotype: Partitioning,
422
+ statistic: Statistic,
423
+ data: pd.DataFrame,
424
+ statistic_result: StatisticResult,
425
+ ):
426
+ super().__init__(gt_clf, statistic)
427
+
428
+ assert isinstance(phenotype, Partitioning)
429
+ self._phenotype = phenotype
430
+
431
+ assert isinstance(data, pd.DataFrame) and all(
432
+ col in data.columns for col in MonoPhenotypeAnalysisResult.DATA_COLUMNS
433
+ )
434
+ self._data = data
435
+
436
+ assert isinstance(statistic_result, StatisticResult)
437
+ self._statistic_result = statistic_result
438
+
439
+ @property
440
+ def phenotype(self) -> Partitioning:
441
+ """
442
+ Get the :class:`~gpsea.analysis.Partitioning` that produced the phenotype.
443
+ """
444
+ return self._phenotype
445
+
446
+ @property
447
+ def data(self) -> pd.DataFrame:
448
+ """
449
+ Get the data frame with genotype and phenotype values for each tested individual.
450
+
451
+ The index of the data frame contains the identifiers of the tested individuals,
452
+ and the values are stored in `genotype` and `phenotype` columns.
453
+
454
+ The `genotype` column includes the genotype category ID
455
+ (:attr:`~gpsea.analysis.clf.PatientCategory.cat_id`)
456
+ or `None` if the individual could not be assigned into a genotype group.
457
+ The `phenotype` contains the phenotype values, and the data type depends on the analysis.
458
+
459
+ Here are some common phenotype data types:
460
+
461
+ * a phenotype score computed in :class:`~gpsea.analysis.pscore.PhenotypeScoreAnalysis` is a `float`
462
+ * survival computed in :class:`~gpsea.analysis.temporal.SurvivalAnalysis`
463
+ is of type :class:`~gpsea.analysis.temporal.Survival`
464
+ """
465
+ return self._data
466
+
467
+ def complete_records(self) -> pd.DataFrame:
468
+ """
469
+ Get the :attr:`~gpsea.analysis.MonoPhenotypeAnalysisResult.data` rows
470
+ where both `genotype` and `phenotype` columns are available (i.e. not `None` or `NaN`).
471
+ """
472
+ return self._data.loc[
473
+ self._data[MonoPhenotypeAnalysisResult.GT_COL].notna()
474
+ & self._data[MonoPhenotypeAnalysisResult.PH_COL].notna()
475
+ ]
476
+
477
+ def statistic_result(self) -> StatisticResult:
478
+ """
479
+ Get statistic result with the nominal p value and the associated statistics.
480
+ """
481
+ return self._statistic_result
482
+
483
+ @property
484
+ def pval(self) -> float:
485
+ """
486
+ Get the p value of the test.
487
+ """
488
+ return self._statistic_result.pval
489
+
490
+ def __eq__(self, value: object) -> bool:
491
+ return (
492
+ isinstance(value, MonoPhenotypeAnalysisResult)
493
+ and super(AnalysisResult, self).__eq__(value)
494
+ and self._phenotype == value._phenotype
495
+ and self._statistic_result == value._statistic_result
496
+ and self._data.equals(value._data)
497
+ )
498
+
499
+ def __hash__(self) -> int:
500
+ return hash(
501
+ (
502
+ super(AnalysisResult, self).__hash__(),
503
+ self._phenotype,
504
+ self._statistic_result,
505
+ self._data,
506
+ )
507
+ )
@@ -0,0 +1,69 @@
1
+ import abc
2
+ import os
3
+ import typing
4
+
5
+ from gpsea.model import Patient
6
+
7
+ from ._util import Summarizable
8
+
9
+
10
+ class Partitioning(Summarizable, metaclass=abc.ABCMeta):
11
+ """
12
+ `Partitioning` is a superclass of all classes that assign a group,
13
+ compute a score or survival for an individual.
14
+ """
15
+
16
+ @property
17
+ @abc.abstractmethod
18
+ def name(self) -> str:
19
+ """
20
+ Get the name of the partitioning.
21
+ """
22
+ pass
23
+
24
+ @property
25
+ @abc.abstractmethod
26
+ def description(self) -> str:
27
+ """
28
+ Get a description of the partitioning.
29
+ """
30
+ pass
31
+
32
+ @property
33
+ @abc.abstractmethod
34
+ def variable_name(self) -> str:
35
+ """
36
+ Get a `str` with the name of the variable investigated by the partitioning.
37
+
38
+ For instance `Sex`, `Allele groups`, `HP:0001250`, `OMIM:256000`
39
+ """
40
+ pass
41
+
42
+ def summarize(
43
+ self,
44
+ out: typing.TextIO,
45
+ ):
46
+ """
47
+ Summarize the item while also considering `other` (default `None`).
48
+ """
49
+ out.write(self.name)
50
+ out.write(os.linesep)
51
+ out.write(self.description)
52
+ out.write(os.linesep)
53
+
54
+ @staticmethod
55
+ def _check_patient(patient: Patient):
56
+ """
57
+ Check if the `patient` meets the partitioning requirements.
58
+ """
59
+ if not isinstance(patient, Patient):
60
+ raise ValueError(f"patient must be type Patient but was type {type(patient)}")
61
+
62
+
63
+ class ContinuousPartitioning(Partitioning, metaclass=abc.ABCMeta):
64
+ """
65
+ `ContinuousPartitioning` computes a score that is a real number.
66
+
67
+ The class is just a marker class at this time.
68
+ """
69
+ pass
@@ -0,0 +1,29 @@
1
+ import abc
2
+ import io
3
+ import typing
4
+
5
+
6
+ class Summarizable(metaclass=abc.ABCMeta):
7
+ """
8
+ A mixin for entities that can summarize themselves into a provided IO handle.
9
+ """
10
+
11
+ @abc.abstractmethod
12
+ def summarize(
13
+ self,
14
+ out: typing.TextIO,
15
+ ):
16
+ """
17
+ Summarize the item into the provided IO handle.
18
+
19
+ :param out: an IO handle to write into.
20
+ """
21
+ pass
22
+
23
+ def summary(self) -> str:
24
+ """
25
+ Get the summary.
26
+ """
27
+ buf = io.StringIO()
28
+ self.summarize(buf)
29
+ return buf.getvalue()
@@ -0,0 +1,40 @@
1
+ from ._api import Classifier, PatientCategory, Categorization
2
+ from ._api import C, GenotypeClassifier
3
+ from ._api import P, PhenotypeClassifier, PhenotypeCategorization
4
+ from ._counter import AlleleCounter
5
+ from ._pheno import HpoClassifier, DiseasePresenceClassifier
6
+ from ._gt_classifiers import (
7
+ sex_classifier,
8
+ diagnosis_classifier,
9
+ monoallelic_classifier,
10
+ biallelic_classifier,
11
+ allele_count,
12
+ random_classifier,
13
+ )
14
+ from ._util import (
15
+ prepare_classifiers_for_terms_of_interest,
16
+ prepare_hpo_terms_of_interest,
17
+ )
18
+
19
+
20
+ __all__ = [
21
+ "Classifier",
22
+ "PatientCategory",
23
+ "Categorization",
24
+ "GenotypeClassifier",
25
+ "C",
26
+ "AlleleCounter",
27
+ "sex_classifier",
28
+ "diagnosis_classifier",
29
+ "monoallelic_classifier",
30
+ "biallelic_classifier",
31
+ "allele_count",
32
+ "random_classifier",
33
+ "PhenotypeClassifier",
34
+ "PhenotypeCategorization",
35
+ "P",
36
+ "HpoClassifier",
37
+ "DiseasePresenceClassifier",
38
+ "prepare_classifiers_for_terms_of_interest",
39
+ "prepare_hpo_terms_of_interest",
40
+ ]