htslib 0.0.10 → 0.1.0
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.
- checksums.yaml +4 -4
- data/README.md +16 -16
- data/lib/hts/bam/aux.rb +39 -0
- data/lib/hts/bam/cigar.rb +0 -3
- data/lib/hts/bam/flag.rb +21 -57
- data/lib/hts/bam/header.rb +1 -4
- data/lib/hts/bam/record.rb +91 -47
- data/lib/hts/bam.rb +14 -15
- data/lib/hts/bcf/format.rb +82 -11
- data/lib/hts/bcf/info.rb +56 -31
- data/lib/hts/bcf/record.rb +43 -16
- data/lib/hts/bcf.rb +40 -15
- data/lib/hts/faidx.rb +0 -3
- data/lib/hts/ffi_ext/pointer.rb +18 -0
- data/lib/hts/hts.rb +22 -4
- data/lib/hts/libhts/constants.rb +3 -3
- data/lib/hts/libhts/cram.rb +287 -292
- data/lib/hts/libhts/vcf.rb +14 -0
- data/lib/hts/libhts.rb +4 -0
- data/lib/hts/{tabix.rb → tbx.rb} +4 -11
- data/lib/hts/version.rb +1 -1
- data/lib/htslib.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c90062954caa8dc2155f77ff3d03534d933ba81148f9bdb7c8dc173f73efca64
|
4
|
+
data.tar.gz: fb24ced637a8b9b897ecd9c5afed1c6f75902995e48c25daa849baa06611517b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fed4e6689d48493416ebd409401df284fedaddb567d3558196b3ba3c253376dd1c4bea8db536fdb6ff8bbebe85512850d7d5c17605544ce26a09e814e7172eb5
|
7
|
+
data.tar.gz: b9d596e6445671254568b4bd3cc5b694a1011914ae67b1afc11aaa66a195f49b7371d001f9e10cc204a4c20e501cd8eb63092d63b9b392c701571a12843b5c91
|
data/README.md
CHANGED
@@ -43,21 +43,21 @@ Read SAM / BAM / CRAM - Sequence Alignment Map file
|
|
43
43
|
```ruby
|
44
44
|
require 'htslib'
|
45
45
|
|
46
|
-
bam = HTS::Bam.open("
|
46
|
+
bam = HTS::Bam.open("test/fixtures/moo.bam")
|
47
47
|
|
48
48
|
bam.each do |r|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
49
|
+
pp name: r.qname,
|
50
|
+
flag: r.flag,
|
51
|
+
chrm: r.chrom,
|
52
|
+
strt: r.pos + 1,
|
53
|
+
mapq: r.mapq,
|
54
|
+
cigr: r.cigar.to_s,
|
55
|
+
mchr: r.mate_chrom,
|
56
|
+
mpos: r.mpos + 1,
|
57
|
+
isiz: r.isize,
|
58
|
+
seqs: r.seq,
|
59
|
+
qual: r.qual.map { |i| (i + 33).chr }.join,
|
60
|
+
MC: r.aux("MC")
|
61
61
|
end
|
62
62
|
|
63
63
|
bam.close
|
@@ -75,9 +75,9 @@ bcf.each do |r|
|
|
75
75
|
qual: r.qual.round(2),
|
76
76
|
ref: r.ref,
|
77
77
|
alt: r.alt,
|
78
|
-
filter: r.filter
|
79
|
-
|
80
|
-
|
78
|
+
filter: r.filter,
|
79
|
+
info: r.info.to_h,
|
80
|
+
format: r.format.to_h
|
81
81
|
end
|
82
82
|
|
83
83
|
bcf.close
|
data/lib/hts/bam/aux.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTS
|
4
|
+
class Bam < Hts
|
5
|
+
class Aux
|
6
|
+
def initialize(record)
|
7
|
+
@record = record
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(key, type = nil)
|
11
|
+
aux = LibHTS.bam_aux_get(@record.struct, key)
|
12
|
+
return nil if aux.null?
|
13
|
+
|
14
|
+
type ||= aux.read_string(1)
|
15
|
+
|
16
|
+
# A (character), B (general array),
|
17
|
+
# f (real number), H (hexadecimal array),
|
18
|
+
# i (integer), or Z (string).
|
19
|
+
|
20
|
+
case type
|
21
|
+
when "i", "I", "c", "C", "s", "S"
|
22
|
+
LibHTS.bam_aux2i(aux)
|
23
|
+
when "f", "d"
|
24
|
+
LibHTS.bam_aux2f(aux)
|
25
|
+
when "Z", "H"
|
26
|
+
LibHTS.bam_aux2Z(aux)
|
27
|
+
when "A" # char
|
28
|
+
LibHTS.bam_aux2A(aux).chr
|
29
|
+
else
|
30
|
+
raise NotImplementedError, "type: #{t}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](key)
|
35
|
+
get(key)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/hts/bam/cigar.rb
CHANGED
data/lib/hts/bam/flag.rb
CHANGED
@@ -27,70 +27,34 @@ module HTS
|
|
27
27
|
# BAM_FDUP = 1024
|
28
28
|
# BAM_FSUPPLEMENTARY = 2048
|
29
29
|
|
30
|
-
# TODO: Enabling bitwise operations
|
31
|
-
# hts-nim
|
32
|
-
# proc `and`*(f: Flag, o: uint16): uint16 {. borrow, inline .}
|
33
|
-
# proc `and`*(f: Flag, o: Flag): uint16 {. borrow, inline .}
|
34
|
-
# proc `or`*(f: Flag, o: uint16): uint16 {. borrow .}
|
35
|
-
# proc `or`*(o: uint16, f: Flag): uint16 {. borrow .}
|
36
|
-
# proc `==`*(f: Flag, o: Flag): bool {. borrow, inline .}
|
37
|
-
# proc `==`*(f: Flag, o: uint16): bool {. borrow, inline .}
|
38
|
-
# proc `==`*(o: uint16, f: Flag): bool {. borrow, inline .}
|
30
|
+
# TODO: Enabling bitwise operations?
|
39
31
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def mate_unmapped?
|
53
|
-
has_flag? LibHTS::BAM_FMUNMAP
|
54
|
-
end
|
55
|
-
|
56
|
-
def reverse?
|
57
|
-
has_flag? LibHTS::BAM_FREVERSE
|
58
|
-
end
|
59
|
-
|
60
|
-
def mate_reverse?
|
61
|
-
has_flag? LibHTS::BAM_FMREVERSE
|
62
|
-
end
|
63
|
-
|
64
|
-
def read1?
|
65
|
-
has_flag? LibHTS::BAM_FREAD1
|
66
|
-
end
|
67
|
-
|
68
|
-
def read2?
|
69
|
-
has_flag? LibHTS::BAM_FREAD2
|
70
|
-
end
|
71
|
-
|
72
|
-
def secondary?
|
73
|
-
has_flag? LibHTS::BAM_FSECONDARY
|
74
|
-
end
|
75
|
-
|
76
|
-
def qcfail?
|
77
|
-
has_flag? LibHTS::BAM_FQCFAIL
|
78
|
-
end
|
79
|
-
|
80
|
-
def dup?
|
81
|
-
has_flag? LibHTS::BAM_FDUP
|
82
|
-
end
|
32
|
+
TABLE = { paired?: LibHTS::BAM_FPAIRED,
|
33
|
+
proper_pair?: LibHTS::BAM_FPROPER_PAIR,
|
34
|
+
unmapped?: LibHTS::BAM_FUNMAP,
|
35
|
+
mate_unmapped?: LibHTS::BAM_FMUNMAP,
|
36
|
+
reverse?: LibHTS::BAM_FREVERSE,
|
37
|
+
mate_reverse?: LibHTS::BAM_FMREVERSE,
|
38
|
+
read1?: LibHTS::BAM_FREAD1,
|
39
|
+
read2?: LibHTS::BAM_FREAD2,
|
40
|
+
secondary?: LibHTS::BAM_FSECONDARY,
|
41
|
+
qcfail?: LibHTS::BAM_FQCFAIL,
|
42
|
+
duplicate?: LibHTS::BAM_FDUP,
|
43
|
+
supplementary?: LibHTS::BAM_FSUPPLEMENTARY }.freeze
|
83
44
|
|
84
|
-
|
85
|
-
|
45
|
+
TABLE.each do |name, v|
|
46
|
+
define_method(name) do
|
47
|
+
has_flag?(v)
|
48
|
+
end
|
86
49
|
end
|
87
50
|
|
88
|
-
def has_flag?(
|
89
|
-
(@value &
|
51
|
+
def has_flag?(f)
|
52
|
+
(@value & f) != 0
|
90
53
|
end
|
91
54
|
|
92
55
|
def to_s
|
93
|
-
|
56
|
+
LibHTS.bam_flag2str(@value)
|
57
|
+
# "0x#{format('%x', @value)}\t#{@value}\t#{LibHTS.bam_flag2str(@value)}"
|
94
58
|
end
|
95
59
|
end
|
96
60
|
end
|
data/lib/hts/bam/header.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Based on hts-python
|
4
|
-
# https://github.com/quinlan-lab/hts-python
|
5
|
-
|
6
3
|
module HTS
|
7
4
|
class Bam < Hts
|
8
5
|
class Header
|
@@ -28,7 +25,7 @@ module HTS
|
|
28
25
|
end
|
29
26
|
end
|
30
27
|
|
31
|
-
def
|
28
|
+
def target_len
|
32
29
|
Array.new(target_count) do |i|
|
33
30
|
LibHTS.sam_hdr_tid2len(@sam_hdr, i)
|
34
31
|
end
|
data/lib/hts/bam/record.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative "flag"
|
4
|
+
require_relative "cigar"
|
5
|
+
require_relative "aux"
|
5
6
|
|
6
7
|
module HTS
|
7
8
|
class Bam < Hts
|
@@ -38,26 +39,49 @@ module HTS
|
|
38
39
|
@bam1[:core][:tid]
|
39
40
|
end
|
40
41
|
|
42
|
+
def tid=(tid)
|
43
|
+
@bam1[:core][:tid] = tid
|
44
|
+
end
|
45
|
+
|
41
46
|
# returns the tid of the mate or -1 if not mapped.
|
42
|
-
def
|
47
|
+
def mtid
|
43
48
|
@bam1[:core][:mtid]
|
44
49
|
end
|
45
50
|
|
51
|
+
def mtid=(mtid)
|
52
|
+
@bam1[:core][:mtid] = mtid
|
53
|
+
end
|
54
|
+
|
46
55
|
# returns 0-based start position.
|
47
|
-
def
|
56
|
+
def pos
|
48
57
|
@bam1[:core][:pos]
|
49
58
|
end
|
50
59
|
|
51
|
-
|
52
|
-
|
53
|
-
LibHTS.bam_endpos @bam1
|
60
|
+
def pos=(pos)
|
61
|
+
@bam1[:core][:pos] = pos
|
54
62
|
end
|
55
63
|
|
56
64
|
# returns 0-based mate position
|
57
|
-
def
|
65
|
+
def mpos
|
58
66
|
@bam1[:core][:mpos]
|
59
67
|
end
|
60
|
-
|
68
|
+
|
69
|
+
def mpos=(mpos)
|
70
|
+
@bam1[:core][:mpos] = mpos
|
71
|
+
end
|
72
|
+
|
73
|
+
def bin
|
74
|
+
@bam1[:core][:bin]
|
75
|
+
end
|
76
|
+
|
77
|
+
def bin=(bin)
|
78
|
+
@bam1[:core][:bin] = bin
|
79
|
+
end
|
80
|
+
|
81
|
+
# returns end position of the read.
|
82
|
+
def endpos
|
83
|
+
LibHTS.bam_endpos @bam1
|
84
|
+
end
|
61
85
|
|
62
86
|
# returns the chromosome or '' if not mapped.
|
63
87
|
def chrom
|
@@ -66,37 +90,44 @@ module HTS
|
|
66
90
|
LibHTS.sam_hdr_tid2name(@header, tid)
|
67
91
|
end
|
68
92
|
|
69
|
-
|
70
|
-
def contig
|
71
|
-
chrom
|
72
|
-
end
|
93
|
+
alias contig chrom
|
73
94
|
|
74
95
|
# returns the chromosome of the mate or '' if not mapped.
|
75
96
|
def mate_chrom
|
76
|
-
mtid = mate_tid
|
77
97
|
return "" if mtid == -1
|
78
98
|
|
79
99
|
LibHTS.sam_hdr_tid2name(@header, mtid)
|
80
100
|
end
|
81
101
|
|
102
|
+
alias mate_contig mate_chrom
|
103
|
+
|
104
|
+
# Get strand information.
|
82
105
|
def strand
|
83
106
|
LibHTS.bam_is_rev(@bam1) ? "-" : "+"
|
84
107
|
end
|
85
108
|
|
86
|
-
# def start=(v)
|
87
|
-
# raise 'Not Implemented'
|
88
|
-
# end
|
89
|
-
|
90
109
|
# insert size
|
91
110
|
def insert_size
|
92
111
|
@bam1[:core][:isize]
|
93
112
|
end
|
94
113
|
|
114
|
+
alias isize insert_size
|
115
|
+
|
116
|
+
def insert_size=(isize)
|
117
|
+
@bam1[:core][:isize] = isize
|
118
|
+
end
|
119
|
+
|
120
|
+
alias isize= insert_size=
|
121
|
+
|
95
122
|
# mapping quality
|
96
|
-
def
|
123
|
+
def mapq
|
97
124
|
@bam1[:core][:qual]
|
98
125
|
end
|
99
126
|
|
127
|
+
def mapq=(mapq)
|
128
|
+
@bam1[:core][:qual] = mapq
|
129
|
+
end
|
130
|
+
|
100
131
|
# returns a `Cigar` object.
|
101
132
|
def cigar
|
102
133
|
Cigar.new(LibHTS.bam_get_cigar(@bam1), @bam1[:core][:n_cigar])
|
@@ -117,7 +148,7 @@ module HTS
|
|
117
148
|
end
|
118
149
|
|
119
150
|
# return the read sequence
|
120
|
-
def
|
151
|
+
def seq
|
121
152
|
r = LibHTS.bam_get_seq(@bam1)
|
122
153
|
seq = String.new
|
123
154
|
(@bam1[:core][:l_qseq]).times do |i|
|
@@ -125,64 +156,77 @@ module HTS
|
|
125
156
|
end
|
126
157
|
seq
|
127
158
|
end
|
159
|
+
alias sequence seq
|
160
|
+
|
161
|
+
def len
|
162
|
+
@bam1[:core][:l_qseq]
|
163
|
+
end
|
128
164
|
|
129
165
|
# return only the base of the requested index "i" of the query sequence.
|
130
|
-
def
|
166
|
+
def base(n)
|
131
167
|
n += @bam1[:core][:l_qseq] if n < 0
|
132
|
-
return "." if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg.
|
168
|
+
return "." if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base(-1000)
|
133
169
|
|
134
170
|
r = LibHTS.bam_get_seq(@bam1)
|
135
171
|
SEQ_NT16_STR[LibHTS.bam_seqi(r, n)]
|
136
172
|
end
|
137
173
|
|
138
174
|
# return the base qualities
|
139
|
-
def
|
175
|
+
def qual
|
140
176
|
q_ptr = LibHTS.bam_get_qual(@bam1)
|
141
177
|
q_ptr.read_array_of_uint8(@bam1[:core][:l_qseq])
|
142
178
|
end
|
143
179
|
|
144
180
|
# return only the base quality of the requested index "i" of the query sequence.
|
145
|
-
def
|
181
|
+
def base_qual(n)
|
146
182
|
n += @bam1[:core][:l_qseq] if n < 0
|
147
|
-
return 0 if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg.
|
183
|
+
return 0 if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base_qual(-1000)
|
148
184
|
|
149
185
|
q_ptr = LibHTS.bam_get_qual(@bam1)
|
150
186
|
q_ptr.get_uint8(n)
|
151
187
|
end
|
152
188
|
|
153
|
-
def flag_str
|
154
|
-
LibHTS.bam_flag2str(@bam1[:core][:flag])
|
155
|
-
end
|
156
|
-
|
157
189
|
# returns a `Flag` object.
|
158
190
|
def flag
|
159
191
|
Flag.new(@bam1[:core][:flag])
|
160
192
|
end
|
161
193
|
|
162
|
-
def
|
163
|
-
|
164
|
-
|
194
|
+
def flag=(flag)
|
195
|
+
case flag
|
196
|
+
when Integer
|
197
|
+
@bam1[:core][:flag] = flag
|
198
|
+
when Flag
|
199
|
+
@bam1[:core][:flag] = flag.value
|
200
|
+
else
|
201
|
+
raise "Invalid flag type: #{flag.class}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# retruns the auxillary fields.
|
206
|
+
def aux(key = nil)
|
207
|
+
aux = Aux.new(self)
|
208
|
+
if key
|
209
|
+
aux.get(key)
|
210
|
+
else
|
211
|
+
aux
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# TODO: add a method to get the auxillary fields as a hash.
|
165
216
|
|
166
|
-
|
217
|
+
# TODO: add a method to set the auxillary fields.
|
167
218
|
|
168
|
-
|
169
|
-
# f (real number), H (hexadecimal array),
|
170
|
-
# i (integer), or Z (string).
|
219
|
+
# TODO: add a method to remove the auxillary fields.
|
171
220
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
LibHTS.bam_aux2Z(aux)
|
179
|
-
when "A" # char
|
180
|
-
LibHTS.bam_aux2A(aux).chr
|
221
|
+
# TODO: add a method to set variable length data (qname, cigar, seq, qual).
|
222
|
+
|
223
|
+
# Calling flag is delegated to the Flag object.
|
224
|
+
Flag::TABLE.each_key do |m|
|
225
|
+
define_method(m) do
|
226
|
+
flag.send(m)
|
181
227
|
end
|
182
228
|
end
|
183
229
|
|
184
|
-
# def tags; end
|
185
|
-
|
186
230
|
def to_s
|
187
231
|
kstr = LibHTS::KString.new
|
188
232
|
raise "Failed to format bam record" if LibHTS.sam_format1(@header.struct, @bam1, kstr) == -1
|
data/lib/hts/bam.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Based on hts-python
|
4
|
-
# https://github.com/quinlan-lab/hts-python
|
5
|
-
|
6
3
|
require_relative "../htslib"
|
7
4
|
|
8
5
|
require_relative "hts"
|
@@ -15,7 +12,7 @@ module HTS
|
|
15
12
|
class Bam
|
16
13
|
include Enumerable
|
17
14
|
|
18
|
-
attr_reader :file_name, :
|
15
|
+
attr_reader :file_name, :index_name, :mode, :header
|
19
16
|
|
20
17
|
def self.open(*args, **kw)
|
21
18
|
file = new(*args, **kw) # do not yield
|
@@ -38,9 +35,10 @@ module HTS
|
|
38
35
|
|
39
36
|
# NOTE: Do not check for the existence of local files, since file_names may be remote URIs.
|
40
37
|
|
41
|
-
@file_name
|
42
|
-
@
|
43
|
-
@
|
38
|
+
@file_name = file_name
|
39
|
+
@index_name = index
|
40
|
+
@mode = mode
|
41
|
+
@hts_file = LibHTS.hts_open(@file_name, mode)
|
44
42
|
|
45
43
|
raise Errno::ENOENT, "Failed to open #{@file_name}" if @hts_file.null?
|
46
44
|
|
@@ -66,11 +64,10 @@ module HTS
|
|
66
64
|
end
|
67
65
|
|
68
66
|
def create_index(index_name = nil)
|
67
|
+
warn "Create index for #{@file_name} to #{index_name}"
|
69
68
|
if index
|
70
|
-
warn "Create index for #{@file_name} to #{index_name}"
|
71
69
|
LibHTS.sam_index_build2(@file_name, index_name, -1)
|
72
70
|
else
|
73
|
-
warn "Create index for #{@file_name} to #{index_name}"
|
74
71
|
LibHTS.sam_index_build(@file_name, -1)
|
75
72
|
end
|
76
73
|
end
|
@@ -91,21 +88,20 @@ module HTS
|
|
91
88
|
def close
|
92
89
|
LibHTS.hts_idx_destroy(@idx) if @idx&.null?
|
93
90
|
@idx = nil
|
94
|
-
|
95
|
-
@hts_file = nil
|
96
|
-
end
|
97
|
-
|
98
|
-
def closed?
|
99
|
-
@hts_file.nil? || @hts_file.null?
|
91
|
+
super
|
100
92
|
end
|
101
93
|
|
102
94
|
def write_header(header)
|
95
|
+
raise IOError, "closed stream" if closed?
|
96
|
+
|
103
97
|
@header = header.dup
|
104
98
|
LibHTS.hts_set_fai_filename(@hts_file, @file_name)
|
105
99
|
LibHTS.sam_hdr_write(@hts_file, header)
|
106
100
|
end
|
107
101
|
|
108
102
|
def write(aln)
|
103
|
+
raise IOError, "closed stream" if closed?
|
104
|
+
|
109
105
|
aln_dup = aln.dup
|
110
106
|
LibHTS.sam_write1(@hts_file, header, aln_dup) > 0 || raise
|
111
107
|
end
|
@@ -114,6 +110,7 @@ module HTS
|
|
114
110
|
# Generate a new Record object each time.
|
115
111
|
# Slower than each.
|
116
112
|
def each_copy
|
113
|
+
raise IOError, "closed stream" if closed?
|
117
114
|
return to_enum(__method__) unless block_given?
|
118
115
|
|
119
116
|
while LibHTS.sam_read1(@hts_file, header, bam1 = LibHTS.bam_init1) != -1
|
@@ -127,6 +124,7 @@ module HTS
|
|
127
124
|
# Record object is reused.
|
128
125
|
# Faster than each_copy.
|
129
126
|
def each
|
127
|
+
raise IOError, "closed stream" if closed?
|
130
128
|
# Each does not always start at the beginning of the file.
|
131
129
|
# This is the common behavior of IO objects in Ruby.
|
132
130
|
# This may change in the future.
|
@@ -140,6 +138,7 @@ module HTS
|
|
140
138
|
|
141
139
|
# query [WIP]
|
142
140
|
def query(region)
|
141
|
+
raise IOError, "closed stream" if closed?
|
143
142
|
raise "Index file is required to call the query method." unless index_loaded?
|
144
143
|
|
145
144
|
qiter = LibHTS.sam_itr_querys(@idx, header, region)
|
data/lib/hts/bcf/format.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# https://github.com/brentp/hts-nim/blob/master/src/hts/vcf.nim
|
4
|
-
# This is a port from Nim.
|
5
|
-
# TODO: Make it more like Ruby.
|
6
|
-
|
7
3
|
module HTS
|
8
4
|
class Bcf < Hts
|
9
5
|
class Format
|
@@ -32,6 +28,10 @@ module HTS
|
|
32
28
|
get(key, :string)
|
33
29
|
end
|
34
30
|
|
31
|
+
def [](key)
|
32
|
+
get(key)
|
33
|
+
end
|
34
|
+
|
35
35
|
def get(key, type = nil)
|
36
36
|
n = FFI::MemoryPointer.new(:int)
|
37
37
|
p1 = @p1
|
@@ -45,7 +45,15 @@ module HTS
|
|
45
45
|
p1.read_pointer
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
# The GT FORMAT field is special in that it is marked as a string in the header,
|
49
|
+
# but it is actually encoded as an integer.
|
50
|
+
if key == "GT"
|
51
|
+
type = :int
|
52
|
+
elsif type.nil?
|
53
|
+
type = ht_type_to_sym(get_fmt_type(key))
|
54
|
+
end
|
55
|
+
|
56
|
+
case type&.to_sym
|
49
57
|
when :int, :int32
|
50
58
|
format_values.call(LibHTS::BCF_HT_INT)
|
51
59
|
.read_array_of_int32(n.read_int)
|
@@ -53,22 +61,85 @@ module HTS
|
|
53
61
|
format_values.call(LibHTS::BCF_HT_REAL)
|
54
62
|
.read_array_of_float(n.read_int)
|
55
63
|
when :flag
|
56
|
-
raise NotImplementedError, "Flag type not implemented yet."
|
64
|
+
raise NotImplementedError, "Flag type not implemented yet. " \
|
65
|
+
"Please file an issue on GitHub."
|
57
66
|
# format_values.call(LibHTS::BCF_HT_FLAG)
|
58
67
|
# .read_int == 1
|
59
68
|
when :string, :str
|
60
|
-
raise NotImplementedError, "String type not implemented yet."
|
69
|
+
raise NotImplementedError, "String type not implemented yet. " \
|
70
|
+
"Please file an issue on GitHub."
|
61
71
|
# format_values.call(LibHTS::BCF_HT_STR)
|
62
72
|
# .read_string
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
66
|
-
def
|
76
|
+
def fields
|
77
|
+
ids.map do |id|
|
78
|
+
name = LibHTS.bcf_hdr_int2id(@record.header.struct, LibHTS::BCF_DT_ID, id)
|
79
|
+
num = LibHTS.bcf_hdr_id2number(@record.header.struct, LibHTS::BCF_HL_FMT, id)
|
80
|
+
type = LibHTS.bcf_hdr_id2type(@record.header.struct, LibHTS::BCF_HL_FMT, id)
|
81
|
+
{
|
82
|
+
name: name,
|
83
|
+
n: num,
|
84
|
+
type: ht_type_to_sym(type),
|
85
|
+
id: id
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
67
89
|
|
68
|
-
|
69
|
-
|
90
|
+
def length
|
91
|
+
@record.struct[:n_fmt]
|
92
|
+
end
|
93
|
+
|
94
|
+
def size
|
95
|
+
length
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_h
|
99
|
+
ret = {}
|
100
|
+
ids.each do |id|
|
101
|
+
name = LibHTS.bcf_hdr_int2id(@record.header.struct, LibHTS::BCF_DT_ID, id)
|
102
|
+
ret[name] = get(name)
|
103
|
+
end
|
104
|
+
ret
|
105
|
+
end
|
70
106
|
|
71
|
-
def genotypes; end
|
107
|
+
# def genotypes; end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def fmt_ptr
|
112
|
+
@record.struct[:d][:fmt].to_ptr
|
113
|
+
end
|
114
|
+
|
115
|
+
def ids
|
116
|
+
fmt_ptr.read_array_of_struct(LibHTS::BcfFmt, length).map do |fmt|
|
117
|
+
fmt[:id]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_fmt_type(qname)
|
122
|
+
@record.struct[:n_fmt].times do |i|
|
123
|
+
fmt = LibHTS::BcfFmt.new(@record.struct[:d][:fmt] + i * LibHTS::BcfFmt.size)
|
124
|
+
id = fmt[:id]
|
125
|
+
name = LibHTS.bcf_hdr_int2id(@record.header.struct, LibHTS::BCF_DT_ID, id)
|
126
|
+
if name == qname
|
127
|
+
type = LibHTS.bcf_hdr_id2type(@record.header.struct, LibHTS::BCF_HL_FMT, id)
|
128
|
+
return type
|
129
|
+
end
|
130
|
+
end
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def ht_type_to_sym(t)
|
135
|
+
case t
|
136
|
+
when LibHTS::BCF_HT_FLAG then :flag
|
137
|
+
when LibHTS::BCF_HT_INT then :int
|
138
|
+
when LibHTS::BCF_HT_REAL then :float
|
139
|
+
when LibHTS::BCF_HT_STR then :string
|
140
|
+
when LibHTS::BCF_HT_LONG then :float
|
141
|
+
end
|
142
|
+
end
|
72
143
|
end
|
73
144
|
end
|
74
145
|
end
|