Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
de4b374
create get_coverage_agg_func
ch-kr Jul 15, 2025
680c312
add dp_field arg to compute_coverage_stats and add periods to docstring
ch-kr Jul 15, 2025
1613d0c
move dp_field per copilot suggestion
ch-kr Jul 16, 2025
ca3d4ff
Update gnomad/utils/sparse_mt.py
ch-kr Jul 16, 2025
dddcc47
Add tests for get_coverage_agg_func
mike-w-wilson Jul 16, 2025
59a16d0
Remove print statements for debugging in test_sparse_mt
mike-w-wilson Jul 17, 2025
e134017
Switch to hail's NaN and remove hardocded expected value -- update te…
mike-w-wilson Jul 17, 2025
69322ed
Remove pytest import and sample_ht as it wasn't being accessed
mike-w-wilson Jul 17, 2025
eb4c34b
Remove unneeded tests and misleading test names
mike-w-wilson Jul 17, 2025
3d54723
Update tests/utils/test_sparse_mt.py
mike-w-wilson Jul 17, 2025
430f84e
Update tests/utils/test_sparse_mt.py
mike-w-wilson Jul 17, 2025
13284d6
Remove unneeded sparse_mt test
mike-w-wilson Jul 18, 2025
f910760
asked cursor to add periods to comments
ch-kr Jul 18, 2025
b7aa2d3
asked cursor to rename functions _ if they weren't being used
ch-kr Jul 18, 2025
a38abff
asked cursor to remove irrelevant comment
ch-kr Jul 18, 2025
dc87fed
asked cursor to rename function and remove unncessary max_cov_bin=50 …
ch-kr Jul 18, 2025
26e8df2
unused transform_func > _
ch-kr Jul 18, 2025
36a7515
asked cursor to remove redundant custom field name tests
ch-kr Jul 18, 2025
f948c77
asked cursor to update test_transform_and_aggregation_integration
ch-kr Jul 18, 2025
63c775f
improve median approx test documentation
ch-kr Jul 18, 2025
27b4525
ask cursor to fix its comment 'accept actual behavior'
ch-kr Jul 18, 2025
23059d5
ask cursor to fix vague > 0 assert
ch-kr Jul 18, 2025
8cc1133
add link to hail docs
ch-kr Jul 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 40 additions & 16 deletions gnomad/utils/sparse_mt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,36 @@ def compute_stats_per_ref_site(
return ht


def get_coverage_agg_func(
dp_field: str = "DP", max_cov_bin: int = 100
) -> Tuple[Callable, Callable]:
"""
Get a transformation and aggregation function for computing coverage.

Can be used as an entry aggregation function in `compute_stats_per_ref_site`.

:param dp_field: Depth field to use for computing coverage. Default is 'DP'.
:param max_cov_bin: Maximum coverage bin (used when computing samples over X bin). Default is 100.
:return: Tuple of functions to transform and aggregate coverage.
"""
return (
lambda t: hl.if_else(
hl.is_missing(t[dp_field]) | hl.is_nan(t[dp_field]), 0, t[dp_field]
),
lambda dp: hl.struct(
# This expression creates a counter DP -> number of samples for DP
# between 0 and max_cov_bin.
coverage_counter=hl.agg.counter(hl.min(max_cov_bin, dp)),
mean=hl.bind(
lambda mean_dp: hl.if_else(hl.is_nan(mean_dp), 0, mean_dp),
Comment thread
ch-kr marked this conversation as resolved.
Comment thread
ch-kr marked this conversation as resolved.
Comment thread
ch-kr marked this conversation as resolved.
hl.agg.mean(dp),
Comment thread
ch-kr marked this conversation as resolved.
),
Comment thread
ch-kr marked this conversation as resolved.
Comment thread
ch-kr marked this conversation as resolved.
Comment thread
ch-kr marked this conversation as resolved.
Comment thread
ch-kr marked this conversation as resolved.
median_approx=hl.or_else(hl.agg.approx_median(dp), 0),
total_DP=hl.agg.sum(dp),
),
)


def compute_coverage_stats(
mtds: Union[hl.MatrixTable, hl.vds.VariantDataset],
reference_ht: hl.Table,
Expand All @@ -1283,6 +1313,7 @@ def compute_coverage_stats(
row_key_fields: List[str] = ["locus"],
strata_expr: Optional[List[Dict[str, hl.expr.StringExpression]]] = None,
group_membership_ht: Optional[hl.Table] = None,
dp_field: str = "DP",
) -> hl.Table:
"""
Compute coverage statistics for every base of the `reference_ht` provided.
Expand All @@ -1297,18 +1328,19 @@ def compute_coverage_stats(
computed on. It needs to be keyed by `locus`. The `reference_ht` can e.g. be
created using `get_reference_ht`.

:param mtds: Input sparse MT or VDS
:param reference_ht: Input reference HT
:param interval_ht: Optional Table containing intervals to filter to
:param coverage_over_x_bins: List of boundaries for computing samples over X
:param mtds: Input sparse MT or VDS.
:param reference_ht: Input reference HT.
:param interval_ht: Optional Table containing intervals to filter to.
:param coverage_over_x_bins: List of boundaries for computing samples over X.
:param row_key_fields: List of row key fields to use for joining `mtds` with
`reference_ht`
`reference_ht`.
:param strata_expr: Optional list of dicts containing expressions to stratify the
coverage stats by. Only one of `group_membership_ht` or `strata_expr` can be
specified.
:param group_membership_ht: Optional Table containing group membership annotations
to stratify the coverage stats by. Only one of `group_membership_ht` or
`strata_expr` can be specified.
:param dp_field: Name of sample depth field. Default is DP.
:return: Table with per-base coverage stats.
"""
is_vds = isinstance(mtds, hl.vds.VariantDataset)
Expand All @@ -1331,16 +1363,8 @@ def compute_coverage_stats(
max_cov_bin = cov_bins[-1]
cov_bins = hl.array(cov_bins)
entry_agg_funcs = {
"coverage_stats": (
lambda t: hl.if_else(hl.is_missing(t.DP) | hl.is_nan(t.DP), 0, t.DP),
lambda dp: hl.struct(
# This expression creates a counter DP -> number of samples for DP
# between 0 and max_cov_bin.
coverage_counter=hl.agg.counter(hl.min(max_cov_bin, dp)),
mean=hl.if_else(hl.is_nan(hl.agg.mean(dp)), 0, hl.agg.mean(dp)),
median_approx=hl.or_else(hl.agg.approx_median(dp), 0),
total_DP=hl.agg.sum(dp),
),
"coverage_stats": get_coverage_agg_func(
dp_field=dp_field, max_cov_bin=max_cov_bin
)
}

Expand All @@ -1350,7 +1374,7 @@ def compute_coverage_stats(
entry_agg_funcs,
row_key_fields=row_key_fields,
interval_ht=interval_ht,
entry_keep_fields=[gt_field, "DP"],
entry_keep_fields=[gt_field, dp_field],
Copy link

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here gt_field is a set (e.g., {'GT'}) rather than a string, so wrapping it in a list and then calling set(entry_keep_fields) will error on the unhashable set. Unpack the set (e.g., *gt_field) or use its single element instead of including the set directly.

Copilot uses AI. Check for mistakes.
strata_expr=strata_expr,
group_membership_ht=group_membership_ht,
)
Expand Down
Loading
Loading