/
allnan.py
295 lines (246 loc) · 7.24 KB
/
allnan.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
"allnan template"
from copy import deepcopy
import bottleneck as bn
__all__ = ["allnan"]
FLOAT_DTYPES = [x for x in bn.dtypes if 'float' in x]
INT_DTYPES = [x for x in bn.dtypes if 'int' in x]
# Float dtypes (not axis=None) ----------------------------------------------
floats = {}
floats['dtypes'] = FLOAT_DTYPES
floats['axisNone'] = False
floats['force_output_dtype'] = 'bool'
floats['reuse_non_nan_func'] = False
floats['top'] = """
@cython.boundscheck(False)
@cython.wraparound(False)
def NAME_NDIMd_DTYPE_axisAXIS(np.ndarray[np.DTYPE_t, ndim=NDIM] a):
"Check for all NaNs in NDIMd array with dtype=DTYPE along axis=AXIS."
cdef int f = 1
cdef np.DTYPE_t ai
"""
loop = {}
loop[2] = """\
for iINDEX0 in range(nINDEX0):
f = 1
for iINDEX1 in range(nINDEX1):
ai = a[INDEXALL]
if ai == ai:
y[INDEXPOP] = 0
f = 0
break
if f == 1:
y[INDEXPOP] = 1
return y
"""
loop[3] = """\
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
f = 1
for iINDEX2 in range(nINDEX2):
ai = a[INDEXALL]
if ai == ai:
y[INDEXPOP] = 0
f = 0
break
if f == 1:
y[INDEXPOP] = 1
return y
"""
floats['loop'] = loop
# Float dtypes (axis=None) --------------------------------------------------
floats_None = deepcopy(floats)
floats_None['axisNone'] = True
loop = {}
loop[1] = """\
for iINDEX0 in range(nINDEX0):
ai = a[INDEXALL]
if ai == ai:
return np.bool_(False)
return np.bool_(True)
"""
loop[2] = """\
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
ai = a[INDEXALL]
if ai == ai:
return np.bool_(False)
return np.bool_(True)
"""
loop[3] = """\
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
for iINDEX2 in range(nINDEX2):
ai = a[INDEXALL]
if ai == ai:
return np.bool_(False)
return np.bool_(True)
"""
floats_None['loop'] = loop
# Int dtypes (not axis=None) ------------------------------------------------
ints = deepcopy(floats)
ints['dtypes'] = INT_DTYPES
loop = {}
loop[2] = """\
if nINDEX1 == 0:
f = 1
else:
f = 0
for iINDEX0 in range(nINDEX0):
y[INDEXPOP] = f
return y
"""
loop[3] = """\
if nINDEX2 == 0:
f = 1
else:
f = 0
for iINDEX0 in range(nINDEX0):
for iINDEX1 in range(nINDEX1):
y[INDEXPOP] = f
return y
"""
ints['loop'] = loop
# Int dtypes (axis=None) ----------------------------------------------------
ints_None = deepcopy(ints)
ints_None['axisNone'] = True
loop = {}
loop[1] = """\
if n0 == 0:
return np.bool_(True)
else:
return np.bool_(False)
"""
loop[2] = """\
if n0* n1 == 0:
return np.bool_(True)
else:
return np.bool_(False)
"""
loop[3] = """\
if n0* n1 * n2 == 0:
return np.bool_(True)
else:
return np.bool_(False)
"""
ints_None['loop'] = loop
# Slow, unaccelerated ndim/dtype --------------------------------------------
slow = {}
slow['name'] = "allnan"
slow['signature'] = "arr"
slow['func'] = "bn.slow.allnan(arr, axis=AXIS)"
# Template ------------------------------------------------------------------
allnan = {}
allnan['name'] = 'allnan'
allnan['is_reducing_function'] = True
allnan['cdef_output'] = True
allnan['slow'] = slow
allnan['templates'] = {}
allnan['templates']['float'] = floats
allnan['templates']['float_None'] = floats_None
allnan['templates']['int'] = ints
allnan['templates']['int_None'] = ints_None
allnan['pyx_file'] = 'func/allnan.pyx'
allnan['main'] = '''"allnan auto-generated from template"
def allnan(arr, axis=None):
"""
Test whether all array elements along a given axis are NaN.
Returns single boolean unless `axis` is not ``None``.
Note that allnan([]) is True to match np.isnan([]).all().
Parameters
----------
arr : array_like
Input array. If `arr` is not an array, a conversion is attempted.
axis : {int, None}, optional
Axis along which NaNs are searched. The default (`axis` = ``None``)
is to search for NaNs over a flattened input array. `axis` may be
negative, in which case it counts from the last to the first axis.
Returns
-------
y : bool or ndarray
A new boolean or `ndarray` is returned.
See also
--------
bottleneck.anynan: Test if any array element along given axis is NaN
Examples
--------
>>> bn.allnan(1)
False
>>> bn.allnan(np.nan)
True
>>> bn.allnan([1, np.nan])
False
>>> a = np.array([[1, np.nan], [1, np.nan]])
>>> bn.allnan(a)
False
>>> bn.allnan(a, axis=0)
array([False, True], dtype=bool)
An empty array returns True:
>>> bn.allnan([])
True
which is similar to:
>>> all([])
True
>>> np.isnan([]).all()
True
"""
func, arr = allnan_selector(arr, axis)
return func(arr)
def allnan_selector(arr, axis):
"""
Return allnan function and array that matches `arr` and `axis`.
Under the hood Bottleneck uses a separate Cython function for each
combination of ndim, dtype, and axis. A lot of the overhead in bn.allnan()
is in checking that `axis` is within range, converting `arr` into an
array (if it is not already an array), and selecting the function to use.
You can get rid of the overhead by doing all this before you, for example,
enter an inner loop, by using this function.
Parameters
----------
arr : array_like
Input array. If `arr` is not an array, a conversion is attempted.
axis : {int, None}
Axis along which NaNs are searched.
Returns
-------
func : function
The allnan function that matches the number of dimensions and
dtype of the input array and the axis.
a : ndarray
If the input array `arr` is not a ndarray, then `a` will contain the
result of converting `arr` into a ndarray.
Examples
--------
Create a numpy array:
>>> arr = np.array([1.0, 2.0, 3.0])
Obtain the function needed to determine if `arr` contains all NaNs:
>>> func, a = bn.func.allnan_selector(arr, axis=0)
>>> func
<function allnan_1d_float64_axisNone>
Use the returned function and array to determine is all elements are
NaN:
>>> func(a)
False
"""
cdef np.ndarray a
if type(arr) is np.ndarray:
a = arr
else:
a = np.array(arr, copy=False)
cdef int ndim = PyArray_NDIM(a)
cdef int dtype = PyArray_TYPE(a)
if (axis is not None) and (axis < 0):
axis += ndim
cdef tuple key = (ndim, dtype, axis)
try:
func = allnan_dict[key]
except KeyError:
if axis is not None:
if (axis < 0) or (axis >= ndim):
raise ValueError("axis(=%d) out of bounds" % axis)
try:
func = allnan_slow_dict[axis]
except KeyError:
tup = (str(ndim), str(a.dtype), str(axis))
raise TypeError("Unsupported ndim/dtype/axis (%s/%s/%s)." % tup)
return func, a
'''