1import numpy as np
2import bottleneck as bn
3
4INT_DTYPES = [np.int64, np.int32]
5FLOAT_DTYPES = [np.float64, np.float32]
6DTYPES = tuple(FLOAT_DTYPES + INT_DTYPES)
7
8
9def get_functions(module_name, as_string=False):
10 """Returns a list of functions, optionally as string function names"""
11 if module_name == "all":
12 funcs = []
13 funcs_in_dict = func_dict()
14 for key in funcs_in_dict:
15 for func in funcs_in_dict[key]:
16 funcs.append(func)
17 else:
18 funcs = func_dict()[module_name]
19 if as_string:
20 funcs = [f.__name__ for f in funcs]
21 return funcs
22
23
24def func_dict():
25 d = {}
26 d["reduce"] = [
27 bn.nansum,
28 bn.nanmean,
29 bn.nanstd,
30 bn.nanvar,
31 bn.nanmin,
32 bn.nanmax,
33 bn.median,
34 bn.nanmedian,
35 bn.ss,
36 bn.nanargmin,
37 bn.nanargmax,
38 bn.anynan,
39 bn.allnan,
40 ]
41 d["move"] = [
42 bn.move_sum,
43 bn.move_mean,
44 bn.move_std,
45 bn.move_var,
46 bn.move_min,
47 bn.move_max,
48 bn.move_argmin,
49 bn.move_argmax,
50 bn.move_median,
51 bn.move_rank,
52 ]
53 d["nonreduce"] = [bn.replace]
54 d["nonreduce_axis"] = [
55 bn.partition,
56 bn.argpartition,
57 bn.rankdata,
58 bn.nanrankdata,
59 bn.push,
60 ]
61 return d
62
63
64# ---------------------------------------------------------------------------
65
66
67def arrays(func_name, dtypes=DTYPES):
68 return array_iter(array_generator, func_name, dtypes)
69
70
71def array_iter(arrays_func, *args):
72 for a in arrays_func(*args):
73 if a.ndim < 2:
74 yield a
75 # this is good for an extra check but in everyday development it
76 # is a pain because it doubles the unit test run time
77 # elif a.ndim == 3:
78 # for axes in permutations(range(a.ndim)):
79 # yield np.transpose(a, axes)
80 else:
81 yield a
82 yield a.T
83
84
85def array_generator(func_name, dtypes):
86 """Iterator that yields arrays to use for unit testing."""
87
88 f_dtypes = list(set(dtypes) & set(FLOAT_DTYPES))
89
90 # define nan and inf
91 if func_name in ("partition", "argpartition"):
92 nan = 0
93 else:
94 nan = np.nan
95 if func_name in ("move_sum", "move_mean", "move_std", "move_var"):
96 # these functions can't handle inf
97 inf = 8
98 else:
99 inf = np.inf
100
101 # nan and inf
102 for dtype in f_dtypes:
103 yield np.array([inf, nan], dtype=dtype)
104 yield np.array([inf, -inf], dtype=dtype)
105 yield np.array([nan, 2, 3], dtype=dtype)
106 yield np.array([-inf, 2, 3], dtype=dtype)
107 if func_name != "nanargmin":
108 yield np.array([nan, inf], dtype=dtype)
109
110 # byte swapped
111 yield np.array([1, 2, 3], dtype=">f4")
112 yield np.array([1, 2, 3], dtype="<f4")
113
114 # make sure slow is callable
115 yield np.array([1, 2, 3], dtype=np.float16)
116
117 # regression tests
118 for dtype in dtypes:
119 yield np.array([1, 2, 3], dtype=dtype) + 1e9 # check that move_std is robust
120 yield np.array([0, 0, 0], dtype=dtype) # nanargmax/nanargmin
121
122 for dtype in f_dtypes:
123 yield np.array([1, nan, nan, 2], dtype=dtype) # nanmedian
124
125 yield np.array([2 ** 31], dtype=np.int64) # overflows on windows
126
127 for dtype in dtypes:
128 yield np.array([[1, 2], [3, 4]], dtype=dtype)[..., np.newaxis] # issue #183
129
130 # ties
131 for dtype in dtypes:
132 yield np.array([0, 0, 0], dtype=dtype)
133 yield np.array([1, 1, 1], dtype=dtype)
134
135 # 0d input
136 if not func_name.startswith("move"):
137 for dtype in dtypes:
138 yield np.array(-9, dtype=dtype)
139 yield np.array(0, dtype=dtype)
140 yield np.array(9, dtype=dtype)
141 if dtype in f_dtypes:
142 yield np.array(-inf, dtype=dtype)
143 yield np.array(inf, dtype=dtype)
144 yield np.array(nan, dtype=dtype)
145
146 # automate a bunch of arrays to test
147 ss = {}
148 ss[0] = {"size": 0, "shapes": [(0,), (0, 0), (2, 0), (2, 0, 1)]}
149 ss[1] = {"size": 8, "shapes": [(8,)]}
150 ss[2] = {"size": 12, "shapes": [(2, 6), (3, 4)]}
151 ss[3] = {"size": 16, "shapes": [(2, 2, 4)]}
152 ss[4] = {"size": 24, "shapes": [(1, 2, 3, 4)]}
153 for seed in (1, 2):
154 rs = np.random.RandomState(seed)
155 for ndim in ss:
156 size = ss[ndim]["size"]
157 shapes = ss[ndim]["shapes"]
158 for dtype in dtypes:
159 a = np.arange(size, dtype=dtype)
160 if issubclass(a.dtype.type, np.inexact):
161 if func_name not in ("nanargmin", "nanargmax"):
162 # numpy can't handle eg np.nanargmin([np.nan, np.inf])
163 idx = rs.rand(*a.shape) < 0.2
164 a[idx] = inf
165 idx = rs.rand(*a.shape) < 0.2
166 a[idx] = nan
167 idx = rs.rand(*a.shape) < 0.2
168 a[idx] *= -1
169 rs.shuffle(a)
170 for shape in shapes:
171 yield a.reshape(shape)
172
173 # non-contiguous arrays
174 for dtype in dtypes:
175 yield np.array([[1, 2], [3, 4]], dtype=dtype)[:, [1]] # gh 161
176
177 for dtype in dtypes:
178 # 1d
179 a = np.arange(12).astype(dtype)
180 for start in range(3):
181 for step in range(1, 3):
182 yield a[start::step] # don't use astype here; copy created
183 for dtype in dtypes:
184 # 2d
185 a = np.arange(12).reshape(4, 3).astype(dtype)
186 yield a[::2]
187 yield a[:, ::2]
188 yield a[::2][:, ::2]
189 for dtype in dtypes:
190 # 3d
191 a = np.arange(24).reshape(2, 3, 4).astype(dtype)
192 for start in range(2):
193 for step in range(1, 2):
194 yield a[start::step]
195 yield a[:, start::step]
196 yield a[:, :, start::step]
197 yield a[start::step][::2]
198 yield a[start::step][::2][:, ::2]
199
200
201def array_order(a):
202 f = a.flags
203 string = []
204 if f.c_contiguous:
205 string.append("C")
206 if f.f_contiguous:
207 string.append("F")
208 if len(string) == 0:
209 string.append("N")
210 return ",".join(string)