agglovar 0.0.1.dev14__tar.gz → 0.0.1.dev16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agglovar-0.0.1.dev14/src/agglovar.egg-info → agglovar-0.0.1.dev16}/PKG-INFO +1 -1
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/pyproject.toml +1 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/__init__.py +1 -1
- agglovar-0.0.1.dev16/src/agglovar/expr/variant.py +95 -0
- agglovar-0.0.1.dev16/src/agglovar/merge/base.py +297 -0
- agglovar-0.0.1.dev16/src/agglovar/merge/cumulative.py +484 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/seqmatch.py +1 -1
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/util/lazy.py +44 -15
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16/src/agglovar.egg-info}/PKG-INFO +1 -1
- agglovar-0.0.1.dev14/src/agglovar/expr/variant.py +0 -61
- agglovar-0.0.1.dev14/src/agglovar/merge/base.py +0 -198
- agglovar-0.0.1.dev14/src/agglovar/merge/cumulative.py +0 -398
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/LICENSE +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/README.md +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/setup.cfg +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/align/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/align/op.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/align/score.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/bed/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/bed/col.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/bed/intersect.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/bed/join.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/bed/merge.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/dev/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/dev/imports.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/expr/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/fa.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/io.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/kmer/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/kmer/plot.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/kmer/util.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/config/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/config/parser.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/config/stage.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/config/strategy.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/om/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/om/_executor_base.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/merge/om/_executor_nr.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/meta/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/meta/decorators.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/meta/descriptors.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/pairwise/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/pairwise/base.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/pairwise/overlap/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/pairwise/overlap/_const.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/pairwise/overlap/_overlap.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/pairwise/overlap/_stage.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/pairwise/weights.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/schema.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/util/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/util/str.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/util/var.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/vcf/__init__.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/vcf/_vcf_const.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/vcf/_vcf_header.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/vcf/_vcf_read.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar/vcf/_vcf_write.py +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar.egg-info/SOURCES.txt +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar.egg-info/dependency_links.txt +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar.egg-info/requires.txt +0 -0
- {agglovar-0.0.1.dev14 → agglovar-0.0.1.dev16}/src/agglovar.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Polars expressions used by variant calling routines."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
'id_snv_expr',
|
|
5
|
+
'id_nonsnv_expr',
|
|
6
|
+
'sort_cols',
|
|
7
|
+
'id_expr_noalias',
|
|
8
|
+
'id_expr',
|
|
9
|
+
'lead_src_expr',
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
import polars as pl
|
|
15
|
+
|
|
16
|
+
id_snv_expr = (
|
|
17
|
+
pl.concat_str(
|
|
18
|
+
pl.col('chrom'),
|
|
19
|
+
pl.lit('-'),
|
|
20
|
+
pl.col('pos') + 1,
|
|
21
|
+
pl.lit('-SNV-'),
|
|
22
|
+
pl.col('ref').str.to_uppercase(),
|
|
23
|
+
pl.col('alt').str.to_uppercase(),
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
"""Expression for generating SNV IDs."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
id_nonsnv_expr = (
|
|
30
|
+
pl.concat_str(
|
|
31
|
+
pl.col('chrom'),
|
|
32
|
+
pl.lit('-'),
|
|
33
|
+
(pl.col('pos') + 1).cast(pl.String),
|
|
34
|
+
pl.lit('-'),
|
|
35
|
+
pl.col('vartype').str.to_uppercase(),
|
|
36
|
+
pl.lit('-'),
|
|
37
|
+
pl.col('varlen').cast(pl.String)
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
"""Expression for generating non-SNV IDs."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
sort_cols = (
|
|
44
|
+
pl.col('chrom'),
|
|
45
|
+
pl.col('pos'),
|
|
46
|
+
pl.col('end'),
|
|
47
|
+
pl.col('filter').list.len(),
|
|
48
|
+
pl.coalesce(
|
|
49
|
+
pl.col('^id$'),
|
|
50
|
+
pl.lit(None),
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
"""Sort columns for variant tables."""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
id_expr_noalias = (
|
|
57
|
+
pl.when(pl.col('vartype').str.to_uppercase() == 'SNV')
|
|
58
|
+
.then(id_snv_expr)
|
|
59
|
+
.otherwise(id_nonsnv_expr)
|
|
60
|
+
)
|
|
61
|
+
"""Expression for generating variant IDs."""
|
|
62
|
+
|
|
63
|
+
id_expr = id_expr_noalias.alias('id')
|
|
64
|
+
"""Expression for generating variant IDs including alias "id" for the result (ideal for `with_column()`)."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def lead_src_expr(
|
|
68
|
+
mg_src_col: str = 'mg_src',
|
|
69
|
+
mg_src_lead_col: str = 'mg_src_lead',
|
|
70
|
+
alias: Optional[str] = 'lead_src',
|
|
71
|
+
) -> pl.Expr:
|
|
72
|
+
"""Extract the lead source entry from a merged callset.
|
|
73
|
+
|
|
74
|
+
A merge produces an ``mg_src`` list column with one struct per contributing source and an
|
|
75
|
+
``mg_src_lead`` column holding the integer position within that list of the lead
|
|
76
|
+
(representative) source. This routine gathers that lead entry back out as a struct, so an API
|
|
77
|
+
user can read the lead source directly instead of manually indexing the list with
|
|
78
|
+
``pl.col('mg_src').list.get(pl.col('mg_src_lead'))``.
|
|
79
|
+
|
|
80
|
+
Agglovar does not use this internally; the merge stores only the index to avoid duplicating the
|
|
81
|
+
entry. It is provided as a convenience for downstream API users.
|
|
82
|
+
|
|
83
|
+
:param mg_src_col: Name of the ``mg_src`` list column.
|
|
84
|
+
:param mg_src_lead_col: Name of the ``mg_src_lead`` index column.
|
|
85
|
+
:param alias: Output column name for the resulting struct, or `None` to leave it unaliased.
|
|
86
|
+
|
|
87
|
+
:returns: An expression yielding the lead source struct, carrying the same fields as an
|
|
88
|
+
``mg_src`` entry ("src_index", "src_name", "src_meta", "var_index", "var_id").
|
|
89
|
+
"""
|
|
90
|
+
expr = pl.col(mg_src_col).list.get(pl.col(mg_src_lead_col))
|
|
91
|
+
|
|
92
|
+
if alias is not None:
|
|
93
|
+
expr = expr.alias(alias)
|
|
94
|
+
|
|
95
|
+
return expr
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""Base class for callset intersects.
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
'CallsetDef',
|
|
8
|
+
'MergeBase',
|
|
9
|
+
'CallsetDefInputType',
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from collections.abc import Container, Iterable
|
|
14
|
+
import json
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import (
|
|
17
|
+
Any,
|
|
18
|
+
NamedTuple,
|
|
19
|
+
Optional,
|
|
20
|
+
TypeAlias,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
import polars as pl
|
|
24
|
+
|
|
25
|
+
from ..meta.decorators import lockable
|
|
26
|
+
from ..util.str import collision_rename
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CallsetDef(NamedTuple):
|
|
30
|
+
"""Callset definition.
|
|
31
|
+
|
|
32
|
+
:ivar table: The callset table to be merged.
|
|
33
|
+
:ivar name: The name of the callset within a collection of callsets.
|
|
34
|
+
:ivar metadata: The metadata of the callset or None if no metadata is provided.
|
|
35
|
+
"""
|
|
36
|
+
table: pl.LazyFrame
|
|
37
|
+
name: str
|
|
38
|
+
metadata: Optional[str]
|
|
39
|
+
|
|
40
|
+
def __str__(self) -> str:
|
|
41
|
+
return self.name
|
|
42
|
+
|
|
43
|
+
def __repr__(self) -> str:
|
|
44
|
+
return f'CallsetDef(table={self.table!r}, name={self.name}, metadata={self.metadata})'
|
|
45
|
+
|
|
46
|
+
CallsetDefInputType: TypeAlias = (
|
|
47
|
+
pl.DataFrame
|
|
48
|
+
| pl.LazyFrame
|
|
49
|
+
| tuple[
|
|
50
|
+
pl.DataFrame | pl.LazyFrame,
|
|
51
|
+
]
|
|
52
|
+
| tuple [
|
|
53
|
+
pl.DataFrame | pl.LazyFrame,
|
|
54
|
+
Optional[str],
|
|
55
|
+
]
|
|
56
|
+
| tuple [
|
|
57
|
+
pl.DataFrame | pl.LazyFrame,
|
|
58
|
+
Optional[str],
|
|
59
|
+
Optional[str | dict],
|
|
60
|
+
]
|
|
61
|
+
| CallsetDef
|
|
62
|
+
)
|
|
63
|
+
"""Alias for acceptable input types."""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _get_definition(
|
|
67
|
+
callset_def: CallsetDefInputType,
|
|
68
|
+
callset_index: int,
|
|
69
|
+
name_set: set[str],
|
|
70
|
+
retain_index: bool,
|
|
71
|
+
pre_filter: Iterable[pl.Expr],
|
|
72
|
+
) -> CallsetDef:
|
|
73
|
+
"""Get a callset definition from an input type.
|
|
74
|
+
|
|
75
|
+
:param callset_def: The callset definition to be processed.
|
|
76
|
+
:param callset_index: The index of the callset.
|
|
77
|
+
:param name_set: A set of names that have already been used.
|
|
78
|
+
:param retain_index: If `True`, do not drop an existing "_index" column if it exists.
|
|
79
|
+
:param pre_filter: If set, filter each table with these expressions. Filter is applied
|
|
80
|
+
last (after "_index" is set).
|
|
81
|
+
|
|
82
|
+
:return: A callset definition.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
# Get callset table and name
|
|
86
|
+
callset_name = None
|
|
87
|
+
callset_meta = None
|
|
88
|
+
|
|
89
|
+
if isinstance(callset_def, CallsetDef):
|
|
90
|
+
callset_table = callset_def.table
|
|
91
|
+
callset_name = callset_def.name
|
|
92
|
+
callset_meta = callset_def.metadata
|
|
93
|
+
|
|
94
|
+
elif isinstance(callset_def, pl.LazyFrame):
|
|
95
|
+
callset_table = callset_def
|
|
96
|
+
|
|
97
|
+
elif isinstance(callset_def, pl.DataFrame):
|
|
98
|
+
callset_table = callset_def.lazy()
|
|
99
|
+
|
|
100
|
+
elif isinstance(callset_def, tuple):
|
|
101
|
+
if len(callset_def) < 1:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f'Callset must be a Polars table or an iterable '
|
|
104
|
+
f'of callset definition elements (table, name, metadata): Received an empty iterable'
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if isinstance(callset_def[0], pl.LazyFrame):
|
|
108
|
+
callset_table = callset_def[0]
|
|
109
|
+
elif isinstance(callset_def[0], pl.DataFrame):
|
|
110
|
+
callset_table = callset_def[0].lazy()
|
|
111
|
+
else:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f'First element of callset definition must be a Polars table: '
|
|
114
|
+
f'Received {type(callset_def[0])}'
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
callset_name = str(callset_def[1]) if len(callset_def) > 1 else None
|
|
118
|
+
|
|
119
|
+
callset_meta = callset_def[2] if len(callset_def) > 2 else None
|
|
120
|
+
|
|
121
|
+
if callset_meta is not None:
|
|
122
|
+
if isinstance(callset_meta, dict):
|
|
123
|
+
callset_meta = json.dumps(callset_meta)
|
|
124
|
+
else:
|
|
125
|
+
callset_meta = str(callset_meta)
|
|
126
|
+
|
|
127
|
+
if len(callset_def) > 3:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f'Input callsets must be a sequence of up to three items (table, name, metadata): '
|
|
130
|
+
f'Received {len(callset_def)} items'
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
else:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f'Callset must be a Polars table or an iterable '
|
|
136
|
+
f'of callset definition elements (table, name, metadata): Received {type(callset_def)}'
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Unique callset name
|
|
140
|
+
if callset_name is None:
|
|
141
|
+
callset_name = f'source_{callset_index + 1}'
|
|
142
|
+
|
|
143
|
+
callset_name = _get_name(callset_name, name_set)
|
|
144
|
+
|
|
145
|
+
# Set index if missing or forced
|
|
146
|
+
if not (retain_index and '_index' in callset_table.collect_schema().names()):
|
|
147
|
+
callset_table = (
|
|
148
|
+
callset_table
|
|
149
|
+
.drop('_index', strict=False)
|
|
150
|
+
.with_row_index('_index')
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Add missing IDs
|
|
154
|
+
callset_table = (
|
|
155
|
+
callset_table
|
|
156
|
+
.rename({'_index': '_mg_src_index'})
|
|
157
|
+
.with_columns(
|
|
158
|
+
pl.coalesce(
|
|
159
|
+
pl.col(r'^id$'), # Existing ID if present
|
|
160
|
+
pl.lit(None)
|
|
161
|
+
)
|
|
162
|
+
.cast(pl.String)
|
|
163
|
+
.fill_null(
|
|
164
|
+
pl.concat_str(pl.lit('var'),
|
|
165
|
+
pl.col('_mg_src_index')),
|
|
166
|
+
)
|
|
167
|
+
.alias('id')
|
|
168
|
+
)
|
|
169
|
+
.filter(
|
|
170
|
+
*pre_filter
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
name_set.add(callset_name)
|
|
175
|
+
|
|
176
|
+
return CallsetDef(
|
|
177
|
+
table=callset_table,
|
|
178
|
+
name=callset_name,
|
|
179
|
+
metadata=callset_meta,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@lockable
|
|
185
|
+
class MergeBase(ABC):
|
|
186
|
+
"""Base class for callset intersects."""
|
|
187
|
+
|
|
188
|
+
@abstractmethod
|
|
189
|
+
def __call__(
|
|
190
|
+
self,
|
|
191
|
+
callsets: Iterable[CallsetDefInputType],
|
|
192
|
+
retain_index: bool = False,
|
|
193
|
+
pre_filter: Optional[Iterable[pl.Expr]] = None,
|
|
194
|
+
temp_dir: bool | str | Path = False,
|
|
195
|
+
) -> pl.LazyFrame:
|
|
196
|
+
"""
|
|
197
|
+
Intersect callsets.
|
|
198
|
+
|
|
199
|
+
:param callsets: Callsets to intersect.
|
|
200
|
+
:param retain_index: If `True`, do not drop an existing "_index" column if it exists.
|
|
201
|
+
:param pre_filter: If set, filter each table with these expressions. Filter is applied
|
|
202
|
+
last (after "_index" is set).
|
|
203
|
+
:param temp_dir: How the underlying pairwise intersect materialises prepared tables before
|
|
204
|
+
its chunked loop. ``False`` (default) keeps tables in memory; ``True`` writes them to
|
|
205
|
+
the system temp directory as parquet; a ``str``/``Path`` writes them to that directory.
|
|
206
|
+
Temp files are always removed on exit.
|
|
207
|
+
|
|
208
|
+
:return: A merged callset table.
|
|
209
|
+
"""
|
|
210
|
+
...
|
|
211
|
+
|
|
212
|
+
@staticmethod
|
|
213
|
+
def get_intersect_tuples(
|
|
214
|
+
callsets: Iterable[CallsetDefInputType],
|
|
215
|
+
retain_index: bool = False,
|
|
216
|
+
pre_filter: Optional[Iterable[pl.Expr] | pl.Expr] = None,
|
|
217
|
+
) -> list[CallsetDef]:
|
|
218
|
+
"""
|
|
219
|
+
Transform input callset definitions into a normalized list of :class:`CallsetDef`.
|
|
220
|
+
|
|
221
|
+
Each returned :class:`CallsetDef` (a named tuple) has three fields:
|
|
222
|
+
|
|
223
|
+
1. ``table``: A lazy frame.
|
|
224
|
+
2. ``name``: A name for the source.
|
|
225
|
+
3. ``metadata``: A metadata string for this source, or ``None`` if none was given.
|
|
226
|
+
|
|
227
|
+
The source index is not stored on the definition; it is implicitly the position of the
|
|
228
|
+
element in the returned list (0 for the first source, incrementing by 1).
|
|
229
|
+
|
|
230
|
+
If a source name is given, the given name is used. If it is not, then a default name is
|
|
231
|
+
generated from the source index (e.g. "source_1" for the first source). Names are
|
|
232
|
+
de-duplicated against names already seen.
|
|
233
|
+
|
|
234
|
+
Lazy frames are transformed to add a source index ("_mg_src_index" column) and to ensure a
|
|
235
|
+
variant ID is present ("id" column), filling missing IDs from the index.
|
|
236
|
+
|
|
237
|
+
:param callsets: Callsets parameter. May be an iterable of DataFrames, LazyFrames,
|
|
238
|
+
``(table, name)`` tuples, ``(table, name, metadata)`` tuples, or :class:`CallsetDef`
|
|
239
|
+
objects.
|
|
240
|
+
:param retain_index: If `True`, do not drop an existing "_index" column if it exists.
|
|
241
|
+
:param pre_filter: If set, filter each table with these expressions. Filter is applied
|
|
242
|
+
last (after "_index" is set).
|
|
243
|
+
|
|
244
|
+
:return: A list of :class:`CallsetDef`, one per input source, in input order.
|
|
245
|
+
"""
|
|
246
|
+
name_set = set()
|
|
247
|
+
callset_table: pl.LazyFrame
|
|
248
|
+
|
|
249
|
+
callset_tuple_list: list[CallsetDef] = []
|
|
250
|
+
|
|
251
|
+
if isinstance(pre_filter, pl.Expr):
|
|
252
|
+
pre_filter = [pre_filter]
|
|
253
|
+
else:
|
|
254
|
+
pre_filter = list(pre_filter if pre_filter is not None else [])
|
|
255
|
+
|
|
256
|
+
if callsets is None:
|
|
257
|
+
raise ValueError('Missing callsets')
|
|
258
|
+
|
|
259
|
+
for callset_def in callsets:
|
|
260
|
+
try:
|
|
261
|
+
callset = _get_definition(
|
|
262
|
+
callset_def=callset_def,
|
|
263
|
+
callset_index=len(callset_tuple_list),
|
|
264
|
+
name_set=name_set,
|
|
265
|
+
retain_index=retain_index,
|
|
266
|
+
pre_filter=pre_filter,
|
|
267
|
+
)
|
|
268
|
+
except ValueError as e:
|
|
269
|
+
raise ValueError(
|
|
270
|
+
f'Failed getting callset at index {len(callset_tuple_list)}: {e}'
|
|
271
|
+
) from e
|
|
272
|
+
|
|
273
|
+
# Append
|
|
274
|
+
callset_tuple_list.append(callset)
|
|
275
|
+
|
|
276
|
+
return callset_tuple_list
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _get_name(
|
|
280
|
+
name: Optional[Any],
|
|
281
|
+
*args: Container[str]
|
|
282
|
+
) -> str:
|
|
283
|
+
"""Get a name for a source with a default set for None.
|
|
284
|
+
|
|
285
|
+
Gets a name for the variant call input source and de-duplicates names.
|
|
286
|
+
|
|
287
|
+
:param name: Input source name or None to choose a default name.
|
|
288
|
+
:param args: Containers with other names to avoid collisions with.
|
|
289
|
+
|
|
290
|
+
:returns: A name for this input source.
|
|
291
|
+
"""
|
|
292
|
+
if name is None:
|
|
293
|
+
name = f'source.1'
|
|
294
|
+
|
|
295
|
+
name = collision_rename(str(name), '.', *args)
|
|
296
|
+
|
|
297
|
+
return str(name)
|