diff --git a/.coveragerc b/.coveragerc index 3846718..dd3297e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -9,4 +9,5 @@ exclude_lines = raise NotImplementedError if __name__ == .__main__.: ignore_errors = True -omit = \ No newline at end of file +omit = + odte/__init__.py \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..671cd05 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,56 @@ +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '16 17 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8dc9ca2..6c1a964 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, ubuntu-latest] + os: [macos-latest, ubuntu-latest, windows-latest] python: [3.8] steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2a87625..6398008 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: hooks: - id: black exclude: ".virtual_documents" - language_version: python3.8 + language_version: python3.9 - repo: https://gitlab.com/pycqa/flake8 rev: 3.8.4 hooks: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..72646ac --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +SHELL := /bin/bash +.DEFAULT_GOAL := help +.PHONY: coverage deps help lint push test doc build + +coverage: ## Run tests with coverage + coverage erase + coverage run -m unittest -v odte.tests + coverage report -m + +deps: ## Install dependencies + pip install -r requirements.txt + +lint: ## Lint and static-check + black stree + flake8 stree + mypy stree + +push: ## Push code with tags + git push && git push --tags + +test: ## Run tests + python -m unittest -v odte.tests + +doc: ## Update documentation + make -C docs --makefile=Makefile html + +build: ## Build package + rm -fr dist/* + rm -fr build/* + python setup.py sdist bdist_wheel + +doc-clean: ## Update documentation + make -C docs --makefile=Makefile clean + +help: ## Show help message + @IFS=$$'\n' ; \ + help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##/:/'`); \ + printf "%s\n\n" "Usage: make [task]"; \ + printf "%-20s %s\n" "task" "help" ; \ + printf "%-20s %s\n" "------" "----" ; \ + for help_line in $${help_lines[@]}; do \ + IFS=$$':' ; \ + help_split=($$help_line) ; \ + help_command=`echo $${help_split[0]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \ + help_info=`echo $${help_split[2]} | sed -e 's/^ *//' -e 's/ *$$//'` ; \ + printf '\033[36m'; \ + printf "%-20s %s" $$help_command ; \ + printf '\033[0m'; \ + printf "%s\n" $$help_info; \ + done diff --git a/README.md b/README.md index 2a09a71..98b75cb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ ![CI](https://github.com/Doctorado-ML/Odte/workflows/CI/badge.svg) [![codecov](https://codecov.io/gh/Doctorado-ML/odte/branch/master/graph/badge.svg)](https://codecov.io/gh/Doctorado-ML/odte) -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c85f935ac6a0482ab67d3ebed4611459)](https://www.codacy.com/gh/Doctorado-ML/Odte?utm_source=github.com&utm_medium=referral&utm_content=Doctorado-ML/Odte&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f4b5ef87584b4095b6e49aefbe594c82)](https://www.codacy.com/gh/Doctorado-ML/Odte/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Doctorado-ML/Odte&utm_campaign=Badge_Grade) +[![PyPI version](https://badge.fury.io/py/Odte.svg)](https://badge.fury.io/py/Odte) +![https://img.shields.io/badge/python-3.8%2B-blue](https://img.shields.io/badge/python-3.8%2B-brightgreen) +doi # Odte diff --git a/codecov.yml b/codecov.yml index 222249f..38df3f6 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,12 +1,12 @@ -overage: +coverage: status: project: default: - target: 90% + target: 100% comment: layout: "reach, diff, flags, files" behavior: default - require_changes: false + require_changes: false require_base: yes - require_head: yes - branches: null \ No newline at end of file + require_head: yes + branches: null diff --git a/odte/__init__.py b/odte/__init__.py index 660abd7..1389a2c 100644 --- a/odte/__init__.py +++ b/odte/__init__.py @@ -1,3 +1,10 @@ from .Odte import Odte +__version__ = "0.3.0" + +__author__ = "Ricardo Montañana Gómez" +__copyright__ = "Copyright 2020-2021, Ricardo Montañana Gómez" +__license__ = "MIT License" +__author_email__ = "ricardo.montanana@alu.uclm.es" + __all__ = ["Odte"] diff --git a/odte/tests/Odte_tests.py b/odte/tests/Odte_tests.py index e0309f5..267172e 100644 --- a/odte/tests/Odte_tests.py +++ b/odte/tests/Odte_tests.py @@ -31,13 +31,13 @@ class Odte_test(unittest.TestCase): def test_initialize_max_feature(self): expected_values = [ - [6, 7, 8, 15], - [3, 4, 5, 6, 10, 13], + [4, 7, 12, 14], + [2, 4, 6, 7, 12, 14], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [6, 7, 8, 15], - [6, 7, 8, 15], - [6, 7, 8, 15], + [4, 7, 12, 14], + [4, 7, 12, 14], + [4, 7, 12, 14], ] X, y = load_dataset( random_state=self._random_state, n_features=16, n_samples=10 @@ -122,7 +122,7 @@ class Odte_test(unittest.TestCase): def test_score(self): X, y = load_dataset(self._random_state) - expected = 0.9526666666666667 + expected = 0.9513333333333334 tclf = Odte( random_state=self._random_state, max_features=None, @@ -132,24 +132,40 @@ class Odte_test(unittest.TestCase): self.assertAlmostEqual(expected, computed) def test_score_splitter_max_features(self): - X, y = load_dataset(self._random_state, n_features=12, n_samples=150) + X, y = load_dataset(self._random_state, n_features=16, n_samples=500) results = [ - 0.86, - 0.8933333333333333, - 0.9933333333333333, - 0.9933333333333333, + 0.948, + 0.924, + 0.926, + 0.94, + 0.932, + 0.936, + 0.962, + 0.962, + 0.962, + 0.962, + 0.962, + 0.962, + 0.962, ] random.seed(self._random_state) for max_features in ["auto", None]: - for splitter in ["best", "random"]: + for splitter in [ + "best", + "random", + "trandom", + "mutual", + "iwss", + "cfs", + ]: tclf = Odte( base_estimator=Stree(), random_state=self._random_state, - max_features=max_features, - n_estimators=10, + n_estimators=3, ) tclf.set_params( **dict( + base_estimator__max_features=max_features, base_estimator__splitter=splitter, base_estimator__random_state=self._random_state, ) diff --git a/requirements.txt b/requirements.txt index caf8f9f..35bba27 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1 @@ -numpy -scikit-learn -pandas -ipympl -git+https://github.com/doctorado-ml/stree \ No newline at end of file +stree \ No newline at end of file diff --git a/setup.py b/setup.py index c8a1e94..3858f7b 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,35 @@ import setuptools -__version__ = "0.2.0" -__author__ = "Ricardo Montañana Gómez" - def readme(): with open("README.md") as f: return f.read() +def get_data(field): + item = "" + with open("stree/__init__.py") as f: + for line in f.readlines(): + if line.startswith(f"__{field}__"): + delim = '"' if '"' in line else "'" + item = line.split(delim)[1] + break + else: + raise RuntimeError(f"Unable to find {field} string.") + return item + + setuptools.setup( name="Odte", - version=__version__, - license="MIT License", + version=get_data("version"), + license=get_data("license"), description="Oblique decision tree Ensemble", long_description=readme(), long_description_content_type="text/markdown", packages=setuptools.find_packages(), - url="https://github.com/doctorado-ml/stree", - author=__author__, - author_email="ricardo.montanana@alu.uclm.es", + url="https://github.com/doctorado-ml/odte", + author=get_data("author"), + author_email=get_data("author_email"), keywords="scikit-learn oblique-classifier oblique-decision-tree decision-\ tree ensemble svm svc", classifiers=[ @@ -30,7 +40,7 @@ setuptools.setup( "Topic :: Scientific/Engineering :: Artificial Intelligence", "Intended Audience :: Science/Research", ], - install_requires=["scikit-learn", "numpy", "ipympl", "stree"], + install_requires=["stree"], test_suite="odte.tests", zip_safe=False, )