diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 33f0491..28e43d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: true matrix: - python-version: [3.7, 3.8, 3.9] + python-version: ["3.9", "3.10", "3.11", "3.12"] PIP_FLAGS: [""] MINIMUM_REQUIREMENTS: [0] include: @@ -17,11 +17,6 @@ jobs: python-version: 3.7 MINIMUM_REQUIREMENTS: 1 OPTIONS_NAME: "min" - - platform_id: manylinux_x86_64 - python-version: 3.9 - PIP_FLAGS: "--pre" - # test pre-releases - OPTIONS_NAME: "pre" steps: - name: Checkout 🛎️ diff --git a/MANIFEST.in b/MANIFEST.in index bfe5ffd..7e0019a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ recursive-include iced *.c *.h *.py -recursive-include *.matrix *.bed +recursive-include iced *.matrix *.bed *.mat include pyproject.toml diff --git a/iced/normalization/_normalization_.pyx b/iced/normalization/_normalization_.pyx index 2d22ecf..da6c556 100644 --- a/iced/normalization/_normalization_.pyx +++ b/iced/normalization/_normalization_.pyx @@ -1,4 +1,4 @@ -import numpy as np +#import numpy as np cimport cython cimport numpy as np diff --git a/iced/scripts/ice b/iced/scripts/ice deleted file mode 100755 index b7297b8..0000000 --- a/iced/scripts/ice +++ /dev/null @@ -1,127 +0,0 @@ -#! /usr/bin/env python -from __future__ import print_function -import sys -import argparse -import numpy as np -from scipy import sparse - -import iced -from iced.io import load_counts, savetxt, write_counts - - -parser = argparse.ArgumentParser("ICE normalization") -parser.add_argument('filename', - metavar='File to load', - type=str, - help='Path to file of contact counts to load') -parser.add_argument("--results_filename", - "-r", - type=str, - default=None, - help="results_filename") -parser.add_argument("--filtering_perc", "-f", - type=float, - default=None, - help="Percentage of reads to filter out") -parser.add_argument("--filter_low_counts_perc", - type=float, - default=0.02, - help="Percentage of reads to filter out") -parser.add_argument("--filter_high_counts_perc", - type=float, - default=0, - help="Percentage of reads to filter out") -parser.add_argument("--remove-all-zeros-loci", default=False, - action="store_true", - help="If provided, all non-interacting loci will be " - "removed prior to the filtering strategy chosen.") -parser.add_argument("--max_iter", "-m", default=100, type=int, - help="Maximum number of iterations") -parser.add_argument("--eps", "-e", default=0.1, type=float, - help="Precision") -parser.add_argument("--dense", "-d", default=False, action="store_true") -parser.add_argument("--output-bias", "-b", default=False, help="Output the bias vector") -parser.add_argument("--verbose", "-v", default=False, type=bool) -parser.add_argument("--base", default=None, type=int, - help="Indicates whether the matrix file is 0 or 1-based") - - -args = parser.parse_args() -filename = args.filename - -# Deprecating filtering_perc option -filter_low_counts = None -if "--filtering_perc" in sys.argv: - DeprecationWarning( - "Option '--filtering_perc' is deprecated. Please use " - "'--filter_low_counts_perc' instead.'") - # And print it again because deprecation warnings are not displayed for - # recent versions of python - print("--filtering_perc is deprecated. Please use filter_low_counts_perc") - print("instead. This option will be removed in ice 0.3") - filter_low_counts = args.filtering_perc -if "--filter_low_counts_perc" in sys.argv and "--filtering_perc" in sys.argv: - raise Warning("This two options are incompatible") -if "--filtering_perc" is None and "--filter_low_counts_perc" not in sys.argv: - filter_low_counts_perc = 0.02 -elif args.filter_low_counts_perc is not None: - filter_low_counts_perc = args.filter_low_counts_perc - -if args.base is None: - base = 1 - print("Assuming the file is 1-based. If this is not the desired option, " - "set option --base to 0") -else: - base = args.base - -if args.verbose: - print("Using iced version %s" % iced.__version__) - print("Loading files...") - -# Loads file as i, j, counts -counts = load_counts(filename, base=base) - - -if args.dense: - counts = np.array(counts.todense()) -else: - counts = sparse.csr_matrix(counts) - -if args.verbose: - print("Normalizing...") - -if filter_low_counts_perc != 0: - counts = iced.filter.filter_low_counts(counts, - percentage=filter_low_counts_perc, - remove_all_zeros_loci=args.remove_all_zeros_loci, - copy=False, sparsity=False, verbose=args.verbose) -if args.filter_high_counts_perc != 0: - counts = iced.filter.filter_high_counts( - counts, - percentage=args.filter_high_counts_perc, - copy=False) - -counts, bias = iced.normalization.ICE_normalization( - counts, max_iter=args.max_iter, copy=False, - verbose=args.verbose, eps=args.eps, output_bias=True) - -if args.results_filename is None: - results_filename = ".".join( - filename.split(".")[:-1]) + "_normalized." + filename.split(".")[-1] -else: - results_filename = args.results_filename - -counts = sparse.coo_matrix(counts) - -if args.verbose: - print("Writing results...") - -#write_counts(results_filename, counts) -write_counts( - results_filename, - counts, base=base) - - - -if args.output_bias: - np.savetxt(results_filename + ".biases", bias) diff --git a/iced/scripts/ice.py b/iced/scripts/ice.py new file mode 100755 index 0000000..1b6d0c9 --- /dev/null +++ b/iced/scripts/ice.py @@ -0,0 +1,127 @@ +#! /usr/bin/env python +from __future__ import print_function +import sys +import argparse +import numpy as np +from scipy import sparse + +import iced +from iced.io import load_counts, savetxt, write_counts + +def main(): + parser = argparse.ArgumentParser("ICE normalization") + parser.add_argument('filename', + metavar='File to load', + type=str, + help='Path to file of contact counts to load') + parser.add_argument("--results_filename", + "-r", + type=str, + default=None, + help="results_filename") + parser.add_argument("--filtering_perc", "-f", + type=float, + default=None, + help="Percentage of reads to filter out") + parser.add_argument("--filter_low_counts_perc", + type=float, + default=0.02, + help="Percentage of reads to filter out") + parser.add_argument("--filter_high_counts_perc", + type=float, + default=0, + help="Percentage of reads to filter out") + parser.add_argument("--remove-all-zeros-loci", default=False, + action="store_true", + help="If provided, all non-interacting loci will be " + "removed prior to the filtering strategy chosen.") + parser.add_argument("--max_iter", "-m", default=100, type=int, + help="Maximum number of iterations") + parser.add_argument("--eps", "-e", default=0.1, type=float, + help="Precision") + parser.add_argument("--dense", "-d", default=False, action="store_true") + parser.add_argument("--output-bias", "-b", default=False, help="Output the bias vector") + parser.add_argument("--verbose", "-v", default=False, type=bool) + parser.add_argument("--base", default=None, type=int, + help="Indicates whether the matrix file is 0 or 1-based") + + + args = parser.parse_args() + filename = args.filename + + # Deprecating filtering_perc option + filter_low_counts = None + if "--filtering_perc" in sys.argv: + DeprecationWarning( + "Option '--filtering_perc' is deprecated. Please use " + "'--filter_low_counts_perc' instead.'") + # And print it again because deprecation warnings are not displayed for + # recent versions of python + print("--filtering_perc is deprecated. Please use filter_low_counts_perc") + print("instead. This option will be removed in ice 0.3") + filter_low_counts = args.filtering_perc + if "--filter_low_counts_perc" in sys.argv and "--filtering_perc" in sys.argv: + raise Warning("This two options are incompatible") + if "--filtering_perc" is None and "--filter_low_counts_perc" not in sys.argv: + filter_low_counts_perc = 0.02 + elif args.filter_low_counts_perc is not None: + filter_low_counts_perc = args.filter_low_counts_perc + + if args.base is None: + base = 1 + print("Assuming the file is 1-based. If this is not the desired option, " + "set option --base to 0") + else: + base = args.base + + if args.verbose: + print("Using iced version %s" % iced.__version__) + print("Loading files...") + + # Loads file as i, j, counts + counts = load_counts(filename, base=base) + + + if args.dense: + counts = np.array(counts.todense()) + else: + counts = sparse.csr_matrix(counts) + + if args.verbose: + print("Normalizing...") + + if filter_low_counts_perc != 0: + counts = iced.filter.filter_low_counts(counts, + percentage=filter_low_counts_perc, + remove_all_zeros_loci=args.remove_all_zeros_loci, + copy=False, sparsity=False, verbose=args.verbose) + if args.filter_high_counts_perc != 0: + counts = iced.filter.filter_high_counts( + counts, + percentage=args.filter_high_counts_perc, + copy=False) + + counts, bias = iced.normalization.ICE_normalization( + counts, max_iter=args.max_iter, copy=False, + verbose=args.verbose, eps=args.eps, output_bias=True) + + if args.results_filename is None: + results_filename = ".".join( + filename.split(".")[:-1]) + "_normalized." + filename.split(".")[-1] + else: + results_filename = args.results_filename + + counts = sparse.coo_matrix(counts) + + if args.verbose: + print("Writing results...") + + #write_counts(results_filename, counts) + write_counts( + results_filename, + counts, base=base) + + + + if args.output_bias: + np.savetxt(results_filename + ".biases", bias) diff --git a/setup.py b/setup.py index 386ff89..0bc1164 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,7 @@ import os import sys +from setuptools import Extension, setup, find_packages +import numpy as np DISTNAME = 'iced' @@ -7,106 +9,49 @@ MAINTAINER = 'Nelle Varoquaux' MAINTAINER_EMAIL = 'nelle.varoquaux@gmail.com' VERSION = "0.6.0a0.dev0" +LICENSE = "BSD" SCIPY_MIN_VERSION = '0.19.0' NUMPY_MIN_VERSION = '1.16.0' +extension_config = { + "_filter": [ + {"sources": ["_filter.pyx"]} + ], + "normalization": [ + {"sources": ["_normalization.pyx"]} + ] +} + + +setup( + name=DISTNAME, + version=VERSION, + author=MAINTAINER, + author_email=MAINTAINER_EMAIL, + description=DESCRIPTION, + license=LICENSE, + classifiers=[ + "Development Status :: 3 - Alpha", + "Topic :: Utilities", + "License :: OSI Approved :: BSD License", + ], + packages=find_packages(where="."), + ext_modules=[ + Extension(name="iced._filter_", + sources=["iced/_filter_.pyx"], + include_dirs=[np.get_include()] + ), + Extension(name="iced.normalization/_normalization_", + sources=["iced/normalization/_normalization_.pyx"], + include_dirs=[np.get_include()] + )], + include_package_data=True, + entry_points={ + 'console_scripts': [ + 'ice = iced.scripts.ice:main', + ] + } +) -# Optional setuptools features -# We need to import setuptools early, if we want setuptools features, -# as it monkey-patches the 'setup' function -# For some commands, use setuptools -SETUPTOOLS_COMMANDS = set([ - 'develop', 'release', 'bdist_egg', 'bdist_rpm', - 'bdist_wininst', 'install_egg_info', 'build_sphinx', - 'egg_info', 'easy_install', 'upload', 'bdist_wheel', - '--single-version-externally-managed', -]) - -if SETUPTOOLS_COMMANDS.intersection(sys.argv): - import setuptools - - extra_setuptools_args = dict( - zip_safe=False, # the package can run out of an .egg file - include_package_data=True, - extras_require={ - 'alldeps': ( - 'numpy >= {0}'.format(NUMPY_MIN_VERSION), - 'scipy >= {0}'.format(SCIPY_MIN_VERSION), - ), - }, - ) -else: - extra_setuptools_args = dict() - - -def configuration(parent_package='', top_path=None): - if os.path.exists('MANIFEST'): - os.remove('MANIFEST') - - from numpy.distutils.misc_util import Configuration - - config = Configuration(None, parent_package, top_path) - # Avoid non-useful msg: - # "Ignoring attempt to set 'name' (from ... " - config.set_options(ignore_setup_xxx_py=True, - assume_default_configuration=True, - delegate_options_to_subpackages=True, - quiet=True) - - config.add_subpackage('iced') - - return config - - -def setup_package(): - metadata = dict( - configuration=configuration, - name=DISTNAME, - maintainer=MAINTAINER, - maintainer_email=MAINTAINER_EMAIL, - description=DESCRIPTION, - version=VERSION, - scripts=['iced/scripts/ice'], - classifiers=[ - 'Intended Audience :: Science/Research', - 'Intended Audience :: Developers', - 'License :: OSI Approved', - 'Programming Language :: C', - 'Programming Language :: Python', - 'Topic :: Software Development', - 'Topic :: Scientific/Engineering', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: Unix', - 'Operating System :: MacOS'], - **extra_setuptools_args) - - if len(sys.argv) == 1 or ( - len(sys.argv) >= 2 and ('--help' in sys.argv[1:] or - sys.argv[1] in ('--help-commands', - 'egg_info', - '--version', - 'clean'))): - # For these actions, NumPy is not required - # - # They are required to succeed without Numpy for example when - # pip is used to install Scikit-learn when Numpy is not yet present in - # the system. - try: - from setuptools import setup - except ImportError: - from distutils.core import setup - - metadata['version'] = VERSION - else: - from numpy.distutils.core import setup - - metadata['configuration'] = configuration - - setup(**metadata) - - -if __name__ == "__main__": - setup_package()