gain.genomic_resources.implementations.genomic_scores_impl package
Submodules
gain.genomic_resources.implementations.genomic_scores_impl.allele module
AlleleScoreImplementation – the allele score’s page.
The genomic-score page plus an Alleles section: the accessors that
section calls, and nothing else. Whether a statistic reads alleles is
decided on the built score class, by
region_alleles_for(),
and both scan paths read it from there; the resource protocol every kind
answers alike is on GenomicScoreImplementation.
- class gain.genomic_resources.implementations.genomic_scores_impl.allele.AlleleScoreImplementation(resource: GenomicResource)[source]
Bases:
GenomicScoreImplementationAssists in the management of an allele score resource.
It carries its own info page, which is the genomic-score page plus an Alleles section. The section lives in a template that FILLS a block the shared template leaves empty, as the other kinds’ sections do, so a kind whose rows carry no ref/alt pair renders no section at all – rather than a heading permanently reading “not computed”, which is what one shared template rendering every section produced.
- static get_allele_complex_grid_image_filename() str[source]
The info page’s one statement of the complex grid’s path.
- static get_allele_deletion_lengths_image_filename() str[source]
The info page’s one statement of the deletion image’s path.
- get_allele_display() AlleleSectionDisplay | None[source]
The Alleles section’s payload, or
Noneif not built.
- static get_allele_insertion_lengths_image_filename() str[source]
The info page’s one statement of the insertion image’s path.
- get_allele_statistics() AlleleStatistics | None[source]
The resource’s allele statistics, or
Noneif not built.Absence is an expected state, for the reason
get_coverage_statistics()gives: the rollout is lazy.
- template_name: ClassVar[str] = 'allele_score.jinja'
gain.genomic_resources.implementations.genomic_scores_impl.base module
- class gain.genomic_resources.implementations.genomic_scores_impl.base.GenomicScoreImplementation(resource: GenomicResource)[source]
Bases:
ScoreImplementationBaseWhat every genomic-score kind answers alike.
The resource protocol – the statistics build tasks, the file set, the hashes – and the page protocol:
get_infoandget_statistics_infohand the repository the page builder passes to whichever kind renders, through_render_repo. It names no kind’s template and no kind’s section accessors; each kind is a subclass that does both, and the factory inbuildersand the entry points hand out only those. Nothing instantiates this class for a real resource.- calc_info_hash() bytes[source]
Compute and return the info hash.
- calc_statistics_hash() bytes[source]
Compute the statistics hash.
This hash is used to decide whether the resource statistics should be recomputed.
- create_statistics_build_tasks(**kwargs: Any) list[TaskDesc][source]
Create tasks for calculating resource statistics for task graph.
Kept abstract: gene and genomic scores build statistics with genuinely different task shapes (a single DataFrame scan versus a region-split DAG), so each family provides its own.
- property files: set[str]
Return a list of resource files the implementation utilises.
- get_chrom_lengths(grr: GenomicResourceRepo | None) dict[str, ChromLength][source]
The ladder’s answer per contig of the score, in table order.
The genome the
reference_genomelabel names – resolved throughgrr– is the top rung; the table’s own answer (the bigWig header, the tabix probe) the rest, per contig. A contig with no length keeps the reason (EMPTY/UNDETERMINED) in its record.Opens the score if it is closed, and closes it again only in that case – an already-open score stays open for its owner.
- get_config_histograms() dict[str, Any][source]
Collect all configurations of histograms for the genomic score.
- get_info(**kwargs: Any) str[source]
Construct the contents of the implementation’s HTML info page.
- get_statistics_info(**kwargs: Any) str[source]
Construct the contents of the implementation’s HTML statistics info page.
- property resource_id: str
The id of the resource this implementation wraps.
- score: GenomicScore
- styles_template_name: ClassVar[str] = 'genomic_score_styles.jinja'
gain.genomic_resources.implementations.genomic_scores_impl.builders module
The factory that builds a score implementation from a resource.
The same dispatch the gain.genomic_resources.implementations entry
points make by type name, for a caller holding a resource rather than a
type – the sibling of
build_score_from_resource(),
which picks the score class the same way.
- gain.genomic_resources.implementations.genomic_scores_impl.builders.build_score_implementation_from_resource(resource: GenomicResource) GenomicScoreImplementation[source]
Builds score implementation based on resource type.
Each kind gets the class that renders its page; the base is never what a real resource gets, since it names no kind’s template. The ladder is the one
build_score_from_resourceclimbs, and a type that is no kind is refused here as it is there, rather than handed the position class to fail one step later.
gain.genomic_resources.implementations.genomic_scores_impl.fragment module
FragmentScoreImplementation – the fragment score’s page.
The genomic-score page plus a Fragments section: the accessors that
section calls, and nothing else. A fragment’s weight-1 rule is declared
on FragmentScore (record_weight) and read by both scan paths from
there; the resource protocol every kind answers alike is on
GenomicScoreImplementation.
- class gain.genomic_resources.implementations.genomic_scores_impl.fragment.FragmentScoreImplementation(resource: GenomicResource)[source]
Bases:
GenomicScoreImplementationAssists in the management of a fragment score resource.
Its page is the genomic-score page plus one section, Fragments, which only this kind publishes – and no Coverage, which since gain#1127 only a position score has. The section lives in a template that FILLS a block the shared template leaves empty, so a kind with no fragments renders no section at all – rather than a heading permanently reading “not computed”, which is what gating one shared template on a boolean produced for Coverage on allele scores.
- get_fragment_display() FragmentDisplay | None[source]
The Fragments section’s payload, or
Noneif not computed.Nonemeans the statistic is not built – the file is simply absent, which renders the section’s “not computed” fallback. Since gain#1127 that is the ONE way it can be missing: the tally has its own file, where as a group inside the coverage one it could also be absent from a file that existed.
- static get_fragment_lengths_image_filename() str[source]
The info page’s one statement of the fragment image’s path.
- get_fragment_statistics() FragmentStatistics | None[source]
The resource’s fragment statistics, or
Noneif not built.Absence is an expected state, for the reason
get_coverage_statistics()gives: the rollout is lazy.
- template_name: ClassVar[str] = 'fragment_score.jinja'
gain.genomic_resources.implementations.genomic_scores_impl.position module
PositionScoreImplementation – the position score’s page.
The genomic-score page plus a Coverage section: the accessors that
section calls, and nothing else. Covered positions are this kind’s
statistic and only this kind’s – the union of the rows’ spans measures
what a resource covers exactly when they are pairwise disjoint
(gain#1118, gain#1127). The resource protocol every kind answers alike
is on GenomicScoreImplementation.
- class gain.genomic_resources.implementations.genomic_scores_impl.position.PositionScoreImplementation(resource: GenomicResource)[source]
Bases:
GenomicScoreImplementationAssists in the management of a position score resource.
Its page is the genomic-score page plus a Coverage section, which
position_score.jinjafills as the other kinds’ templates fill theirs.- get_coverage_display() CoverageDisplay | None[source]
The Coverage section’s payload: raw counts plus fractions.
Nonewhen the statistic is not built. This frame’s whole job is the genome rung of the denominator ladder – it needs the repository handed to the enclosingget_info()/get_statistics_info()call, and the cache it goes through is shared with the scan’s contig splitting. Invoked outside a page build no repository is available and that rung resolves nothing, which degrades to raw counts rather than failing.
- static get_coverage_segment_lengths_image_filename() str[source]
The info page’s one statement of the global histogram’s path.
- get_coverage_statistics() CoverageStatistics | None[source]
The resource’s coverage statistics, or
Noneif not built.Absence is an expected state, not an error: statistics roll out lazily as resources are rebuilt (
calc_statistics_hashdoes not know about this file), so a resource built before the statistic existed simply has nothing to show yet.Read on each call, like its two siblings. It was memoized while the Coverage and Fragments sections both read this file; since gain#1127 gave fragments a file of their own there is one caller, called once per render, and the memo saved nothing.
- template_name: ClassVar[str] = 'position_score.jinja'
gain.genomic_resources.implementations.genomic_scores_impl.scan module
- class gain.genomic_resources.implementations.genomic_scores_impl.scan.RegionScanResult(histograms: dict[str, Histogram], coverage: RegionCoverage | None, fragments: RegionFragments | None, alleles: RegionAlleles | None)[source]
Bases:
NamedTupleWhat one region’s statistics task hands to the merge step.
- alleles: RegionAlleles | None
Alias for field number 3
- coverage: RegionCoverage | None
Alias for field number 1
- fragments: RegionFragments | None
Alias for field number 2
- histograms: dict[str, NullHistogram | CategoricalHistogram | NumberHistogram]
Alias for field number 0
- gain.genomic_resources.implementations.genomic_scores_impl.scan.bulk_region_scan(resource: GenomicResource, result: dict[str, AccT], chrom: str, start: int, end: int, accumulate: Callable[[tuple[ndarray, ndarray, dict[str, ndarray]], dict[str, AccT], tuple[str, int | None, int | None], GenomicScore], None], *, batches: Callable[[GenomicScore, str, int, int, list[str]], Iterable[tuple[ndarray, ndarray, dict[str, ndarray]]]] | None = None, score: GenomicScore | None = None) dict[str, AccT][source]
Drive a bulk region scan, folding each batch into
result.Public though only this module calls it: ADR 0001 names it as the shared driver behind both bulk passes, so it is part of what
scanstates about itself rather than a helper.The shared skeleton of
do_histogram_bulk()anddo_min_max_bulk(): open the score and stream the region’s column-array batches throughaccumulate(which mutatesresult). The caller supplies the pre-builtresult– empty histograms or seededMinMaxValue– and the matching accumulator. Batches are keyed by SCORE ID: the score resolves each id to its payload column itself (gain#398), so nothing here handles column indices.This is the scan’s vectorized door, and the counterpart of
scan_region(): the batches are read throughvalidate_record_arrays, one visible extra link over the stream the scan is already pulling, so that a pass cannot be added that quietly reads unvalidated (ADR 0008). That function dispatches on the score’s class to the rule registered for its kind (ADR 0027); nothing here knows what the rule is.The validator is per REGION, and a region lies within one contig, so the ordering carry never spans a contig boundary – the same reason the per-record validators reset on a change of chromosome.
A
scorehanded in is opened here and closed on return; see_score_for().
- gain.genomic_resources.implementations.genomic_scores_impl.scan.bulk_scan_eligible(resource: GenomicResource, score_ids: list[str], *, score: GenomicScore | None = None) bool[source]
Whether a vectorized region scan may serve these scores.
The shared gate for the histogram and min/max bulk paths. Both conditions belong to the score, so both are asked of it:
every score of a value type the column parse defines (
float,int,str) – asked of the score, which owns that parse;and the backend serves the bulk read at all – asked of the score, not tested on the table’s class. This is what keeps a VCF-backed allele score on the per-record path: its record payload is not a raw row, so its table declares no column-array support.
No resource kind is tested. Eligibility is a claim about a score, so it is asked of one: handed a resource that cannot become a score, and no
scoreto use instead, this raisesValueErrorout of the factory rather than answeringFalse, which would read as a fact about a score and send a caller holding the wrong resource quietly down the per-record path. With ascoregiven the factory is not called, and the pair is the caller’s to match (see_score_for()).A new kind needs no guard here either:
record_weightis abstract onGenomicScore, a kind absent fromrecord_validation’s registry is refused by the array door below, and a backend serving no column arrays is refused above. ADR 0001 records why the kind condition was retired, and ADR 0027 why the second of those three refusals is now a run-time one rather than something mypy and pylint catch first.Answered WITHOUT opening the score: the table and the score definitions are both built in
GenomicScore.__init__, so nothing here needs a file handle. That is also whyscoremay be handed in already closed – a caller that has finished reading through it can still ask this (see_score_for()for why it would want to).
- gain.genomic_resources.implementations.genomic_scores_impl.scan.can_bulk_histogram(resource: GenomicResource, all_hist_confs: dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig], *, score: GenomicScore | None = None) bool[source]
Whether the vectorized scan may serve this histogram build.
bulk_scan_eligible()plus the conditions that are this caller’s alone – every score must feed a histogram that can accumulate a whole batch, and be handed the batch shape that histogram accepts:a NUMBER histogram takes a
floator anintscore, whose columns the bulk read yields as thefloat64NumberHistogram.add_batchaccumulates;a CATEGORICAL histogram takes a
strscore, whose column the bulk read yields as thestrobjectsCategoricalHistogram.add_batchcounts;a NULL histogram has nothing to accumulate and is skipped by both paths, so it constrains neither.
The two pairings are the whole rule, and the mismatches are what it exists to keep out: the per-record path meets a value its histogram refuses one at a time, catches the
TypeErrorand nullifies that one score, whereas a batch of the wrong shape is not a value the histogram can refuse – it is a coercion failure insideadd_batch. So a categorical histogram over anintscore keepsdo_histogram(), which handles it as it always has.A number histogram over a
strscore never reaches either path: such a config is refused when the score is CONSTRUCTED (refuse_unfoldable_histograms()), so no definition carrying it survives to be scanned. The condition stays stated here because it is this gate’s own – the batch shapes it admits are not a consequence of that refusal, and aboolscore separates them: the per-value rule keeps it, this one does not.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.can_bulk_min_max(resource: GenomicResource, score_ids: list[str], *, score: GenomicScore | None = None) bool[source]
Whether the vectorized scan may serve this min/max pass.
bulk_scan_eligible()plus the one condition that is this caller’s alone: every score must be a NUMBER. The reduction ismin()/max()over the non-nan values of a float64 column, and astrscore’s column is an object array, whichnp.isnanrefuses outright.A str score is never SCHEDULED for a min/max pass at all: the only thing that schedules one is a number histogram without a view range, and a score construction carrying that pairing raises (
refuse_unfoldable_histograms()), so no such resource reaches a scan. The condition stays stated rather than assumed from that one, because it is this pass’s own: left ungated, a column of nothing but NA sentinels would raise here, out of a generator and past every nullify handler, where the per-record path yields an empty min/max and nullifies that one histogram.Written out rather than read off
_BULK_HISTOGRAM_VALUE_TYPES, which the tuple happens to match: that constant says what a HISTOGRAM can accumulate a batch of, and this pass accumulates no histogram. Sharing the literal would make a future widening of the number histogram’s batch types widen this gate as a side effect.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.do_histogram(resource: GenomicResource, all_hist_confs: dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig], chrom: str, start: int | None, end: int | None, *, coverage: RegionCoverage | None = None, fragments: RegionFragments | None = None, alleles: RegionAlleles | None = None, score: GenomicScore | None = None) dict[str, NullHistogram | CategoricalHistogram | NumberHistogram][source]
Histogram a region record by record, per score.
The per-record histogram pass, and the floor
do_histogram_bulk()is required to match to the bit. Reads throughscan_region()and weighs each owned record the way the score’s kind weighs it (record_weight).coverage,fragmentsandallelesride the same read rather than costing the region a second one, and are accumulated IN PLACE – the caller owns them, because it is the task’s return value that has to travel under a distributed executor.A histogram that refuses a value is nullified on its own and the resource’s other scores carry on.
A
scorehanded in is opened here and closed on return; see_score_for().
- gain.genomic_resources.implementations.genomic_scores_impl.scan.do_histogram_bulk(resource: GenomicResource, all_hist_confs: dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig], chrom: str, start: int, end: int, *, coverage: RegionCoverage | None = None, fragments: RegionFragments | None = None, alleles: RegionAlleles | None = None, score: GenomicScore | None = None) dict[str, NullHistogram | CategoricalHistogram | NumberHistogram][source]
Vectorized equivalent of
do_histogram().Reads a region as batches of column arrays – tabix pulls raw pysam rows directly and bigWig converts each fetched interval chunk in one shot, neither building a
Recordper row – and accumulates each score’s histogram with the histogram’s ownadd_batchrather than a per-recordadd_value. The selection, the weight, the overlap rule and the value coercion are identical to the per-record path (pinned by the bulk-vs-per-record tests, and by both paths reading one statement of the per-kind rules); the dispatch restricts this to the score and histogram combinationscan_bulk_histogram()admits, over tabix/bigWig tables – everything else keepsdo_histogram().
- gain.genomic_resources.implementations.genomic_scores_impl.scan.do_histogram_task(resource: GenomicResource, all_hist_confs: dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig], chrom: str, start: int | None, end: int | None) RegionScanResult[source]
Compute a region’s histograms, bulk-vectorized where eligible.
The bulk path needs a bounded region: a concrete contig, because its overlap guard runs along a single chromosome’s records, and concrete bounds, because that is what the score’s bulk read takes. Any unbounded scan keeps the per-record path.
A resource the scan refuses is reported here and the refusal re-raised, for the reason
do_min_max_task()gives.The per-region statistics ride the same read, each accumulated by whichever path serves the histograms and carried out in the task’s RETURN value — a mutated argument would not travel under a distributed executor, whose task results arrive serialized. Which kinds get which accumulator is not decided here: each statistics module answers for its own, gated on the built score’s class –
region_coverage_for(),region_fragments_for()andregion_alleles_for()– and the three are asked independently, so a kind publishes any of them without the others. An allele score’sRegionAlleleshas one extra condition on the bulk path: it needs the nucleotides, so a backend that will not serve them sends the region back to the per-record read rather than to a statistic with no class data.ONE score serves the whole invocation – the three gates, the allele probe below, the bulk gate, and whichever scan takes the region; see
_score_for(). An allele score’s probe OPENS it and hands it on closed; the scan reopens it, as it would have opened a fresh one.Built BEFORE the
try, unlikedo_min_max_task()’s, because the gates and the allele probe need it and both precede thetry. So a construction that refuses this resource is not attributed here.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.do_min_max(resource: GenomicResource, score_ids: list[str], chrom: str, start: int | None, end: int | None, *, score: GenomicScore | None = None) dict[str, MinMaxValue][source]
Reduce a region to a min and a max per score, record by record.
The per-record min/max pass, and the floor the vectorized
do_min_max_bulk()is required to match. Reads throughscan_region(), so the validation every pass composes is composed here too (ADR 0008), and reduces only the records the region OWNS – min/max would survive double-counting, but a region measuring differently by which path served it is what the parity tests refuse.A value the reducer refuses nullifies THAT score and the pass carries on, exactly as
do_histogram()has always done – the asymmetry between the two was what made a single unfoldable value cost a whole statistics build (gain#1285, gain#1313). The refused score is replaced with aNullMinMaxValue, which is the latch as much as the outcome: itsadd_valueis a no-op, so the refusal costs one exception per region rather than one per record.A
scorehanded in is opened here and closed on return; see_score_for().
- gain.genomic_resources.implementations.genomic_scores_impl.scan.do_min_max_bulk(resource: GenomicResource, score_ids: list[str], chrom: str, start: int, end: int, *, score: GenomicScore | None = None) dict[str, MinMaxValue][source]
Vectorized equivalent of
do_min_max().Reads the region as column-array batches of already-parsed values (the same producer the histogram bulk path uses) and reduces each score with
min()/max()over the batch’s non-nan subset, rather than a per-recordMinMaxValue.add_value. The parse, the region selection, the overlap rule and the record count are identical to the per-record path – both read the same per-kind facts off the score class.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.do_min_max_task(resource: GenomicResource, score_ids: list[str], chrom: str, start: int | None, end: int | None) dict[str, MinMaxValue][source]
Compute a region’s min/max, bulk-vectorized where eligible.
Mirrors
do_histogram_task(): the bulk path needs a bounded region – a concrete contig for its overlap guard, and concrete bounds because that is what the score’s bulk read takes – so any unbounded scan keeps the per-recorddo_min_max().A resource the scan refuses is reported here and the refusal re-raised: this is the only frame that knows both which resource the scan was reading and that the reading is what failed, and the task graph’s own report names no resource.
ONE score, built here and threaded to the gate and to whichever scan serves the region – see
_score_for(). Built inside thetry, because a construction that refuses the resource is exactly the failure this frame exists to attribute. (Its histogram sibling builds outside, for a reason its own docstring gives.)
- gain.genomic_resources.implementations.genomic_scores_impl.scan.do_noregion_histograms(resource: GenomicResource) None[source]
Compute a resource’s statistics in one task, contig by contig.
The
--region-size 0path: one task, no splitting within a contig. The region-read family requires a contig, so the iteration over contigs is stated here rather than smuggled down as a null.Each contig is scanned through the very task functions a split run calls and folded together by the very functions that fold those tasks’ results, so this produces what a split run produces – not something merely believed to match, and a resource either of them refuses is attributed there rather than reaching the reader as a reason-less “statistics were not built”. An empty contig (an unscored alt, of which hg38 has hundreds) contributes an empty histogram, which merges cleanly;
merge_histogramsonly nullifies on a genuine error.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.merge_and_save_histograms(resource: GenomicResource, *results: RegionScanResult) dict[str, NullHistogram | CategoricalHistogram | NumberHistogram][source]
Fold every region’s scan results together and save all four.
The scan’s last task. Histograms, coverage, fragments and alleles each merge across the regions and are written into the resource – the one place any of the four is produced, so no SECOND code path can refresh one of them and leave the others stale. Within this one the writes are sequential and there is no rollback, so a raise partway does leave a mixture.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.merge_histograms(resource: GenomicResource, *calculated_histograms: dict[str, Any]) dict[str, NullHistogram | CategoricalHistogram | NumberHistogram][source]
Fold each region’s histograms into one histogram per score.
A score that cannot be histogrammed is nullified on its own, exactly as the per-region accumulation path nullifies its own overflow: the merge of a resource’s scores is per score, so one un-histogrammable score must not cost the rest of the resource its histograms (gain#465). A categorical score can stay within
UNIQUE_VALUES_LIMITin every single region and exceed it only in their union, so the merge is the first place its failure appears.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.merge_min_max(score_ids: list[str], all_hist_confs: dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig], *calculate_tasks: dict[str, MinMaxValue]) dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig][source]
Fold every region’s min/max together and view-range the confs.
The merge stage between the min/max tasks and the histogram tasks: each region hands up its own
MinMaxValueper score, they combine into one, andupdate_hist_confs()writes the result into the histogram configs the histogram pass will build from. A histogram whose config already carries a view range never scheduled a min/max task and is not touched.A region that REFUSED a score nullifies it for the whole fold, the way
merge_histogramslets one region’sNullHistogramnullify a score: the regions that folded values cannot answer for the one that could not.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.scan_region(score: GenomicScore, chrom: str, start: int | None, end: int | None, score_ids: list[str], *, alleles: RegionAlleles | None = None) Generator[tuple[int, int, list[str | int | float | bool | None]], None, None][source]
Read a region the way the statistics scan reads it.
Every per-record pass reads here, so validation is composed in the open – one visible extra link over the record stream – rather than each pass being trusted to remember it (ADR 0008). What the pass then sees is exactly what a reader sees: the same transform, over the same records, in the same order.
One read.
validate_recordsis a transducer over the very stream the transform consumes, not a second pass over the region – and so is the optional allele fold, which reads the nucleotides off the RAW records the transform is about to collapse to points (gain#777).
- gain.genomic_resources.implementations.genomic_scores_impl.scan.unpack_score_defs(resource: GenomicResource) tuple[list[str], dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig]][source]
Extracts scores with min/max and histogram configs for a score.
It refuses nothing. A configured NUMBER histogram over a value type no number histogram can fold used to be nullified here (gain#1285), which left the resource building with one score silently histogram-less and the reason only in the log (gain#1307); gain#1336 moved that to score CONSTRUCTION, so a resource whose config states the pairing never reaches a statistics build at all. What is still left to the per-value catch in
do_histogram()is the other direction – a CATEGORICAL histogram meeting a value it cannot fold – because that is a fact about a value rather than about the config.
- gain.genomic_resources.implementations.genomic_scores_impl.scan.update_hist_confs(all_hist_confs: dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig], minmax_task: dict[str, MinMaxValue] | None) dict[str, NullHistogramConfig | CategoricalHistogramConfig | NumberHistogramConfig][source]
Give each number histogram the view range its min/max found.
Nonemeans no min/max pass ran, so the configs stand as they are. A score whose min or max came back nan has no values to bin – its histogram is nullified with that as the reason, which is a resource fact worth reporting, not a failure to raise on.A score the min/max pass REFUSED arrives here the same way, since a refusal leaves the nan seed untouched, but it is a different fact about the resource – values that could not be folded, rather than no values – so it is nullified with the refusal as the reason instead.
Module contents
The genomic-score resource implementations, and the scan behind them.
Laid out the way gain.genomic_resources.genomic_scores is: a
kind-neutral base, one module per kind, and a factory – with the
statistics machinery beside them in a module of its own.
base–GenomicScoreImplementation, what every kind answers alike: the task-graph wiring that schedulesscan’s functions, the resource file set, the hashes, and the page protocol. It names no template; a kind does. The class still answers to two readers – the templates and the resource protocol – and separating those is gain#1037, deliberately not done here.position,alleleandfragment– the kinds. Each names the template that fills its section of the page and defines the accessors that section calls, and nothing else.builders– the factory that picks a kind from a resource’s type, as the entry points do from a type name.scan– the statistics machinery. Module-level functions that read a region and reduce it: the per-record and vectorized scans, the eligibility gates that choose between them, the task bodies, and the merge-and-save step. None of it needs an implementation object; it reads a resource and returns a result, which is what a task body should be. This is the half carrying the numeric and task history (gain#794, gain#857), which is why it kept the original file’s blame (gain#1007); the base kept the class’s for the same reason (gain#1210). Its__all__states the surface, in tiers.
The dependency runs one way: the classes import scan, never the
reverse, which tests/test_architecture.py pins from the AST.
This module is the facade, and the names below are a published surface:
four core/pyproject.toml entry points name this package path rather
than a module directly, so the layout stays rearrangeable and a later
split cannot break them. test_genomic_scores_impl_facade.py pins
what they promise.