pav3 3.0.0.dev16__tar.gz → 3.0.0.dev18__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.
Files changed (97) hide show
  1. {pav3-3.0.0.dev16/src/pav3.egg-info → pav3-3.0.0.dev18}/PKG-INFO +1 -1
  2. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/__init__.py +1 -1
  3. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/trim.py +2 -2
  4. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/call/intra.py +3 -0
  5. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/workflow/rules/call.smk +47 -36
  6. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/struct.py +10 -0
  7. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/vcf.py +62 -129
  8. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18/src/pav3.egg-info}/PKG-INFO +1 -1
  9. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/LICENSE +0 -0
  10. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/README.md +0 -0
  11. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/pyproject.toml +0 -0
  12. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/setup.cfg +0 -0
  13. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/__main__.py +0 -0
  14. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/__init__.py +0 -0
  15. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/features.py +0 -0
  16. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/lcmodel/__init__.py +0 -0
  17. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/lcmodel/_lcmodel.py +0 -0
  18. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/lcmodel/_lcmodel_logistic.py +0 -0
  19. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/lcmodel/_lcmodel_null.py +0 -0
  20. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/lcmodel/_util.py +0 -0
  21. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/lift.py +0 -0
  22. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/op.py +0 -0
  23. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/records.py +0 -0
  24. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/score.py +0 -0
  25. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/align/tables.py +0 -0
  26. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/anno.py +0 -0
  27. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/call/__init__.py +0 -0
  28. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/call/expr.py +0 -0
  29. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/call/integrate.py +0 -0
  30. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/call/util.py +0 -0
  31. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/cli/__init__.py +0 -0
  32. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/cli/_cli.py +0 -0
  33. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/cli/_common_opt.py +0 -0
  34. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/cli/_subcommand_batch.py +0 -0
  35. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/cli/_subcommand_call.py +0 -0
  36. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/cli/_subcommand_license.py +0 -0
  37. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/const.py +0 -0
  38. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/default/model.json +0 -0
  39. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/human_95_80/model.json +0 -0
  40. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/human_95_80/model_info.tar.gz +0 -0
  41. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/human_95_80/weights.npz +0 -0
  42. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/human_98_80/model.json +0 -0
  43. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/human_98_80/model_info.tar.gz +0 -0
  44. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/human_98_80/weights.npz +0 -0
  45. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/prim_90_50/model.json +0 -0
  46. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/prim_90_50/model_info.tar.gz +0 -0
  47. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/prim_90_50/weights.npz +0 -0
  48. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/primdiv_85_50/model.json +0 -0
  49. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/primdiv_85_50/model_info.tar.gz +0 -0
  50. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/lcmodel/primdiv_85_50/weights.npz +0 -0
  51. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/tracks/alignment_track_fields.tsv +0 -0
  52. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/tracks/variant_track_fields.tsv +0 -0
  53. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/workflow/Snakefile +0 -0
  54. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/workflow/profiles/pav_default/config.yaml +0 -0
  55. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/workflow/rules/align.smk +0 -0
  56. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/workflow/rules/data.smk +0 -0
  57. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/workflow/rules/pipeline.smk +0 -0
  58. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/data/workflow/rules/tracks.smk +0 -0
  59. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/dev/__init__.py +0 -0
  60. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/dev/imports.py +0 -0
  61. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/dev/inter.py +0 -0
  62. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/dev/path.py +0 -0
  63. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/dev/sm.py +0 -0
  64. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/dev/tables.py +0 -0
  65. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/fig/__init__.py +0 -0
  66. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/fig/const.py +0 -0
  67. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/fig/density.py +0 -0
  68. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/fig/tracks.py +0 -0
  69. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/fig/util.py +0 -0
  70. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/inv.py +0 -0
  71. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/io.py +0 -0
  72. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/kde.py +0 -0
  73. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/__init__.py +0 -0
  74. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/call.py +0 -0
  75. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/chain.py +0 -0
  76. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/interval.py +0 -0
  77. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/io.py +0 -0
  78. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/region_kde.py +0 -0
  79. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/resources.py +0 -0
  80. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/lgsv/variant.py +0 -0
  81. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/params.py +0 -0
  82. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/pipeline.py +0 -0
  83. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/region.py +0 -0
  84. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/schema.py +0 -0
  85. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/seq.py +0 -0
  86. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/util.py +0 -0
  87. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/workflow/__init__.py +0 -0
  88. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/workflow/__pycache__/__init__.cpython-312.pyc +0 -0
  89. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/workflow/__pycache__/call.cpython-312.pyc +0 -0
  90. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/workflow/__pycache__/resource.cpython-312.pyc +0 -0
  91. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/workflow/call.py +0 -0
  92. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3/workflow/resource.py +0 -0
  93. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3.egg-info/SOURCES.txt +0 -0
  94. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3.egg-info/dependency_links.txt +0 -0
  95. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3.egg-info/entry_points.txt +0 -0
  96. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3.egg-info/requires.txt +0 -0
  97. {pav3-3.0.0.dev16 → pav3-3.0.0.dev18}/src/pav3.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pav3
3
- Version: 3.0.0.dev16
3
+ Version: 3.0.0.dev18
4
4
  Summary: Assembly-based variant discovery
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
1
  """PAV Python library routines."""
2
2
 
3
- __version__ = '3.0.0.dev16'
3
+ __version__ = '3.0.0.dev18'
4
4
 
5
5
  __all__ = [
6
6
  'align',
@@ -83,7 +83,7 @@ TRIM_DESC = {
83
83
  }
84
84
  """Trim mode description."""
85
85
 
86
- DEFAULT_MIN_TRIM_QRY_LEN = 500
86
+ DEFAULT_MIN_TRIM_QRY_LEN = 1
87
87
  """Default minimum query length. Alignments with fewer query bases are removed."""
88
88
 
89
89
 
@@ -111,7 +111,7 @@ def trim_alignments_qry(
111
111
  raise ValueError('Query FAI dataframe is None.')
112
112
 
113
113
  if min_trim_qry_len < 1:
114
- raise ValueError('min_trim_qry_len must be at least 1.')
114
+ min_trim_qry_len = 1
115
115
 
116
116
  score_model = get_score_model(score_model)
117
117
 
@@ -409,6 +409,8 @@ def variant_tables_inv(
409
409
  )
410
410
 
411
411
  # Create variant tables
412
+ inv_schema = {col: type_ for col, type_ in schema.VARIANT.items() if col in set(get_inv_row().keys())}
413
+
412
414
  variant_table_list = []
413
415
 
414
416
  log_file = None
@@ -439,6 +441,7 @@ def variant_tables_inv(
439
441
  df_inv = (
440
442
  pl.from_dicts(
441
443
  variant_table_list,
444
+ schema=inv_schema,
442
445
  )
443
446
  .with_columns(
444
447
  expr.id_nonsnv().alias('id'),
@@ -29,8 +29,6 @@ global temp
29
29
  # Rules
30
30
  #
31
31
 
32
- CALL_VCF_VARTYPES = ('insdel', 'inv', 'snv', 'cpx', 'dup')
33
-
34
32
  # Generate the VCF
35
33
  rule call_vcf:
36
34
  input:
@@ -38,7 +36,7 @@ rule call_vcf:
38
36
  'results/{asm_name}/call/call_{vartype}.parquet',
39
37
  ASM_TABLE, PAV_CONFIG,
40
38
  asm_name=wildcards.asm_name,
41
- vartype=CALL_VCF_VARTYPES,
39
+ vartype=pav3.vcf.VARTYPES,
42
40
  ),
43
41
  callable_ref=lambda wildcards: pav3.pipeline.expand_pattern(
44
42
  'results/{asm_name}/call_hap/callable_ref_{hap}.parquet',
@@ -86,14 +84,14 @@ rule call_vcf:
86
84
  with pav3.io.TempDirContainer(
87
85
  prefix=f'pav_vcf_{wildcards.asm_name}_'
88
86
  ) as temp_file_container:
89
- for vartype in CALL_VCF_VARTYPES:
87
+ for vartype in pav3.vcf.VARTYPES:
90
88
 
91
89
  # Read variants
92
90
  df = (
93
91
  pl.scan_parquet(
94
92
  pattern_var.format(
95
93
  asm_name=wildcards.asm_name,
96
- vartype=vartype
94
+ vartype=vartype,
97
95
  )
98
96
  )
99
97
  .with_row_index('_index')
@@ -103,11 +101,15 @@ rule call_vcf:
103
101
  df = df.with_columns(pl.lit('SNV').alias('vartype'))
104
102
 
105
103
  # Initialize VCF fields (all but FORMAT and sample columns)
106
- df = pav3.vcf.init_vcf_fields(
107
- df,
108
- ref_fa=ref_fa,
109
- use_sym=None,
110
- )
104
+ try:
105
+ df = pav3.vcf.init_vcf_fields(
106
+ df,
107
+ ref_fa=ref_fa,
108
+ use_sym=None,
109
+ vartype=vartype,
110
+ )
111
+ except Exception as e:
112
+ raise RuntimeError(f'Failed to initialize VCF fields for {vartype}: {e}') from e
111
113
 
112
114
  # Create a sample column
113
115
  df = df.with_columns(
@@ -117,36 +119,45 @@ rule call_vcf:
117
119
  )
118
120
 
119
121
  # Append genotypes
120
- df = (
121
- df.join(
122
- pav3.vcf.gt_column(
123
- df,
124
- hap_source,
125
- hap_callable,
126
- col_name='_vcf_field_gt',
127
- separator='|',
128
- ),
129
- on='_index',
130
- how='left'
131
- )
132
- .with_columns(
133
- pl.col('_vcf_sample_0').list.concat('_vcf_field_gt'),
134
- pl.col('_vcf_format').list.concat(pl.lit('GT'))
122
+ try:
123
+ df = (
124
+ df.join(
125
+ pav3.vcf.gt_column(
126
+ df,
127
+ hap_source,
128
+ hap_callable,
129
+ col_name='_vcf_field_gt',
130
+ separator='|',
131
+ ),
132
+ on='_index',
133
+ how='left'
134
+ )
135
+ .with_columns(
136
+ pl.col('_vcf_sample_0').list.concat('_vcf_field_gt'),
137
+ pl.col('_vcf_format').list.concat(pl.lit('GT'))
138
+ )
139
+ .drop('_vcf_field_gt')
135
140
  )
136
- .drop('_vcf_field_gt')
137
- )
141
+ except Exception as e:
142
+ raise RuntimeError(f'Failed to append genotypes for {vartype}: {e}') from e
138
143
 
139
144
  # Add INFO fields
140
- df = pav3.vcf.standard_info_fields(df)
145
+ try:
146
+ df = pav3.vcf.standard_info_fields(df)
147
+ except Exception as e:
148
+ raise RuntimeError(f'Failed to add INFO fields for {vartype}: {e}') from e
141
149
 
142
150
  # Finalize VCF fields
143
- df = pav3.vcf.reformat_vcf_table(
144
- df,
145
- sample_columns={0: wildcards.asm_name}
146
- )
151
+ try:
152
+ df = pav3.vcf.reformat_vcf_table(
153
+ df,
154
+ sample_columns={0: wildcards.asm_name}
155
+ )
147
156
 
148
- # Write VCF
149
- df.sink_parquet(temp_file_container.next())
157
+ # Write VCF
158
+ df.sink_parquet(temp_file_container.next())
159
+ except Exception as e:
160
+ raise RuntimeError(f'Failed to finalize VCF fields for {vartype}: {e}') from e
150
161
 
151
162
  # Write
152
163
  header_list = pav3.vcf.get_headers(
@@ -862,8 +873,8 @@ rule call_intra_inv:
862
873
  df_inv = pav3.call.intra.variant_tables_inv(
863
874
  df_align=df_align,
864
875
  df_flag=df_flag,
865
- ref_fa_filename=str(ref_fa_filename),
866
- qry_fa_filename=str(qry_fa_filename),
876
+ ref_fa_filename=ref_fa_filename,
877
+ qry_fa_filename=qry_fa_filename,
867
878
  df_ref_fai=df_ref_fai,
868
879
  df_qry_fai=df_qry_fai,
869
880
  pav_params=pav_params,
@@ -143,6 +143,9 @@ def get_ref_trace(
143
143
  on='_index_aligned',
144
144
  how='left'
145
145
  )
146
+ .with_columns(
147
+ pl.col('is_rev') ^ interval.is_rev
148
+ )
146
149
  .group_by('_index_trace')
147
150
  .agg(
148
151
  (
@@ -159,6 +162,13 @@ def get_ref_trace(
159
162
  )
160
163
  .drop(['_index_aligned', '_index_trace'])
161
164
 
165
+ # Adjust anchor-only segments at the edges to include only segments that are a single fwd copy
166
+ .with_columns(
167
+ (pl.col('_all_anchor') & (
168
+ (pl.col('fwd_count') == 1) & (pl.col('rev_count') == 0)
169
+ )).alias('_all_anchor')
170
+ )
171
+
162
172
  # Drop anchor-only segments at the edges
163
173
  .filter(
164
174
  ~pl.col('_all_anchor') | (
@@ -1,7 +1,30 @@
1
1
  """Transform variant calls from Parquet files to VCF files."""
2
2
 
3
+ __all__ = [
4
+ 'InfoField',
5
+ 'FilterField',
6
+ 'FormatField',
7
+ 'AltField',
8
+ 'VCF_VERSION',
9
+ 'INFO_FIELDS',
10
+ 'FILTER_FIELDS',
11
+ 'FORMAT_FIELDS',
12
+ 'ALT_FIELDS',
13
+ 'VARTYPES',
14
+ 'VARTYPE_COMPAT',
15
+ 'get_headers',
16
+ 'init_vcf_fields',
17
+ 'vcf_fields_seq_insdel',
18
+ 'vcf_fields_sym',
19
+ 'vcf_fields_snv',
20
+ 'gt_column',
21
+ 'standard_info_fields',
22
+ 'reformat_vcf_table',
23
+ 'get_hap_source',
24
+ ]
25
+
26
+
3
27
  from collections.abc import Iterable, Mapping
4
- from dataclasses import dataclass
5
28
  import datetime
6
29
  from pathlib import Path
7
30
  import threading
@@ -170,6 +193,18 @@ ALT_FIELDS = [
170
193
  AltField('CPX', 'Complex variant'),
171
194
  ]
172
195
 
196
+ VARTYPES = ('insdel', 'inv', 'snv', 'cpx', 'dup')
197
+
198
+ VARTYPE_COMPAT = {
199
+ 'insdel': {'INS', 'DEL'},
200
+ 'ins': {'INS'},
201
+ 'del': {'DEL'},
202
+ 'inv': {'INV'},
203
+ 'snv': {'SNV'},
204
+ 'cpx': {'CPX'},
205
+ 'dup': {'DUP'},
206
+ }
207
+
173
208
 
174
209
  def get_headers(
175
210
  ref_filename: Optional[str | Path] = None,
@@ -221,7 +256,7 @@ def get_headers(
221
256
 
222
257
  def init_vcf_fields(
223
258
  df: pl.LazyFrame,
224
- ref_fa: Optional[str] = None,
259
+ ref_fa: Optional[str | Path] = None,
225
260
  use_sym: Optional[bool] = None,
226
261
  vartype: Optional[str] = None,
227
262
  ) -> pl.LazyFrame:
@@ -234,11 +269,30 @@ def init_vcf_fields(
234
269
  :param ref_fa: Reference FASTA filename. Required for non-SNV variants.
235
270
  :param use_sym: Whether to use symbolic alternate records. If None, INS, DEL, and SNV ore not
236
271
  symbolic, and all other variant types are.
272
+ :param vartype: Expected variant type or types (i.e. "insdel" allows "INS" and "DEL"). Setting
273
+ this field ensures the variant types is handled consisntently if all variant types
274
+ are not in table `df` or `df` is empty.
237
275
  """
276
+ df_schema = df.collect_schema()
277
+
238
278
  vartype_set = set(
239
279
  df.select(pl.col('vartype').unique()).collect().to_series()
240
280
  )
241
281
 
282
+ if vartype is not None:
283
+ vartype_compat = VARTYPE_COMPAT.get(vartype, None)
284
+
285
+ if vartype_compat is None:
286
+ raise ValueError(f'Unknown variant type: {vartype}')
287
+
288
+ if no_compat_set := vartype_set - vartype_compat:
289
+ raise ValueError(
290
+ f'Declared variant type "{vartype}" is not compatible with types found '
291
+ f'in the variant table: {", ".join(sorted([str(_).upper() for _ in no_compat_set]))}'
292
+ )
293
+
294
+ vartype_set = vartype_compat
295
+
242
296
  if 'SNV' in vartype_set and len(vartype_set) > 1:
243
297
  raise ValueError(
244
298
  f'Cannot mix SNV variants with other variant types: '
@@ -335,7 +389,7 @@ def init_vcf_fields(
335
389
 
336
390
  def vcf_fields_seq_insdel(
337
391
  df: pl.LazyFrame,
338
- ref_fa: str,
392
+ ref_fa: Path | str,
339
393
  ) -> pl.LazyFrame:
340
394
  """Set POS, REF, and ALT VCF fields for insertions and deletions (non-symbolic).
341
395
 
@@ -464,7 +518,7 @@ def vcf_fields_seq_insdel(
464
518
 
465
519
  def vcf_fields_sym(
466
520
  df: pl.LazyFrame,
467
- ref_fa: str,
521
+ ref_fa: Path | str,
468
522
  ) -> pl.LazyFrame:
469
523
  """Get POS, REF, and ALT fields for symbolic alternate records.
470
524
 
@@ -955,17 +1009,17 @@ class _LockingPysam():
955
1009
 
956
1010
  Pysam is not thread-safe, and will fail when called through Polars. This class explicitly locks it.
957
1011
  """
958
- filename: str
1012
+ filename: Path
959
1013
 
960
1014
  def __init__(
961
1015
  self,
962
- filename,
1016
+ filename: Path | str,
963
1017
  ):
964
1018
  """Create a new locking object.
965
1019
 
966
1020
  :param filename: FASTA file name.
967
1021
  """
968
- self.filename = filename
1022
+ self.filename = Path(filename)
969
1023
  self.ref_file = None
970
1024
  self.lock = threading.Lock()
971
1025
 
@@ -973,7 +1027,7 @@ class _LockingPysam():
973
1027
  if self.ref_file is not None:
974
1028
  raise RuntimeError('Ref file already open')
975
1029
 
976
- self.ref_file = pysam.FastaFile(self.filename)
1030
+ self.ref_file = pysam.FastaFile(str(self.filename))
977
1031
 
978
1032
  return self
979
1033
 
@@ -995,124 +1049,3 @@ class _LockingPysam():
995
1049
 
996
1050
  with self.lock:
997
1051
  return self.ref_file.fetch(chrom, start, end)
998
-
999
-
1000
- #
1001
- # Refactor
1002
- #
1003
-
1004
-
1005
- # def _init_vcf_fields(
1006
- # df: pl.LazyFrame | pl.DataFrame,
1007
- # ) -> pl.LazyFrame:
1008
- # """Initialize VCF fields common across all variant types (does not set REF, ALT, or POS).
1009
- #
1010
- # :param df: Variant table.
1011
- # """
1012
- #
1013
- # if isinstance(df, pl.DataFrame):
1014
- # df = df.lazy()
1015
- #
1016
- # return (
1017
- # df.with_columns(
1018
- # pl.col('chrom').alias('#CHROM'),
1019
- # pl.col('id').alias('ID'),
1020
- # pl.lit('.').alias('QUAL'),
1021
- # (
1022
- # pl.when(pl.col('filter').is_not_null())
1023
- # .then(pl.col('filter').list.join(';'))
1024
- # .otherwise(pl.lit('PASS'))
1025
- # ).alias('FILTER'),
1026
- # pl.lit([]).cast(pl.List(pl.String)).alias('_info'),
1027
- # pl.lit([]).cast(pl.List(pl.Struct({'fmt': pl.String, 'sample': pl.String}))).alias('_sample')
1028
- # )
1029
- # .with_columns(
1030
- # pl.col('_info').list.concat([
1031
- # pl.concat_str(pl.lit('ID='), 'id'),
1032
- # pl.concat_str(pl.lit('QRY_REGION='), pl.col('chrom'), pl.lit(':'), pl.col('qry_pos') + 1, pl.lit('-'), pl.col('qry_end')),
1033
- # # pl.concat_str(pl.lit('QRY_STRAND='), pl.when(pl.col('is_rev')).then('-').otherwise('+')),
1034
- # pl.concat_str(pl.lit('ALIGN_INDEX='), pl.col('align_index').cast(pl.List(pl.String)).list.join(','))
1035
- # ])
1036
- # )
1037
- # )
1038
-
1039
- # def _init_table():
1040
- # df = (
1041
- # df.with_columns(
1042
- # pl.col('chrom').alias('#CHROM'),
1043
- # # POS
1044
- # pl.col('id').alias('ID'),
1045
- # # REF
1046
- # # ALT
1047
- # pl.lit('.').alias('QUAL'),
1048
- # (
1049
- # pl.when(pl.col('filter').is_not_null())
1050
- # .then(pl.col('filter').list.join(';'))
1051
- # .otherwise(pl.lit('PASS'))
1052
- # ).alias('FILTER'),
1053
- # # INFO
1054
- # # FORMAT
1055
- # # Sample
1056
- # )
1057
- # )
1058
-
1059
- # def _init_table_other(
1060
- #
1061
- # ):
1062
- # df = (
1063
- # df.with_columns(
1064
- # (pl.col('vartype').is_in(['INV', 'CPX', 'DUP'])).alias('_is_sym'),
1065
- # (pl.col('vartype') == 'SNV').alias('_is_snv'),
1066
- # (pl.col('pos') > 0).alias('_ref_left') # Append reference base to left if True (most variants), move to end if not (variant at position 0)
1067
- # )
1068
- # )
1069
-
1070
- #
1071
- # def _ref_base(df, ref_fa):
1072
- # """
1073
- # Get reference base preceding a variant (SV, indel) or at the point change (SNV).
1074
- #
1075
- # :param df: Variant dataframe as BED.
1076
- # :param ref_fa: Reference file.
1077
- # """
1078
- #
1079
- # # Get the reference base location
1080
- # with pysam.FastaFile(ref_fa) as ref_file:
1081
- #
1082
- # df = (
1083
- # df.with_columns(
1084
- # (
1085
- # pl.when(pl.col('_ref_left'))
1086
- # .then(pl.col('pos') - 1)
1087
- # .otherwise(pl.col('end'))
1088
- # ).alias('_ref_base_loc')
1089
- # )
1090
- # .with_columns(
1091
- # pl.when(pl.col('_is_snv'))
1092
- # .then(pl.col('_ref_base_loc') + 1)
1093
- # .otherwise(pl.col('_ref_base_loc'))
1094
- # ).alias('_ref_base_loc')
1095
- # .with_columns(
1096
- # pl.struct(['chrom', '_ref_base_loc'])
1097
- # .apply(
1098
- # lambda vals: ref_file.fetch(vals['chrom'], vals['_ref_base_loc'], vals['_ref_base_loc'] + 1)
1099
- # ).alias('_ref_base')
1100
- # )
1101
- # .drop('_ref_base_Loc')
1102
- # )
1103
- #
1104
- # # Open and update records
1105
- # with pysam.FastaFile(ref_fa) as ref_file:
1106
- # for index, row in df.iterrows():
1107
- #
1108
- # if row['SVTYPE'] in {'INS', 'DEL', 'INSDEL', 'DUP', 'INV'}:
1109
- # yield ref_file.fetch(row['#CHROM'], row['POS'] + (-1 if row['POS'] > 0 else 0), row['POS']).upper()
1110
- #
1111
- # elif row['SVTYPE'] == 'SNV':
1112
- # if 'REF' in row:
1113
- # yield row['REF']
1114
- # else:
1115
- # yield ref_file.fetch(row['#CHROM'], row['POS'], row['POS'] + 1).upper()
1116
- #
1117
- # else:
1118
- # raise RuntimeError('Unknown variant type: "{}" at index {}'.format(row['VARTYPE'], index))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pav3
3
- Version: 3.0.0.dev16
3
+ Version: 3.0.0.dev18
4
4
  Summary: Assembly-based variant discovery
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.12
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes