[med-svn] [Git][med-team/salmid][master] 7 commits: New upstream version 0.122
Andreas Tille
gitlab at salsa.debian.org
Fri Dec 21 16:15:52 GMT 2018
Andreas Tille pushed to branch master at Debian Med / salmid
Commits:
f350e1fb by Andreas Tille at 2018-12-21T16:09:22Z
New upstream version 0.122
- - - - -
fcead665 by Andreas Tille at 2018-12-21T16:09:42Z
Update upstream source from tag 'upstream/0.122'
Update to upstream version '0.122'
with Debian dir 23c7e6099ec2fa53ccce105d0ea5d48977a8dc4f
- - - - -
89cf6d82 by Andreas Tille at 2018-12-21T16:09:42Z
New upstream version
- - - - -
0e76af94 by Andreas Tille at 2018-12-21T16:10:09Z
Upload to unstable
- - - - -
c5d0367f by Andreas Tille at 2018-12-21T16:13:50Z
Make sure the separating dot appears in upstream version
- - - - -
06240738 by Andreas Tille at 2018-12-21T16:14:47Z
New upstream version 0.1.23
- - - - -
01f8c384 by Andreas Tille at 2018-12-21T16:15:07Z
Update upstream source from tag 'upstream/0.1.23'
Update to upstream version '0.1.23'
with Debian dir e66054ff8ae2788a8cde19382ff1976a3a043f54
- - - - -
11 changed files:
- README.md
- − __pycache__/version.cpython-34.pyc
- debian/changelog
- debian/watch
- + pyproject.toml
- + salmid/__init__.py
- SalmID.py → salmid/core.py
- invA_mers_dict → salmid/invA_mers_dict
- rpoB_mers_dict → salmid/rpoB_mers_dict
- + salmid/version.py
- − version.py
Changes:
=====================================
README.md
=====================================
@@ -1,3 +1,5 @@
+[![DOI](https://zenodo.org/badge/97020646.svg)](https://zenodo.org/badge/latestdoi/97020646)
+
# SalmID
Rapid tool to check taxonomic ID of single isolate samples. Currently only IDs Salmonella species and subspecies, and some common contaminants (Listeria, Escherichia).
@@ -5,26 +7,34 @@ Rapid tool to check taxonomic ID of single isolate samples. Currently only IDs S
Python 3
## Installation:
-Clone git to your machine:
+The easy way with homebrew ([Linux](http://linuxbrew.sh/) or [MacOS](https://brew.sh/)):
```
-git clone --recursive https://github.com/hcdenbakker/SalmID.git
+brew install brewsci/bio/salmid
```
+Big thanks to [Torsten Seemann](https://tseemann.github.io/) for including this in homebrew!
-Make SalmID executable:
-```
-cd SalmID
-```
+Alternatively download from GitHub:
+```bash
+git clone https://github.com/hcdenbakker/SalmID.git
```
-chmod +x SalmID.py
+
+build a wheel using [poetry](https://poetry.eustace.io/):
+
+```bash
+cd SalmID
+poetry build
```
+and install using `pip`
-Add the SalmID folder to your path
+```bash
+pip install dist/salmid*.whl
+```
To execute:
```
-SalmID.py
+SalmID.py -e .fastq.gz
```
This will perform a SalmID run on all fastq.gz files in the current directory.
```
@@ -38,4 +48,4 @@ This will perform a SalmID run on all files in directory 'directory_with_data' w
## Todo's and thoughts for future releases:
- Provide coverage estimates for genomes in sample based on kmer frequencies
-- Write code to use SalmID on long read (minion, pacbio) platforms
\ No newline at end of file
+- Write code to use SalmID on long read (minion, pacbio) platforms
=====================================
__pycache__/version.cpython-34.pyc deleted
=====================================
Binary files a/__pycache__/version.cpython-34.pyc and /dev/null differ
=====================================
debian/changelog
=====================================
@@ -1,5 +1,5 @@
-salmid (0.12-1) UNRELEASED; urgency=medium
+salmid (0.1.23-1) UNRELEASED; urgency=medium
* Initial release (Closes: #<bug>)
- -- Andreas Tille <tille at debian.org> Mon, 03 Sep 2018 21:23:47 +0200
+ -- Andreas Tille <tille at debian.org> Fri, 21 Dec 2018 17:09:43 +0100
=====================================
debian/watch
=====================================
@@ -1,3 +1,5 @@
version=4
-https://github.com/hcdenbakker/SalmID/releases .*/archive/@ANY_VERSION@@ARCHIVE_EXT@
+# Upstream sometimes leaves out the separating '.'
+opts="uversionmangle=s/0.1([^.])/0.1.$1/" \
+ https://github.com/hcdenbakker/SalmID/releases .*/archive/@ANY_VERSION@@ARCHIVE_EXT@
=====================================
pyproject.toml
=====================================
@@ -0,0 +1,19 @@
+[tool.poetry]
+name = "salmid"
+version = "0.1.23"
+description = "Rapid tool to check taxonomic ID of single isolate samples. Currently only IDs Salmonella species and subspecies, and some common contaminants (Listeria, Escherichia)."
+authors = ["Henk den Bakker <hcd82599 at uga.edu>"]
+license = "MIT"
+include = [ 'salmid/invA_mers_dict', 'salmid/rpoB_mers_dict' ]
+
+[tool.poetry.dependencies]
+python = "^3.5"
+
+[tool.poetry.dev-dependencies]
+
+[tool.poetry.scripts]
+'SalmID.py' = 'salmid.core:main'
+
+[build-system]
+requires = ["poetry>=0.12"]
+build-backend = "poetry.masonry.api"
=====================================
salmid/__init__.py
=====================================
=====================================
SalmID.py → salmid/core.py
=====================================
@@ -9,12 +9,13 @@ import sys
from argparse import ArgumentParser
try:
- from version import SalmID_version
-except:
+ from .version import SalmID_version
+except ImportError:
SalmID_version = "version unknown"
def reverse_complement(sequence):
+ """return the reverse complement of a nucleotide (including IUPAC ambiguous nuceotide codes)"""
complement = {'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A', 'N': 'N', 'M': 'K', 'R': 'Y', 'W': 'W',
'S': 'S', 'Y': 'R', 'K': 'M', 'V': 'B', 'H': 'D', 'D': 'H', 'B': 'V'}
return "".join(complement[base] for base in reversed(sequence))
@@ -24,63 +25,74 @@ def parse_args():
"Parse the input arguments, use '-h' for help."
parser = ArgumentParser(description='SalmID - rapid Kmer based Salmonella identifier from sequence data')
# inputs
- parser.add_argument('-v','--version', action='version', version='%(prog)s ' + SalmID_version)
+ parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + SalmID_version)
parser.add_argument(
- '-i','--input_file', type=str, required=False, default= 'None', metavar = 'your_fastqgz',
+ '-i', '--input_file', type=str, required=False, default='None', metavar='your_fastqgz',
help='Single fastq.gz file input, include path to file if file is not in same directory ')
parser.add_argument(
- '-e', '--extension', type=str, required=False, default= '.fastq.gz', metavar = 'file_extension',
+ '-e', '--extension', type=str, required=False, default='.fastq.gz', metavar='file_extension',
help='File extension, if specified without "--input_dir", SalmID will attempt to ID all files\n' +
' with this extension in current directory, otherwise files in input directory')
parser.add_argument(
- '-d','--input_dir', type=str, required=False, default='.', metavar = 'directory',
+ '-d', '--input_dir', type=str, required=False, default='.', metavar='directory',
help='Directory which contains data for identification, when not specified files in current directory will be analyzed.')
parser.add_argument(
- '-r', '--report', type=str, required=False, default='percentage', metavar = 'percentage, coverage or taxonomy',
+ '-r', '--report', type=str, required=False, default='percentage', metavar='percentage, coverage or taxonomy',
help='Report either percentage ("percentage") of clade specific kmers recovered, average kmer-coverage ("cov"), or '
'taxonomy (taxonomic species ID, plus observed mean k-mer coverages and expected coverage).')
parser.add_argument(
- '-m', '--mode', type=str, required=False, default='quick', metavar = 'quick or thorough',
+ '-m', '--mode', type=str, required=False, default='quick', metavar='quick or thorough',
help='Quick [quick] or thorough [thorough] mode')
- if len(sys.argv)==1:
+ if len(sys.argv) == 1:
parser.print_help(sys.stderr)
sys.exit(1)
return parser.parse_args()
+
def get_av_read_length(file):
+ """Samples the first 100 reads from a fastq file and return the average read length."""
i = 1
n_reads = 0
total_length = 0
if file.endswith(".gz"):
- file_content=io.BufferedReader(gzip.open(file))
+ file_content = io.BufferedReader(gzip.open(file))
else:
- file_content=open(file,"r").readlines()
+ file_content = open(file, "r").readlines()
for line in file_content:
if i % 4 == 2:
total_length += len(line.strip())
- n_reads +=1
+ n_reads += 1
i += 1
if n_reads == 100:
break
- return total_length/100
+ return total_length / 100
def createKmerDict_reads(list_of_strings, kmer):
+ """Count occurence of K-mers in a list of strings
+
+ Args:
+ list_of_strings(list of str): nucleotide sequences as a list of strings
+ kmer(int): length of the K-mer to count
+
+ Returns:
+ dict: dictionary with kmers as keys, counts for each kmer as values"""
kmer_table = {}
for string in list_of_strings:
sequence = string.strip('\n')
- for i in range(len(sequence)-kmer+1):
- new_mer =sequence[i:i+kmer]
- new_mer_rc = reverse_complement(new_mer)
- if new_mer in kmer_table:
- kmer_table[new_mer.upper()] += 1
- else:
- kmer_table[new_mer.upper()] = 1
- if new_mer_rc in kmer_table:
- kmer_table[new_mer_rc.upper()] += 1
- else:
- kmer_table[new_mer_rc.upper()] = 1
+ if len(sequence) >= kmer:
+ for i in range(len(sequence) - kmer + 1):
+ new_mer = sequence[i:i + kmer]
+ new_mer_rc = reverse_complement(new_mer)
+ if new_mer in kmer_table:
+ kmer_table[new_mer.upper()] += 1
+ else:
+ kmer_table[new_mer.upper()] = 1
+ if new_mer_rc in kmer_table:
+ kmer_table[new_mer_rc.upper()] += 1
+ else:
+ kmer_table[new_mer_rc.upper()] = 1
return kmer_table
@@ -96,16 +108,16 @@ def target_read_kmerizer_multi(file, k, kmerDict_1, kmerDict_2, mode):
reads_2 = []
total_reads = 0
if file.endswith(".gz"):
- file_content=io.BufferedReader(gzip.open(file))
+ file_content = io.BufferedReader(gzip.open(file))
else:
- file_content=open(file,"r").readlines()
+ file_content = open(file, "r").readlines()
for line in file_content:
start = int((len(line) - k) // 2)
if i % 4 == 2:
total_reads += 1
if file.endswith(".gz"):
s1 = line[start:k + start].decode()
- line=line.decode()
+ line = line.decode()
else:
s1 = line[start:k + start]
if s1 in kmerDict_1:
@@ -126,15 +138,16 @@ def target_read_kmerizer_multi(file, k, kmerDict_1, kmerDict_2, mode):
else:
kmer_Dict1 = createKmerDict_reads(reads_1, k)
mers_1 = set([key for key in kmer_Dict1])
- mean_1 = sum([kmer_Dict1[key] for key in kmer_Dict1])/len(mers_1)
+ mean_1 = sum([kmer_Dict1[key] for key in kmer_Dict1]) / len(mers_1)
if len(reads_2) == 0:
kmer_Dict2 = {}
else:
kmer_Dict2 = createKmerDict_reads(reads_2, k)
mers_2 = set([key for key in kmer_Dict2])
- mean_2 = sum([kmer_Dict2[key] for key in kmer_Dict2])/len(mers_2)
+ mean_2 = sum([kmer_Dict2[key] for key in kmer_Dict2]) / len(mers_2)
return kmer_Dict1, kmer_Dict2, mean_1, mean_2, total_reads
+
def mean_cov_selected_kmers(iterable, kmer_dict, clade_specific_kmers):
'''
Given an iterable (list, set, dictrionary) returns mean coverage for the kmers in iterable
@@ -145,10 +158,11 @@ def mean_cov_selected_kmers(iterable, kmer_dict, clade_specific_kmers):
'''
if len(iterable) == 0:
return 0
- return sum([kmer_dict[value] for value in iterable])/len(clade_specific_kmers)
+ return sum([kmer_dict[value] for value in iterable]) / len(clade_specific_kmers)
+
def kmer_lists(query_fastq_gz, k,
- allmers,allmers_rpoB,
+ allmers, allmers_rpoB,
uniqmers_bongori,
uniqmers_I,
uniqmers_IIa,
@@ -165,8 +179,8 @@ def kmer_lists(query_fastq_gz, k,
uniqmers_Listeria_ss_rpoB,
uniqmers_Lmono_rpoB,
mode):
- dict_invA, dict_rpoB, mean_invA, mean_rpoB , total_reads = target_read_kmerizer_multi(query_fastq_gz, k, allmers,
- allmers_rpoB, mode)
+ dict_invA, dict_rpoB, mean_invA, mean_rpoB, total_reads = target_read_kmerizer_multi(query_fastq_gz, k, allmers,
+ allmers_rpoB, mode)
target_mers_invA = set([key for key in dict_invA])
target_mers_rpoB = set([key for key in dict_rpoB])
if target_mers_invA == 0:
@@ -211,17 +225,18 @@ def kmer_lists(query_fastq_gz, k,
S_enterica_rpoB_cov, bongori_invA_cov, I_invA_cov, IIa_invA_cov, IIb_invA_cov,
IIIa_invA_cov, IIIb_invA_cov, IV_invA_cov, VI_invA_cov, VII_invA_cov, VIII_invA_cov]
locus_scores = [p_Listeria_ss, p_Lmono, p_Escherichia, p_bongori_rpoB, p_Senterica, p_bongori,
- p_I, p_IIa,p_IIb, p_IIIa, p_IIIb, p_IV, p_VI, p_VII, p_VIII]
+ p_I, p_IIa, p_IIb, p_IIIa, p_IIIb, p_IV, p_VI, p_VII, p_VIII]
return locus_scores, coverages, total_reads
+
def report_taxon(locus_covs, average_read_length, number_of_reads):
- list_taxa = [ 'Listeria ss', 'Listeria monocytogenes', 'Escherichia sp.',
+ list_taxa = [ 'Listeria ss', 'Listeria monocytogenes', 'Escherichia sp.', # noqa: E201
'Salmonella bongori (rpoB)', 'Salmonella enterica (rpoB)',
'Salmonella bongori (invA)', 'S. enterica subsp. enterica (invA)',
- 'S. enterica subsp. salamae (invA: clade a)','S. enterica subsp. salamae (invA: clade b)',
+ 'S. enterica subsp. salamae (invA: clade a)', 'S. enterica subsp. salamae (invA: clade b)',
'S. enterica subsp. arizonae (invA)', 'S. enterica subsp. diarizonae (invA)',
'S. enterica subsp. houtenae (invA)', 'S. enterica subsp. indica (invA)',
- 'S. enterica subsp. VII (invA)', 'S. enterica subsp. salamae (invA: clade VIII)']
+ 'S. enterica subsp. VII (invA)', 'S. enterica subsp. salamae (invA: clade VIII)' ] # noqa: E202
if sum(locus_covs) < 1:
rpoB = ('No rpoB matches!', 0)
invA = ('No invA matches!', 0)
@@ -229,18 +244,18 @@ def report_taxon(locus_covs, average_read_length, number_of_reads):
else:
# given list of scores get taxon
if sum(locus_covs[0:5]) > 0:
- best_rpoB = max(range(len(locus_covs[1:5])), key=lambda x: locus_covs[1:5][x])+1
+ best_rpoB = max(range(len(locus_covs[1:5])), key=lambda x: locus_covs[1:5][x]) + 1
all_rpoB = max(range(len(locus_covs[0:5])), key=lambda x: locus_covs[0:5][x])
if (locus_covs[best_rpoB] != 0) & (all_rpoB == 0):
rpoB = (list_taxa[best_rpoB], locus_covs[best_rpoB])
- elif (all_rpoB == 0) & (round(sum(locus_covs[1:5]),1) < 1):
+ elif (all_rpoB == 0) & (round(sum(locus_covs[1:5]), 1) < 1):
rpoB = (list_taxa[0], locus_covs[0])
else:
rpoB = (list_taxa[best_rpoB], locus_covs[best_rpoB])
else:
rpoB = ('No rpoB matches!', 0)
if sum(locus_covs[5:]) > 0:
- best_invA = max(range(len(locus_covs[5:])), key=lambda x: locus_covs[5:][x])+5
+ best_invA = max(range(len(locus_covs[5:])), key=lambda x: locus_covs[5:][x]) + 5
invA = (list_taxa[best_invA], locus_covs[best_invA])
else:
invA = ('No invA matches!', 0)
@@ -250,7 +265,6 @@ def report_taxon(locus_covs, average_read_length, number_of_reads):
return rpoB, invA, (average_read_length * number_of_reads) / 5000000
-
def main():
ex_dir = os.path.dirname(os.path.realpath(__file__))
args = parse_args()
@@ -260,7 +274,7 @@ def main():
else:
extension = args.extension
inputdir = args.input_dir
- files = [inputdir + '/'+ f for f in os.listdir(inputdir) if f.endswith(extension)]
+ files = [inputdir + '/' + f for f in os.listdir(inputdir) if f.endswith(extension)]
report = args.report
mode = args.mode
f_invA = open(ex_dir + "/invA_mers_dict", "rb")
@@ -288,7 +302,7 @@ def main():
uniqmers_Escherichia_rpoB = sets_dict['uniqmers_Escherichia']
uniqmers_Listeria_ss_rpoB = sets_dict['uniqmers_Listeria_ss']
uniqmers_Lmono_rpoB = sets_dict['uniqmers_L_mono']
- #todo: run kmer_lists() once, create list of tuples containing data to be used fro different reports
+ # todo: run kmer_lists() once, create list of tuples containing data to be used fro different reports
if report == 'taxonomy':
print('file\trpoB\tinvA\texpected coverage')
for f in files:
@@ -317,55 +331,56 @@ def main():
'\t' + str(round(report[2], 1)))
else:
print(
- 'file\tListeria sensu stricto (rpoB)\tL. monocytogenes (rpoB)\tEscherichia spp. (rpoB)\tS. bongori (rpoB)\tS. enterica' +
- '(rpoB)\tS. bongori (invA)\tsubsp. I (invA)\tsubsp. II (clade a: invA)\tsubsp. II' +
- ' (clade b: invA)\tsubsp. IIIa (invA)\tsubsp. IIIb (invA)\tsubsp.IV (invA)\tsubsp. VI (invA)\tsubsp. VII (invA)' +
+ 'file\tListeria sensu stricto (rpoB)\tL. monocytogenes (rpoB)\tEscherichia spp. (rpoB)\tS. bongori (rpoB)\tS. enterica' + # noqa: E122
+ '(rpoB)\tS. bongori (invA)\tsubsp. I (invA)\tsubsp. II (clade a: invA)\tsubsp. II' + # noqa: E122
+ ' (clade b: invA)\tsubsp. IIIa (invA)\tsubsp. IIIb (invA)\tsubsp.IV (invA)\tsubsp. VI (invA)\tsubsp. VII (invA)' + # noqa: E122
'\tsubsp. II (clade VIII : invA)')
if report == 'percentage':
for f in files:
- locus_scores, coverages , reads = kmer_lists( f, 27,
- allmers,allmers_rpoB,
- uniqmers_bongori,
- uniqmers_I,
- uniqmers_IIa,
- uniqmers_IIb,
- uniqmers_IIIa,
- uniqmers_IIIb,
- uniqmers_IV,
- uniqmers_VI,
- uniqmers_VII,
- uniqmers_VIII,
- uniqmers_bongori_rpoB,
- uniqmers_S_enterica_rpoB,
- uniqmers_Escherichia_rpoB,
- uniqmers_Listeria_ss_rpoB,
- uniqmers_Lmono_rpoB,
- mode)
+ locus_scores, coverages, reads = kmer_lists(f, 27,
+ allmers, allmers_rpoB,
+ uniqmers_bongori,
+ uniqmers_I,
+ uniqmers_IIa,
+ uniqmers_IIb,
+ uniqmers_IIIa,
+ uniqmers_IIIb,
+ uniqmers_IV,
+ uniqmers_VI,
+ uniqmers_VII,
+ uniqmers_VIII,
+ uniqmers_bongori_rpoB,
+ uniqmers_S_enterica_rpoB,
+ uniqmers_Escherichia_rpoB,
+ uniqmers_Listeria_ss_rpoB,
+ uniqmers_Lmono_rpoB,
+ mode)
pretty_scores = [str(round(score)) for score in locus_scores]
- print(f.split('/')[-1] +'\t' + '\t'.join(pretty_scores))
+ print(f.split('/')[-1] + '\t' + '\t'.join(pretty_scores))
else:
for f in files:
- locus_scores, coverages , reads = kmer_lists( f, 27,
- allmers,allmers_rpoB,
- uniqmers_bongori,
- uniqmers_I,
- uniqmers_IIa,
- uniqmers_IIb,
- uniqmers_IIIa,
- uniqmers_IIIb,
- uniqmers_IV,
- uniqmers_VI,
- uniqmers_VII,
- uniqmers_VIII,
- uniqmers_bongori_rpoB,
- uniqmers_S_enterica_rpoB,
- uniqmers_Escherichia_rpoB,
- uniqmers_Listeria_ss_rpoB,
- uniqmers_Lmono_rpoB,
- mode)
+ locus_scores, coverages, reads = kmer_lists(f, 27,
+ allmers, allmers_rpoB,
+ uniqmers_bongori,
+ uniqmers_I,
+ uniqmers_IIa,
+ uniqmers_IIb,
+ uniqmers_IIIa,
+ uniqmers_IIIb,
+ uniqmers_IV,
+ uniqmers_VI,
+ uniqmers_VII,
+ uniqmers_VIII,
+ uniqmers_bongori_rpoB,
+ uniqmers_S_enterica_rpoB,
+ uniqmers_Escherichia_rpoB,
+ uniqmers_Listeria_ss_rpoB,
+ uniqmers_Lmono_rpoB,
+ mode)
pretty_covs = [str(round(cov, 1)) for cov in coverages]
print(f.split('/')[-1] + '\t' + '\t'.join(pretty_covs))
+
if __name__ == '__main__':
main()
=====================================
invA_mers_dict → salmid/invA_mers_dict
=====================================
=====================================
rpoB_mers_dict → salmid/rpoB_mers_dict
=====================================
=====================================
salmid/version.py
=====================================
@@ -0,0 +1 @@
+SalmID_version = '0.1.23'
=====================================
version.py deleted
=====================================
@@ -1 +0,0 @@
-SalmID_version = '0.12'
View it on GitLab: https://salsa.debian.org/med-team/salmid/compare/879befa3a162df02b94523e2f1a65a665b11af9d...01f8c384eb8d8656455c5891542a4bc3091847ba
--
View it on GitLab: https://salsa.debian.org/med-team/salmid/compare/879befa3a162df02b94523e2f1a65a665b11af9d...01f8c384eb8d8656455c5891542a4bc3091847ba
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20181221/b762ccc3/attachment-0001.html>
More information about the debian-med-commit
mailing list