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.
Files changed (55) hide show
  1. {xengsort-2.1.1/xengsort.egg-info → xengsort-2.2.0}/PKG-INFO +1 -1
  2. {xengsort-2.1.1 → xengsort-2.2.0}/pyproject.toml +1 -1
  3. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/cptasks_kmers.py +4 -4
  4. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/hash_s3c_fbcbvb.py +1 -1
  5. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/fastqio.py +226 -2
  6. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/llvm.py +4 -2
  7. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/srhash.py +1 -1
  8. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/config/classify.yaml +1 -1
  9. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_classify.py +245 -79
  10. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_index.py +5 -5
  11. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_main.py +4 -2
  12. {xengsort-2.1.1 → xengsort-2.2.0/xengsort.egg-info}/PKG-INFO +1 -1
  13. {xengsort-2.1.1 → xengsort-2.2.0}/LICENSE +0 -0
  14. {xengsort-2.1.1 → xengsort-2.2.0}/MANIFEST.in +0 -0
  15. {xengsort-2.1.1 → xengsort-2.2.0}/README.md +0 -0
  16. {xengsort-2.1.1 → xengsort-2.2.0}/setup.cfg +0 -0
  17. {xengsort-2.1.1 → xengsort-2.2.0}/tests/test_trimmed_reads.py +0 -0
  18. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/dnaencode.py +0 -0
  19. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/dnaencode_fast.py +0 -0
  20. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/fastcash_info.py +0 -0
  21. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/fastcash_main.py +0 -0
  22. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/fastcash_optimize.py +0 -0
  23. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/hash_minimizer.py +0 -0
  24. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/hash_srh_fbcbvb.py +0 -0
  25. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/binaryio.py +0 -0
  26. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/fastaio.py +0 -0
  27. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/generalio.py +0 -0
  28. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/hashio.py +0 -0
  29. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/seqio.py +0 -0
  30. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/textio.py +0 -0
  31. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/io/xorio.py +0 -0
  32. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/kmers.py +0 -0
  33. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/aligned_arrays.py +0 -0
  34. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/bitarray.py +0 -0
  35. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/conpro.py +0 -0
  36. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/debug.py +0 -0
  37. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/intbitarray.py +0 -0
  38. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/libc.py +0 -0
  39. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/numbautils.py +0 -0
  40. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/lowlevel/packedarray.py +0 -0
  41. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/mask.py +0 -0
  42. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/mathutils.py +0 -0
  43. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/parameters.py +0 -0
  44. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/subtable_hashfunctions.py +0 -0
  45. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/values/xenograft.py +0 -0
  46. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/config/index.yaml +0 -0
  47. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_info.py +0 -0
  48. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_load.py +0 -0
  49. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_remove.py +0 -0
  50. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort/xengsort/xengsort_weak.py +0 -0
  51. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/SOURCES.txt +0 -0
  52. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/dependency_links.txt +0 -0
  53. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/entry_points.txt +0 -0
  54. {xengsort-2.1.1 → xengsort-2.2.0}/xengsort.egg-info/requires.txt +0 -0
  55. {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.1.1
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
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "xengsort"
8
- version = "2.1.1"
8
+ version = "2.2.0"
9
9
 
10
10
  license = {file = "LICENSE"}
11
11
 
@@ -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
- bisulfite=False):
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 bisulfite:
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 bisulfite:
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 bisulfite:
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 prefetch_for_key(table, key):
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 a[i]"""
305
- return prefetch_address(array_ptr + (nbits * i) // 8)
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
- "prefetch_for_key",
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)
@@ -16,7 +16,7 @@ params_count:
16
16
  Mn_factor: 4
17
17
  Mn_min: 3
18
18
 
19
- params_bisulfite:
19
+ params_methylation:
20
20
  nothing: 0
21
21
  few: 6
22
22
  Ag: 3
@@ -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 == 'bisulfite':
355
- debugprint0("- Using count based classification mode for bisulfite sequencing data")
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
- bisulfite_paired = np.array(matrix, dtype=np.uint8)
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 get_bisulfite_classification(ht, sq, kcount):
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 == "bisulfite":
525
+ elif mode == "methylation":
508
526
  local_seq = np.empty(250, dtype=np.uint8)
509
- for i in range(n):
510
- seq_len = linemarks[i, 1] - linemarks[i, 0]
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
- counts[:] = 0
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
- get_bisulfite_classification(ht, local_seq[:seq_len], counts)
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
- # G -> A
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
- get_bisulfite_classification(ht, local_seq[:seq_len], counts)
526
- classification = classify(counts)
527
- classifications[i] = classification
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 == "bisulfite":
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
- max_seq_len = max(seq_len, seq_len1)
605
- if max_seq_len > local_seq.size:
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
- # R1
610
- counts[:] = 0
611
- # C -> T
612
- local_seq[:seq_len] = buf[linemarks[i, 0]:linemarks[i, 1]]
613
- quick_dna_to_2bits_ct(local_seq[:seq_len])
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
- # R2
622
- counts[:] = 0
623
- # C -> T
624
- local_seq[:seq_len1] = buf1[linemarks1[i, 0]:linemarks1[i, 1]]
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
- # G -> A
629
- local_seq[:seq_len1] = buf1[linemarks1[i, 0]:linemarks1[i, 1]]
630
- quick_dna_to_2bits_ga(local_seq[:seq_len1])
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
- classifications[i] = bisulfite_paired[r1_classification, r2_classification]
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
- streams = ((host1, host2), (graft1, graft2), (ambiguous1, ambiguous2), (both1, both2), (neither1, neither2))
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
- streams = (host, graft, ambiguous, both, neither)
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
- print("\n## Classification Statistics")
817
- print("\n```")
818
- print("prefix\thost\tgraft\tambiguous\tboth\tneither")
819
- print(f"{prefix}\t{str_counts}")
820
- print("```\n")
821
- print("```")
822
- print(f"| prefix | {prefix} ")
823
- for i in range(len(classes)):
824
- print(f"| {classes[i]:9s} | {stats[i]:{ndigits}d} | {percentages[i]:5.2f}% |")
825
- print("```")
826
- print()
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 'bisulfite' in appinfo:
866
- if appinfo['bisulfite']:
867
- debugprint0('- The provided index is build for WGBS analysis.\n The classification mode will be switched to bisulfite.')
868
- mode = 'bisulfite'
869
- if mode == 'bisulfite':
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_" + 'bisulfite']
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, bisulfite, *,
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, bisulfite=bisulfite)
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.bisulfite,
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.bisulfite,
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, bisulfite=args.bisulfite)
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("--params_bisulfite", type=dict, help=SUPPRESS)
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.1.1
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