Skip to content

Commit eb8def8

Browse files
authored
Merge pull request #66 from deeptools/implement63
Fix #63
2 parents a2b1f46 + 44789f5 commit eb8def8

File tree

2 files changed

+203
-10
lines changed

2 files changed

+203
-10
lines changed

pyBigWig.c

Lines changed: 197 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "numpy/npy_common.h"
88
#include "numpy/halffloat.h"
99
#include "numpy/ndarrayobject.h"
10+
#include "numpy/arrayscalars.h"
1011

1112
int lsize = NPY_SIZEOF_LONG;
1213

@@ -79,6 +80,52 @@ uint32_t getNumpyU32(PyArrayObject *obj, Py_ssize_t i) {
7980
return 0;
8081
};
8182

83+
long getNumpyL(PyObject *obj) {
84+
short s;
85+
int i;
86+
long l;
87+
long long ll;
88+
unsigned short us;
89+
unsigned int ui;
90+
unsigned long ul;
91+
unsigned long long ull;
92+
93+
if(!PyArray_IsIntegerScalar(obj)) {
94+
PyErr_SetString(PyExc_RuntimeError, "Received non-Integer scalar type for conversion to long!\n");
95+
return 0;
96+
}
97+
98+
if(PyArray_IsScalar(obj, Short)) {
99+
s = ((PyShortScalarObject *)obj)->obval;
100+
l = s;
101+
} else if(PyArray_IsScalar(obj, Int)) {
102+
i = ((PyLongScalarObject *)obj)->obval;
103+
l = i;
104+
} else if(PyArray_IsScalar(obj, Long)) {
105+
l = ((PyLongScalarObject *)obj)->obval;
106+
} else if(PyArray_IsScalar(obj, LongLong)) {
107+
ll = ((PyLongScalarObject *)obj)->obval;
108+
l = ll;
109+
} else if(PyArray_IsScalar(obj, UShort)) {
110+
us = ((PyLongScalarObject *)obj)->obval;
111+
l = us;
112+
} else if(PyArray_IsScalar(obj, UInt)) {
113+
ui = ((PyLongScalarObject *)obj)->obval;
114+
l = ui;
115+
} else if(PyArray_IsScalar(obj, ULong)) {
116+
ul = ((PyLongScalarObject *)obj)->obval;
117+
l = ul;
118+
} else if(PyArray_IsScalar(obj, ULongLong)) {
119+
ull = ((PyLongScalarObject *)obj)->obval;
120+
l = ull;
121+
} else {
122+
PyErr_SetString(PyExc_RuntimeError, "Received unknown scalar type for conversion to long!\n");
123+
return 0;
124+
}
125+
126+
return l;
127+
}
128+
82129
//Raises an exception on error, which should be checked
83130
float getNumpyF(PyArrayObject *obj, Py_ssize_t i) {
84131
int dtype;
@@ -324,7 +371,7 @@ static PyObject *pyBwGetStats(pyBigWigFile_t *self, PyObject *args, PyObject *kw
324371
unsigned long startl = 0, endl = -1;
325372
static char *kwd_list[] = {"chrom", "start", "end", "type", "nBins", "exact", NULL};
326373
char *chrom, *type = "mean";
327-
PyObject *ret, *exact = Py_False;
374+
PyObject *ret, *exact = Py_False, *starto = NULL, *endo = NULL;
328375
int i, nBins = 1;
329376
errno = 0; //In the off-chance that something elsewhere got an error and didn't clear it...
330377

@@ -338,7 +385,7 @@ static PyObject *pyBwGetStats(pyBigWigFile_t *self, PyObject *args, PyObject *kw
338385
return NULL;
339386
}
340387

341-
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|kksiO", kwd_list, &chrom, &startl, &endl, &type, &nBins, &exact)) {
388+
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|OOsiO", kwd_list, &chrom, &starto, &endo, &type, &nBins, &exact)) {
342389
PyErr_SetString(PyExc_RuntimeError, "You must supply at least a chromosome!");
343390
return NULL;
344391
}
@@ -347,6 +394,43 @@ static PyObject *pyBwGetStats(pyBigWigFile_t *self, PyObject *args, PyObject *kw
347394
if(!nBins) nBins = 1; //For some reason, not specifying this overrides the default!
348395
if(!type) type = "mean";
349396
tid = bwGetTid(bw, chrom);
397+
398+
if(starto) {
399+
#ifdef WITHNUMPY
400+
if(PyArray_IsScalar(starto, Integer)) {
401+
startl = (long) getNumpyL(starto);
402+
} else
403+
#endif
404+
if(PyLong_Check(starto)) {
405+
startl = PyLong_AsLong(starto);
406+
#if PY_MAJOR_VERSION < 3
407+
} else if(PyInt_Check(starto)) {
408+
startl = PyInt_AsLong(starto);
409+
#endif
410+
} else {
411+
PyErr_SetString(PyExc_RuntimeError, "The start coordinate must be a number!");
412+
return NULL;
413+
}
414+
}
415+
416+
if(endo) {
417+
#ifdef WITHNUMPY
418+
if(PyArray_IsScalar(endo, Integer)) {
419+
endl = (long) getNumpyL(endo);
420+
} else
421+
#endif
422+
if(PyLong_Check(endo)) {
423+
endl = PyLong_AsLong(endo);
424+
#if PY_MAJOR_VERSION < 3
425+
} else if(PyInt_Check(endo)) {
426+
endl = PyInt_AsLong(endo);
427+
#endif
428+
} else {
429+
PyErr_SetString(PyExc_RuntimeError, "The end coordinate must be a number!");
430+
return NULL;
431+
}
432+
}
433+
350434
if(endl == (unsigned long) -1 && tid != (uint32_t) -1) endl = bw->cl->len[tid];
351435
if(tid == (uint32_t) -1 || startl > end || endl > end) {
352436
PyErr_SetString(PyExc_RuntimeError, "Invalid interval bounds!");
@@ -412,7 +496,7 @@ static PyObject *pyBwGetValues(pyBigWigFile_t *self, PyObject *args) {
412496
uint32_t start, end = -1, tid;
413497
unsigned long startl, endl;
414498
char *chrom;
415-
PyObject *ret;
499+
PyObject *ret, *starto = NULL, *endo = NULL;
416500
bwOverlappingIntervals_t *o;
417501

418502
if(!bw) {
@@ -429,20 +513,54 @@ static PyObject *pyBwGetValues(pyBigWigFile_t *self, PyObject *args) {
429513
static char *kwd_list[] = {"chrom", "start", "end", "numpy", NULL};
430514
PyObject *outputNumpy = Py_False;
431515

432-
if(!PyArg_ParseTupleAndKeywords(args, kwds, "skk|O", kwd_list, &chrom, &startl, &endl, &outputNumpy)) {
516+
if(!PyArg_ParseTupleAndKeywords(args, kwds, "sOO|O", kwd_list, &chrom, &starto, &endo, &outputNumpy)) {
433517
#else
434-
if(!PyArg_ParseTuple(args, "skk", &chrom, &startl, &endl)) {
518+
if(!PyArg_ParseTuple(args, "sOO", &chrom, &starto, &endo)) {
435519
#endif
436520
PyErr_SetString(PyExc_RuntimeError, "You must supply a chromosome, start and end position.\n");
437521
return NULL;
438522
}
439523

440524
tid = bwGetTid(bw, chrom);
525+
526+
#ifdef WITHNUMPY
527+
if(PyArray_IsScalar(starto, Integer)) {
528+
startl = (long) getNumpyL(starto);
529+
} else
530+
#endif
531+
if(PyLong_Check(starto)) {
532+
startl = PyLong_AsLong(starto);
533+
#if PY_MAJOR_VERSION < 3
534+
} else if(PyInt_Check(starto)) {
535+
startl = PyInt_AsLong(starto);
536+
#endif
537+
} else {
538+
PyErr_SetString(PyExc_RuntimeError, "The start coordinate must be a number!");
539+
return NULL;
540+
}
541+
542+
#ifdef WITHNUMPY
543+
if(PyArray_IsScalar(endo, Integer)) {
544+
endl = (long) getNumpyL(endo);
545+
} else
546+
#endif
547+
if(PyLong_Check(endo)) {
548+
endl = PyLong_AsLong(endo);
549+
#if PY_MAJOR_VERSION < 3
550+
} else if(PyInt_Check(endo)) {
551+
endl = PyInt_AsLong(endo);
552+
#endif
553+
} else {
554+
PyErr_SetString(PyExc_RuntimeError, "The end coordinate must be a number!");
555+
return NULL;
556+
}
557+
441558
if(endl == (unsigned long) -1 && tid != (uint32_t) -1) endl = bw->cl->len[tid];
442559
if(tid == (uint32_t) -1 || startl > end || endl > end) {
443560
PyErr_SetString(PyExc_RuntimeError, "Invalid interval bounds!");
444561
return NULL;
445562
}
563+
446564
start = (uint32_t) startl;
447565
end = (uint32_t) endl;
448566
if(end <= start || end > bw->cl->len[tid] || start >= end) {
@@ -496,7 +614,7 @@ static PyObject *pyBwGetIntervals(pyBigWigFile_t *self, PyObject *args, PyObject
496614
static char *kwd_list[] = {"chrom", "start", "end", NULL};
497615
bwOverlappingIntervals_t *intervals = NULL;
498616
char *chrom;
499-
PyObject *ret;
617+
PyObject *ret, *starto = NULL, *endo = NULL;
500618

501619
if(!bw) {
502620
PyErr_SetString(PyExc_RuntimeError, "The bigWig file handle is not opened!");
@@ -508,7 +626,7 @@ static PyObject *pyBwGetIntervals(pyBigWigFile_t *self, PyObject *args, PyObject
508626
return NULL;
509627
}
510628

511-
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|kk", kwd_list, &chrom, &startl, &endl)) {
629+
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|OO", kwd_list, &chrom, &starto, &endo)) {
512630
PyErr_SetString(PyExc_RuntimeError, "You must supply at least a chromosome.\n");
513631
return NULL;
514632
}
@@ -520,6 +638,43 @@ static PyObject *pyBwGetIntervals(pyBigWigFile_t *self, PyObject *args, PyObject
520638
PyErr_SetString(PyExc_RuntimeError, "Invalid interval bounds!");
521639
return NULL;
522640
}
641+
642+
if(starto) {
643+
#ifdef WITHNUMPY
644+
if(PyArray_IsScalar(starto, Integer)) {
645+
startl = (long) getNumpyL(starto);
646+
} else
647+
#endif
648+
if(PyLong_Check(starto)) {
649+
startl = PyLong_AsLong(starto);
650+
#if PY_MAJOR_VERSION < 3
651+
} else if(PyInt_Check(starto)) {
652+
startl = PyInt_AsLong(starto);
653+
#endif
654+
} else {
655+
PyErr_SetString(PyExc_RuntimeError, "The start coordinate must be a number!");
656+
return NULL;
657+
}
658+
}
659+
660+
if(endo) {
661+
#ifdef WITHNUMPY
662+
if(PyArray_IsScalar(endo, Integer)) {
663+
endl = (long) getNumpyL(endo);
664+
} else
665+
#endif
666+
if(PyLong_Check(endo)) {
667+
endl = PyLong_AsLong(endo);
668+
#if PY_MAJOR_VERSION < 3
669+
} else if(PyInt_Check(endo)) {
670+
endl = PyInt_AsLong(endo);
671+
#endif
672+
} else {
673+
PyErr_SetString(PyExc_RuntimeError, "The end coordinate must be a number!");
674+
return NULL;
675+
}
676+
}
677+
523678
start = (uint32_t) startl;
524679
end = (uint32_t) endl;
525680
if(end <= start || end > bw->cl->len[tid] || start >= end) {
@@ -1548,7 +1703,7 @@ static PyObject *pyBBGetEntries(pyBigWigFile_t *self, PyObject *args, PyObject *
15481703
unsigned long startl, endl;
15491704
char *chrom;
15501705
static char *kwd_list[] = {"chrom", "start", "end", "withString", NULL};
1551-
PyObject *ret, *t;
1706+
PyObject *ret, *t, *starto = NULL, *endo = NULL;
15521707
PyObject *withStringPy = Py_True;
15531708
int withString = 1;
15541709
bbOverlappingEntries_t *o;
@@ -1563,12 +1718,45 @@ static PyObject *pyBBGetEntries(pyBigWigFile_t *self, PyObject *args, PyObject *
15631718
return NULL;
15641719
}
15651720

1566-
if(!PyArg_ParseTupleAndKeywords(args, kwds, "skk|O", kwd_list, &chrom, &startl, &endl, &withStringPy)) {
1721+
if(!PyArg_ParseTupleAndKeywords(args, kwds, "sOO|O", kwd_list, &chrom, &starto, &endo, &withStringPy)) {
15671722
PyErr_SetString(PyExc_RuntimeError, "You must supply a chromosome, start and end position.\n");
15681723
return NULL;
15691724
}
15701725

15711726
tid = bwGetTid(bw, chrom);
1727+
1728+
#ifdef WITHNUMPY
1729+
if(PyArray_IsScalar(starto, Integer)) {
1730+
startl = (long) getNumpyL(starto);
1731+
} else
1732+
#endif
1733+
if(PyLong_Check(starto)) {
1734+
startl = PyLong_AsLong(starto);
1735+
#if PY_MAJOR_VERSION < 3
1736+
} else if(PyInt_Check(starto)) {
1737+
startl = PyInt_AsLong(starto);
1738+
#endif
1739+
} else {
1740+
PyErr_SetString(PyExc_RuntimeError, "The start coordinate must be a number!");
1741+
return NULL;
1742+
}
1743+
1744+
#ifdef WITHNUMPY
1745+
if(PyArray_IsScalar(endo, Integer)) {
1746+
endl = (long) getNumpyL(endo);
1747+
} else
1748+
#endif
1749+
if(PyLong_Check(endo)) {
1750+
endl = PyLong_AsLong(endo);
1751+
#if PY_MAJOR_VERSION < 3
1752+
} else if(PyInt_Check(endo)) {
1753+
endl = PyInt_AsLong(endo);
1754+
#endif
1755+
} else {
1756+
PyErr_SetString(PyExc_RuntimeError, "The end coordinate must be a number!");
1757+
return NULL;
1758+
}
1759+
15721760
if(endl == (unsigned long) -1 && tid != (uint32_t) -1) endl = bw->cl->len[tid];
15731761
if(tid == (uint32_t) -1 || startl > end || endl > end) {
15741762
PyErr_SetString(PyExc_RuntimeError, "Invalid interval bounds!");

pyBigWigTest/test.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import sys
55
import hashlib
6+
import numpy as np
67

78
class TestRemote():
89
fname = "http://raw.githubusercontent.com/dpryan79/pyBigWig/master/pyBigWigTest/test.bw"
@@ -28,14 +29,17 @@ def doStats(self, bw):
2829
assert(bw.stats("1", 0, 3) == [0.2000000054637591])
2930
assert(bw.stats("1", 0, 3, type="max") == [0.30000001192092896])
3031
assert(bw.stats("1",99,200, type="max", nBins=2) == [1.399999976158142, 1.5])
32+
assert(bw.stats("1",np.int64(99), np.int64(200), type="max", nBins=2) == [1.399999976158142, 1.5])
3133
assert(bw.stats("1") == [1.3351851569281683])
3234

3335
def doValues(self, bw):
3436
assert(bw.values("1", 0, 3) == [0.10000000149011612, 0.20000000298023224, 0.30000001192092896])
37+
assert(bw.values("1", np.int64(0), np.int64(3)) == [0.10000000149011612, 0.20000000298023224, 0.30000001192092896])
3538
#assert(bw.values("1", 0, 4) == [0.10000000149011612, 0.20000000298023224, 0.30000001192092896, 'nan'])
3639

3740
def doIntervals(self, bw):
3841
assert(bw.intervals("1", 0, 3) == ((0, 1, 0.10000000149011612), (1, 2, 0.20000000298023224), (2, 3, 0.30000001192092896)))
42+
assert(bw.intervals("1", np.int64(0), np.int64(3)) == ((0, 1, 0.10000000149011612), (1, 2, 0.20000000298023224), (2, 3, 0.30000001192092896)))
3943
assert(bw.intervals("1") == ((0, 1, 0.10000000149011612), (1, 2, 0.20000000298023224), (2, 3, 0.30000001192092896), (100, 150, 1.399999976158142), (150, 151, 1.5)))
4044

4145
def doSum(self, bw):
@@ -161,7 +165,6 @@ def doWriteEmpty(self):
161165
os.remove(oname)
162166

163167
def doWriteNumpy(self):
164-
import numpy as np
165168
ofile = tempfile.NamedTemporaryFile(delete=False)
166169
oname = ofile.name
167170
ofile.close()
@@ -237,6 +240,8 @@ def testBigBed(self):
237240
o = bb.entries('chr1',10000000,10020000)
238241
expected = [(10009333, 10009640, '61035\t130\t-\t0.026\t0.42\t404'), (10014007, 10014289, '61047\t136\t-\t0.029\t0.42\t404'), (10014373, 10024307, '61048\t630\t-\t5.420\t0.00\t2672399')]
239242
assert(o == expected)
243+
o = bb.entries('chr1',np.int64(10000000),np.int64(10020000))
244+
assert(o == expected)
240245
bb.close()
241246

242247
class TestNumpy():

0 commit comments

Comments
 (0)