Skip to content

Commit

Permalink
Release 1.2.1 (#28)
Browse files Browse the repository at this point in the history
* updating project metadata

* fixing CI yaml

* using venv

* trying again

* redoing CI

* fixing tests

* changing some settings

* updates

* fixing build

* trying to fix this

* fixing release

* bumping version

* better code organization

* updates

* adding initial cm impl

* adding unique, dispatching pattern to Py objects, renaming ext

* rustfmt

* cm dispatched

* rustfmt

* tests and benchmarks added

* bump version

* 100% test coverage

* updating readme

* Threading enabled (#9)

* bumping version

* major refactor leveraging macros

* bumping version and updating test

* adding executed notebook

* fixing performance w/ bool

* multiclass implemented and tested ready for 1.0.0

* shifting to u32 for numpy compatability

* bumping version

* changing to i64 for better compatability

* Major additions to Python API to include high-level helpers

* changing to dev deps

* ooops

* bandit! (#25)

* Add 3.11 (#26)

* add 3.11

* add 3.11 to tests

* Adding `mypy` type checking (#27)

* passing mypy type checking

* not needed

* adding mypy to actions

* bumping to 1.2.1
  • Loading branch information
zachcoleman authored Dec 27, 2022
1 parent 3876527 commit 1652f33
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 92 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.11", "3.10","3.9", "3.8","3.7"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -24,7 +24,12 @@ jobs:
source .venv/bin/activate
python -m pip install --upgrade pip
pip install maturin
maturin develop --extras=test
maturin develop --extras=dev
- name: Validate types with mypy
run: |
source .venv/bin/activate
pip install mypy
mypy fast_stats/
- name: Test with pytest
run: |
source .venv/bin/activate
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,5 @@ Cargo.lock
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
*.pdb
.DS_Store
10 changes: 8 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ repos:
- repo: https://github.com/ambv/black
rev: 22.3.0
hooks:
- id: black
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
hooks:
- id: flake8
- id: flake8
- repo: https://github.com/PyCQA/bandit
rev: 1.7.4
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
additional_dependencies: [ "bandit[toml]" ]
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fast-stats"
version = "1.2.0"
version = "1.2.1"
edition = "2021"

[lib]
Expand Down
26 changes: 26 additions & 0 deletions fast_stats/_fast_stats_ext.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import List, Set, Tuple, Union

import numpy as np

def _binary_precision_reqs(
actual: np.ndarray, pred: np.ndarray
) -> Tuple[int, int, int]: ...
def _binary_recall_reqs(
actual: np.ndarray, pred: np.ndarray
) -> Tuple[int, int, int]: ...
def _binary_f1_score_reqs(
actual: np.ndarray, pred: np.ndarray
) -> Tuple[int, int, int]: ...
def _precision(
actual: np.ndarray, pred: np.ndarray, labels: Union[List, np.ndarray]
) -> np.ndarray: ...
def _recall(
actual: np.ndarray, pred: np.ndarray, labels: Union[List, np.ndarray]
) -> np.ndarray: ...
def _f1_score(
actual: np.ndarray, pred: np.ndarray, labels: Union[List, np.ndarray]
) -> np.ndarray: ...
def _confusion_matrix(
actual: np.ndarray, pred: np.ndarray, labels: Union[List, np.ndarray]
) -> np.ndarray: ...
def _unique(arr: np.ndarray) -> Set: ...
52 changes: 31 additions & 21 deletions fast_stats/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
_binary_precision_reqs,
_binary_recall_reqs,
)
from .exceptions import ShapeError

Result = Union[None, float]

Expand Down Expand Up @@ -53,13 +54,15 @@ def binary_precision(
Returns:
Result: None or float depending on values and zero division
"""
assert y_true.shape == y_pred.shape, "y_true and y_pred must be same shape"
assert all(
if not all(
[
isinstance(y_pred, np.ndarray),
isinstance(y_true, np.ndarray),
]
), "y_true and y_pred must be numpy arrays"
):
raise TypeError("y_true and y_pred must be numpy arrays")
if y_true.shape != y_pred.shape:
raise ShapeError("y_true and y_pred must be same shape")
zero_division = ZeroDivision(zero_division)

tp, tp_fp, _ = _binary_precision_reqs(y_true, y_pred)
Expand All @@ -80,13 +83,15 @@ def binary_recall(
Returns:
Result: None or float depending on values and zero division
"""
assert y_true.shape == y_pred.shape, "y_true and y_pred must be same shape"
assert all(
if not all(
[
isinstance(y_pred, np.ndarray),
isinstance(y_true, np.ndarray),
]
), "y_true and y_pred must be numpy arrays"
):
raise TypeError("y_true and y_pred must be numpy arrays")
if y_true.shape != y_pred.shape:
raise ShapeError("y_true and y_pred must be same shape")
zero_division = ZeroDivision(zero_division)

tp, tp_fn, _ = _binary_recall_reqs(y_true, y_pred)
Expand All @@ -107,33 +112,34 @@ def binary_f1_score(
Returns:
Result: None or float depending on values and zero division
"""
assert y_true.shape == y_pred.shape, "y_true and y_pred must be same shape"
assert all(
if not all(
[
isinstance(y_pred, np.ndarray),
isinstance(y_true, np.ndarray),
]
), "y_true and y_pred must be numpy arrays"
):
raise TypeError("y_true and y_pred must be numpy arrays")
if y_true.shape != y_pred.shape:
raise ShapeError("y_true and y_pred must be same shape")
zero_division = ZeroDivision(zero_division)

tp, tp_fp, tp_fn = _binary_f1_score_reqs(y_true, y_pred)
p, r = _precision(tp, tp_fp, zero_division.ZERO), _recall(
tp, tp_fn, zero_division.ZERO
p, r = _precision(tp, tp_fp, ZeroDivision.ZERO), _recall(
tp, tp_fn, ZeroDivision.ZERO
)

if p + r == 0:
if p + r == 0: # type: ignore
if zero_division == ZeroDivision.NONE:
return None
elif zero_division == ZeroDivision.ZERO:
return 0.0

return 2 * p * r / (p + r)
return 2 * p * r / (p + r) # type: ignore


def binary_tp_fp_fn(
y_true: np.ndarray,
y_pred: np.ndarray,
) -> Tuple[float]:
) -> Tuple[int, int, int]:
"""Binary calculations for TP, FP, and FN
Args:
Expand All @@ -142,13 +148,15 @@ def binary_tp_fp_fn(
Returns:
Tuple[int]: counts for TP, FP, and FN
"""
assert y_true.shape == y_pred.shape, "y_true and y_pred must be same shape"
assert all(
if not all(
[
isinstance(y_pred, np.ndarray),
isinstance(y_true, np.ndarray),
]
), "y_true and y_pred must be numpy arrays"
):
raise TypeError("y_true and y_pred must be numpy arrays")
if y_true.shape != y_pred.shape:
raise ShapeError("y_true and y_pred must be same shape")

tp, tp_fp, tp_fn = _binary_f1_score_reqs(y_true, y_pred)
fp, fn = tp_fp - tp, tp_fn - tp
Expand All @@ -168,13 +176,15 @@ def binary_stats(
Returns:
Dict[str, Result]: stats for precision, recall and f1-score
"""
assert y_true.shape == y_pred.shape, "y_true and y_pred must be same shape"
assert all(
if not all(
[
isinstance(y_pred, np.ndarray),
isinstance(y_true, np.ndarray),
]
), "y_true and y_pred must be numpy arrays"
):
raise TypeError("y_true and y_pred must be numpy arrays")
if y_true.shape != y_pred.shape:
raise ShapeError("y_true and y_pred must be same shape")
zero_division = ZeroDivision(zero_division)

tp, tp_fp, tp_fn = _binary_f1_score_reqs(y_true, y_pred)
Expand Down
15 changes: 10 additions & 5 deletions fast_stats/confusion_matrix.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from typing import List, Union
from typing import List, Optional, Union

import numpy as np

from ._fast_stats_ext import _confusion_matrix, _unique
from .exceptions import ShapeError


def confusion_matrix(
y_true: np.ndarray, y_pred: np.ndarray, labels: Union[List, np.ndarray] = None
y_true: np.ndarray,
y_pred: np.ndarray,
labels: Optional[Union[List, np.ndarray]] = None,
) -> np.ndarray:
"""Calculation of confusion matrix
Expand All @@ -18,13 +21,15 @@ def confusion_matrix(
Returns:
confusion matrix (np.ndarray): 2D np.ndarray confusion matrix
"""
assert y_true.shape == y_pred.shape, "y_true and y_pred must be same shape"
assert all(
if not all(
[
isinstance(y_pred, np.ndarray),
isinstance(y_true, np.ndarray),
]
), "y_true and y_pred must be numpy arrays"
):
raise TypeError("y_true and y_pred must be numpy arrays")
if y_true.shape != y_pred.shape:
raise ShapeError("y_true and y_pred must be same shape")

if labels is None:
labels = np.array(
Expand Down
4 changes: 4 additions & 0 deletions fast_stats/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class ShapeError(Exception):
"""Raised for mismatching numpy array shapes"""

pass
Loading

0 comments on commit 1652f33

Please sign in to comment.