Skip to content

Commit 7e01429

Browse files
committed
fix #234 : implemented support for np.__array_ufunc__
- renamed npufuncs.py as npfuncs.py
1 parent 27a79f8 commit 7e01429

File tree

9 files changed

+427
-327
lines changed

9 files changed

+427
-327
lines changed

doc/source/changes/version_0_30.rst.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ Miscellaneous improvements
223223

224224
* implemented :py:obj:`LArray.reverse()` method to reverse one or several axes of an array (closes :issue:`631`).
225225

226+
* made it possible to pass LArray objects to Numpy ufuncs directly (e.g. np.sqrt(ndtest(3)), closes :issue:`234`).
227+
226228

227229
Fixes
228230
-----

larray/__init__.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@
1212
from larray.core.session import Session, local_arrays, global_arrays, arrays
1313
from larray.core.constants import nan, inf, pi, e, euler_gamma
1414
from larray.core.metadata import Metadata
15-
from larray.core.ufuncs import maximum, minimum, where
16-
from larray.core.npufuncs import (sin, cos, tan, arcsin, arccos, arctan, hypot, arctan2, degrees,
17-
radians, unwrap, sinh, cosh, tanh, arcsinh, arccosh, arctanh,
18-
angle, real, imag, conj,
19-
round, around, round_, rint, fix, floor, ceil, trunc,
20-
exp, expm1, exp2, log, log10, log2, log1p, logaddexp, logaddexp2,
21-
i0, sinc, signbit, copysign, frexp, ldexp,
22-
convolve, clip, sqrt, absolute, fabs, sign, fmax, fmin, nan_to_num,
23-
real_if_close, interp, isnan, isinf, inverse)
15+
from larray.core.npfuncs import (round, around, round_, where, inverse, i0, sinc, clip,
16+
angle, real, imag, convolve, nan_to_num, real_if_close, interp)
17+
from larray.core.ufuncs import (sin, cos, tan, arcsin, arccos, arctan, hypot, arctan2, sinh, cosh,
18+
tanh, arcsinh, arccosh, arctanh, conj, rint, floor, ceil, trunc,
19+
exp, expm1, exp2, log, log10, log2, log1p, logaddexp, logaddexp2,
20+
signbit, copysign, frexp, ldexp, sqrt, absolute, fabs, sign,
21+
fmax, fmin, isnan, isinf, maximum, minimum, degrees, radians,
22+
unwrap, fix)
2423

2524
from larray.inout.misc import from_lists, from_string
2625
from larray.inout.pandas import from_frame, from_series
@@ -56,15 +55,16 @@
5655
# metadata
5756
'Metadata',
5857
# ufuncs
59-
'maximum', 'minimum', 'where',
58+
'maximum', 'minimum',
6059
'sin', 'cos', 'tan', 'arcsin', 'arccos', 'arctan', 'hypot', 'arctan2', 'degrees', 'radians',
6160
'unwrap', 'sinh', 'cosh', 'tanh', 'arcsinh', 'arccosh', 'arctanh',
62-
'angle', 'real', 'imag', 'conj',
63-
'round', 'around', 'round_', 'rint', 'fix', 'floor', 'ceil', 'trunc',
64-
'exp', 'expm1', 'exp2', 'log', 'log10', 'log2', 'log1p', 'logaddexp', 'logaddexp2',
65-
'i0', 'sinc', 'signbit', 'copysign', 'frexp', 'ldexp',
66-
'convolve', 'clip', 'sqrt', 'absolute', 'fabs', 'sign', 'fmax', 'fmin', 'nan_to_num',
67-
'real_if_close', 'interp', 'isnan', 'isinf', 'inverse',
61+
'conj', 'rint', 'fix', 'floor', 'ceil', 'trunc', 'exp', 'expm1', 'exp2',
62+
'log', 'log10', 'log2', 'log1p', 'logaddexp', 'logaddexp2',
63+
'signbit', 'copysign', 'frexp', 'ldexp', 'sqrt', 'absolute', 'fabs',
64+
'sign', 'fmax', 'fmin', 'isnan', 'isinf',
65+
# npfuncs
66+
'round', 'around', 'round_', 'where', 'inverse', 'i0', 'sinc', 'angle', 'real', 'imag',
67+
'convolve', 'nan_to_num', 'real_if_close', 'interp', 'clip',
6868
# inout
6969
'from_lists', 'from_string', 'from_frame', 'from_series', 'read_csv', 'read_tsv',
7070
'read_eurostat', 'read_excel', 'read_hdf', 'read_sas', 'open_excel', 'Workbook',

larray/core/array.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
from larray.core.constants import nan
6060
from larray.core.metadata import Metadata
6161
from larray.core.expr import ExprNode
62+
from larray.core.ufuncs import SupportsNumpyUfuncs
6263
from larray.core.group import (Group, IGroup, LGroup, remove_nested_groups, _to_key, _to_keys,
6364
_translate_sheet_name, _translate_group_key_hdf)
6465
from larray.core.axis import Axis, AxisReference, AxisCollection, X, _make_axis
@@ -560,7 +561,7 @@ def _handle_deprecated_argument_title(meta, title):
560561
return meta
561562

562563

563-
class LArray(ABCLArray):
564+
class LArray(ABCLArray, SupportsNumpyUfuncs):
564565
"""
565566
A LArray object represents a multidimensional, homogeneous array of fixed-size items with labeled axes.
566567
@@ -5326,7 +5327,7 @@ def eq(self, other, rtol=0, atol=0, nans_equal=False):
53265327
if not nans_equal:
53275328
return self == other
53285329
else:
5329-
from larray.core.npufuncs import isnan
5330+
from larray.core.ufuncs import isnan
53305331

53315332
def general_isnan(a):
53325333
if np.issubclass_(a.dtype.type, np.inexact):
@@ -6076,7 +6077,7 @@ def clip(self, minval=None, maxval=None, out=None):
60766077
a1 0 1 2
60776078
a2 2 2 2
60786079
"""
6079-
from larray.core.npufuncs import clip
6080+
from larray.core.npfuncs import clip
60806081
return clip(self, minval, maxval, out)
60816082

60826083
@deprecate_kwarg('transpose', 'wide')

larray/core/npfuncs.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# numpy funcs which are not ufuncs -- this module is excluded from pytest
2+
3+
import numpy as np
4+
5+
from larray.core.ufuncs import _broadcastify, _copy_npfunc_doc
6+
7+
8+
def broadcastify(npfunc, copy_doc=True):
9+
# intentionally not using functools.wraps, because it does not work for wrapping a function from another module
10+
def wrapper(*args, **kwargs):
11+
return _broadcastify(npfunc, *args, **kwargs)
12+
if copy_doc:
13+
_copy_npfunc_doc(wrapper, npfunc)
14+
return wrapper
15+
16+
17+
# Rounding
18+
19+
# all 3 are equivalent, I am unsure I should support around and round_
20+
around = broadcastify(np.around)
21+
round_ = broadcastify(np.round_)
22+
round = broadcastify(np.round)
23+
24+
# Sums, products, differences
25+
26+
# prod = broadcastify(np.prod)
27+
# sum = broadcastify(np.sum)
28+
# nansum = broadcastify(np.nansum)
29+
# cumprod = broadcastify(np.cumprod)
30+
# cumsum = broadcastify(np.cumsum)
31+
32+
# cannot use a simple wrapped ufunc because those ufuncs do not preserve
33+
# shape or dimensions so labels are wrong
34+
# diff = broadcastify(np.diff)
35+
# ediff1d = broadcastify(np.ediff1d)
36+
# gradient = broadcastify(np.gradient)
37+
# cross = broadcastify(np.cross)
38+
# trapz = broadcastify(np.trapz)
39+
40+
# Other special functions
41+
42+
i0 = broadcastify(np.i0)
43+
sinc = broadcastify(np.sinc)
44+
45+
# Handling complex numbers
46+
47+
angle = broadcastify(np.angle)
48+
real = broadcastify(np.real)
49+
imag = broadcastify(np.imag)
50+
51+
# Miscellaneous
52+
53+
# put clip here even it is a ufunc because it ends up within a recursion loop with LArray.clip
54+
clip = broadcastify(np.clip)
55+
56+
where = broadcastify(np.where, copy_doc=False)
57+
where.__doc__ = r"""
58+
Return elements, either from `x` or `y`, depending on `condition`.
59+
60+
If only `condition` is given, return ``condition.nonzero()``.
61+
62+
Parameters
63+
----------
64+
condition : boolean LArray
65+
When True, yield `x`, otherwise yield `y`.
66+
x, y : LArray
67+
Values from which to choose.
68+
69+
Returns
70+
-------
71+
out : LArray
72+
If both `x` and `y` are specified, the output array contains
73+
elements of `x` where `condition` is True, and elements from
74+
`y` elsewhere.
75+
76+
Examples
77+
--------
78+
>>> from larray import LArray
79+
>>> arr = LArray([[10, 7, 5, 9],
80+
... [5, 8, 3, 7],
81+
... [6, 2, 0, 9],
82+
... [9, 10, 5, 6]], "a=a0..a3;b=b0..b3")
83+
>>> arr
84+
a\b b0 b1 b2 b3
85+
a0 10 7 5 9
86+
a1 5 8 3 7
87+
a2 6 2 0 9
88+
a3 9 10 5 6
89+
90+
Simple use
91+
92+
>>> where_(arr <= 5, 0, arr)
93+
a\b b0 b1 b2 b3
94+
a0 10 7 0 9
95+
a1 0 8 0 7
96+
a2 6 0 0 9
97+
a3 9 10 0 6
98+
99+
With broadcasting
100+
101+
>>> mean_by_col = arr.mean('a')
102+
>>> mean_by_col
103+
b b0 b1 b2 b3
104+
7.5 6.75 3.25 7.75
105+
>>> # for each column, set values below the mean value to the mean value
106+
>>> where_(arr < mean_by_col, mean_by_col, arr)
107+
a\b b0 b1 b2 b3
108+
a0 10.0 7.0 5.0 9.0
109+
a1 7.5 8.0 3.25 7.75
110+
a2 7.5 6.75 3.25 9.0
111+
a3 9.0 10.0 5.0 7.75
112+
"""
113+
114+
nan_to_num = broadcastify(np.nan_to_num)
115+
real_if_close = broadcastify(np.real_if_close)
116+
convolve = broadcastify(np.convolve)
117+
inverse = broadcastify(np.linalg.inv)
118+
119+
# XXX: create a new LArray method instead ?
120+
# TODO: should appear in the API doc if it actually works with LArrays,
121+
# which I have never tested (and I doubt is the case).
122+
# Might be worth having specific documentation if it works well.
123+
# My guess is that we should rather make a new LArray method for that one.
124+
interp = broadcastify(np.interp)

larray/core/npufuncs.py

Lines changed: 0 additions & 135 deletions
This file was deleted.

0 commit comments

Comments
 (0)