Skip to content

Commit

Permalink
Release 0.11.0
Browse files Browse the repository at this point in the history
  • Loading branch information
davehadley committed Nov 5, 2022
1 parent 505eced commit d34ef32
Show file tree
Hide file tree
Showing 21 changed files with 359 additions and 201 deletions.
57 changes: 25 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,46 @@ name: ci

on:
push:
branches: [master, dev]
branches: [dev]
pull_request:
branches: [master, dev]
schedule:
# run once per week in case latest version of depedencies or compiler cause issues
- cron: '41 3 * * 0'
branches: [dev]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Run pre-commit checks
run: |
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
source $HOME/.poetry/env && \
poetry install && \
poetry run pre-commit install && \
poetry run pre-commit run --all-files
- name: Build docs
run: pip install . && pip install -r docs/requirements.txt && sphinx-build -W docs docs-build

build-linux:
runs-on: ubuntu-latest
needs: pre-commit
strategy:
matrix:
python-version: [ '3.6', '3.7', '3.8', '3.9' ]
python-version: [ '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', ]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '${{ matrix.python-version }}'
- name: Setup Poetry
run: |
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
source $HOME/.poetry/env
curl -sSL https://install.python-poetry.org | python - --version 1.1.15
- name: Cache Poetry
uses: actions/cache@v3
env:
cache-name: cache-poetry
with:
path: ~/.cache/pypoetry/
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }}
- name: Install Dependencies
run: source $HOME/.poetry/env && poetry install
run: export PATH=$HOME/.local/bin:${PATH} && poetry install
- name: pytest
run: source $HOME/.poetry/env && poetry run pytest
run: export PATH=$HOME/.local/bin:${PATH} && poetry run pytest
- name: Build
run: source $HOME/.poetry/env && poetry build
run: export PATH=$HOME/.local/bin:${PATH} && poetry build
- name: Publish (dry-run)
run: source $HOME/.poetry/env && poetry publish --dry-run --username username --password password
run: export PATH=$HOME/.local/bin:${PATH} && poetry publish --dry-run --username username --password password
- name: Run pre-commit checks
run: |
export PATH=$HOME/.local/bin:${PATH} && \
poetry run pre-commit install && \
poetry run pre-commit run --all-files
- name: Build docs
run: pip install . && pip install -r docs/requirements.txt && sphinx-build -W docs docs-build
14 changes: 10 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,29 @@ default_stages: [commit]
repos:

- repo: https://github.com/timothycrosley/isort
rev: 5.9.3
rev: 5.10.1
hooks:
- id: isort
stages: [ commit ]
args: ["--profile", "black"]

- repo: https://github.com/ambv/black
rev: 21.10b0
rev: 22.3.0
hooks:
- id: black
stages: [commit]


- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
rev: 5.0.4
hooks:
- id: flake8
stages: [commit]

- repo: https://github.com/pre-commit/pre-commit-hooks
# don't update this beyond 4.0.1 otherwise it stops working on python3.6
rev: v4.0.1

hooks:
- id: check-added-large-files
- id: check-ast
Expand All @@ -37,7 +39,10 @@ repos:
hooks:
- id: mypy
name: mypy
entry: poetry run mypy --install-types --strict --non-interactive src tests
# Don't mypy if the python version is 3.6 as numpy has no type-stubs
# Don't mypy if the python version is 3.7 as numpy types hints are missing
# on some numpy methods
entry: bash -c "python -c \"import sys; sys.exit(sys.version_info[1]>7)\" || poetry run mypy --strict --non-interactive --install-types src tests"
pass_filenames: false
language: system
types: [python]
Expand All @@ -56,6 +61,7 @@ repos:
"-dno-else-return", # Disable no-else-return
"-dinvalid-name", # Too many warnings, too difficult to fix
"-dduplicate-code", # Too many false positives
"-dline-too-long", # Black should handle this
]
exclude: |
(?x)^(
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# 0.11.0 (2022-11-04)

- Add `ignore_nan` option to some methods to use `numpy.nanmean`,
`numpy.nanstd` and `numpy.nanpercentile` instead of `numpy.mean`,
`numpy.std` and `numpy.percentile`.
- Add more flexibility to plotting functions to display percentiles or
mean/standard deviation.
- Improved type hints.

# 0.10.0 (2022-03-24)

- Improved type hinting.
Expand Down
4 changes: 2 additions & 2 deletions docs/pyplots/twodim2_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def stddev_bootstrap_2d(nevents=100, numbins=1, numsamples=1000):
def stddev_analytic(nevents=100, numbins=1):
sigma = np.sqrt(nevents / numbins)
correlation = 1.0 / numbins
cov = correlation * sigma ** 2
return np.sqrt(2.0 * sigma ** 2 + 2.0 * cov)
cov = correlation * sigma**2
return np.sqrt(2.0 * sigma**2 + 2.0 * cov)


def example2d():
Expand Down
25 changes: 20 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "bootstraphistogram"
version = "0.10.0"
version = "0.11.0"
description = "Poisson bootstrap histogram."
license = "MIT"
authors = ["David Hadley <[email protected]>"]
Expand All @@ -13,7 +13,16 @@ include = ["bootstraphistogram/py.typed"]

[tool.poetry.dependencies]
python = "^3.6.2"
numpy = "^1.19.0"
# The simplest numpy dependency spec fails to install with python 3.10 and 3.11
# numpy = "^1.19.0"
numpy = [
# Manually specifying the numpy-python compatibility seems to be necessary
# to ensure that poetry will install the latest supported numpy versions
# This should be simplified once python3.6 support is dropped
{version = ">=1.19.0,<1.20", python = "<3.7"},
{version = ">=1.19.0,<1.22.0", python = ">=3.7,<3.8"},
{version = "^1.19.0", python = ">=3.8"},
]
boost-histogram = ">=1.0.0"
matplotlib = "^3.1"

Expand All @@ -22,8 +31,11 @@ pytest = "^6.0"
pre-commit = "^2.15.0"
tox = "^3.24.4"
memory-profiler = "^0.58.0"
pylint = "^2.12.1"
mypy = "^0.941"
pylint = "==2.13.9"
mypy = [
# Fix for https://github.com/python/mypy/issues/13627
{version = "^0.982", python = ">=3.8" }
]

[tool.black]
target_version = ['py36', 'py37', 'py38']
Expand All @@ -36,4 +48,7 @@ build-backend = "poetry.core.masonry.api"
minversion = "6.0"
addopts = " --doctest-modules "
doctest_optionflags = " NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL NUMBER ELLIPSIS"
testpaths = ["tests", "src", "docs"]
testpaths = ["tests", "src", "docs"]

[tool.mypy]
strict = true
2 changes: 1 addition & 1 deletion src/bootstraphistogram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from bootstraphistogram.bootstrapmoment import BootstrapMoment
from bootstraphistogram.valuewithsamples import ValueWithSamples

__version__ = "0.10.0"
__version__ = "0.11.0"
__license__ = "MIT"
__author__ = "David Hadley"
__url__ = "https://github.com/davehadley/bootstraphistogram"
Expand Down
100 changes: 66 additions & 34 deletions src/bootstraphistogram/bootstrapefficiency.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"""Implements :py:class:`BoostrapEfficiency`, a tool for calculating binned efficiencies."""

from copy import copy, deepcopy
from typing import Any, NamedTuple, Optional, Tuple, Union, cast
from typing import TYPE_CHECKING, Any, NamedTuple, Optional, Tuple, Union, cast

import boost_histogram as bh
import numpy as np # type: ignore
import numpy as np

from bootstraphistogram.bootstraphistogram import BootstrapHistogram

try:
from numpy.typing import ArrayLike # type: ignore
except (ImportError, ModuleNotFoundError):
ArrayLike = np.ndarray
if TYPE_CHECKING:
try:
from numpy.typing import ArrayLike, NDArray
except (ImportError, ModuleNotFoundError):
pass


class BootstrapEfficiency:
Expand Down Expand Up @@ -51,9 +52,9 @@ class Array(NamedTuple):
"""A result type to store arrays returned by
:py:class:`bootstraphistogram.BootstrapEfficiency`"""

numerator: np.ndarray
denominator: np.ndarray
efficiency: np.ndarray
numerator: "NDArray[Any]"
denominator: "NDArray[Any]"
efficiency: "NDArray[Any]"

def __init__(
self,
Expand Down Expand Up @@ -125,32 +126,64 @@ def samples(self) -> "BootstrapEfficiency.Histogram":
"""
return self._hist_to_result(self._hist.samples, nanto=self._nanto)

def mean(self, flow: bool = False) -> "BootstrapEfficiency.Array":
"""Binned mean of the bootstrap samples."""
def mean(
self, flow: bool = False, ignore_nan: bool = True
) -> "BootstrapEfficiency.Array":
"""Binned mean of the bootstrap samples.
Parameters
----------
flow: bool
If True under and overflow bins are included.
ignore_nan : bool
If True :py:func:`numpy.nanmean` is used.
"""
samples = self.samples
mean = np.nanmean if ignore_nan else np.mean
return BootstrapEfficiency.Array(
*[np.mean(hst.view(flow=flow), axis=-1) for hst in samples]
*[mean(hst.view(flow=flow), axis=-1) for hst in samples]
)

def std(self, flow: bool = False) -> np.ndarray:
"""Binned standard deviation of the boostrap samples."""
def std(
self, flow: bool = False, ignore_nan: bool = True
) -> "BootstrapEfficiency.Array":
"""Binned standard deviation of the boostrap samples.
Parameters
----------
flow: bool
If True under and overflow bins are included.
ignore_nan : bool
If True :py:func:`numpy.nanstd` is used.
"""
samples = self.samples
std = np.nanstd if ignore_nan else np.std
return BootstrapEfficiency.Array(
*[np.std(hst.view(flow=flow), axis=-1) for hst in samples]
*[std(hst.view(flow=flow), axis=-1) for hst in samples]
)

def percentile(
self, q: float, flow: bool = False, interpolation: str = "linear"
) -> np.ndarray:
self,
q: float,
flow: bool = False,
interpolation: str = "linear",
ignore_nan: bool = True,
) -> "BootstrapEfficiency.Array":
"""
Binned q-th percentile of the bootstrap samples.
Parameters
----------
q : float
The percentile, a number between 0 and 100 (inclusive).
flow: bool
If True under and overflow bins are included.
interpolation : str
As :py:func:`numpy.percentile`.
ignore_nan : bool
If True :py:func:`numpy.nanpercentile` is used.
Returns
-------
Expand All @@ -159,11 +192,10 @@ def percentile(
bin in the histogram, .
"""
samples = self.samples
percentile = np.nanpercentile if ignore_nan else np.percentile
return BootstrapEfficiency.Array(
*[
np.nanpercentile(
hst.view(flow=flow), q, axis=-1, interpolation=interpolation
)
percentile(hst.view(flow=flow), q, axis=-1, interpolation=interpolation) # type: ignore
for hst in samples
]
)
Expand All @@ -184,10 +216,10 @@ def axes(self) -> Tuple[bh.axis.Axis, ...]:

def fill(
self,
selected: ArrayLike,
*args: ArrayLike,
weight: Optional[ArrayLike] = None,
seed: Optional[ArrayLike] = None,
selected: "ArrayLike",
*args: "ArrayLike",
weight: Optional["ArrayLike"] = None,
seed: Optional["ArrayLike"] = None,
**kwargs: Any,
) -> "BootstrapEfficiency":
"""
Expand All @@ -196,14 +228,14 @@ def fill(
Parameters
----------
selected: ArrayLike
selected: "ArrayLike"
A 1D boolean array determining whether an event enters the numerator or
denominator.
*args : ArrayLike
*args : "ArrayLike"
An 1D array containing coordinates for each dimension of the histogram.
weight : Optional[ArrayLike]
weight : Optional["ArrayLike"]
Entry weights used to fill the histogram.
seed: Optional[ArrayLike]
seed: Optional["ArrayLike"]
Per-element seed. Overrides the Generator given in the constructor and
uses a pseudo-random number generator seeded by the given value.
This may be useful when filling multiple histograms with data that is not
Expand All @@ -218,16 +250,16 @@ def fill(
Reference to this object. This is done to maintain consistency with
`boost_histogram.Histogram`.
"""
args = tuple(np.asarray(a) for a in args)
selected = np.asarray(selected).astype(bool)
self._validate_fill_inputs(args, selected)
self._hist.fill(selected, *args, weight=weight, seed=seed, **kwargs)
arrays = tuple(np.asarray(a) for a in args)
selectedarray = np.asarray(selected).astype(bool)
self._validate_fill_inputs(arrays, selectedarray)
self._hist.fill(selectedarray, *arrays, weight=weight, seed=seed, **kwargs)
return self

@staticmethod
def _validate_fill_inputs(
args: ArrayLike,
selected: ArrayLike,
args: "Tuple[NDArray[Any], ...]",
selected: "NDArray[Any]",
) -> None:
if len(args) <= 0:
raise ValueError("fill must be provided at least 1 array as input.")
Expand Down
Loading

0 comments on commit d34ef32

Please sign in to comment.