numpy-unit

Build Status Documentation Status PyPI version codecov license

This package provides a tool for scientific computing by keeping track of the unit when performing classical operations on a multi-dimensionnal array with (almost) no extra-cost comparing to the standard numpy array.
The ArrayUnit class supports every operation a numpy.ndarray can handle (because it is a derived class of numpy.ndarray) but the operators are overloaded in order to perform transformations on the Unit contained in every ArrayUnit.

Because any ``ArrayUnit`` is a numpy.ndarray, classical ndarray methods work and returns an ArrayUnit whenever possible. It means that mean, std, var, min, max, ravel, flatten, fill, reshape, diagonal, sum, prod behaves as you expect and the result is encapsulated in an ArrayUnit with the Unit corresponding (:warning: var and prod do change the Unit).

Install

pip install numpy-unit

Examples

>>> import numpy as np
>>> from numpy_unit import Unit, ArrayUnit
>>>
>>> m = Unit('m')
>>> sm2 = Unit('s', -2)
>>> complex_unit = Unit({'my_unit': 0.5, '€': 1, 'capita': 2}) * (sm2**0.5) / m
>>> print(complex_unit)
€·capita²·my_unit^0.5·m⁻¹·s⁻¹
>>>
>>> arr = np.linspace(1,10,10, dtype=float)
>>> a = ArrayUnit(arr, m)
>>> b = ArrayUnit(arr**2, sm2)
>>> print(a, '\\n+\\n', 1, '\\n=\\n', a + 1)
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.] m
+
1
=
[ 2.  3.  4.  5.  6.  7.  8.  9. 10. 11.] m
>>> print(a, '\\n*\\n', b, '\\n=\\n', a * b)
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.] m
*
[  1.   4.   9.  16.  25.  36.  49.  64.  81. 100.] s⁻²
=
[   1.    8.   27.   64.  125.  216.  343.  512.  729. 1000.] m·s⁻²
>>>
>>>
>>> b = ArrayUnit(np.random.random((2, 4)), Unit('banana'))
>>> b
ArrayUnit([[0.7257637 , 0.04797737, 0.88016759, 0.69852201],
           [0.12102613, 0.07913234, 0.38511503, 0.3645144 ]]) banana
>>> b.mean(axis=0)
ArrayUnit([0.42339491, 0.06355485, 0.63264131, 0.5315182 ]) banana
>>> b.prod(axis=1)
ArrayUnit([0.02140805, 0.00134443]) banana⁴

The following rules applied (where {op} is one of the following: [+, -, *, /, //, %]):

  • ArrayUnit {op} Object returns an ArrayUnit with the same unit as the ArrayUnit
  • Object {op} ArrayUnit returns an ArrayUnit with the same unit as the ArrayUnit
  • ArrayUnit {op} ArrayUnit returns an ArrayUnit combining the Unit of the 2 ArrayUnit or an Error
  • An Error might be raised only when two ArrayUnit are conflicting and that ArrayUnit.is_strict is set to True. Otherwise, it would print a warning.
  • An ArrayUnit is equal to a numpy.ndarray if and only if their underlying arrays are equal (np.array_equal) and the Unit of the ArrayUnit is empty.

Development

Doc of the master branch on readthedocs.io.

Features

  • [x] Basic unit system handling comparison, multiplication, division, modulo and power
  • [x] ArrayUnit wrapper for unit + ndarray
  • [x] Operators on ArrayUnit (and their variants r{op} and i{op})
    • [ ] eq, ne // how to deal with this? Current implementation should be changed because we can’t use array mask now
    • [x] add
    • [x] sub
    • [x] mul
    • [x] truediv, floordiv
    • [x] mod
    • [x] pow (but not rpow)
  • [x] Rewrite ndarray methods changing the Unit
    • [x] var
    • [x] prod
  • [ ] conda release
  • [ ] push to a dev branch before master?! (not mandatory when no one is using the package though)

class Unit

class numpy_unit.Unit(u={}, n=1)

The Unit class that is defined by a set of (base unit, power).

It is defined by 0, 1 or many base units (str) raised to a power. For instance, an acceleration can be represented by the following dict {‘m’: 1, ‘s’: -2} (m·s⁻²).

These operators are implemented: ==, *, /, //, __pow__ as well as their __r{op}__ (except for the __pow__). However no in-place operators (__i{op}__) has been implemented yet.

Parameters:
  • u
    • If u is a string, create a Unit with a single base unit u raised to the power n (default to 1).
    • If u is a Unit, create a copy of it.
    • If u is a dict where each key is a string an each value a scalar (np.isscalar), create an Unit based on these base unit and raised to the power associated.
  • n (int, optional) – If u is a str, n is the power to which u is raised to create the new Unit. Default to 1.

Example

>>> Unit('m') == Unit('m', 1) and Unit('m') == Unit({'m': 1})
True
>>> Unit('m') * Unit('sec', -2) == Unit({'m': 1, 'sec': -2})
True
>>> Unit('€') ** 2
€²
>>> Unit('€') + Unit('$')
TypeError: unsupported operand type(s) for +: 'Unit' and 'Unit'

class ArrayUnit

class numpy_unit.ArrayUnit

The class defining a multi dimensionnal array combined with a Unit.

These operators are implemented: ==, +, -, *, /, //, %, __pow__ as well as their __r{op}__ (except for the __pow__) and their __i{op}__ variants. The level of strictness can be set in order to add, substract or use a modulo between two ArrayUnit with different Unit.

The following rules applied (where {op} is one of the following: [+, -, *, /, //, %]):

  • ArrayUnit {op} Object returns an ArrayUnit with the same unit as the ArrayUnit
  • Object {op} ArrayUnit returns an ArrayUnit with the same unit as the ArrayUnit
  • ArrayUnit {op} ArrayUnit returns an ArrayUnit combining the Unit of the 2 ArrayUnit or an Error
  • An Error might be raised only when two ArrayUnit are conflicting and that ArrayUnit.is_strict is set to True. Otherwise, it would print a warning.
  • An ArrayUnit is equal to a numpy.ndarray if and only if their underlying arrays are equal (np.array_equal) and the Unit of the ArrayUnit is empty.
Parameters:
  • input_array (numpy.ndarray) – The array on which the ArrayUnit will be based on. No copy is made, i.e. the original array and self will share the same underlying memory.
  • unit (Unit) – The Unit in which the values of the input_array are expressed.
is_set

Set the strictness to either True or False. If it is set True, a ValueError might be raised while adding, substracting or modulo two ArrayUnit with different Unit. If it is set to True, a warning is triggered when making an impossible operation.

Type:bool

Examples

>>> ArrayUnit.is_strict = True
>>> m = Unit('m')
>>> s = Unit('s', -2)
>>> arr = np.linspace(1,10,10, dtype=float)
>>> a = ArrayUnit(arr, m)
>>> b = ArrayUnit(arr**2, s)
>>> print(a, '\n+\n', 1, '\n=\n', a + 1)
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.] m
+
1
=
[ 2.  3.  4.  5.  6.  7.  8.  9. 10. 11.] m
>>> print(a, '\n-\n', arr, '\n=\n', a - arr)
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.] m
-
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.]
=
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] m
>>> print(a, '\n*\n', b, '\n=\n', a * b)
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.] m
*
[  1.   4.   9.  16.  25.  36.  49.  64.  81. 100.] s⁻²
=
[   1.    8.   27.   64.  125.  216.  343.  512.  729. 1000.] m·s⁻²
>>> print(b, '\n//\n', a, '\n=\n', b / a)
[  1.   4.   9.  16.  25.  36.  49.  64.  81. 100.] s⁻²
//
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.] m
=
[ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.] m⁻¹·s⁻²
>>>
>>> b = ArrayUnit(np.random.random((2, 4)), Unit('banana'))
>>> b
ArrayUnit([[0.7257637 , 0.04797737, 0.88016759, 0.69852201],
   [0.12102613, 0.07913234, 0.38511503, 0.3645144 ]]) banana
>>> b.mean(axis=0)
ArrayUnit([0.42339491, 0.06355485, 0.63264131, 0.5315182 ]) banana
>>> b.prod(axis=1)
ArrayUnit([0.02140805, 0.00134443]) banana⁴
prod(axis=None, dtype=None, out=None, keepdims=False, initial=1, where=True)

Return the product of the array elements over the given axis

Refer to numpy.prod for full documentation.

See also

numpy.prod()
equivalent function
var(axis=None, dtype=None, out=None, ddof=0, keepdims=False)

Returns the variance of the array elements, along given axis.

Refer to numpy.var for full documentation.

See also

numpy.var()
equivalent function

Indices and tables