Genomic scores

Genomic scores associate values with genomic positions, alleles, or intervals. GAIn distinguishes three types of genomic scores according to what each record describes:

Type

What a record represents

Examples

Position scores

A genomic position or a run of positions

Conservation scores such as phastCons100way

Allele scores

An allele or substitution at a genomic position

Variant-level scores such as CADD

Fragment scores

A genomic interval with associated attributes

CNV collections, ATAC fragments, and region annotations

All three types share the GenomicScore base class. It provides common functionality for opening and closing score resources, inspecting score definitions, retrieving records, and filtering data. Each specialized class also provides methods appropriate to the genomic features it represents. This chapter covers the shared Python interface. The following chapters describe the three score types individually.

Accessing a genomic score

The build_score_from_resource_id() function builds a genomic score object from a resource ID and a repository:

import gain.grr as grr

repo = grr.build_genomic_resource_repository()
score = grr.build_score_from_resource_id("hg38/scores/phastCons100way", repo)

If you already have a GenomicResource object, use build_score_from_resource() instead:

resource = repo.get_resource("hg38/scores/phastCons100way")
score = grr.build_score_from_resource(resource)

Both functions are available through gain.grr. They determine the score type from the resource definition and return the corresponding PositionScore, AlleleScore, or FragmentScore object. Each type also has its own builder functions, described in the corresponding chapter.

Opening and closing a genomic score

A GenomicScore object is initially closed. It uses an underlying data source, such as a tabix-indexed file, a bigWig, or a VCF, that must be opened before reading scores. Call open() to open the object and close() when you finish. The open() method returns the score object, so it can be chained to a builder function.

A with block closes the score automatically, including when an error occurs inside the block:

with grr.build_score_from_resource_id(
    "hg38/scores/phastCons100way", repo
).open() as score:
    print(score.get_scores_at_position("chr21", 5_030_000))

The with statement does not open the score automatically, so the .open() call is necessary. Attempting to read a closed score raises ValueError: genomic score <id> is not open. Requiring an explicit open() call makes the cost of initializing a score clear and helps prevent file handles from being left open. You can check whether a score is open using is_open(). This behavior applies to all three genomic score types.

Score definitions

A genomic score resource can provide one or more scores. The resource definition describes these in a scores: block, as explained in the Scores section of the GRR documentation.

In Python, each declared score is represented by a GenomicScoreDef object. It contains information such as the score ID, value type, rules for converting values read from the data file, and the associated histogram configuration. Score definitions can be inspected through score.score_definitions or retrieved individually with get_score_definition():

for score_def in score.score_definitions.values():
    print(score_def.score_id, score_def.value_type)

one = score.get_score_definition("phastCons100way")

parse_value() converts an individual raw value to its declared type. parse_array() performs the same conversion on an array of values. The array method supports efficient reads of large regions without constructing a separate Python object for every record.

Chromosome names and lengths

get_all_chromosomes() returns the contigs represented in the score’s data, in the order stored in the table. has_chromosome() checks whether a particular contig is present.

Chromosome lengths can come from several sources. GAIn may use the reference genome associated with the score, the header of a bigWig file, an estimate from a tabix index, or the extent of records in an in-memory table. These sources are identified by ChromLengthSource. A length obtained from a reference genome or bigWig header can describe the complete chromosome, whereas a tabix estimate provides an upper bound derived from the indexed data rather than the true chromosome length.

By default, GAIn selects the best available source. You can inspect the available sources with chrom_length_sources, request a specific source, and determine which source supplied a result:

with grr.build_score_from_resource_id(
    "hg38/scores/phastCons100way", repo
).open() as score:
    print(score.chrom_length_sources)
    print(score.get_chrom_length("chr21"))
    print(score.get_chrom_length_source("chr21"))
    print(score.get_chrom_length("chr21", source="tabix_estimate"))
    print(score.get_all_chrom_lengths(source="reference_genome"))

get_all_chrom_lengths() returns lengths for the contigs covered by the requested source. The reference-genome lengths reach the score through the statistics/chrom_lengths.json file created during repository repair. If a resource was repaired before that file was introduced, it may have only table-derived lengths until grr_manage repo-repair is run again. Requesting the unavailable reference_genome source then raises a ValueError.

A ValueError is also raised when a chromosome-length operation cannot provide an answer, for example when the score is closed, the contig is absent from the score, the requested source has no length for it, or no available source can determine its length.

Reading score records and arrays

fetch_records() yields the records in a genomic region, one at a time. This is the general interface for reading the underlying score data.

For score types and backends that support it, fetch_region_value_arrays() retrieves entire columns as arrays instead of returning a separate record for each row. This is useful when working with large numbers of score values. Before using it, check supports_region_value_arrays() for the scores you want to retrieve. Support depends on the requested value types as well as the underlying data backend.

The position, allele, and fragment score chapters describe methods for retrieving score values at specific genomic locations.

Filtering scores

compile_filter() converts a boolean expression involving the score’s columns into a ScoreFilter object. This filter can be passed to read methods through the score_filter argument, so that records are selected as they are read.

For example, the following code selects records with a phastCons100way score greater than 0.9:

with grr.build_score_from_resource_id(
    "hg38/scores/phastCons100way", repo
).open() as score:
    keep = score.compile_filter("phastCons100way > 0.9")
    for record in score.fetch_records(
        "chr21", 5_030_000, 5_040_000, score_filter=keep
    ):
        print(record)

Each filter is associated with the score object that created it. A filter compiled for one score cannot be used with another score object, even when their columns have the same names.

Aggregating scores across regions

Allele and fragment scores provide methods for aggregating the values found in a genomic region. These methods are get_allele_scores_in_region_agg() and get_fragment_scores_overlapping_region_agg(). They process the matching records in a single traversal and return an AlleleAggregate or FragmentAggregate object, respectively.

Each aggregate contains the reduced values and information about the records used to calculate them. For allele scores, this includes the matched allele keys, when the call asks for them with the allele_keys argument. For fragment scores, it includes the number of fragments. Keeping these results together ensures that the values and accompanying information describe the same set of records.

The values are returned in the order of the aggregation queries, rather than indexed by score ID. For example, requesting the same score with two different aggregators produces two values, one for each query. The allele and fragment score chapters describe their aggregation methods in more detail.

Score histograms

A genomic score may have a histogram describing the distribution of its values. The get_score_histogram() method retrieves the histogram associated with a score:

hist = score.get_score_histogram("phastCons100way")

The result may be a numeric, categorical, or null histogram. These types are described in Reading a number histogram, Reading a categorical histogram, and Reading a null histogram. Check the histogram type before using attributes specific to that type.

When a resource defines only one score, the score ID can be omitted from get_score_histogram(), get_histogram_config(), get_score_range(), and the two methods that retrieve histogram image URLs. These methods then use that resource’s single score automatically. The dedicated Histograms chapter explains histogram configurations, contents, and retrieval in more detail.

API

GenomicScore is defined in the module gain.genomic_resources.genomic_scores.base and is imported from the package gain.genomic_resources.genomic_scores. GenomicScoreDef is defined in the module gain.genomic_resources.score_def. ScoreFilter is defined in the module gain.genomic_resources.score_filter. GenomicScore is also exported by gain.grr, as grr.GenomicScore. The generic builders build_score_from_resource_id and build_score_from_resource are defined in the module gain.genomic_resources.genomic_scores.builders and are imported from the package gain.genomic_resources.genomic_scores. They are also exported by gain.grr, as grr.build_score_from_resource_id and grr.build_score_from_resource.

build_score_from_resource(resource)

Build the score of a score resource, whatever its kind.

build_score_from_resource_id(resource_id[, grr])

Build the score of the score resource with a given id.

GenomicScore(resource)

Base class of the position, allele and fragment scores.

GenomicScore.chrom_length_source

Return the source of chromosome lengths in the score's own table.

GenomicScore.chrom_length_sources

The sources that the score can answer a length from, best first.

GenomicScore.close()

Close the underlying table and mark the score not open.

GenomicScore.compile_filter(expression)

Compile a boolean expression into a filter over this score.

GenomicScore.fetch_records(chrom, pos_begin, ...)

Yield the records of a region, optionally filtered.

GenomicScore.fetch_region_segments_scores(chrom)

Yield (begin, end, values) for each record that touches a region.

GenomicScore.fetch_region_value_arrays(...)

Fetch a region as column arrays, without a record for each row.

GenomicScore.get_all_chrom_lengths([source])

Return the length of every chromosome.

GenomicScore.get_all_chromosomes()

Return the chromosome names that the score's table holds.

GenomicScore.get_chrom_length(chrom[, source])

Return the length of a chromosome.

GenomicScore.get_chrom_length_source(chrom)

Return the source that answers get_chrom_length().

GenomicScore.get_config()

Return the configuration of the score.

GenomicScore.get_score_value_from_record(...)

Read one score off a record of this score's table.

GenomicScore.has_chromosome(chrom)

Report whether the score's table carries a chromosome.

GenomicScore.is_open()

Report whether the score is open.

GenomicScore.open()

Open the score for reading and return it.

GenomicScore.supports_region_value_arrays(scores)

Report whether fetch_region_value_arrays() serves these scores.

GenomicScoreDef(score_id, value_type, desc, ...)

The definition of one score of a genomic score resource.

GenomicScoreDef.parse_array(cells)

Turn a whole column of raw cells into values, vectorized.

GenomicScoreDef.parse_value(value)

Turn one raw cell into the value of this score.

ScoreFilter(score, expression, predicate)

A compiled record filter, bound to the score that compiled it.

ScoreFilter.require_owner(score)

Refuse the records of a score that did not compile this filter.

ScoreFilter.select(score, records)

Yield the records of score that this filter accepts.

build_score_from_resource(resource: GenomicResource) → GenomicScore[source]

Build the score of a score resource, whatever its kind.

The function reads the type of the resource and calls the factory of that kind. Use the factory of the kind when the type is known. Use this function when the type is not.

Parameters:

resource (GenomicResource) – A resource of type position_score, allele_score or fragment_score. The legacy type cnv_collection also names a fragment score.

Returns:

A new PositionScore, AlleleScore or FragmentScore. Each call returns a new score that the caller owns, so closing it affects nothing else. The score is closed: call open() before the first read.

Return type:

GenomicScore

Raises:

ValueError – If the resource is not of a score type, or if it declares a retired type such as np_score.

build_score_from_resource_id(resource_id: str, grr: GenomicResourceRepo | None = None) → GenomicScore[source]

Build the score of the score resource with a given id.

The function finds the resource in the repository and calls build_score_from_resource().

Parameters:
  • resource_id (str) – The id of a score resource in the repository.

  • grr (GenomicResourceRepo | None, default None) – The repository that resolves resource_id. When it is None, the function builds the default repository with build_genomic_resource_repository().

Returns:

A new PositionScore, AlleleScore or FragmentScore. The score is closed: call open() before the first read.

Return type:

GenomicScore

Raises:
  • FileNotFoundError – If the repository has no resource with this id.

  • ValueError – If the resource is not of a score type, or if it declares a retired type such as np_score.

class GenomicScore(resource: GenomicResource)[source]

Base class of the position, allele and fragment scores.

A genomic score gives access to the values of a genomic resource that attaches scores to places in the genome. The class owns what the three kinds share: the score definitions, the open and close lifecycle, the filter compiler, and the record and column-array reads. A score is built closed, so call open() before the first read.

The data of the resource is a tabix file, a VCF, a bigWig or an in-memory table. A score is not safe for concurrent use.

Variables:
  • resource (GenomicResource) – The genomic resource of the score.

  • resource_id (str) – The id of the resource.

  • config (dict) – The validated and normalized configuration.

  • table (GenomicPositionTable) – The table that holds the data.

  • score_definitions (dict[str, GenomicScoreDef]) – The definitions of the scores, keyed by score id.

  • table_loaded (bool) – True while the table is open.

  • DEFAULT_AGGREGATORS (dict[str, str | None]) – The aggregator that each kind of score value gets when its config states none. The value types of a score are the keys. Each kind of score sets it.

See also

PositionScore, AlleleScore, FragmentScore

property chrom_length_source: ChromLengthSource

Return the source of chromosome lengths in the score’s own table.

The answer is known without opening the table, so it works on a score that is not open.

property chrom_length_sources: list[ChromLengthSource]

The sources that the score can answer a length from, best first.

The sources of the stored lengths file, or the source of the table alone. Reading the property on a score that is not open raises ValueError.

close() → None[source]

Close the underlying table and mark the score not open.

open() can run again afterwards. Do not call close while another thread reads the score: a reader that passed is_open() can find the table released. A holder that shares one score across threads must serialise the reads, open and close itself.

compile_filter(expression: str) → ScoreFilter[source]

Compile a boolean expression into a filter over this score.

The expression names scores of this resource and relates them with >, >=, <, <=, ==, != and in. The relations combine with not, and and or. Pass the result to a read as score_filter.

Parameters:

expression (str) – The filter expression, for example "phastCons100way > 0.9".

Returns:

A filter bound to this score. The reads of another score refuse it.

Return type:

ScoreFilter

Raises:

ScoreFilterError – If the expression does not parse, or if it names a score that the resource does not define.

fetch_records(chrom: str, pos_begin: int | None, pos_end: int | None, *, score_filter: ScoreFilter | None = None) → Generator[tuple[Any, ...], None, None][source]

Yield the records of a region, optionally filtered.

Read the positional fields of a record from its slots (record[CHROM], record[POS_BEGIN], …). Read a score value with get_score_value_from_record(). The generator checks nothing before the first next(): a closed score, an unknown contig and a filter of another score are all reported there, not from the call.

Parameters:
  • chrom (str) – The chromosome name. It is required. To read every record of the table, use score.table.get_all_records().

  • pos_begin (int | None) – The start of the region. None means no lower bound.

  • pos_end (int | None) – The end of the region. None means no upper bound.

  • score_filter (ScoreFilter | None, default None) – A filter from compile_filter(). When it is not None, the generator yields only the records that the filter accepts.

Yields:

Record – The records that overlap the region, in table order. A region without records yields nothing.

Raises:
  • ValueError – If the score is not open, or if the table does not hold chrom.

  • ScoreFilterError – If score_filter was compiled by another score.

fetch_region_segments_scores(chrom: str, pos_begin: int | None = None, pos_end: int | None = None, scores: Sequence[str] | None = None, *, score_filter: ScoreFilter | None = None) → Generator[tuple[int, int, list[str | int | float | bool | None]], None, None][source]

Yield (begin, end, values) for each record that touches a region.

A segment is one record at its own extent, not one value for each position. A record on the edge of the region arrives whole. To answer a question about the window, clip the segments with gain.genomic_resources.genomic_scores.records.clip_span.

Parameters:
  • chrom (str) – The chromosome name.

  • pos_begin (int | None, default None) – The start of the region. None means no lower bound.

  • pos_end (int | None, default None) – The end of the region. None means no upper bound.

  • scores (Sequence[str] | None, default None) – The ids of the scores to read. None means every score of the resource.

  • score_filter (ScoreFilter | None, default None) – A filter from compile_filter(). The filter reads the whole record, so it can name a score outside scores.

Returns:

The segments, in table order. The values follow the order of scores. A region without records yields nothing.

Return type:

Generator[tuple[int, int, list[ScoreValue]], None, None]

Raises:
  • ValueError – If the score is not open, if the table does not hold chrom, or if a score id is unknown. The call raises these errors.

  • ScoreFilterError – If score_filter was compiled by another score. The first next() raises this error, not the call.

fetch_region_value_arrays(chrom: str, pos_begin: int | None, pos_end: int | None, scores: list[str], *, batch_size: int = 100000) → Generator[tuple[ndarray, ndarray, dict[str, ndarray]], None, None][source]

Fetch a region as column arrays, without a record for each row.

The bulk form of fetch_records(), for a caller that scans a whole region and wants columns. Call supports_region_value_arrays() first. The method checks its arguments when it is called, not at the first next().

The values are parsed by parse_array(). The dtype of an array follows the value type of the score, not the backend: a float or int score gives float64 with nan for no value, and a str score gives an object array with None. The method does not clip: a record that overlaps the start of the region arrives whole.

Parameters:
  • chrom (str) – The chromosome name.

  • pos_begin (int | None) – The start of the region. None means no lower bound.

  • pos_end (int | None) – The end of the region. None means no upper bound.

  • scores (list[str]) – The ids of the scores to read. Each id gets an array of its own.

  • batch_size (int, default 100000) – A hint for the rows of a batch. It is a ceiling: a batch also stays within a budget of raw cells, so a read of many scores gets fewer rows. A bigWig backend sizes its batches itself and ignores the hint.

Returns:

Batches of (pos_begin, pos_end, {score_id: values}). The first two items are one-based position arrays. The generator yields nothing for a region without records.

Return type:

Generator[RecordArrays, None, None]

Raises:
get_all_chrom_lengths(source: ChromLengthSource | str | None = None) → dict[str, int][source]

Return the length of every chromosome.

Parameters:

source (ChromLengthSource | str | None, default None) – The source to answer from, or its string value. None means the best-ranked source for each chromosome.

Returns:

The lengths, keyed by chromosome in table order. A chromosome that the chosen view cannot answer is left out. The dict is empty if no chromosome has an answer.

Return type:

dict[str, int]

Raises:

ValueError – If the score is not open, if source is none of the four sources, or if source is reference_genome on a score with no stored lengths.

get_all_chromosomes() → list[str][source]

Return the chromosome names that the score’s table holds.

Returns:

The names, in table order.

Return type:

list[str]

Raises:

ValueError – If the score is not open.

get_chrom_length(chrom: str, source: ChromLengthSource | str | None = None) → int[source]

Return the length of a chromosome.

The best-ranked source answers by default. The source tells how far to trust the number: only the reference genome and a bigWig header are exact. get_chrom_length_source() says which source answered.

Parameters:
  • chrom (str) – The chromosome name.

  • source (ChromLengthSource | str | None, default None) – The source to answer from, or its string value. None means the best-ranked source.

Returns:

The length of chrom.

Return type:

int

Raises:

ValueError – If the score is not open, if the table does not hold chrom, if source is none of the four sources, if the source has no answer for chrom, or if no source can measure chrom. A source of reference_genome raises on a score whose repair did not store the lengths.

get_chrom_length_source(chrom: str) → ChromLengthSource[source]

Return the source that answers get_chrom_length().

Parameters:

chrom (str) – The chromosome name.

Returns:

The best-ranked source that has an answer for chrom.

Return type:

ChromLengthSource

Raises:

ValueError – For the same reasons as get_chrom_length(), including a chromosome that no source answers.

get_config() → dict[str, Any][source]

Return the configuration of the score.

Returns:

The configuration of the resource, as validated and normalized when the score was built. It is the object the score holds, not a copy.

Return type:

dict[str, Any]

get_score_value_from_record(record: tuple[Any, ...], score_id: str) → str | int | float | bool | None[source]

Read one score off a record of this score’s table.

Parameters:
  • record (Record) – A record from fetch_records() of this score.

  • score_id (str) – The id of a score of the resource.

Returns:

The parsed value. None if the cell holds no value: a missing cell, an NA sentinel of the score, or a cell that does not parse.

Return type:

ScoreValue

Raises:

KeyError – If the resource does not define score_id.

has_chromosome(chrom: str) → bool[source]

Report whether the score’s table carries a chromosome.

Parameters:

chrom (str) – The chromosome name.

Returns:

True if the table holds chrom, False otherwise.

Return type:

bool

Raises:

ValueError – If the score is not open.

is_open() → bool[source]

Report whether the score is open.

Returns:

True if open() has run and close() has not run since. The answer is a snapshot for the owner of the score. It does not protect against a close() in another thread.

Return type:

bool

open() → Self[source]

Open the score for reading and return it.

Call open before the first read. Entering a with block does not open the score. Opening an open score does nothing.

Returns:

This score, so the call can chain: score.open().

Return type:

GenomicScore

Raises:

ValueError – If the score definitions do not fit the table, for example when a configured column does not exist.

supports_region_value_arrays(scores: list[str]) → bool[source]

Report whether fetch_region_value_arrays() serves these scores.

The answer is True only if the backend serves the column-array read and every named score has a value type that the array parse supports. The method works on a score that is not open.

Parameters:

scores (list[str]) – The ids of the scores to read.

Returns:

True if fetch_region_value_arrays() accepts these scores. False if the backend has no array read, if a score id is unknown, or if a score has a value type that the parse does not support. An empty list gives the answer of the backend alone.

Return type:

bool

class GenomicScoreDef(score_id: str, value_type: str, desc: str, small_values_desc: str | None, large_values_desc: str | None, hist_conf: HistogramConfig | None, aggregator: str | None, col_name: str | None, col_index: int | None, value_parser: Any, na_values: Any)[source]

The definition of one score of a genomic score resource.

Each entry of the scores: block of a resource becomes one definition. The definition knows the id, the value type and the description of a score, how to turn a raw cell into a value, and which histogram configuration belongs to it. Read the definitions from GenomicScore.score_definitions.

Variables:
  • score_id (str) – The id of the score.

  • value_type (str) – The type of the values: float, int, str or bool.

  • desc (str) – The description of the score.

  • small_values_desc (str | None) – The description of the small values.

  • large_values_desc (str | None) – The description of the large values.

  • hist_conf (HistogramConfig | None) – The histogram configuration.

  • aggregator (str | None) – The aggregator that reduces several values for one annotatable to one. It is None until the resource type fills its default.

  • number_mismatch_warned (bool) – Internal. True after a VCF INFO field with a wrong value count was reported once.

  • empty_element_warned (bool) – Internal. True after an empty element of a multi-valued VCF INFO field was reported once.

parse_array(cells: ndarray) → ndarray[source]

Turn a whole column of raw cells into values, vectorized.

The result equals [parse_value(c) for c in cells], with the non-value shown in a form that the array can hold. The result has one of two shapes, chosen by value_type. A float or int score gives a float64 array with nan for no value. A str score gives an object array with None for no value.

Parameters:

cells (np.ndarray) – The raw cells of one column.

Returns:

The parsed values, one for each cell. The array of an empty column is empty.

Return type:

np.ndarray

Raises:

TypeError – If value_type is not float, int or str. Ask GenomicScore.supports_region_value_arrays first.

parse_value(value: str | int | float | None) → str | int | float | bool | None[source]

Turn one raw cell into the value of this score.

Parameters:

value (str | int | float | None) – The raw cell.

Returns:

The parsed value. None if the cell is None (for example an absent VCF INFO key), if it is an NA sentinel of the score, or if it does not parse. A cell that does not parse is logged and skipped, so one bad cell does not abort a scan.

Return type:

ScoreValue

class ScoreFilter(score: GenomicScore, expression: str, predicate: Callable[[Record], bool])[source]

A compiled record filter, bound to the score that compiled it.

Get a filter from GenomicScore.compile_filter() and pass it to a read as score_filter. The filter works only on the records of the score that compiled it.

Variables:
  • score (GenomicScore) – The score that compiled the filter.

  • expression (str) – The source expression. Error messages and repr show it.

require_owner(score: GenomicScore) → None[source]

Refuse the records of a score that did not compile this filter.

Parameters:

score (GenomicScore) – The score whose records the caller is about to read.

Raises:

ScoreFilterError – If score is not the score that compiled the filter.

select(score: GenomicScore, records: Iterable[Record]) → Iterator[Record][source]

Yield the records of score that this filter accepts.

Parameters:
  • score (GenomicScore) – The score that the records come from.

  • records (Iterable[Record]) – The records to test.

Returns:

The accepted records, in the order of records. The iterator is lazy. It is empty if the filter accepts none.

Return type:

Iterator[Record]

Raises:

ScoreFilterError – If score did not compile the filter. The call raises the error before any record is read.