-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathparse_dbnsfp_variants.py
More file actions
140 lines (104 loc) · 4.55 KB
/
Copy pathparse_dbnsfp_variants.py
File metadata and controls
140 lines (104 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# eam
# 2021-02-17
"""
This script generates a Hail Table from the dbNSFP v4.1a.
Used for annotations and downstream analysis.
Source: http://database.liulab.science/dbNSFP
It takes as input an unique block-compressed gz file.
It can be generated as follow:
zcat *.gz | bgzip -c > dbNSFP4.1a_variant.bgz
"""
import hail as hl
import os
from utils.config import NFS_DIR
def import_dbnsfp_table(path_file_in: str) -> hl.Table:
"""Import the dbNSFP BGZ-compressed flat file as a Hail Table."""
ht = hl.import_table(paths=path_file_in,
min_partitions=1000,
impute=False,
missing='.',
force_bgz=True)
return ht
def parse_chromosome_and_variant_key(ht: hl.Table) -> hl.Table:
"""Rename the chr field, build a variant key, and parse it into locus/alleles."""
# parse chromosome field
ht = ht.rename({'#chr': 'chr'})
ht = ht.annotate(chr='chr' + hl.str(ht['chr']))
# annotate variant field (chr:pos:ref:alt)
variant_key_expr = hl.array([ht.chr,
hl.str(ht['pos(1-based)']),
ht.ref,
ht.alt])
ht = ht.annotate(variant_key=hl.delimit(variant_key_expr, ':'))
# parse variant field (-> locus, alleles)
ht = (ht
.annotate(**hl.parse_variant(ht.variant_key, reference_genome='GRCh38'))
)
return ht
def rekey_table(ht: hl.Table) -> hl.Table:
"""Rearrange table fields and generate a table keyed by locus and alleles."""
# rearrange tables fields and generate keyed table
tb_fields = ht.row
ht = (ht.select('locus',
'alleles',
*[f for f in tb_fields if f not in ['locus', 'alleles']])
.key_by('locus', 'alleles')
)
return ht
def map_scores_to_transcripts(ht: hl.Table) -> hl.Table:
"""Split Ensembl transcript IDs and map score fields to per-transcript dicts."""
# Map scores to transcript. If a score is site-specific rather than
# transcript-specific, map the same value to all transcripts.
# TODO: split genename, protein name, VEP_canonical ect...
ht = (ht
.annotate(Ensembl_transcriptid=ht.Ensembl_transcriptid.split(";"))
)
scores_fields = [f for f in ht.row if f.endswith('_score') or f == 'CADD_phred']
ht = (ht
.annotate(**{f: hl.if_else(ht[f].contains(";"),
hl.dict(hl.zip(ht.Ensembl_transcriptid,
hl.map(lambda x:
hl.parse_float(x),
ht[f].split(";")))),
hl.dict(hl.zip(ht.Ensembl_transcriptid,
hl.map(lambda x:
hl.parse_float(ht[f]),
ht.Ensembl_transcriptid)))
)
for f in scores_fields
})
)
return ht
def nest_field_groups(ht: hl.Table, tb_fields) -> hl.Table:
"""Transmute related fields into nested structs and print the table schema."""
# nest related fields into structures (easier to analyse/filter later)
field_groups = ['gnomAD', 'ExAC', '1000Gp3', 'ESP6500', 'clinvar'] # fold these groups into structures
# TODO: split parse int, float fields
ht = (ht
.transmute(**{f_root: hl.struct(**{field: ht[field]
for field in tb_fields if field.startswith(f_root)}
)
for f_root in field_groups}
)
)
ht.describe()
return ht
def main() -> None:
"""Orchestrate dbNSFP table import, parsing, annotation, and export."""
hl.init(default_reference='GRCh38')
nfs_dir = NFS_DIR
path_file_in = f'{nfs_dir}/resources/dbNSFP/variants/dbNSFP4.1a_variant.bgz'
path_ht_out = f'{os.path.splitext(path_file_in)[0]}.ht'
ht = import_dbnsfp_table(path_file_in)
ht = parse_chromosome_and_variant_key(ht)
ht = rekey_table(ht)
# capture tb_fields before score mapping changes the schema
tb_fields = ht.row
ht = map_scores_to_transcripts(ht)
ht = nest_field_groups(ht, tb_fields)
# export table
ht.write(path_ht_out,
overwrite=True)
hl.stop()
if __name__ == '__main__':
main()