6
6
from larray .core .array import LArray , make_args_broadcastable
7
7
8
8
9
- def broadcastify (func ):
10
- # intentionally not using functools.wraps, because it does not work for wrapping a function from another module
9
+ def wrap_elementwise_array_func (func ):
10
+ r"""
11
+ Wrap a function using numpy arrays to work with LArray arrays instead.
12
+
13
+ Parameters
14
+ ----------
15
+ func : function
16
+ A function taking numpy arrays as arguments and returning numpy arrays of the same shape. If the function
17
+ takes several arguments, this wrapping code assumes the result will have the combination of all axes present.
18
+ In numpy talk, arguments will be broadcasted to each other.
19
+
20
+ Returns
21
+ -------
22
+ function
23
+ A function taking LArray arguments and returning LArrays.
24
+
25
+ Examples
26
+ --------
27
+ For example, if we want to apply the Hodrick-Prescott filter from statsmodels we can use this:
28
+
29
+ >>> from statsmodels.tsa.filters.hp_filter import hpfilter # doctest: +SKIP
30
+ >>> hpfilter = wrap_elementwise_array_func(hpfilter) # doctest: +SKIP
31
+
32
+ hpfilter is now a function taking a one dimensional LArray as input and returning a one dimensional LArray as output
33
+
34
+ Now let us suppose we have a ND array such as:
35
+
36
+ >>> from larray.random import normal
37
+ >>> arr = normal(axes="sex=M,F;year=2016..2018") # doctest: +SKIP
38
+ >>> arr # doctest: +SKIP
39
+ sex\year 2016 2017 2018
40
+ M -1.15 0.56 -1.06
41
+ F -0.48 -0.39 -0.98
42
+
43
+ We can apply an Hodrick-Prescott filter to it by using:
44
+
45
+ >>> # 6.25 is the recommended smoothing value for annual data
46
+ >>> cycle, trend = arr.apply(hpfilter, 6.25, axes="year") # doctest: +SKIP
47
+ >>> trend # doctest: +SKIP
48
+ sex\year 2016 2017 2018
49
+ M -0.61 -0.52 -0.52
50
+ F -0.37 -0.61 -0.87
51
+ """
11
52
def wrapper (* args , ** kwargs ):
12
53
raw_bcast_args , raw_bcast_kwargs , res_axes = make_args_broadcastable (args , kwargs )
13
54
@@ -25,11 +66,21 @@ def wrapper(*args, **kwargs):
25
66
# it does this because numpy calls __array_wrap__ on the argument with the highest __array_priority__
26
67
res_data = func (* raw_bcast_args , ** raw_bcast_kwargs )
27
68
if res_axes :
28
- return LArray (res_data , res_axes )
69
+ if isinstance (res_data , tuple ):
70
+ return tuple (LArray (res_arr , res_axes ) for res_arr in res_data )
71
+ else :
72
+ return LArray (res_data , res_axes )
29
73
else :
30
74
return res_data
31
- # copy meaningful attributes (numpy ufuncs do not have __annotations__ nor __qualname__)
75
+ # copy function name. We are intentionally not using functools.wraps, because it does not work for wrapping a
76
+ # function from another module
32
77
wrapper .__name__ = func .__name__
78
+ return wrapper
79
+
80
+
81
+ # TODO: rename to wrap_numpy_func
82
+ def broadcastify (func ):
83
+ wrapper = wrap_elementwise_array_func (func )
33
84
# update documentation by inserting a warning message after the short description of the numpy function
34
85
# (otherwise the description of ufuncs given in the corresponding API 'autosummary' tables will always
35
86
# start with 'larray specific variant of ...' without giving a meaningful description of what does the ufunc)
0 commit comments