Skip to content

Commit ba6c9e5

Browse files
committed
support bed files of regions
1 parent 85b3bd1 commit ba6c9e5

File tree

4 files changed

+59
-15
lines changed

4 files changed

+59
-15
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# v0.0.3
2+
3+
+ support bed file of regions where previously only a single chrom:start-stop region
4+
was allowed.
5+

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ To get the mean value for a given region (in this case on chromosome 22)
2626

2727
```Shell
2828
bigwig stats --stat mean $bigwig 22:145000-155000
29+
# or a bed file or regions
30+
bigwig stats --stat mean $bigwig $bed
2931
```
3032

33+
Output is tab-delimited `chrom`, `start`, `stop`, `stat` for each row in the bed (or just once for the region).
34+
3135
The supported stats are `mean`, `min`, `max`, `coverage`, `sum` with a special-case for the stat of `header` which
3236
shows the chromosomes, lengths and mean coverages for each chromosome in the bigwig file.
3337

src/bigwigpkg/cli.nim

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,23 @@ import argparse
77

88
type region = tuple[chrom: string, start: int, stop: int]
99

10-
proc parse_region(reg:string): region =
11-
if reg == "": return ("", 0, -1)
10+
proc looks_like_region_file(f:string): bool =
11+
if ':' in f and '-' in f: return false
12+
if not f.fileExists: return false
13+
var fh:HTSFile
14+
if not open(fh, f):
15+
stderr.write_line &"[slivar] tried '{f}' as a region file but couldn't open. Trying as an actual region"
16+
return false
17+
defer:
18+
fh.close()
19+
for l in fh.lines:
20+
if l[0] == '#' or l.strip().len == 0: continue
21+
var toks = l.strip().split("\t")
22+
if toks.len >= 3 and toks[1].isdigit and toks[2].isdigit: return true
23+
stderr.write_line &"[slivar] tried '{f}' as a region file but it did not have proper format. Trying as an actual region"
24+
return false
25+
26+
proc parse_colon_region(reg: string): region {.inline.} =
1227
let chrom_rest = reg.split(':', maxsplit=1)
1328
if chrom_rest.len == 1:
1429
return (chrom_rest[0], 0, -1)
@@ -17,18 +32,38 @@ proc parse_region(reg:string): region =
1732
result.chrom = chrom_rest[0]
1833
result.start = max(0, parseInt(ss[0]) - 1)
1934
result.stop = parseInt(ss[1])
20-
doAssert result.stop >= result.start, ("[bigwig] invalid region:" & reg)
35+
if result.stop < result.start:
36+
quit ("[bigwig] ERROR. invalid region:" & reg)
37+
38+
proc parse_one_region(reg:string): region {.inline.} =
39+
if reg == "": return ("", 0, -1)
40+
let chrom_rest = reg.rsplit('\t', maxsplit=4)
41+
if chrom_rest.len == 1:
42+
return parse_colon_region(reg)
43+
result.chrom = chrom_rest[0]
44+
result.start = max(0, parseInt(chrom_rest[1]))
45+
result.stop = parseInt(chrom_rest[2])
46+
if result.stop < result.start:
47+
quit ("[bigwig] ERROR. invalid region:" & reg)
48+
49+
iterator parse_region(reg_or_bed:string): region {.inline.} =
50+
if reg_or_bed.looks_like_region_file:
51+
for l in reg_or_bed.hts_lines:
52+
yield parse_one_region(l.strip(leading=false, chars={'\n', '\r'}))
53+
else:
54+
yield parse_one_region(reg_or_bed)
55+
2156

2257
proc from_fai(path: string): BigWigHeader =
2358
## create a bigwig header from an fai (fasta index) or a genome file
2459
for l in path.lines:
2560
let vals = l.strip().split('\t')
2661
result.add((name: vals[0], length: parseInt(vals[1]), tid: result.len.uint32))
2762

28-
proc ffloat(f:float, precision:int=5): string =
63+
proc ffloat(f:float, precision:int=5): string {.inline.} =
2964
result = format_float(f, ffDecimal, precision=precision)
3065
result = result.strip(leading=false, chars={'0'})
31-
if result[result.high] == '.': result &= '0'
66+
if result[result.high] == '.': result.setLen(result.high)
3267

3368
proc write_region_from(ofh:File, bw:var BigWig, reg:region) =
3469
for iv in bw.intervals(reg.chrom, reg.start, reg.stop):
@@ -162,7 +197,7 @@ proc stats_main*() =
162197
option("-s", "--stat", choices= @["mean", "coverage", "min", "max", "sum", "header"], default="mean", help="statistic to output. 'header' will show the lengths, mean and coverage for each chromosome in the bigwig.")
163198
option("--bins", default="1", help="integer number of bins")
164199
arg("input", nargs=1)
165-
arg("region", nargs=1, help="chromosome, or chrom:start-stop region to extract stats")
200+
arg("region", nargs=1, help="BED file or regions or chromosome, or chrom:start-stop region to extract stats")
166201

167202
var args = commandLineParams()
168203
if len(args) > 0 and args[0] == "stats":
@@ -203,10 +238,10 @@ proc stats_main*() =
203238
var bins = parseInt(opts.bins)
204239

205240
try:
206-
var region = opts.region.parse_region
207-
var st = bw.stats(region.chrom, region.start, region.stop, stat=stat, nBins=bins)
208-
for v in st:
209-
echo ffloat(v)
241+
for region in opts.region.parse_region:
242+
var st = bw.stats(region.chrom, region.start, region.stop, stat=stat, nBins=bins)
243+
for v in st:
244+
echo &"{region.chrom}\t{region.start}\t{region.stop}\t{ffloat(v)}"
210245
except:
211246
echo "error:", getCurrentExceptionMsg()
212247
quit 1
@@ -256,8 +291,8 @@ proc view_main*() =
256291
ofh.write_region_from(bw, reg)
257292

258293
else:
259-
var region = opts.region.parse_region
260-
ofh.write_region_from(bw, region)
294+
for region in opts.region.parse_region:
295+
ofh.write_region_from(bw, region)
261296

262297
ofh.close
263298

@@ -277,8 +312,8 @@ proc view_main*() =
277312
ofh.write_region_from(bw, reg, chunksize)
278313

279314
else:
280-
var region = opts.region.parse_region
281-
ofh.write_region_from(bw, region, chunksize)
315+
for region in opts.region.parse_region:
316+
ofh.write_region_from(bw, region, chunksize)
282317

283318
ofh.close
284319
bw.close

src/bigwigpkg/version.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
const bigwigVersion* = "0.0.2"
1+
const bigwigVersion* = "0.0.3"
22
const bigwigGitCommit* = staticExec("git rev-parse --verify HEAD")

0 commit comments

Comments
 (0)