xengsort 2.1.0__tar.gz → 2.2.0__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.
- {xengsort-2.1.0/xengsort.egg-info → xengsort-2.2.0}/PKG-INFO +1 -1
- {xengsort-2.1.0 → xengsort-2.2.0}/pyproject.toml +1 -1
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/cptasks_kmers.py +4 -4
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/hash_s3c_fbcbvb.py +1 -1
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/fastqio.py +226 -2
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/generalio.py +7 -2
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/llvm.py +4 -2
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/srhash.py +1 -1
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/config/classify.yaml +1 -1
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/xengsort_classify.py +261 -83
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/xengsort_index.py +5 -5
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/xengsort_main.py +4 -2
- {xengsort-2.1.0 → xengsort-2.2.0/xengsort.egg-info}/PKG-INFO +1 -1
- {xengsort-2.1.0 → xengsort-2.2.0}/LICENSE +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/MANIFEST.in +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/README.md +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/setup.cfg +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/tests/test_trimmed_reads.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/dnaencode.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/dnaencode_fast.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/fastcash_info.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/fastcash_main.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/fastcash_optimize.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/hash_minimizer.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/hash_srh_fbcbvb.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/binaryio.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/fastaio.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/hashio.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/seqio.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/textio.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/io/xorio.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/kmers.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/aligned_arrays.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/bitarray.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/conpro.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/debug.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/intbitarray.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/libc.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/numbautils.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/lowlevel/packedarray.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/mask.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/mathutils.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/parameters.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/subtable_hashfunctions.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/values/xenograft.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/config/index.yaml +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/xengsort_info.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/xengsort_load.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/xengsort_remove.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort/xengsort/xengsort_weak.py +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort.egg-info/SOURCES.txt +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort.egg-info/dependency_links.txt +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort.egg-info/entry_points.txt +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort.egg-info/requires.txt +0 -0
- {xengsort-2.1.0 → xengsort-2.2.0}/xengsort.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xengsort
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: xenograft sorting of FASTQ reads into five categories: host, graft, both, neither, ambiguous.
|
|
5
5
|
Author-email: Jens Zentgraf <zentgraf@cs.uni-saarland.de>, Sven Rahmann <rahmann@cs.uni-saarland.de>
|
|
6
6
|
License: MIT License
|
|
@@ -33,7 +33,7 @@ def compile_cptask_scatter_kmers_from_linemarked(
|
|
|
33
33
|
mask, rcmode, hf0,
|
|
34
34
|
nsubtables, noutbuffers_per_subtable, outbufsize,
|
|
35
35
|
new_encoding=False,
|
|
36
|
-
|
|
36
|
+
methylation=False):
|
|
37
37
|
"""
|
|
38
38
|
Compile a cptask function that
|
|
39
39
|
creates (gapped) k-mers from linemarked buffers.
|
|
@@ -47,7 +47,7 @@ def compile_cptask_scatter_kmers_from_linemarked(
|
|
|
47
47
|
"""
|
|
48
48
|
debugprint0, debugprint1, debugprint2 = debug.debugprint
|
|
49
49
|
both = (rcmode[0].lower() == "b")
|
|
50
|
-
if
|
|
50
|
+
if methylation:
|
|
51
51
|
if new_encoding:
|
|
52
52
|
raise NotImplementedError('WGBS with new encoding not supported')
|
|
53
53
|
|
|
@@ -202,7 +202,7 @@ def compile_cptask_scatter_kmers_from_linemarked(
|
|
|
202
202
|
nactive = -1 # active input buffer
|
|
203
203
|
outidx = np.full(nsubtables, (-1), dtype=np.int32)
|
|
204
204
|
outpos = np.full(nsubtables, outbufsize, dtype=np.int32)
|
|
205
|
-
if
|
|
205
|
+
if methylation:
|
|
206
206
|
seq_buffer = np.empty(inbuffers[0].size, dtype=np.uint8)
|
|
207
207
|
while True:
|
|
208
208
|
nactive, wait = find_buffer_for_reading(incontrol, nactive)
|
|
@@ -217,7 +217,7 @@ def compile_cptask_scatter_kmers_from_linemarked(
|
|
|
217
217
|
errorcode = 0
|
|
218
218
|
linemarks = linemarkbuffers[nactive]
|
|
219
219
|
for i in range(nseqs):
|
|
220
|
-
if
|
|
220
|
+
if methylation:
|
|
221
221
|
seq_len = linemarks[i, 1] - linemarks[i, 0]
|
|
222
222
|
seq_buffer[0:seq_len] = active_buffer[linemarks[i, 0]:linemarks[i, 1]]
|
|
223
223
|
quick_dna_to_2bits_ct(seq_buffer[0:seq_len])
|
|
@@ -137,7 +137,7 @@ def build_hash(universe, n, subtables, bucketsize,
|
|
|
137
137
|
|
|
138
138
|
@njit(nogil=True, locals=dict(
|
|
139
139
|
bucket=int64, startbit=uint64))
|
|
140
|
-
def
|
|
140
|
+
def prefetch_key(table, key):
|
|
141
141
|
st, sk = get_subtable_subkey_from_key(key)
|
|
142
142
|
bucket, _ = get_bs1(sk)
|
|
143
143
|
startbit = st * subtablebits + bucket * bucketsizebits
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from argparse import ArgumentParser
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
|
-
from numba import njit, int32, int64
|
|
4
|
+
from numba import njit, int32, int64, uint64
|
|
5
5
|
|
|
6
6
|
from .seqio import FormatError, _universal_reads
|
|
7
7
|
from ..lowlevel.libc import read_block
|
|
@@ -14,6 +14,7 @@ debugprint0, debugprint1, debugprint2 = debug.debugprint
|
|
|
14
14
|
|
|
15
15
|
# Python FASTQ/gz handling ######################################
|
|
16
16
|
|
|
17
|
+
|
|
17
18
|
def fastq_reads(files, sequences_only=False, dirty=False):
|
|
18
19
|
"""
|
|
19
20
|
For the given 'files':
|
|
@@ -274,6 +275,229 @@ def cptask_read_pairedfastq_into_linemarked_buffers(fd, inbuffers, incontrol, in
|
|
|
274
275
|
return (wait_read, wait_write, errorcode)
|
|
275
276
|
|
|
276
277
|
|
|
278
|
+
# read fastq without computing linemarks
|
|
279
|
+
|
|
280
|
+
@njit(nogil=True, locals=dict(
|
|
281
|
+
ntodo1=int32, ntodo2=int32, errorcode=int32, offset1=int32, offset2=int32,
|
|
282
|
+
skip=int32, nxt1=int32, nxt2=int32, nseqs=int32, nseqs1=int32, nseqs2=int32, nlines1=int32, nlines2=int32,
|
|
283
|
+
wait=int64, wait_read=int64, wait_write=int64, buffersize=int64))
|
|
284
|
+
def cptask_read_pairedfastq_into_buffers(fd, inbuffers, incontrol, ininfos, outbuffers, outcontrol, outinfos):
|
|
285
|
+
"""
|
|
286
|
+
Has to be run as a thread within a consumer producer task.
|
|
287
|
+
Keep reading bytes into one of the outbuffers (cycling),
|
|
288
|
+
and compute linemarks in one of the outinfos (same index, cycling),
|
|
289
|
+
until EOF is reached or an error occurs.
|
|
290
|
+
Return 0 (EOF), or an error code (negative), corresponding to -os.errno.
|
|
291
|
+
"""
|
|
292
|
+
fd1, fd2 = fd
|
|
293
|
+
M, N = outinfos.shape
|
|
294
|
+
assert N % 4 == 0
|
|
295
|
+
|
|
296
|
+
offset1 = offset2 = ntodo1 = ntodo2 = wait_read = wait_write = 0
|
|
297
|
+
nseqs1 = nseqs2 = 0
|
|
298
|
+
nactive = -1
|
|
299
|
+
active_buffer = outbuffers[0] # irrelevant
|
|
300
|
+
buffersize = active_buffer.shape[0] // 2
|
|
301
|
+
active_buffer1 = active_buffer[:len(active_buffer) // 2]
|
|
302
|
+
active_buffer2 = active_buffer[len(active_buffer) // 2:]
|
|
303
|
+
|
|
304
|
+
while True:
|
|
305
|
+
old = nactive
|
|
306
|
+
old_buffer1 = active_buffer1
|
|
307
|
+
old_buffer2 = active_buffer2
|
|
308
|
+
nactive, wait = find_buffer_for_writing(outcontrol, old)
|
|
309
|
+
if nactive < 0:
|
|
310
|
+
break
|
|
311
|
+
wait_write += wait
|
|
312
|
+
active_buffer = outbuffers[nactive]
|
|
313
|
+
active_buffer1 = active_buffer[:buffersize]
|
|
314
|
+
active_buffer2 = active_buffer[buffersize:buffersize * 2]
|
|
315
|
+
|
|
316
|
+
if old >= 0:
|
|
317
|
+
assert nactive != old
|
|
318
|
+
if offset1 > 0:
|
|
319
|
+
for i in range(offset1):
|
|
320
|
+
active_buffer1[i] = old_buffer1[ntodo1 - offset1 + i]
|
|
321
|
+
if offset2 > 0:
|
|
322
|
+
for i in range(offset2):
|
|
323
|
+
active_buffer2[i] = old_buffer2[ntodo2 - offset2 + i]
|
|
324
|
+
mark_buffer_for_reading(outcontrol, old)
|
|
325
|
+
|
|
326
|
+
errorcode = outcontrol[nactive, 1]
|
|
327
|
+
if errorcode != 0:
|
|
328
|
+
errorcode = -errorcode
|
|
329
|
+
break
|
|
330
|
+
|
|
331
|
+
# only read if the buffer is filled less than 80%
|
|
332
|
+
if offset1 < active_buffer1.size * 4 // 5 or nseqs1 == 0:
|
|
333
|
+
inner_offset1 = offset1
|
|
334
|
+
while inner_offset1 < active_buffer1.size * 4 // 5 or nseqs1 == 0:
|
|
335
|
+
ntodo1 = read_block(int32(fd1), active_buffer1, inner_offset1)
|
|
336
|
+
if ntodo1 == 0:
|
|
337
|
+
ntodo1 = inner_offset1
|
|
338
|
+
break
|
|
339
|
+
if ntodo1 == -99_999:
|
|
340
|
+
debugprint0("- Failed to read file into buffer (probably due to too long reads, len(header+seq+quality) must be smaller than buffersize, default 65kB).")
|
|
341
|
+
debugprint0("- Please increase buffersize using --buffersize.")
|
|
342
|
+
break
|
|
343
|
+
inner_offset1 = ntodo1
|
|
344
|
+
if inner_offset1 >= active_buffer1.size * 4 // 5:
|
|
345
|
+
break
|
|
346
|
+
else:
|
|
347
|
+
ntodo1 = offset1
|
|
348
|
+
|
|
349
|
+
if offset2 < active_buffer2.size * 4 // 5 or nseqs2 == 0:
|
|
350
|
+
inner_offset2 = offset2
|
|
351
|
+
while inner_offset2 < active_buffer2.size * 4 // 5 or nseqs2 == 0:
|
|
352
|
+
ntodo2 = read_block(int32(fd2), active_buffer2, inner_offset2)
|
|
353
|
+
if ntodo2 == 0:
|
|
354
|
+
ntodo2 = inner_offset2
|
|
355
|
+
break
|
|
356
|
+
if ntodo2 == -99_999:
|
|
357
|
+
debugprint0("- Failed to read file into buffer (probably due to too long reads, len(header+seq+quality) must be smaller than buffersize, default 65kB).")
|
|
358
|
+
debugprint0("- Please increase buffersize using --buffersize.")
|
|
359
|
+
break
|
|
360
|
+
inner_offset2 = ntodo2
|
|
361
|
+
if inner_offset2 >= active_buffer2.size * 4 // 5:
|
|
362
|
+
break
|
|
363
|
+
else:
|
|
364
|
+
ntodo2 = offset2
|
|
365
|
+
|
|
366
|
+
if ntodo1 <= 0 or ntodo2 <= 0:
|
|
367
|
+
# Error reading first file
|
|
368
|
+
if ntodo1 < 0:
|
|
369
|
+
errorcode = ntodo1
|
|
370
|
+
break
|
|
371
|
+
# Error reading second file
|
|
372
|
+
if ntodo2 < 0:
|
|
373
|
+
errorcode = ntodo2
|
|
374
|
+
break
|
|
375
|
+
# ntodo1=ntodo2=0 nothing read and no offset left; we are done
|
|
376
|
+
if ntodo1 == ntodo2 == offset1 == offset2 == 0:
|
|
377
|
+
errorcode = ntodo1
|
|
378
|
+
break
|
|
379
|
+
if ntodo1 == 0:
|
|
380
|
+
ntodo1 = offset1
|
|
381
|
+
elif ntodo2 == 0:
|
|
382
|
+
ntodo2 = offset2
|
|
383
|
+
else:
|
|
384
|
+
assert False
|
|
385
|
+
|
|
386
|
+
nlines1, nlines2 = 0, 0
|
|
387
|
+
for i in range(ntodo1):
|
|
388
|
+
nlines1 += active_buffer1[i] == 10
|
|
389
|
+
for i in range(ntodo2):
|
|
390
|
+
nlines2 += active_buffer2[i] == 10
|
|
391
|
+
nseqs1 = nlines1 // 4
|
|
392
|
+
nseqs2 = nlines2 // 4
|
|
393
|
+
nseqs = min(nseqs1, nseqs2)
|
|
394
|
+
|
|
395
|
+
outcontrol[nactive, 7] = nseqs
|
|
396
|
+
outcontrol[nactive, 6] = 0
|
|
397
|
+
|
|
398
|
+
rm_lines = nlines1 - nseqs * 4
|
|
399
|
+
assert rm_lines >= 0
|
|
400
|
+
if rm_lines == 0 and active_buffer1[ntodo1 - 1] == 10:
|
|
401
|
+
offset1 = 0
|
|
402
|
+
else:
|
|
403
|
+
nlines1 = offset1 = 0
|
|
404
|
+
for i in range(ntodo1):
|
|
405
|
+
nlines1 += active_buffer1[ntodo1 - i - 1] == 10
|
|
406
|
+
if nlines1 == rm_lines + 1:
|
|
407
|
+
break
|
|
408
|
+
offset1 += 1
|
|
409
|
+
|
|
410
|
+
rm_lines = nlines2 - nseqs * 4
|
|
411
|
+
assert rm_lines >= 0
|
|
412
|
+
if rm_lines == 0 and active_buffer2[ntodo2 - 1] == 10:
|
|
413
|
+
offset2 = 0
|
|
414
|
+
else:
|
|
415
|
+
nlines2 = offset2 = 0
|
|
416
|
+
for i in range(ntodo2):
|
|
417
|
+
nlines2 += active_buffer2[ntodo2 - i - 1] == 10
|
|
418
|
+
if nlines2 == rm_lines + 1:
|
|
419
|
+
break
|
|
420
|
+
offset2 += 1
|
|
421
|
+
|
|
422
|
+
mark_buffer_for_writing(outcontrol, nactive, force=True) # nothing left to read; re-use buffer
|
|
423
|
+
return (wait_read, wait_write, errorcode)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@njit(nogil=True, locals=dict(
|
|
427
|
+
ntodo1=int32, ntodo2=int32, errorcode=int32, offset1=int32, offset2=int32,
|
|
428
|
+
skip=int32, nxt1=int32, nxt2=int32, nseqs=int32, nseqs1=int32, nseqs2=int32, nlines1=int32, nlines2=int32,
|
|
429
|
+
wait=int64, wait_read=int64, wait_write=int64, buffersize=int64))
|
|
430
|
+
def cptask_read_fastq_into_buffers(fd, inbuffers, incontrol, ininfos, outbuffers, outcontrol, outinfos):
|
|
431
|
+
"""
|
|
432
|
+
Has to be run as a thread within a consumer producer task.
|
|
433
|
+
Keep reading bytes into one of the outbuffers (cycling),
|
|
434
|
+
and compute linemarks in one of the outinfos (same index, cycling),
|
|
435
|
+
until EOF is reached or an error occurs.
|
|
436
|
+
Return 0 (EOF), or an error code (negative), corresponding to -os.errno.
|
|
437
|
+
"""
|
|
438
|
+
M, N = outinfos.shape
|
|
439
|
+
assert N % 4 == 0
|
|
440
|
+
|
|
441
|
+
offset = ntodo = wait_read = wait_write = 0
|
|
442
|
+
nseqs = 0
|
|
443
|
+
nactive = -1
|
|
444
|
+
active_buffer = outbuffers[0] # irrelevant
|
|
445
|
+
|
|
446
|
+
while True:
|
|
447
|
+
old = nactive
|
|
448
|
+
old_buffer = active_buffer
|
|
449
|
+
nactive, wait = find_buffer_for_writing(outcontrol, old)
|
|
450
|
+
if nactive < 0:
|
|
451
|
+
break
|
|
452
|
+
wait_write += wait
|
|
453
|
+
active_buffer = outbuffers[nactive]
|
|
454
|
+
|
|
455
|
+
if old >= 0:
|
|
456
|
+
assert nactive != old
|
|
457
|
+
if offset > 0:
|
|
458
|
+
for i in range(offset):
|
|
459
|
+
active_buffer[i] = old_buffer[ntodo - offset + i]
|
|
460
|
+
mark_buffer_for_reading(outcontrol, old)
|
|
461
|
+
|
|
462
|
+
errorcode = outcontrol[nactive, 1]
|
|
463
|
+
if errorcode != 0:
|
|
464
|
+
errorcode = -errorcode
|
|
465
|
+
break
|
|
466
|
+
|
|
467
|
+
ntodo = read_block(int32(fd), active_buffer, offset)
|
|
468
|
+
if ntodo <= 0:
|
|
469
|
+
if ntodo < 0 or offset == 0:
|
|
470
|
+
errorcode = ntodo
|
|
471
|
+
if errorcode == -99_999:
|
|
472
|
+
debugprint0("- Failed to read file into buffer (probably due to too long reads, len(header+seq+quality) must be smaller than buffersize, default 65kB).")
|
|
473
|
+
debugprint0("- Please increase buffersize using --buffersize.")
|
|
474
|
+
break
|
|
475
|
+
ntodo = offset
|
|
476
|
+
|
|
477
|
+
nlines = 0
|
|
478
|
+
for i in range(ntodo):
|
|
479
|
+
nlines += active_buffer[i] == 10
|
|
480
|
+
nseqs = nlines // 4
|
|
481
|
+
|
|
482
|
+
outcontrol[nactive, 7] = nseqs
|
|
483
|
+
outcontrol[nactive, 6] = 0
|
|
484
|
+
|
|
485
|
+
rm_lines = nlines - nseqs * 4
|
|
486
|
+
assert rm_lines >= 0
|
|
487
|
+
if rm_lines == 0 and active_buffer[ntodo - 1] == 10:
|
|
488
|
+
offset = 0
|
|
489
|
+
else:
|
|
490
|
+
nlines = offset = 0
|
|
491
|
+
for i in range(ntodo):
|
|
492
|
+
nlines += active_buffer[ntodo - i - 1] == 10
|
|
493
|
+
if nlines == rm_lines + 1:
|
|
494
|
+
break
|
|
495
|
+
offset += 1
|
|
496
|
+
|
|
497
|
+
mark_buffer_for_writing(outcontrol, nactive, force=True) # nothing left to read; re-use buffer
|
|
498
|
+
return (wait_read, wait_write, errorcode)
|
|
499
|
+
|
|
500
|
+
|
|
277
501
|
@njit(nogil=True, locals=dict(
|
|
278
502
|
ntodo=int32, errorcode=int32, offset=int32, wait=int64,
|
|
279
503
|
wait_write=int64, sequence_id=int64, nseq=int64))
|
|
@@ -332,7 +556,7 @@ def cptask_read_fastq_seqid_pos_into_buffers(fd, inbuffers, incontrol, ininfos,
|
|
|
332
556
|
|
|
333
557
|
# numba FASTQ handling ######################################
|
|
334
558
|
|
|
335
|
-
@njit(nogil=True)
|
|
559
|
+
@njit(nogil=True, locals=dict(M=int64, i=uint64, m=int64, nxt=int64, line=uint64))
|
|
336
560
|
def _find_fastq_seqmarks(buf, linemarks, sequence_id=-1):
|
|
337
561
|
"""
|
|
338
562
|
Find start and end positions of lines in byte buffer 'buf'.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from os.path import splitext
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from shlex import split as cmdsplit
|
|
4
5
|
from subprocess import Popen, PIPE
|
|
5
6
|
from shutil import which
|
|
@@ -79,8 +80,12 @@ class InputFileHandler:
|
|
|
79
80
|
file_extension = splitext(fname)[1]
|
|
80
81
|
file_type = _filetypes.get(file_extension, None)
|
|
81
82
|
if file_type is None:
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
if Path(filename).is_fifo():
|
|
84
|
+
file_type = 'fifo'
|
|
85
|
+
debugprint0(f'Warning: InputFileHandler: {filename} is a stream. No file type set.')
|
|
86
|
+
else:
|
|
87
|
+
debugprint0(f"Error: InputFileHandler: Unknown file type for '{filename}'")
|
|
88
|
+
sys.exit(1)
|
|
84
89
|
|
|
85
90
|
if is_compressed:
|
|
86
91
|
if which(tool.split()[0]) is None:
|
|
@@ -298,11 +298,13 @@ def compile_prefetch_array_element(nbits=None):
|
|
|
298
298
|
sig = numba.void(numba.types.uintp)
|
|
299
299
|
return sig, codegen
|
|
300
300
|
|
|
301
|
+
# ToDo make consistent to either get position or startbit!!!
|
|
302
|
+
# prefetch_array_element_ptr gets starbit and prefetch_array_element position
|
|
301
303
|
if nbits is not None:
|
|
302
304
|
@numba.njit(nogil=True)
|
|
303
305
|
def prefetch_array_element_ptr(array_ptr, i):
|
|
304
|
-
"""prefetch array element
|
|
305
|
-
return prefetch_address(array_ptr +
|
|
306
|
+
"""prefetch array element"""
|
|
307
|
+
return prefetch_address(array_ptr + i // 8)
|
|
306
308
|
|
|
307
309
|
return prefetch_array_element_ptr
|
|
308
310
|
|
|
@@ -92,7 +92,7 @@ SRHash_private = namedtuple("SRHash_private", [
|
|
|
92
92
|
"get_subkey_from_bucket_signature",
|
|
93
93
|
"get_subkey_choice_from_bucket_signature",
|
|
94
94
|
"prefetch_bucket",
|
|
95
|
-
"
|
|
95
|
+
"prefetch_key",
|
|
96
96
|
|
|
97
97
|
# Copies of the public functions but with subtable/subkey interface
|
|
98
98
|
# (only defined for hashes with subtables; otherwise None)
|
|
@@ -28,6 +28,9 @@ from ..lowlevel import debug
|
|
|
28
28
|
from ..mask import create_mask
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
MAXUINT64 = np.iinfo(np.uint64).max
|
|
32
|
+
|
|
33
|
+
|
|
31
34
|
# ####### Classification methods ######
|
|
32
35
|
def compile_count_based_classification(params):
|
|
33
36
|
Mh_factor = params.get("Mh", 4)
|
|
@@ -327,8 +330,7 @@ def compile_classify_read_from_fastq(
|
|
|
327
330
|
mode, mask, rcmode, bits, path, h, threads, pairs,
|
|
328
331
|
bufsize=2**23, chunkreads=(2**23) // 200, quick=False,
|
|
329
332
|
filt=False, count=False, prefetchlevel=0, params=dict(),
|
|
330
|
-
compression="gz", show_progress=False):
|
|
331
|
-
|
|
333
|
+
compression="gz", show_progress=False, use_start_sites=False):
|
|
332
334
|
|
|
333
335
|
if compression != "none":
|
|
334
336
|
compression = "." + compression
|
|
@@ -339,6 +341,10 @@ def compile_classify_read_from_fastq(
|
|
|
339
341
|
ba = None
|
|
340
342
|
count_kmers = True
|
|
341
343
|
|
|
344
|
+
if mode != "methylation" and use_start_sites:
|
|
345
|
+
debugprint0('Using start_sites and without a methylation index is not supported')
|
|
346
|
+
exit(1)
|
|
347
|
+
|
|
342
348
|
if mode == "count":
|
|
343
349
|
debugprint0("- Using count based classification mode")
|
|
344
350
|
classify = compile_count_based_classification(params)
|
|
@@ -351,8 +357,8 @@ def compile_classify_read_from_fastq(
|
|
|
351
357
|
elif mode == "quick":
|
|
352
358
|
debugprint0("- using quick classification mode")
|
|
353
359
|
assert False
|
|
354
|
-
elif mode == '
|
|
355
|
-
debugprint0("- Using count based classification mode for
|
|
360
|
+
elif mode == 'methylation':
|
|
361
|
+
debugprint0("- Using count based classification mode for methylation sequencing data")
|
|
356
362
|
classify = compile_count_based_classification(params)
|
|
357
363
|
ct_table = _TABLE_DNA_TO_2BITS.copy()
|
|
358
364
|
ct_table[99] = 3
|
|
@@ -363,13 +369,39 @@ def compile_classify_read_from_fastq(
|
|
|
363
369
|
ga_table[103] = 0
|
|
364
370
|
ga_table[71] = 0
|
|
365
371
|
quick_dna_to_2bits_ga = compile_quick_dna_to_2bits(ga_table)
|
|
372
|
+
|
|
373
|
+
# Classification matrix for pairs
|
|
374
|
+
# H: Host G: Graft A: Ambiguous B: Both N: Neither
|
|
375
|
+
# Given the two classifications of the read pair, we return the value in the matrix
|
|
376
|
+
# H G A B N
|
|
377
|
+
# 0 1 2 3 4
|
|
378
|
+
# H 0 0 2 0 0 0
|
|
379
|
+
# G 1 2 1 1 1 1
|
|
380
|
+
# A 2 0 1 2 2 2
|
|
381
|
+
# B 3 0 1 2 3 2
|
|
382
|
+
# N 4 0 1 2 2 4
|
|
383
|
+
matrix = [[0, 2, 0, 0, 0], [2, 1, 1, 1, 1], [0, 1, 2, 2, 2], [0, 1, 2, 3, 2], [0, 1, 2, 2, 4]]
|
|
384
|
+
|
|
385
|
+
methylation_paired = np.array(matrix, dtype=np.uint8)
|
|
386
|
+
|
|
387
|
+
if use_start_sites:
|
|
388
|
+
start_sites = np.frombuffer(b"CGGTGG", dtype=np.uint8)
|
|
366
389
|
else:
|
|
367
390
|
raise ValueError(f"Unknown mode '{mode}'")
|
|
368
391
|
|
|
392
|
+
@njit(nogil=True, locals=dict(code=uint64))
|
|
393
|
+
def _collect_code(_, code, pos, codes):
|
|
394
|
+
"""Store the k-mer code at its sequence position; hash lookup deferred."""
|
|
395
|
+
codes[pos] = code
|
|
396
|
+
|
|
397
|
+
_, collect_kmers = compile_positional_kmer_processor(mask.tuple, _collect_code, rcmode=rcmode, new_encoding=False)
|
|
398
|
+
|
|
369
399
|
get_kmer_values = compile_get_kmer_values(mask, rcmode, h, prefetchlevel, count_kmers, ba)
|
|
370
400
|
_, twobit_to_code = compile_twobit_to_codes(mask, rcmode)
|
|
371
401
|
|
|
372
402
|
get_value = h.get_value
|
|
403
|
+
prefetch_key = h.private.prefetch_key
|
|
404
|
+
LOOKAHEAD = 16
|
|
373
405
|
k, w = mask.k, mask.w
|
|
374
406
|
|
|
375
407
|
|
|
@@ -422,7 +454,7 @@ def compile_classify_read_from_fastq(
|
|
|
422
454
|
@njit(nogil=True, locals=dict(
|
|
423
455
|
third_kmer=uint64, third=uint64,
|
|
424
456
|
thirdlast_kmer=uint64, thirdlast=uint64))
|
|
425
|
-
def
|
|
457
|
+
def get_methylation_classification(ht, sq, kcount):
|
|
426
458
|
# if quick:
|
|
427
459
|
# # get 2 k-mers (3rd and 3rd-last)
|
|
428
460
|
# third_kmer = twobit_to_code(sq, 2)
|
|
@@ -490,33 +522,132 @@ def compile_classify_read_from_fastq(
|
|
|
490
522
|
sq = buf[linemarks[i, 0]:linemarks[i, 1]]
|
|
491
523
|
classifications[i] = get_classification(ht, sq, counts)
|
|
492
524
|
twobits_to_dna_inplace(buf, linemarks[i, 0], linemarks[i, 1])
|
|
493
|
-
elif mode == "
|
|
525
|
+
elif mode == "methylation":
|
|
494
526
|
local_seq = np.empty(250, dtype=np.uint8)
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
527
|
+
counts2 = np.zeros(8, dtype=np.uint32)
|
|
528
|
+
max_kmers = buf.size // 2 # Upper limit for number of k-mers in a buffer. Buffer size divided by two for qualities
|
|
529
|
+
codes_ct = np.empty(max_kmers, dtype=np.uint64)
|
|
530
|
+
codes_ga = np.empty(max_kmers, dtype=np.uint64)
|
|
531
|
+
start_codes = 0
|
|
532
|
+
# pass 1.
|
|
533
|
+
|
|
534
|
+
# Does it make sense to process the buffer twice for ct and ga so the codes_ arrays are linear?
|
|
535
|
+
for readnum in range(n):
|
|
536
|
+
|
|
537
|
+
if use_start_sites:
|
|
538
|
+
if not ((buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[:3]).all() or \
|
|
539
|
+
(buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[3:]).all()):
|
|
540
|
+
continue
|
|
541
|
+
seq_len = linemarks[readnum, 1] - linemarks[readnum, 0]
|
|
542
|
+
nk = seq_len - w + 1
|
|
498
543
|
if seq_len > local_seq.size:
|
|
499
544
|
debugprint2('WGBS, local sequence length increased to ', seq_len * 2)
|
|
500
545
|
local_seq = np.empty(seq_len * 2, dtype=np.uint8)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
# C -> T
|
|
504
|
-
local_seq[:seq_len] = buf[linemarks[i, 0]:linemarks[i, 1]]
|
|
546
|
+
local_seq[:seq_len] = buf[linemarks[readnum, 0]:linemarks[readnum, 1]]
|
|
547
|
+
end_codes = start_codes + nk
|
|
505
548
|
quick_dna_to_2bits_ct(local_seq[:seq_len])
|
|
506
|
-
|
|
549
|
+
codes_ct[start_codes:end_codes] = MAXUINT64
|
|
550
|
+
collect_kmers(None, local_seq[:seq_len], 0, seq_len, codes_ct[start_codes:end_codes])
|
|
507
551
|
|
|
508
|
-
|
|
509
|
-
local_seq[:seq_len] = buf[linemarks[i, 0]:linemarks[i, 1]]
|
|
552
|
+
local_seq[:seq_len] = buf[linemarks[readnum, 0]:linemarks[readnum, 1]]
|
|
510
553
|
quick_dna_to_2bits_ga(local_seq[:seq_len])
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
554
|
+
codes_ga[start_codes:end_codes] = MAXUINT64
|
|
555
|
+
collect_kmers(None, local_seq[:seq_len], 0, seq_len, codes_ga[start_codes:end_codes])
|
|
556
|
+
start_codes = end_codes
|
|
557
|
+
|
|
558
|
+
total_kmers = start_codes
|
|
559
|
+
# 2. Pass: classify all reads
|
|
560
|
+
start_codes = 0
|
|
561
|
+
found = 0
|
|
562
|
+
not_found = 0
|
|
563
|
+
for readnum, (start_seq, end_seq, start_record, end_record) in enumerate(linemarks):
|
|
564
|
+
if use_start_sites:
|
|
565
|
+
if not ((buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[:3]).all() or \
|
|
566
|
+
(buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[3:]).all()):
|
|
567
|
+
classifications[readnum] = 5 # no start site
|
|
568
|
+
continue
|
|
569
|
+
counts[:] = 0
|
|
570
|
+
counts2[:] = 0
|
|
571
|
+
nk = end_seq - start_seq - w + 1
|
|
572
|
+
|
|
573
|
+
for pos in range(nk):
|
|
574
|
+
code_pos = pos + start_codes
|
|
575
|
+
if code_pos + LOOKAHEAD < total_kmers:
|
|
576
|
+
ahead_code = codes_ct[code_pos + LOOKAHEAD]
|
|
577
|
+
if ahead_code != MAXUINT64:
|
|
578
|
+
prefetch_key(ht, ahead_code)
|
|
579
|
+
ahead_code = codes_ga[code_pos + LOOKAHEAD]
|
|
580
|
+
if ahead_code != MAXUINT64:
|
|
581
|
+
prefetch_key(ht, ahead_code)
|
|
582
|
+
|
|
583
|
+
code = codes_ct[code_pos]
|
|
584
|
+
if code != MAXUINT64:
|
|
585
|
+
found += 1
|
|
586
|
+
v = get_value(ht, code)
|
|
587
|
+
counts[v] += 1
|
|
588
|
+
else:
|
|
589
|
+
not_found += 1
|
|
590
|
+
|
|
591
|
+
code = codes_ga[code_pos]
|
|
592
|
+
if code != MAXUINT64:
|
|
593
|
+
found += 1
|
|
594
|
+
v = get_value(ht, code)
|
|
595
|
+
counts2[v] += 1
|
|
596
|
+
else:
|
|
597
|
+
not_found += 1
|
|
598
|
+
if counts[0] < counts2[0] // 2:
|
|
599
|
+
classification = classify(counts)
|
|
600
|
+
elif counts2[0] < counts[0] // 2:
|
|
601
|
+
classification = classify(counts2)
|
|
602
|
+
else:
|
|
603
|
+
counts += counts2
|
|
604
|
+
classification = classify(counts)
|
|
605
|
+
|
|
606
|
+
classifications[readnum] = classification
|
|
607
|
+
start_codes += nk
|
|
514
608
|
|
|
515
609
|
else:
|
|
516
610
|
raise ValueError(f"Classification method {mode} is not supported.")
|
|
517
611
|
|
|
518
612
|
return threadid, (classifications, linemarks)
|
|
519
613
|
|
|
614
|
+
@njit(nogil=True)
|
|
615
|
+
def code_ct(buf, seq_len, local_seq, codes, start_codes, end_codes):
|
|
616
|
+
codes[start_codes:end_codes] = MAXUINT64
|
|
617
|
+
local_seq[:seq_len] = buf
|
|
618
|
+
quick_dna_to_2bits_ct(local_seq[:seq_len])
|
|
619
|
+
codes[start_codes:end_codes] = MAXUINT64
|
|
620
|
+
collect_kmers(None, local_seq[:seq_len], 0, seq_len, codes[start_codes:end_codes])
|
|
621
|
+
|
|
622
|
+
@njit(nogil=True)
|
|
623
|
+
def code_ga(buf, seq_len, local_seq, codes, start_codes, end_codes):
|
|
624
|
+
codes[start_codes:end_codes] = MAXUINT64
|
|
625
|
+
local_seq[:seq_len] = buf
|
|
626
|
+
quick_dna_to_2bits_ga(local_seq[:seq_len])
|
|
627
|
+
codes[start_codes:end_codes] = MAXUINT64
|
|
628
|
+
collect_kmers(None, local_seq[:seq_len], 0, seq_len, codes[start_codes:end_codes])
|
|
629
|
+
|
|
630
|
+
@njit(nogil=True)
|
|
631
|
+
def prefetch_index(ht, arr, index):
|
|
632
|
+
ahead_code = arr[index]
|
|
633
|
+
if ahead_code != MAXUINT64:
|
|
634
|
+
prefetch_key(ht, ahead_code)
|
|
635
|
+
|
|
636
|
+
@njit(nogil=True)
|
|
637
|
+
def get_v(ht, code, counts):
|
|
638
|
+
if code != MAXUINT64:
|
|
639
|
+
v = get_value(ht, code)
|
|
640
|
+
counts[v] += 1
|
|
641
|
+
|
|
642
|
+
@njit(nogil=True)
|
|
643
|
+
def meth_classify(counts_ct, counts_ga):
|
|
644
|
+
if counts_ct[0] < counts_ga[0] // 2:
|
|
645
|
+
return classify(counts_ct)
|
|
646
|
+
if counts_ga[0] < counts_ct[0] // 2:
|
|
647
|
+
return classify(counts_ga)
|
|
648
|
+
counts_ct += counts_ga
|
|
649
|
+
return classify(counts_ct)
|
|
650
|
+
|
|
520
651
|
@njit(nogil=True)
|
|
521
652
|
def classify_paired_kmers_chunkwise(threadid, buf, linemarks, buf1, linemarks1, ht,
|
|
522
653
|
ba_neither=None, ba_stronghost=None, ba_stronggraft=None, ba_both=None,
|
|
@@ -580,47 +711,86 @@ def compile_classify_read_from_fastq(
|
|
|
580
711
|
classifications[i] = get_paired_classification(ht, sq1, sq2, counts)
|
|
581
712
|
twobits_to_dna_inplace(buf, linemarks[i, 0], linemarks[i, 1])
|
|
582
713
|
twobits_to_dna_inplace(buf1, linemarks1[i, 0], linemarks1[i, 1])
|
|
583
|
-
elif mode == "
|
|
584
|
-
|
|
714
|
+
elif mode == "methylation":
|
|
715
|
+
|
|
716
|
+
counts_ct_r1 = np.zeros(8, dtype=np.uint32)
|
|
717
|
+
counts_ga_r1 = np.zeros(8, dtype=np.uint32)
|
|
718
|
+
counts_ct_r2 = np.zeros(8, dtype=np.uint32)
|
|
719
|
+
counts_ga_r2 = np.zeros(8, dtype=np.uint32)
|
|
720
|
+
|
|
721
|
+
max_kmers = buf.size // 2 # Upper limit for number of k-mers in a buffer. Buffer size divided by two for qualities
|
|
722
|
+
codes_ct_r1 = np.empty(max_kmers, dtype=np.uint64)
|
|
723
|
+
codes_ct_r2 = np.empty(max_kmers, dtype=np.uint64)
|
|
724
|
+
codes_ga_r1 = np.empty(max_kmers, dtype=np.uint64)
|
|
725
|
+
codes_ga_r2 = np.empty(max_kmers, dtype=np.uint64)
|
|
585
726
|
local_seq = np.empty(250, dtype=np.uint8)
|
|
586
|
-
for i in range(n):
|
|
587
|
-
seq_len = linemarks[i, 1] - linemarks[i, 0]
|
|
588
|
-
seq_len1 = linemarks1[i, 1] - linemarks1[i, 0]
|
|
589
727
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
debugprint2('WGBS, local sequence length increased to ', max_seq_len * 2)
|
|
593
|
-
local_seq = np.empty(max_seq_len * 2, dtype=np.uint8)
|
|
728
|
+
start_codes = 0
|
|
729
|
+
# pass 1.
|
|
594
730
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
731
|
+
for readnum in range(n):
|
|
732
|
+
seq_len_r1 = linemarks[readnum, 1] - linemarks[readnum, 0]
|
|
733
|
+
seq_len_r2 = linemarks1[readnum, 1] - linemarks1[readnum, 0]
|
|
734
|
+
seq_len = max(seq_len_r1, seq_len_r2)
|
|
735
|
+
nk = seq_len - w + 1
|
|
736
|
+
|
|
737
|
+
if use_start_sites:
|
|
738
|
+
if not ((buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[:3]).all() or \
|
|
739
|
+
(buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[3:]).all()):
|
|
740
|
+
continue
|
|
741
|
+
|
|
742
|
+
if seq_len > local_seq.size:
|
|
743
|
+
debugprint2('WGBS, local sequence length increased to ', seq_len * 2)
|
|
744
|
+
local_seq = np.empty(seq_len * 2, dtype=np.uint8)
|
|
745
|
+
|
|
746
|
+
end_codes = start_codes + nk
|
|
747
|
+
|
|
748
|
+
code_ct(buf[linemarks[readnum, 0]:linemarks[readnum, 1]], seq_len_r1, local_seq, codes_ct_r1, start_codes, end_codes)
|
|
749
|
+
code_ga(buf[linemarks[readnum, 0]:linemarks[readnum, 1]], seq_len_r1, local_seq, codes_ga_r1, start_codes, end_codes)
|
|
750
|
+
|
|
751
|
+
code_ct(buf1[linemarks1[readnum, 0]:linemarks1[readnum, 1]], seq_len_r2, local_seq, codes_ct_r2, start_codes, end_codes)
|
|
752
|
+
code_ga(buf1[linemarks1[readnum, 0]:linemarks1[readnum, 1]], seq_len_r2, local_seq, codes_ga_r2, start_codes, end_codes)
|
|
753
|
+
|
|
754
|
+
start_codes = end_codes
|
|
755
|
+
|
|
756
|
+
total_kmers = start_codes
|
|
757
|
+
# 2. Pass: classify all reads
|
|
758
|
+
start_codes = 0
|
|
759
|
+
for readnum in range(n):
|
|
760
|
+
|
|
761
|
+
if use_start_sites:
|
|
762
|
+
if not ((buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[:3]).all() or \
|
|
763
|
+
(buf[linemarks[readnum, 0]:linemarks[readnum, 0] + 3] == start_sites[3:]).all()):
|
|
764
|
+
classifications[readnum] = 5 # no start site
|
|
765
|
+
continue
|
|
766
|
+
seq_len_r1 = linemarks[readnum, 1] - linemarks[readnum, 0]
|
|
767
|
+
seq_len_r2 = linemarks1[readnum, 1] - linemarks1[readnum, 0]
|
|
768
|
+
seq_len = max(seq_len_r1, seq_len_r2)
|
|
769
|
+
nk = seq_len - w + 1
|
|
770
|
+
|
|
771
|
+
counts_ct_r1[:] = 0
|
|
772
|
+
counts_ga_r1[:] = 0
|
|
773
|
+
counts_ct_r2[:] = 0
|
|
774
|
+
counts_ga_r2[:] = 0
|
|
775
|
+
|
|
776
|
+
for pos in range(nk):
|
|
777
|
+
code_pos = pos + start_codes
|
|
778
|
+
if code_pos + LOOKAHEAD < total_kmers:
|
|
779
|
+
prefetch_index(ht, codes_ct_r1, code_pos + LOOKAHEAD)
|
|
780
|
+
prefetch_index(ht, codes_ga_r1, code_pos + LOOKAHEAD)
|
|
781
|
+
prefetch_index(ht, codes_ct_r2, code_pos + LOOKAHEAD)
|
|
782
|
+
prefetch_index(ht, codes_ga_r2, code_pos + LOOKAHEAD)
|
|
783
|
+
|
|
784
|
+
get_v(ht, codes_ct_r1[code_pos], counts_ct_r1)
|
|
785
|
+
get_v(ht, codes_ga_r1[code_pos], counts_ga_r1)
|
|
786
|
+
get_v(ht, codes_ct_r2[code_pos], counts_ct_r2)
|
|
787
|
+
get_v(ht, codes_ga_r2[code_pos], counts_ga_r2)
|
|
788
|
+
start_codes += nk
|
|
789
|
+
r1_classification = meth_classify(counts_ct_r1, counts_ga_r1)
|
|
790
|
+
r2_classification = meth_classify(counts_ct_r2, counts_ga_r2)
|
|
791
|
+
|
|
792
|
+
classifications[readnum] = methylation_paired[r1_classification, r2_classification]
|
|
606
793
|
|
|
607
|
-
# R2
|
|
608
|
-
counts[:] = 0
|
|
609
|
-
# C -> T
|
|
610
|
-
local_seq[:seq_len1] = buf1[linemarks1[i, 0]:linemarks1[i, 1]]
|
|
611
|
-
quick_dna_to_2bits_ga(local_seq[:seq_len1])
|
|
612
|
-
get_bisulfite_classification(ht, local_seq[:seq_len1], counts)
|
|
613
|
-
|
|
614
|
-
# G -> A
|
|
615
|
-
local_seq[:seq_len1] = buf1[linemarks1[i, 0]:linemarks1[i, 1]]
|
|
616
|
-
quick_dna_to_2bits_ga(local_seq[:seq_len1])
|
|
617
|
-
get_bisulfite_classification(ht, local_seq[:seq_len1], counts)
|
|
618
|
-
r2_clasification = classify(counts)
|
|
619
|
-
|
|
620
|
-
if r1_clasification == r2_clasification:
|
|
621
|
-
classifications[i] = r1_clasification
|
|
622
|
-
else:
|
|
623
|
-
classifications[i] = 2 # ambiguous
|
|
624
794
|
else:
|
|
625
795
|
raise ValueError(f"{mode} is not a supported classification mode")
|
|
626
796
|
|
|
@@ -667,8 +837,10 @@ def compile_classify_read_from_fastq(
|
|
|
667
837
|
yield outfile.file
|
|
668
838
|
|
|
669
839
|
def classify_read_from_fastq_paired(fastqs, pairs, ht):
|
|
670
|
-
counts = [0, 0, 0, 0, 0] # host, graft, amb., both, neither
|
|
840
|
+
counts = [0, 0, 0, 0, 0, 0] # host, graft, amb., both, neither
|
|
671
841
|
nprocessed = 0
|
|
842
|
+
|
|
843
|
+
site_count = count and not use_start_sites
|
|
672
844
|
with cond_contextmgr(path, f"-host.1.fq{compression}", count, filt) as host1, \
|
|
673
845
|
cond_contextmgr(path, f"-host.2.fq{compression}", count, filt) as host2, \
|
|
674
846
|
cond_contextmgr(path, f"-graft.1.fq{compression}", count, filt) as graft1, \
|
|
@@ -678,8 +850,10 @@ def compile_classify_read_from_fastq(
|
|
|
678
850
|
cond_contextmgr(path, f"-both.1.fq{compression}", count, filt) as both1, \
|
|
679
851
|
cond_contextmgr(path, f"-both.2.fq{compression}", count, filt) as both2, \
|
|
680
852
|
cond_contextmgr(path, f"-neither.1.fq{compression}", count, filt) as neither1, \
|
|
681
|
-
cond_contextmgr(path, f"-neither.2.fq{compression}", count, filt) as neither2
|
|
682
|
-
|
|
853
|
+
cond_contextmgr(path, f"-neither.2.fq{compression}", count, filt) as neither2, \
|
|
854
|
+
cond_contextmgr(path, f"-sites.1.fq{compression}", site_count, filt) as sites1, \
|
|
855
|
+
cond_contextmgr(path, f"-sites.2.fq{compression}", site_count, filt) as sites2:
|
|
856
|
+
streams = ((host1, host2), (graft1, graft2), (ambiguous1, ambiguous2), (both1, both2), (neither1, neither2), (sites1, sites2))
|
|
683
857
|
|
|
684
858
|
with ThreadPoolExecutor(max_workers=threads) as executor:
|
|
685
859
|
for fastq1, fastq2 in zip(fastqs, pairs):
|
|
@@ -734,14 +908,16 @@ def compile_classify_read_from_fastq(
|
|
|
734
908
|
return counts
|
|
735
909
|
|
|
736
910
|
def classify_read_from_fastq_single(fastqs, ht):
|
|
737
|
-
counts = [0, 0, 0, 0, 0]
|
|
911
|
+
counts = [0, 0, 0, 0, 0, 0]
|
|
738
912
|
nprocessed = 0
|
|
913
|
+
site_count = count and not use_start_sites
|
|
739
914
|
with cond_contextmgr(path, f"-host.fq{compression}", count, filt) as host, \
|
|
740
915
|
cond_contextmgr(path, f"-graft.fq{compression}", count, filt) as graft, \
|
|
741
916
|
cond_contextmgr(path, f"-ambiguous.fq{compression}", count, filt) as ambiguous, \
|
|
742
917
|
cond_contextmgr(path, f"-both.fq{compression}", count, filt) as both, \
|
|
743
|
-
cond_contextmgr(path, f"-neither.fq{compression}", count, filt) as neither
|
|
744
|
-
|
|
918
|
+
cond_contextmgr(path, f"-neither.fq{compression}", count, filt) as neither, \
|
|
919
|
+
cond_contextmgr(path, f"-sites.fq{compression}", site_count, filt) as sites:
|
|
920
|
+
streams = (host, graft, ambiguous, both, neither, sites)
|
|
745
921
|
|
|
746
922
|
running_jobs = []
|
|
747
923
|
with ThreadPoolExecutor(max_workers=threads) as executor:
|
|
@@ -794,24 +970,26 @@ def compile_classify_read_from_fastq(
|
|
|
794
970
|
return classify_read_from_fastq
|
|
795
971
|
|
|
796
972
|
|
|
797
|
-
def print_class_stats(prefix, stats):
|
|
798
|
-
classes = ["host", "graft", "ambiguous", "both", "neither"]
|
|
973
|
+
def print_class_stats(prefix, stats, sites):
|
|
974
|
+
classes = ["host", "graft", "ambiguous", "both", "neither", 'sites']
|
|
799
975
|
|
|
976
|
+
nstats = len(stats) - (not sites)
|
|
977
|
+
stats = stats[:nstats]
|
|
800
978
|
percentages = [i / sum(stats) * 100 for i in stats]
|
|
801
979
|
str_counts = "\t".join(str(i) for i in stats)
|
|
802
980
|
ndigits = max(map(lambda x: len(str(x)), stats))
|
|
803
981
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
for i in range(len(classes)):
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
982
|
+
debugprint0("\n## Classification Statistics")
|
|
983
|
+
debugprint0("\n```")
|
|
984
|
+
debugprint0("prefix\thost\tgraft\tambiguous\tboth\tneither")
|
|
985
|
+
debugprint0(f"{prefix}\t{str_counts}")
|
|
986
|
+
debugprint0("```\n")
|
|
987
|
+
debugprint0("```")
|
|
988
|
+
debugprint0(f"| prefix | {prefix} ")
|
|
989
|
+
for i in range(len(classes) - (not sites)):
|
|
990
|
+
debugprint0(f"| {classes[i]:9s} | {stats[i]:{ndigits}d} | {percentages[i]:5.2f}% |")
|
|
991
|
+
debugprint0("```")
|
|
992
|
+
debugprint0()
|
|
815
993
|
|
|
816
994
|
|
|
817
995
|
def main(args):
|
|
@@ -850,13 +1028,13 @@ def main(args):
|
|
|
850
1028
|
if mode == "quick":
|
|
851
1029
|
mode = "count"
|
|
852
1030
|
quick = True
|
|
853
|
-
if '
|
|
854
|
-
if appinfo['
|
|
855
|
-
debugprint0('- The provided index is build for WGBS analysis.\n The classification mode will be switched to
|
|
856
|
-
mode = '
|
|
857
|
-
if mode == '
|
|
1031
|
+
if 'methylation' in appinfo:
|
|
1032
|
+
if appinfo['methylation']:
|
|
1033
|
+
debugprint0('- The provided index is build for WGBS analysis.\n The classification mode will be switched to methylation.')
|
|
1034
|
+
mode = 'methylation'
|
|
1035
|
+
if mode == 'methylation':
|
|
858
1036
|
params = args["params_" + 'count']
|
|
859
|
-
params = args["params_" + '
|
|
1037
|
+
params = args["params_" + 'methylation']
|
|
860
1038
|
else:
|
|
861
1039
|
params = args["params_" + mode]
|
|
862
1040
|
if not args.fastq:
|
|
@@ -893,13 +1071,13 @@ def main(args):
|
|
|
893
1071
|
bufsize=chunksize, chunkreads=chunkreads, quick=quick,
|
|
894
1072
|
filt=args.filter, count=args.count, prefetchlevel=args.prefetchlevel,
|
|
895
1073
|
params=params, compression=args.compression,
|
|
896
|
-
show_progress=args.progress)
|
|
1074
|
+
show_progress=args.progress, use_start_sites=args.use_start_sites)
|
|
897
1075
|
|
|
898
1076
|
if paired:
|
|
899
1077
|
counts = classify_read_from_fastq(args.fastq, args.pairs, h.hashtable)
|
|
900
1078
|
else:
|
|
901
1079
|
counts = classify_read_from_fastq(args.fastq, h.hashtable)
|
|
902
|
-
print_class_stats(args.prefix, counts)
|
|
1080
|
+
print_class_stats(args.prefix, counts, args.use_start_sites)
|
|
903
1081
|
|
|
904
1082
|
debugprint0("## Running time statistics\n")
|
|
905
1083
|
timestamp0(starttime, msg="- Running time")
|
|
@@ -104,7 +104,7 @@ def create_new_index(nsubtables, args):
|
|
|
104
104
|
return (h, values, valueset, mask, rcmode)
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
def process_files(constant_value, h, fnames, mask, rcmode,
|
|
107
|
+
def process_files(constant_value, h, fnames, mask, rcmode, methylation, *,
|
|
108
108
|
maxfailures=0, maxwalk=1000,
|
|
109
109
|
threads_read=1, threads_split=1):
|
|
110
110
|
|
|
@@ -134,7 +134,7 @@ def process_files(constant_value, h, fnames, mask, rcmode, bisulfite, *,
|
|
|
134
134
|
outbufsize = 2**16
|
|
135
135
|
cptask_split = compile_cptask_scatter_kmers_from_linemarked(
|
|
136
136
|
mask, rcmode, hashfunc0,
|
|
137
|
-
nsubtables, nbuffers_per_subtable, outbufsize,
|
|
137
|
+
nsubtables, nbuffers_per_subtable, outbufsize, methylation=methylation)
|
|
138
138
|
|
|
139
139
|
splitter_jobs = ConsumerProducer(
|
|
140
140
|
name='kmer_splitter',
|
|
@@ -189,7 +189,7 @@ def main(args):
|
|
|
189
189
|
|
|
190
190
|
# process all host files
|
|
191
191
|
if args.host:
|
|
192
|
-
success_host = process_files(1, h, args.host, mask, rcmode, args.
|
|
192
|
+
success_host = process_files(1, h, args.host, mask, rcmode, args.methylation,
|
|
193
193
|
maxfailures=0, maxwalk=args.maxwalk,
|
|
194
194
|
threads_read=threads_read, threads_split=threads_split)
|
|
195
195
|
|
|
@@ -198,7 +198,7 @@ def main(args):
|
|
|
198
198
|
exit(1)
|
|
199
199
|
|
|
200
200
|
# process all graft files
|
|
201
|
-
success_graft = process_files(2, h, args.graft, mask, rcmode, args.
|
|
201
|
+
success_graft = process_files(2, h, args.graft, mask, rcmode, args.methylation,
|
|
202
202
|
maxfailures=0, maxwalk=args.maxwalk,
|
|
203
203
|
threads_read=threads_read, threads_split=threads_split)
|
|
204
204
|
|
|
@@ -220,7 +220,7 @@ def main(args):
|
|
|
220
220
|
# #################### Save Hash ####################
|
|
221
221
|
|
|
222
222
|
optinfo = dict(walkseed=args.walkseed, maxwalk=args.maxwalk, maxfailures=0)
|
|
223
|
-
appinfo = dict(rcmode=rcmode, mask=mask.tuple, k=mask.k,
|
|
223
|
+
appinfo = dict(rcmode=rcmode, mask=mask.tuple, k=mask.k, methylation=args.methylation)
|
|
224
224
|
save_hash(args.index, h, valuetup, optinfo, appinfo)
|
|
225
225
|
|
|
226
226
|
print_statistics(h, level=args.statistics)
|
|
@@ -40,7 +40,7 @@ def classify(p):
|
|
|
40
40
|
choices=CL_CHOICES, help=f"classification mode {CL_CHOICES}")
|
|
41
41
|
p.add_argument("--params_count", type=dict, help=SUPPRESS)
|
|
42
42
|
# help="All paramteres for classification based on counts as a dictionary. Update in config file.")
|
|
43
|
-
p.add_argument("--
|
|
43
|
+
p.add_argument("--params_methylation", type=dict, help=SUPPRESS)
|
|
44
44
|
# help="All paramteres for classification based on counts as a dictionary. Update in config file.")
|
|
45
45
|
p.add_argument("--params_coverage", type=dict, help=SUPPRESS)
|
|
46
46
|
# help="All paramteres for classification based on coverage as a dictionary. Update in config file.")
|
|
@@ -60,6 +60,8 @@ def classify(p):
|
|
|
60
60
|
help="maximum number of reads per chunk per thread [SIZE_MB*(2**20) // 200]")
|
|
61
61
|
p.add_argument("--progress", "-P", action="store_true",
|
|
62
62
|
help="Show progress")
|
|
63
|
+
p.add_argument("--use-start-sites", action="store_true",
|
|
64
|
+
help="Filter read that do not start with the expected start sites.")
|
|
63
65
|
|
|
64
66
|
|
|
65
67
|
def index(p):
|
|
@@ -118,7 +120,7 @@ def index(p):
|
|
|
118
120
|
help="continue even after this many failures; forever: -1]")
|
|
119
121
|
p.add_argument("--walkseed", type=int, metavar="INT",
|
|
120
122
|
help="seed for random walks while inserting elements")
|
|
121
|
-
p.add_argument("--bisulfite", action='store_true',
|
|
123
|
+
p.add_argument("--bisulfite", "--methylation", action='store_true', dest="methylation",
|
|
122
124
|
help="filtering WGBS data with translated index (EXPERIMENTAL)")
|
|
123
125
|
|
|
124
126
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xengsort
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: xenograft sorting of FASTQ reads into five categories: host, graft, both, neither, ambiguous.
|
|
5
5
|
Author-email: Jens Zentgraf <zentgraf@cs.uni-saarland.de>, Sven Rahmann <rahmann@cs.uni-saarland.de>
|
|
6
6
|
License: MIT License
|
|
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
|
|
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
|
|
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
|
|
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
|