Compare commits

..

6 Commits

9 changed files with 295 additions and 339 deletions

View File

@@ -2,12 +2,12 @@ name: "CodeQL"
on:
push:
branches: [master]
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
branches: [ master ]
schedule:
- cron: "16 17 * * 3"
- cron: '16 17 * * 3'
jobs:
analyze:
@@ -17,7 +17,7 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ["python"]
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
@@ -28,7 +28,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
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.
@@ -39,7 +39,7 @@ jobs:
# 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@v2
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -53,4 +53,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v1

View File

@@ -16,9 +16,9 @@ jobs:
python: [3.8, "3.10"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- name: Install dependencies
@@ -35,7 +35,7 @@ jobs:
coverage run -m unittest -v stree.tests
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml

View File

@@ -1,7 +1,7 @@
![CI](https://github.com/Doctorado-ML/STree/workflows/CI/badge.svg)
[![CodeQL](https://github.com/Doctorado-ML/STree/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Doctorado-ML/STree/actions/workflows/codeql-analysis.yml)
[![codecov](https://codecov.io/gh/doctorado-ml/stree/branch/master/graph/badge.svg)](https://codecov.io/gh/doctorado-ml/stree)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/35fa3dfd53a24a339344b33d9f9f2f3d)](https://www.codacy.com/gh/Doctorado-ML/STree?utm_source=github.com&utm_medium=referral&utm_content=Doctorado-ML/STree&utm_campaign=Badge_Grade)
[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/Doctorado-ML/STree.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Doctorado-ML/STree/context:python)
[![PyPI version](https://badge.fury.io/py/STree.svg)](https://badge.fury.io/py/STree)
![https://img.shields.io/badge/python-3.8%2B-blue](https://img.shields.io/badge/python-3.8%2B-brightgreen)
[![DOI](https://zenodo.org/badge/262658230.svg)](https://zenodo.org/badge/latestdoi/262658230)
@@ -15,7 +15,7 @@ Oblique Tree classifier based on SVM nodes. The nodes are built and splitted wit
## Installation
```bash
pip install Stree
pip install git+https://github.com/doctorado-ml/stree
```
## Documentation

View File

@@ -178,7 +178,7 @@
"outputs": [],
"source": [
"# Stree\n",
"stree = Stree(random_state=random_state, C=.01, max_iter=1000, kernel=\"liblinear\", multiclass_strategy=\"ovr\")"
"stree = Stree(random_state=random_state, C=.01, max_iter=1e3, kernel=\"liblinear\", multiclass_strategy=\"ovr\")"
]
},
{
@@ -198,7 +198,7 @@
"outputs": [],
"source": [
"# SVC (linear)\n",
"svc = LinearSVC(random_state=random_state, C=.01, max_iter=1000)"
"svc = LinearSVC(random_state=random_state, C=.01, max_iter=1e3)"
]
},
{

View File

@@ -133,33 +133,33 @@
" 'base_estimator': [Stree(random_state=random_state)],\n",
" 'n_estimators': [10, 25],\n",
" 'learning_rate': [.5, 1],\n",
" 'estimator__split_criteria': ['max_samples', 'impurity'],\n",
" 'estimator__tol': [.1, 1e-02],\n",
" 'estimator__max_depth': [3, 5, 7],\n",
" 'estimator__C': [1, 7, 55],\n",
" 'estimator__kernel': ['linear']\n",
" 'base_estimator__split_criteria': ['max_samples', 'impurity'],\n",
" 'base_estimator__tol': [.1, 1e-02],\n",
" 'base_estimator__max_depth': [3, 5, 7],\n",
" 'base_estimator__C': [1, 7, 55],\n",
" 'base_estimator__kernel': ['linear']\n",
"},\n",
"{\n",
" 'base_estimator': [Stree(random_state=random_state)],\n",
" 'n_estimators': [10, 25],\n",
" 'learning_rate': [.5, 1],\n",
" 'estimator__split_criteria': ['max_samples', 'impurity'],\n",
" 'estimator__tol': [.1, 1e-02],\n",
" 'estimator__max_depth': [3, 5, 7],\n",
" 'estimator__C': [1, 7, 55],\n",
" 'estimator__degree': [3, 5, 7],\n",
" 'estimator__kernel': ['poly']\n",
" 'base_estimator__split_criteria': ['max_samples', 'impurity'],\n",
" 'base_estimator__tol': [.1, 1e-02],\n",
" 'base_estimator__max_depth': [3, 5, 7],\n",
" 'base_estimator__C': [1, 7, 55],\n",
" 'base_estimator__degree': [3, 5, 7],\n",
" 'base_estimator__kernel': ['poly']\n",
"},\n",
"{\n",
" 'base_estimator': [Stree(random_state=random_state)],\n",
" 'n_estimators': [10, 25],\n",
" 'learning_rate': [.5, 1],\n",
" 'estimator__split_criteria': ['max_samples', 'impurity'],\n",
" 'estimator__tol': [.1, 1e-02],\n",
" 'estimator__max_depth': [3, 5, 7],\n",
" 'estimator__C': [1, 7, 55],\n",
" 'estimator__gamma': [.1, 1, 10],\n",
" 'estimator__kernel': ['rbf']\n",
" 'base_estimator__split_criteria': ['max_samples', 'impurity'],\n",
" 'base_estimator__tol': [.1, 1e-02],\n",
" 'base_estimator__max_depth': [3, 5, 7],\n",
" 'base_estimator__C': [1, 7, 55],\n",
" 'base_estimator__gamma': [.1, 1, 10],\n",
" 'base_estimator__kernel': ['rbf']\n",
"}]"
]
},
@@ -214,7 +214,7 @@
" base_estimator=Stree(C=55, max_depth=7, random_state=1,\n",
" split_criteria='max_samples', tol=0.1),\n",
" learning_rate=0.5, n_estimators=25, random_state=1)\n",
"Best hyperparameters: {'base_estimator': Stree(C=55, max_depth=7, random_state=1, split_criteria='max_samples', tol=0.1), 'estimator__C': 55, 'estimator__kernel': 'linear', 'estimator__max_depth': 7, 'estimator__split_criteria': 'max_samples', 'estimator__tol': 0.1, 'learning_rate': 0.5, 'n_estimators': 25}"
"Best hyperparameters: {'base_estimator': Stree(C=55, max_depth=7, random_state=1, split_criteria='max_samples', tol=0.1), 'base_estimator__C': 55, 'base_estimator__kernel': 'linear', 'base_estimator__max_depth': 7, 'base_estimator__split_criteria': 'max_samples', 'base_estimator__tol': 0.1, 'learning_rate': 0.5, 'n_estimators': 25}"
]
},
{

View File

@@ -267,6 +267,7 @@ class Splitter:
random_state=None,
normalize=False,
):
self._clf = clf
self._random_state = random_state
if random_state is not None:

View File

@@ -139,7 +139,7 @@ class Stree(BaseEstimator, ClassifierMixin):
self,
C: float = 1.0,
kernel: str = "linear",
max_iter: int = int(1e5),
max_iter: int = 1e5,
random_state: int = None,
max_depth: int = None,
tol: float = 1e-4,
@@ -153,6 +153,7 @@ class Stree(BaseEstimator, ClassifierMixin):
multiclass_strategy: str = "ovo",
normalize: bool = False,
):
self.max_iter = max_iter
self.C = C
self.kernel = kernel
@@ -484,43 +485,6 @@ class Stree(BaseEstimator, ClassifierMixin):
X = self.check_predict(X)
return self.classes_[np.argmax(self.__predict_class(X), axis=1)]
def get_nodes(self) -> int:
"""Return the number of nodes in the tree
Returns
-------
int
number of nodes
"""
nodes = 0
for _ in self:
nodes += 1
return nodes
def get_leaves(self) -> int:
"""Return the number of leaves in the tree
Returns
-------
int
number of leaves
"""
leaves = 0
for node in self:
if node.is_leaf():
leaves += 1
return leaves
def get_depth(self) -> int:
"""Return the depth of the tree
Returns
-------
int
depth of the tree
"""
return self.depth_
def nodes_leaves(self) -> tuple:
"""Compute the number of nodes and leaves in the built tree

View File

@@ -1 +1 @@
__version__ = "1.3.2"
__version__ = "1.3.0"

View File

@@ -239,7 +239,6 @@ class Stree_test(unittest.TestCase):
)
tcl.fit(*load_dataset(self._random_state))
self.assertEqual(depth, tcl.depth_)
self.assertEqual(depth, tcl.get_depth())
def test_unfitted_tree_is_iterable(self):
tcl = Stree()
@@ -307,10 +306,10 @@ class Stree_test(unittest.TestCase):
for criteria in ["max_samples", "impurity"]:
for kernel in self._kernels:
clf = Stree(
max_iter=int(1e4),
multiclass_strategy=(
"ovr" if kernel == "liblinear" else "ovo"
),
max_iter=1e4,
multiclass_strategy="ovr"
if kernel == "liblinear"
else "ovo",
kernel=kernel,
random_state=self._random_state,
)
@@ -641,12 +640,10 @@ class Stree_test(unittest.TestCase):
clf = Stree(random_state=self._random_state)
clf.fit(X, y)
self.assertEqual(6, clf.depth_)
self.assertEqual(6, clf.get_depth())
X, y = load_wine(return_X_y=True)
clf = Stree(random_state=self._random_state)
clf.fit(X, y)
self.assertEqual(4, clf.depth_)
self.assertEqual(4, clf.get_depth())
def test_nodes_leaves(self):
"""Check number of nodes and leaves."""
@@ -660,17 +657,13 @@ class Stree_test(unittest.TestCase):
clf.fit(X, y)
nodes, leaves = clf.nodes_leaves()
self.assertEqual(31, nodes)
self.assertEqual(31, clf.get_nodes())
self.assertEqual(16, leaves)
self.assertEqual(16, clf.get_leaves())
X, y = load_wine(return_X_y=True)
clf = Stree(random_state=self._random_state)
clf.fit(X, y)
nodes, leaves = clf.nodes_leaves()
self.assertEqual(11, nodes)
self.assertEqual(11, clf.get_nodes())
self.assertEqual(6, leaves)
self.assertEqual(6, clf.get_leaves())
def test_nodes_leaves_artificial(self):
"""Check leaves of artificial dataset."""
@@ -689,9 +682,7 @@ class Stree_test(unittest.TestCase):
clf.tree_ = n1
nodes, leaves = clf.nodes_leaves()
self.assertEqual(6, nodes)
self.assertEqual(6, clf.get_nodes())
self.assertEqual(2, leaves)
self.assertEqual(2, clf.get_leaves())
def test_bogus_multiclass_strategy(self):
"""Check invalid multiclass strategy."""