2 Commits

Author SHA1 Message Date
d0f1cc5979 fix format issue 2022-03-10 14:32:33 +01:00
b958bccef6 Fix cfs merit formula 2022-03-10 12:56:47 +01:00
11 changed files with 53 additions and 96 deletions

View File

@@ -12,13 +12,11 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest] os: [macos-latest, ubuntu-latest]
python: ["3.10"] python: [3.8]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python }} - name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
@@ -28,37 +26,14 @@ jobs:
pip install -q --upgrade pip pip install -q --upgrade pip
pip install -q cython pip install -q cython
pip install -q numpy pip install -q numpy
pip install -q git+https://github.com/doctorado-ml/mdlp pip install -q git+git://github.com/doctorado-ml/mdlp
pip install -q -r requirements/dev.txt pip install -q -r requirements/dev.txt
pip install -q --upgrade codecov coverage black flake8 codacy-coverage unittest-xml-reporting pip install -q --upgrade codecov coverage black flake8 codacy-coverage
- name: Lint - name: Lint
run: | run: |
black --check --diff mufs black --check --diff mufs
flake8 --count mufs flake8 --count mufs
- name: Tests & coverage - name: Tests & coverage
run: | run: |
mkdir .report coverage run -m unittest -v mufs.tests
coverage run -m xmlrunner -v mufs.tests -o .report
coverage xml -i -o .report/coverage.xml
coverage report -m --fail-under=100 coverage report -m --fail-under=100
- name: Get project version
run: echo "project_version=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- name: Override Coverage Source Path for Sonar
run: sed -i 's/\/home\/runner\/work\/mufs\/mufs\//\/github\/workspace\//g' .report/coverage.xml
- name: SonarQube scanner
uses: sonarsource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
with:
args: >
-Dsonar.projectVersion=${{ env.project_version }}
-Dsonar.python.coverage.reportPaths=.report/coverage.xml
-Dsonar.python.xunit.reportPath=.report/TEST*
# If you wish to fail your job when the Quality Gate is red, uncomment the
# following lines. This would typically be used to fail a deployment.
- name: Quality Gate
uses: sonarsource/sonarqube-quality-gate-action@master
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

View File

@@ -1,12 +1,12 @@
repos: repos:
- repo: https://github.com/ambv/black - repo: https://github.com/ambv/black
rev: 22.3.0 rev: 22.1.0
hooks: hooks:
- id: black - id: black
exclude: ".virtual_documents" exclude: ".virtual_documents"
language_version: python3.8 language_version: python3.8
- repo: https://gitlab.com/pycqa/flake8 - repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2 rev: 3.8.4
hooks: hooks:
- id: flake8 - id: flake8
exclude: ".virtual_documents" exclude: ".virtual_documents"
@@ -16,7 +16,7 @@ repos:
# - id: mypy # - id: mypy
# # args: [--strict, --ignore-missing-imports] # # args: [--strict, --ignore-missing-imports]
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0 rev: v3.4.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: check-case-conflict - id: check-case-conflict

View File

@@ -1,6 +1,6 @@
SHELL := /bin/bash SHELL := /bin/bash
.DEFAULT_GOAL := help .DEFAULT_GOAL := help
.PHONY: coverage deps help lint push test build .PHONY: coverage deps help lint push test doc build
coverage: ## Run tests with coverage coverage: ## Run tests with coverage
coverage erase coverage erase
@@ -26,6 +26,9 @@ build: ## Build package
rm -fr build/* rm -fr build/*
python setup.py sdist bdist_wheel python setup.py sdist bdist_wheel
doc-clean: ## Update documentation
make -C docs --makefile=Makefile clean
help: ## Show help message help: ## Show help message
@IFS=$$'\n' ; \ @IFS=$$'\n' ; \
help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##/:/'`); \ help_lines=(`fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##/:/'`); \

View File

@@ -2,8 +2,6 @@
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/66ad727eb13e4c7a8816db1e44d994a7)](https://www.codacy.com/gh/Doctorado-ML/mufs/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Doctorado-ML/mufs&utm_campaign=Badge_Grade) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/66ad727eb13e4c7a8816db1e44d994a7)](https://www.codacy.com/gh/Doctorado-ML/mufs/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Doctorado-ML/mufs&utm_campaign=Badge_Grade)
[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/Doctorado-ML/mufs.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Doctorado-ML/mufs/context:python) [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/Doctorado-ML/mufs.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Doctorado-ML/mufs/context:python)
[![PyPI version](https://badge.fury.io/py/MUFS.svg)](https://badge.fury.io/py/MUFS) [![PyPI version](https://badge.fury.io/py/MUFS.svg)](https://badge.fury.io/py/MUFS)
[![Technical Debt](https://haystack.rmontanana.es:25000/api/project_badges/measure?project=mufs&metric=sqale_index&token=1119a3bfd4025d50ef3009a44c600c16670ee31a)](https://haystack.rmontanana.es:25000/dashboard?id=mufs)
[![Quality Gate Status](https://haystack.rmontanana.es:25000/api/project_badges/measure?project=mufs&metric=alert_status&token=1119a3bfd4025d50ef3009a44c600c16670ee31a)](https://haystack.rmontanana.es:25000/dashboard?id=mufs)
![https://img.shields.io/badge/python-3.8%2B-blue](https://img.shields.io/badge/python-3.8%2B-brightgreen) ![https://img.shields.io/badge/python-3.8%2B-blue](https://img.shields.io/badge/python-3.8%2B-brightgreen)
# MUFS # MUFS

View File

@@ -3,7 +3,6 @@ from sys import float_info
from itertools import combinations from itertools import combinations
import numpy as np import numpy as np
from .Metrics import Metrics from .Metrics import Metrics
from ._version import __version__
class MUFS: class MUFS:
@@ -41,11 +40,6 @@ class MUFS:
) )
self._fitted = False self._fitted = False
@staticmethod
def version() -> str:
"""Return the version of the package."""
return __version__
def _initialize(self, X, y): def _initialize(self, X, y):
"""Initialize the attributes so support multiple calls using same """Initialize the attributes so support multiple calls using same
object object
@@ -134,7 +128,7 @@ class MUFS:
k = len(features) k = len(features)
for pair in list(combinations(features, 2)): for pair in list(combinations(features, 2)):
rff += self._compute_su_features(*pair) rff += self._compute_su_features(*pair)
return rcf / sqrt(k + (k**2 - k) * rff) return k * rcf / sqrt(k + (k**2 - k) * rff)
def cfs(self, X, y): def cfs(self, X, y):
"""Correlation-based Feature Selection """Correlation-based Feature Selection
@@ -172,10 +166,6 @@ class MUFS:
id_selected = idx id_selected = idx
merit = merit_new merit = merit_new
candidates.pop() candidates.pop()
if id_selected is None:
# No more features to add all merits are nan because of
# constant features
break
candidates.append(feature_order[id_selected]) candidates.append(feature_order[id_selected])
self._scores.append(merit) self._scores.append(merit)
del feature_order[id_selected] del feature_order[id_selected]

View File

@@ -1,8 +1,9 @@
from .Selection import MUFS from .Selection import MUFS
__version__ = "0.1.2"
__author__ = "Ricardo Montañana Gómez" __author__ = "Ricardo Montañana Gómez"
__author_email__ = "Ricardo.Montanana@alu.uclm.es" __author_email__ = "Ricardo.Montanana@alu.uclm.es"
__copyright__ = "Copyright 2021-2022, Ricardo Montañana Gómez" __copyright__ = "Copyright 2021, Ricardo Montañana Gómez"
__license__ = "MIT License" __license__ = "MIT License"
__all__ = ["MUFS"] __all__ = ["MUFS"]

View File

@@ -1 +0,0 @@
__version__ = "0.1.3"

View File

@@ -4,8 +4,8 @@ import pandas as pd
import numpy as np import numpy as np
from mdlp import MDLP from mdlp import MDLP
from sklearn.datasets import load_wine, load_iris from sklearn.datasets import load_wine, load_iris
from ..Selection import MUFS from ..Selection import MUFS
from .._version import __version__
class MUFSTest(unittest.TestCase): class MUFSTest(unittest.TestCase):
@@ -18,11 +18,6 @@ class MUFSTest(unittest.TestCase):
mdlp = MDLP(random_state=1) mdlp = MDLP(random_state=1)
self.X_i = mdlp.fit_transform(self.X_ic, self.y_i).astype("int64") self.X_i = mdlp.fit_transform(self.X_ic, self.y_i).astype("int64")
def test_version(self):
"""Check package version."""
mufs = MUFS()
self.assertEqual(__version__, mufs.version())
def assertListAlmostEqual(self, list1, list2, tol=7): def assertListAlmostEqual(self, list1, list2, tol=7):
self.assertEqual(len(list1), len(list2)) self.assertEqual(len(list1), len(list2))
for a, b in zip(list1, list2): for a, b in zip(list1, list2):
@@ -39,33 +34,38 @@ class MUFSTest(unittest.TestCase):
def test_csf_wine(self): def test_csf_wine(self):
mufs = MUFS() mufs = MUFS()
expected = [6, 12, 9, 4, 10, 0] expected = [6, 12, 9, 4, 10, 0, 7, 8]
self.assertListEqual( self.assertListEqual(
expected, mufs.cfs(self.X_w, self.y_w).get_results() expected, mufs.cfs(self.X_w, self.y_w).get_results()
) )
expected = [ expected = [
0.5218299405215557, 0.5218299405215557,
0.602513857132804, 1.205027714265608,
0.4877384978817362, 1.4632154936452084,
0.3743688234383051, 1.4974752937532203,
0.28795671854246285, 1.4397835927123144,
0.2309165735173175, 1.385499441103905,
1.340618857006277,
1.2989177695790775,
] ]
self.assertListAlmostEqual(expected, mufs.get_scores()) self.assertListAlmostEqual(expected, mufs.get_scores())
def test_csf_wine_cont(self): def test_csf_wine_cont(self):
mufs = MUFS(discrete=False) mufs = MUFS(discrete=False)
expected = [10, 6, 0, 2, 11, 9] expected = [10, 6, 0, 2, 11, 9, 8, 1, 5]
self.assertListEqual( self.assertListEqual(
expected, mufs.cfs(self.X_wc, self.y_w).get_results() expected, mufs.cfs(self.X_wc, self.y_w).get_results()
) )
expected = [ expected = [
0.735264150416997, 0.735264150416997,
0.8321684551546848, 1.6643369103093697,
0.7439915858469107, 2.231974757540732,
0.6238883340158233, 2.4955533360632933,
0.513637402071709, 2.568187010358545,
0.41596400981378984, 2.495784058882739,
2.4409992149141915,
2.3665143407182456,
2.280111788845658,
] ]
self.assertListAlmostEqual(expected, mufs.get_scores()) self.assertListAlmostEqual(expected, mufs.get_scores())
@@ -75,11 +75,7 @@ class MUFSTest(unittest.TestCase):
self.assertListAlmostEqual( self.assertListAlmostEqual(
expected, mufs.cfs(self.X_w, self.y_w).get_results() expected, mufs.cfs(self.X_w, self.y_w).get_results()
) )
expected = [ expected = [0.5218299405215557, 1.205027714265608, 1.4632154936452084]
0.5218299405215557,
0.602513857132804,
0.4877384978817362,
]
self.assertListAlmostEqual(expected, mufs.get_scores()) self.assertListAlmostEqual(expected, mufs.get_scores())
def test_csf_iris(self): def test_csf_iris(self):
@@ -89,9 +85,9 @@ class MUFSTest(unittest.TestCase):
self.assertListEqual(expected, computed) self.assertListEqual(expected, computed)
expected = [ expected = [
0.870521418179061, 0.870521418179061,
0.8968651482682227, 1.7937302965364454,
0.5908278453318913, 1.7724835359956739,
0.40371971570693366, 1.6148788628277346,
] ]
self.assertListAlmostEqual(expected, mufs.get_scores()) self.assertListAlmostEqual(expected, mufs.get_scores())
@@ -159,11 +155,19 @@ class MUFSTest(unittest.TestCase):
def test_iwss_wine(self): def test_iwss_wine(self):
mufs = MUFS() mufs = MUFS()
expected = [6, 9, 12] expected = [6, 9, 12, 0, 11, 10, 5]
self.assertListEqual( self.assertListEqual(
expected, mufs.iwss(self.X_w, self.y_w, 0.2).get_results() expected, mufs.iwss(self.X_w, self.y_w, 0.2).get_results()
) )
expected = [0.5218299405215557, 0.5947822876110085, 0.4877384978817362] expected = [
0.5218299405215557,
1.189564575222017,
1.4632154936452084,
1.428626297656075,
1.3384248731269246,
1.2869213430115078,
1.1949414936926785,
]
self.assertListAlmostEqual(expected, mufs.get_scores()) self.assertListAlmostEqual(expected, mufs.get_scores())
def test_iwss_wine_max_features(self): def test_iwss_wine_max_features(self):
@@ -172,7 +176,7 @@ class MUFSTest(unittest.TestCase):
self.assertListEqual( self.assertListEqual(
expected, mufs.iwss(self.X_w, self.y_w, 0.4).get_results() expected, mufs.iwss(self.X_w, self.y_w, 0.4).get_results()
) )
expected = [0.5218299405215557, 0.5947822876110085, 0.4877384978817362] expected = [0.5218299405215557, 1.189564575222017, 1.4632154936452084]
self.assertListAlmostEqual(expected, mufs.get_scores()) self.assertListAlmostEqual(expected, mufs.get_scores())
def test_iwss_exception(self): def test_iwss_exception(self):

View File

@@ -1,3 +1,3 @@
-r production.txt -r production.txt
mdlp mdlp
pandas pandas

View File

@@ -1,4 +1,3 @@
import os
import setuptools import setuptools
@@ -7,10 +6,9 @@ def readme():
return f.read() return f.read()
def get_data(field): def get_data(field: str):
item = "" item = ""
file_name = "_version.py" if field == "version" else "__init__.py" with open("mufs/__init__.py") as f:
with open(os.path.join("mufs", file_name)) as f:
for line in f.readlines(): for line in f.readlines():
if line.startswith(f"__{field}__"): if line.startswith(f"__{field}__"):
delim = '"' if '"' in line else "'" delim = '"' if '"' in line else "'"
@@ -21,11 +19,6 @@ def get_data(field):
return item return item
def get_requirements():
with open("requirements/production.txt") as f:
return f.read().splitlines()
setuptools.setup( setuptools.setup(
name="MUFS", name="MUFS",
version=get_data("version"), version=get_data("version"),
@@ -45,13 +38,11 @@ setuptools.setup(
"Development Status :: 4 - Beta", "Development Status :: 4 - Beta",
"License :: OSI Approved :: " + get_data("license"), "License :: OSI Approved :: " + get_data("license"),
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Natural Language :: English", "Natural Language :: English",
"Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Scientific/Engineering :: Artificial Intelligence",
"Intended Audience :: Science/Research", "Intended Audience :: Science/Research",
], ],
install_requires=get_requirements(), install_requires=["scikit-learn"],
test_suite="mufs.tests", test_suite="mufs.tests",
zip_safe=False, zip_safe=False,
) )

View File

@@ -1,4 +0,0 @@
sonar.projectKey=mufs
sonar.sourceEncoding=UTF-8
sonar.sources=.
sonar.python.version=3.8, 3.9, 3.10