https://github.com/astropenguin/ndtools
:zap: Collection of tools to extend multidimensional array operations
Science Score: 39.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
✓DOI references
Found 3 DOI reference(s) in README -
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (14.0%) to scientific vocabulary
Keywords
Repository
:zap: Collection of tools to extend multidimensional array operations
Basic Info
- Host: GitHub
- Owner: astropenguin
- License: mit
- Language: Python
- Default Branch: main
- Homepage: https://astropenguin.github.io/ndtools/v1.0.1
- Size: 2.45 MB
Statistics
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
- Releases: 6
Topics
Metadata Files
README.md
ndtools
Collection of tools to extend multidimensional array operations
Installation
shell
pip install ndtools
Usage
ndtools allows you to compare NumPy arrays, pandas Series, xarray DataArrays, and other array-like objects (often called "duck arrays") against custom conditions in an intuitive way.
It achieves this broad compatibility by leveraging standard protocols like __array_ufunc__, ensuring that the comparison logic works seamlessly across different libraries as long as they adhere to these conventions.
Core concepts
At its core, ndtools uses mixin classes to make your own objects comparable with these duck arrays. This allows you to define complex, domain-specific comparison logic that goes beyond simple value checks, while remaining compatible with the wider Python data science ecosystem.
Equatable mixin
Implement Equatable mixin when you need custom equality logic (==, !=).
Simply define __eq__ (or __ne__) on your class, specifying how it should compare against an array's elements.
ndtools leverages NumPy's __array_ufunc__ protocol behind the scenes, ensuring that comparisons like array == YourClass() and YourClass() == array both work seamlessly and symmetrically across compatible array types.
Crucially, ndtools also automatically derives the missing comparison operator for you (e.g., it creates a working __ne__ if you only provide __eq__), reducing boilerplate code.
```python import numpy as np from ndtools import Equatable
class Even(Equatable): def eq(self, array): return array % 2 == 0
Even() == np.arange(3) # -> array([True, False, True]) np.arange(3) == Even() # -> array([True, False, True])
Even() != np.arange(3) # -> array([False, True, False]) np.arange(3) != Even() # -> array([False, True, False]) ```
Orderable mixin
For comparisons involving order (>=, >, <=, <), inherit from Orderable mixin.
Similar in spirit to Python's standard library functools.total_ordering, Orderable significantly simplifies defining ordered comparisons.
You only need to implement one ordering method (e.g., __gt__) and one equality method (__eq__ or __ne__).
From this minimal definition, ndtools automatically and robustly derives all other comparison operators (<, <=, >=, != if needed) based on logical equivalences (e.g., a <= b is equivalent to not (a > b)), again using __array_ufunc__ for broad compatibility.
This powerful mechanism allows you to implement custom sorting criteria or range-like checks with minimal code, while ensuring consistent behavior across all six comparison operators.
Like Equatable, it ensures comparisons work symmetrically (e.g., both array > YourClass() and YourClass() < array work correctly).
```python import numpy as np from dataclasses import dataclass from ndtools import Orderable
@dataclass class Range(Orderable): lower: float upper: float
def __eq__(self, array):
return (array >= self.lower) & (array < self.upper)
def __ge__(self, array):
return array < self.upper
Range(1, 2) == np.arange(3) # -> array([False, True, False]) np.arange(3) == Range(1, 2) # -> array([False, True, False])
Range(1, 2) >= np.arange(3) # -> array([True, True, False]) np.arange(3) <= Range(1, 2) # -> array([True, True, False]) ```
Combining comparables
Multiple comparables can be combined using standard Python logical operators.
This applies to any comparable object inheriting from Combinable, including built-in comparables and primitive types.
Use & (AND) for logical conjunction (all conditions must be True) and | (OR) for logical disjunction (at least one condition must be True).
You can also use Not(comparable) to invert the result of another comparable object.
This allows for the construction of complex, readable query expressions.
The comparison is evaluated element-wise when the combined condition is compared against an array.
Note that primitive types like 0 in the examples below are implicitly treated as comparable values when combined using & or |.
```python import numpy as np from ndtools import Combinable, Equatable
class Even(Combinable, Equatable): def eq(self, array): return array % 2 == 0
class Odd(Combinable, Equatable): def eq(self, array): return array % 2 == 1
Even() | Odd() # -> Any([Even(), Odd()]) Even() & Odd() # -> All([Even(), Odd()])
np.arange(3) == Even() | Odd() # -> array([True, True, True]) np.arange(3) == Even() & Odd() # -> array([False, False, False]) np.arange(3) == Not(1) # -> array([True, False, True]) ```
Built-in comparables
ndtools provides several ready-to-use comparable objects designed for duck arrays.
ANY / NEVER
Comparison with them always evaluates to True or False, respectively.
```python import numpy as np from ndtools import ANY, NEVER
np.arange(3) == ANY # -> array([True, True, True]) np.arange(3) == NEVER # -> array([False, False, False]) ```
Match(pat, case=True, flags=0, na=None)
Checks if string array elements fully match a regular expression pattern (uses pandas.Series.str.fullmatch).
```python import numpy as np from ndtools import Match
np.array(["a", "aa"]) == Match("a+") # -> array([True, True]) ```
Range(lower, upper, bounds="[)")
Checks if array elements are within a specified range.
bounds controls inclusivity ([), [], (], ()).
Use None for unbounded sides.
```python import numpy as np from ndtools import Range
np.arange(3) == Range(1, 2) # -> array([False, True, False]) np.arange(3) < Range(1, 2) # -> array([True, False, False]) np.arange(3) > Range(1, 2) # -> array([False, False, True])
np.arange(3) == Range(None, 2) # -> array([True, True, False]) np.arange(3) == Range(1, None) # -> array([False, True, True]) np.arange(3) == Range(None, None) # -> array([True, True, True]) ```
Where(func, *args, **kwargs)
Checks if func(array, *args, **kwargs) returns True for array elements.
```python import numpy as np from ndtools import Where from numpy.char import isupper
np.array(["A", "b"]) == Where(isupper) # -> array([True, False]) ```
Owner
- Name: Akio Taniguchi
- Login: astropenguin
- Kind: user
- Location: Nagoya, Japan
- Company: Nagoya University
- Website: https://astropengu.in
- Twitter: astropengu_in
- Repositories: 76
- Profile: https://github.com/astropenguin
Project assistant professor (LMT-FINER)
GitHub Events
Total
- Create event: 23
- Release event: 7
- Issues event: 30
- Watch event: 1
- Delete event: 13
- Issue comment event: 2
- Push event: 43
- Pull request event: 30
Last Year
- Create event: 23
- Release event: 7
- Issues event: 30
- Watch event: 1
- Delete event: 13
- Issue comment event: 2
- Push event: 43
- Pull request event: 30
Issues and Pull Requests
Last synced: 6 months ago
All Time
- Total issues: 15
- Total pull requests: 15
- Average time to close issues: 1 day
- Average time to close pull requests: about 7 hours
- Total issue authors: 1
- Total pull request authors: 1
- Average comments per issue: 0.13
- Average comments per pull request: 0.0
- Merged pull requests: 15
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 15
- Pull requests: 15
- Average time to close issues: 1 day
- Average time to close pull requests: about 7 hours
- Issue authors: 1
- Pull request authors: 1
- Average comments per issue: 0.13
- Average comments per pull request: 0.0
- Merged pull requests: 15
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- astropenguin (15)
Pull Request Authors
- astropenguin (29)
Top Labels
Issue Labels
Pull Request Labels
Packages
- Total packages: 1
-
Total downloads:
- pypi 1,524 last-month
- Total dependent packages: 0
- Total dependent repositories: 0
- Total versions: 7
- Total maintainers: 1
pypi.org: ndtools
Collection of tools to extend multidimensional array operations
- Documentation: https://ndtools.readthedocs.io/
- License: MIT License Copyright (c) 2025 Akio Taniguchi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
Latest release: 1.0.1
published 10 months ago
Rankings
Maintainers (1)
Dependencies
- actions/checkout v4 composite
- peaceiris/actions-gh-pages v4 composite
- actions/checkout v4 composite
- actions/checkout v4 composite
- numpy >=1.23,<3.0
- pandas >=1.4,<3.0
- pandas-stubs >=1.4,<3.0
- typing-extensions >=4.0,<5.0
- accessible-pygments 0.0.5
- alabaster 0.7.16
- asttokens 3.0.0
- babel 2.17.0
- beautifulsoup4 4.13.3
- black 25.1.0
- certifi 2025.1.31
- charset-normalizer 3.4.1
- click 8.1.8
- colorama 0.4.6
- decorator 5.2.1
- docutils 0.21.2
- exceptiongroup 1.2.2
- executing 2.2.0
- idna 3.10
- imagesize 1.4.1
- importlib-metadata 8.6.1
- iniconfig 2.1.0
- ipython 8.18.1
- ipython 8.35.0
- ipython 9.1.0
- ipython-pygments-lexers 1.1.1
- jedi 0.19.2
- jinja2 3.1.6
- markdown-it-py 3.0.0
- markupsafe 3.0.2
- matplotlib-inline 0.1.7
- mdit-py-plugins 0.4.2
- mdurl 0.1.2
- mypy-extensions 1.0.0
- myst-parser 3.0.1
- ndtools 0.3.0
- nodeenv 1.9.1
- numpy 2.0.2
- numpy 2.2.4
- packaging 24.2
- pandas 2.2.3
- pandas-stubs 2.2.2.240807
- pandas-stubs 2.2.3.250308
- parso 0.8.4
- pathspec 0.12.1
- pexpect 4.9.0
- platformdirs 4.3.7
- pluggy 1.5.0
- prompt-toolkit 3.0.50
- ptyprocess 0.7.0
- pure-eval 0.2.3
- pydata-sphinx-theme 0.16.1
- pygments 2.19.1
- pyright 1.1.399
- pytest 8.3.5
- python-dateutil 2.9.0.post0
- pytz 2025.2
- pyyaml 6.0.2
- requests 2.32.3
- six 1.17.0
- snowballstemmer 2.2.0
- soupsieve 2.6
- sphinx 7.4.7
- sphinxcontrib-applehelp 2.0.0
- sphinxcontrib-devhelp 2.0.0
- sphinxcontrib-htmlhelp 2.1.0
- sphinxcontrib-jsmath 1.0.1
- sphinxcontrib-qthelp 2.0.0
- sphinxcontrib-serializinghtml 2.0.0
- stack-data 0.6.3
- tomli 2.2.1
- traitlets 5.14.3
- types-pytz 2025.2.0.20250326
- typing-extensions 4.13.2
- tzdata 2025.2
- urllib3 2.4.0
- wcwidth 0.2.13
- zipp 3.21.0