xengsort 2.1.1__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.1/xengsort.egg-info → xengsort-2.2.0}/PKG-INFO +1 -1
- {xengsort-2.1.1 → xengsort-2.2.0}/pyproject.toml +1 -1
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/cptasks_kmers.py +4 -4
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/hash_s3c_fbcbvb.py +1 -1
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/fastqio.py +226 -2
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/llvm.py +4 -2
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/srhash.py +1 -1
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/config/classify.yaml +1 -1
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_classify.py +245 -79
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_index.py +5 -5
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_main.py +4 -2
- {xengsort-2.1.1 → xengsort-2.2.0/xengsort.egg-info}/PKG-INFO +1 -1
- {xengsort-2.1.1 → xengsort-2.2.0}/LICENSE +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/MANIFEST.in +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/README.md +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/setup.cfg +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/tests/test_trimmed_reads.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/dnaencode.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/dnaencode_fast.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/fastcash_info.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/fastcash_main.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/fastcash_optimize.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/hash_minimizer.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/hash_srh_fbcbvb.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/binaryio.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/fastaio.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/generalio.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/hashio.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/seqio.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/textio.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/xorio.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/kmers.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/aligned_arrays.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/bitarray.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/conpro.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/debug.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/intbitarray.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/libc.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/numbautils.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/packedarray.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/mask.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/mathutils.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/parameters.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/subtable_hashfunctions.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/values/xenograft.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/config/index.yaml +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_info.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_load.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_remove.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_weak.py +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/SOURCES.txt +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/dependency_links.txt +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/entry_points.txt +0 -0
- {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/requires.txt +0 -0
- {xengsort-2.1.1 → 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'.
|
|
@@ -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
|
|
@@ -376,14 +382,26 @@ def compile_classify_read_from_fastq(
|
|
|
376
382
|
# N 4 0 1 2 2 4
|
|
377
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]]
|
|
378
384
|
|
|
379
|
-
|
|
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)
|
|
380
389
|
else:
|
|
381
390
|
raise ValueError(f"Unknown mode '{mode}'")
|
|
382
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
|
+
|
|
383
399
|
get_kmer_values = compile_get_kmer_values(mask, rcmode, h, prefetchlevel, count_kmers, ba)
|
|
384
400
|
_, twobit_to_code = compile_twobit_to_codes(mask, rcmode)
|
|
385
401
|
|
|
386
402
|
get_value = h.get_value
|
|
403
|
+
prefetch_key = h.private.prefetch_key
|
|
404
|
+
LOOKAHEAD = 16
|
|
387
405
|
k, w = mask.k, mask.w
|
|
388
406
|
|
|
389
407
|
|
|
@@ -436,7 +454,7 @@ def compile_classify_read_from_fastq(
|
|
|
436
454
|
@njit(nogil=True, locals=dict(
|
|
437
455
|
third_kmer=uint64, third=uint64,
|
|
438
456
|
thirdlast_kmer=uint64, thirdlast=uint64))
|
|
439
|
-
def
|
|
457
|
+
def get_methylation_classification(ht, sq, kcount):
|
|
440
458
|
# if quick:
|
|
441
459
|
# # get 2 k-mers (3rd and 3rd-last)
|
|
442
460
|
# third_kmer = twobit_to_code(sq, 2)
|
|
@@ -504,33 +522,132 @@ def compile_classify_read_from_fastq(
|
|
|
504
522
|
sq = buf[linemarks[i, 0]:linemarks[i, 1]]
|
|
505
523
|
classifications[i] = get_classification(ht, sq, counts)
|
|
506
524
|
twobits_to_dna_inplace(buf, linemarks[i, 0], linemarks[i, 1])
|
|
507
|
-
elif mode == "
|
|
525
|
+
elif mode == "methylation":
|
|
508
526
|
local_seq = np.empty(250, dtype=np.uint8)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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
|
|
512
543
|
if seq_len > local_seq.size:
|
|
513
544
|
debugprint2('WGBS, local sequence length increased to ', seq_len * 2)
|
|
514
545
|
local_seq = np.empty(seq_len * 2, dtype=np.uint8)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
# C -> T
|
|
518
|
-
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
|
|
519
548
|
quick_dna_to_2bits_ct(local_seq[:seq_len])
|
|
520
|
-
|
|
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])
|
|
521
551
|
|
|
522
|
-
|
|
523
|
-
local_seq[:seq_len] = buf[linemarks[i, 0]:linemarks[i, 1]]
|
|
552
|
+
local_seq[:seq_len] = buf[linemarks[readnum, 0]:linemarks[readnum, 1]]
|
|
524
553
|
quick_dna_to_2bits_ga(local_seq[:seq_len])
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
|
528
608
|
|
|
529
609
|
else:
|
|
530
610
|
raise ValueError(f"Classification method {mode} is not supported.")
|
|
531
611
|
|
|
532
612
|
return threadid, (classifications, linemarks)
|
|
533
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
|
+
|
|
534
651
|
@njit(nogil=True)
|
|
535
652
|
def classify_paired_kmers_chunkwise(threadid, buf, linemarks, buf1, linemarks1, ht,
|
|
536
653
|
ba_neither=None, ba_stronghost=None, ba_stronggraft=None, ba_both=None,
|
|
@@ -594,44 +711,85 @@ def compile_classify_read_from_fastq(
|
|
|
594
711
|
classifications[i] = get_paired_classification(ht, sq1, sq2, counts)
|
|
595
712
|
twobits_to_dna_inplace(buf, linemarks[i, 0], linemarks[i, 1])
|
|
596
713
|
twobits_to_dna_inplace(buf1, linemarks1[i, 0], linemarks1[i, 1])
|
|
597
|
-
elif mode == "
|
|
598
|
-
|
|
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)
|
|
599
726
|
local_seq = np.empty(250, dtype=np.uint8)
|
|
600
|
-
for i in range(n):
|
|
601
|
-
seq_len = linemarks[i, 1] - linemarks[i, 0]
|
|
602
|
-
seq_len1 = linemarks1[i, 1] - linemarks1[i, 0]
|
|
603
727
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
debugprint2('WGBS, local sequence length increased to ', max_seq_len * 2)
|
|
607
|
-
local_seq = np.empty(max_seq_len * 2, dtype=np.uint8)
|
|
728
|
+
start_codes = 0
|
|
729
|
+
# pass 1.
|
|
608
730
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
q1 = get_bisulfite_classification(ht, local_seq[:seq_len], counts)
|
|
615
|
-
# G -> A
|
|
616
|
-
local_seq[:seq_len] = buf[linemarks[i, 0]:linemarks[i, 1]]
|
|
617
|
-
quick_dna_to_2bits_ga(local_seq[:seq_len])
|
|
618
|
-
q2 = get_bisulfite_classification(ht, local_seq[:seq_len], counts)
|
|
619
|
-
r1_classification = classify(counts)
|
|
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
|
|
620
736
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
quick_dna_to_2bits_ct(local_seq[:seq_len1])
|
|
626
|
-
get_bisulfite_classification(ht, local_seq[:seq_len1], counts)
|
|
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
|
|
627
741
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
get_bisulfite_classification(ht, local_seq[:seq_len1], counts)
|
|
632
|
-
r2_classification = classify(counts)
|
|
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)
|
|
633
745
|
|
|
634
|
-
|
|
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]
|
|
635
793
|
|
|
636
794
|
else:
|
|
637
795
|
raise ValueError(f"{mode} is not a supported classification mode")
|
|
@@ -679,8 +837,10 @@ def compile_classify_read_from_fastq(
|
|
|
679
837
|
yield outfile.file
|
|
680
838
|
|
|
681
839
|
def classify_read_from_fastq_paired(fastqs, pairs, ht):
|
|
682
|
-
counts = [0, 0, 0, 0, 0] # host, graft, amb., both, neither
|
|
840
|
+
counts = [0, 0, 0, 0, 0, 0] # host, graft, amb., both, neither
|
|
683
841
|
nprocessed = 0
|
|
842
|
+
|
|
843
|
+
site_count = count and not use_start_sites
|
|
684
844
|
with cond_contextmgr(path, f"-host.1.fq{compression}", count, filt) as host1, \
|
|
685
845
|
cond_contextmgr(path, f"-host.2.fq{compression}", count, filt) as host2, \
|
|
686
846
|
cond_contextmgr(path, f"-graft.1.fq{compression}", count, filt) as graft1, \
|
|
@@ -690,8 +850,10 @@ def compile_classify_read_from_fastq(
|
|
|
690
850
|
cond_contextmgr(path, f"-both.1.fq{compression}", count, filt) as both1, \
|
|
691
851
|
cond_contextmgr(path, f"-both.2.fq{compression}", count, filt) as both2, \
|
|
692
852
|
cond_contextmgr(path, f"-neither.1.fq{compression}", count, filt) as neither1, \
|
|
693
|
-
cond_contextmgr(path, f"-neither.2.fq{compression}", count, filt) as neither2
|
|
694
|
-
|
|
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))
|
|
695
857
|
|
|
696
858
|
with ThreadPoolExecutor(max_workers=threads) as executor:
|
|
697
859
|
for fastq1, fastq2 in zip(fastqs, pairs):
|
|
@@ -746,14 +908,16 @@ def compile_classify_read_from_fastq(
|
|
|
746
908
|
return counts
|
|
747
909
|
|
|
748
910
|
def classify_read_from_fastq_single(fastqs, ht):
|
|
749
|
-
counts = [0, 0, 0, 0, 0]
|
|
911
|
+
counts = [0, 0, 0, 0, 0, 0]
|
|
750
912
|
nprocessed = 0
|
|
913
|
+
site_count = count and not use_start_sites
|
|
751
914
|
with cond_contextmgr(path, f"-host.fq{compression}", count, filt) as host, \
|
|
752
915
|
cond_contextmgr(path, f"-graft.fq{compression}", count, filt) as graft, \
|
|
753
916
|
cond_contextmgr(path, f"-ambiguous.fq{compression}", count, filt) as ambiguous, \
|
|
754
917
|
cond_contextmgr(path, f"-both.fq{compression}", count, filt) as both, \
|
|
755
|
-
cond_contextmgr(path, f"-neither.fq{compression}", count, filt) as neither
|
|
756
|
-
|
|
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)
|
|
757
921
|
|
|
758
922
|
running_jobs = []
|
|
759
923
|
with ThreadPoolExecutor(max_workers=threads) as executor:
|
|
@@ -806,24 +970,26 @@ def compile_classify_read_from_fastq(
|
|
|
806
970
|
return classify_read_from_fastq
|
|
807
971
|
|
|
808
972
|
|
|
809
|
-
def print_class_stats(prefix, stats):
|
|
810
|
-
classes = ["host", "graft", "ambiguous", "both", "neither"]
|
|
973
|
+
def print_class_stats(prefix, stats, sites):
|
|
974
|
+
classes = ["host", "graft", "ambiguous", "both", "neither", 'sites']
|
|
811
975
|
|
|
976
|
+
nstats = len(stats) - (not sites)
|
|
977
|
+
stats = stats[:nstats]
|
|
812
978
|
percentages = [i / sum(stats) * 100 for i in stats]
|
|
813
979
|
str_counts = "\t".join(str(i) for i in stats)
|
|
814
980
|
ndigits = max(map(lambda x: len(str(x)), stats))
|
|
815
981
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
for i in range(len(classes)):
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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()
|
|
827
993
|
|
|
828
994
|
|
|
829
995
|
def main(args):
|
|
@@ -862,13 +1028,13 @@ def main(args):
|
|
|
862
1028
|
if mode == "quick":
|
|
863
1029
|
mode = "count"
|
|
864
1030
|
quick = True
|
|
865
|
-
if '
|
|
866
|
-
if appinfo['
|
|
867
|
-
debugprint0('- The provided index is build for WGBS analysis.\n The classification mode will be switched to
|
|
868
|
-
mode = '
|
|
869
|
-
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':
|
|
870
1036
|
params = args["params_" + 'count']
|
|
871
|
-
params = args["params_" + '
|
|
1037
|
+
params = args["params_" + 'methylation']
|
|
872
1038
|
else:
|
|
873
1039
|
params = args["params_" + mode]
|
|
874
1040
|
if not args.fastq:
|
|
@@ -905,13 +1071,13 @@ def main(args):
|
|
|
905
1071
|
bufsize=chunksize, chunkreads=chunkreads, quick=quick,
|
|
906
1072
|
filt=args.filter, count=args.count, prefetchlevel=args.prefetchlevel,
|
|
907
1073
|
params=params, compression=args.compression,
|
|
908
|
-
show_progress=args.progress)
|
|
1074
|
+
show_progress=args.progress, use_start_sites=args.use_start_sites)
|
|
909
1075
|
|
|
910
1076
|
if paired:
|
|
911
1077
|
counts = classify_read_from_fastq(args.fastq, args.pairs, h.hashtable)
|
|
912
1078
|
else:
|
|
913
1079
|
counts = classify_read_from_fastq(args.fastq, h.hashtable)
|
|
914
|
-
print_class_stats(args.prefix, counts)
|
|
1080
|
+
print_class_stats(args.prefix, counts, args.use_start_sites)
|
|
915
1081
|
|
|
916
1082
|
debugprint0("## Running time statistics\n")
|
|
917
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
|
|
File without changes
|