1
1
import sys
2
+ import itertools
3
+ import functools
2
4
3
5
import numpy as np
4
6
5
7
from pandas .core .common import isnull , notnull
6
8
import pandas .core .common as com
7
- import pandas .core .config as cf
8
9
import pandas .lib as lib
9
10
import pandas .algos as algos
10
11
import pandas .hashtable as _hash
17
18
_USE_BOTTLENECK = False
18
19
19
20
20
- def _bottleneck_switch (bn_name , alt , zero_value = None , ** kwargs ):
21
- try :
22
- bn_func = getattr (bn , bn_name )
23
- except (AttributeError , NameError ): # pragma: no cover
24
- bn_func = None
21
+ class disallow (object ):
22
+ def __init__ (self , * dtypes ):
23
+ super (disallow , self ).__init__ ()
24
+ self .dtypes = tuple (np .dtype (dtype ).type for dtype in dtypes )
25
+
26
+ def check (self , obj ):
27
+ return hasattr (obj , 'dtype' ) and issubclass (obj .dtype .type ,
28
+ self .dtypes )
29
+
30
+ def __call__ (self , f ):
31
+ @functools .wraps (f )
32
+ def _f (* args , ** kwargs ):
33
+ obj_iter = itertools .chain (args , kwargs .itervalues ())
34
+ if any (self .check (obj ) for obj in obj_iter ):
35
+ raise TypeError ('reduction operation {0!r} not allowed for '
36
+ 'this dtype' .format (f .__name__ .replace ('nan' ,
37
+ '' )))
38
+ return f (* args , ** kwargs )
39
+ return _f
40
+
41
+
42
+ class bottleneck_switch (object ):
43
+ def __init__ (self , zero_value = None , ** kwargs ):
44
+ self .zero_value = zero_value
45
+ self .kwargs = kwargs
46
+
47
+ def __call__ (self , alt ):
48
+ bn_name = alt .__name__
25
49
26
- def f (values , axis = None , skipna = True , ** kwds ):
27
- if len (kwargs ) > 0 :
28
- for k , v in kwargs .iteritems ():
29
- if k not in kwds :
30
- kwds [k ] = v
31
50
try :
32
- if zero_value is not None and values .size == 0 :
33
- if values .ndim == 1 :
34
- return 0
51
+ bn_func = getattr (bn , bn_name )
52
+ except (AttributeError , NameError ): # pragma: no cover
53
+ bn_func = None
54
+
55
+ @functools .wraps (alt )
56
+ def f (values , axis = None , skipna = True , ** kwds ):
57
+ if len (self .kwargs ) > 0 :
58
+ for k , v in self .kwargs .iteritems ():
59
+ if k not in kwds :
60
+ kwds [k ] = v
61
+ try :
62
+ if self .zero_value is not None and values .size == 0 :
63
+ if values .ndim == 1 :
64
+ return 0
65
+ else :
66
+ result_shape = (values .shape [:axis ] +
67
+ values .shape [axis + 1 :])
68
+ result = np .empty (result_shape )
69
+ result .fill (0 )
70
+ return result
71
+
72
+ if _USE_BOTTLENECK and skipna and _bn_ok_dtype (values .dtype ):
73
+ result = bn_func (values , axis = axis , ** kwds )
74
+ # prefer to treat inf/-inf as NA
75
+ if _has_infs (result ):
76
+ result = alt (values , axis = axis , skipna = skipna , ** kwds )
35
77
else :
36
- result_shape = values .shape [:
37
- axis ] + values .shape [axis + 1 :]
38
- result = np .empty (result_shape )
39
- result .fill (0 )
40
- return result
41
-
42
- if _USE_BOTTLENECK and skipna and _bn_ok_dtype (values .dtype ):
43
- result = bn_func (values , axis = axis , ** kwds )
44
- # prefer to treat inf/-inf as NA
45
- if _has_infs (result ):
46
78
result = alt (values , axis = axis , skipna = skipna , ** kwds )
47
- else :
79
+ except Exception :
48
80
result = alt (values , axis = axis , skipna = skipna , ** kwds )
49
- except Exception :
50
- result = alt (values , axis = axis , skipna = skipna , ** kwds )
51
81
52
- return result
82
+ return result
53
83
54
- return f
84
+ return f
55
85
56
86
57
87
def _bn_ok_dtype (dt ):
@@ -166,13 +196,17 @@ def nanall(values, axis=None, skipna=True):
166
196
values , mask , dtype = _get_values (values , skipna , True , copy = skipna )
167
197
return values .all (axis )
168
198
169
- def _nansum (values , axis = None , skipna = True ):
199
+ @disallow ('M8' )
200
+ @bottleneck_switch (zero_value = 0 )
201
+ def nansum (values , axis = None , skipna = True ):
170
202
values , mask , dtype = _get_values (values , skipna , 0 )
171
203
the_sum = values .sum (axis )
172
204
the_sum = _maybe_null_out (the_sum , axis , mask )
173
205
return the_sum
174
206
175
- def _nanmean (values , axis = None , skipna = True ):
207
+ @disallow ('M8' )
208
+ @bottleneck_switch ()
209
+ def nanmean (values , axis = None , skipna = True ):
176
210
values , mask , dtype = _get_values (values , skipna , 0 )
177
211
the_sum = _ensure_numeric (values .sum (axis ))
178
212
count = _get_counts (mask , axis )
@@ -186,8 +220,9 @@ def _nanmean(values, axis=None, skipna=True):
186
220
the_mean = the_sum / count if count > 0 else np .nan
187
221
return the_mean
188
222
189
-
190
- def _nanmedian (values , axis = None , skipna = True ):
223
+ @disallow ('M8' )
224
+ @bottleneck_switch ()
225
+ def nanmedian (values , axis = None , skipna = True ):
191
226
def get_median (x ):
192
227
mask = notnull (x )
193
228
if not skipna and not mask .all ():
@@ -197,13 +232,31 @@ def get_median(x):
197
232
if values .dtype != np .float64 :
198
233
values = values .astype ('f8' )
199
234
200
- if values .ndim > 1 :
201
- return np .apply_along_axis (get_median , axis , values )
202
- else :
203
- return get_median (values )
235
+ notempty = values .size
204
236
205
-
206
- def _nanvar (values , axis = None , skipna = True , ddof = 1 ):
237
+ # an array from a frame
238
+ if values .ndim > 1 :
239
+ # there's a non-empty array to apply over otherwise numpy raises
240
+ if notempty :
241
+ return np .apply_along_axis (get_median , axis , values )
242
+
243
+ # must return the correct shape, but median is not defined for the
244
+ # empty set so return nans of shape "everything but the passed axis"
245
+ # since "axis" is where the reduction would occur if we had a nonempty
246
+ # array
247
+ shp = np .array (values .shape )
248
+ dims = np .arange (values .ndim )
249
+ ret = np .empty (shp [dims != axis ])
250
+ ret .fill (np .nan )
251
+ return ret
252
+
253
+ # otherwise return a scalar value
254
+ return get_median (values ) if notempty else np .nan
255
+
256
+
257
+ @disallow ('M8' )
258
+ @bottleneck_switch (ddof = 1 )
259
+ def nanvar (values , axis = None , skipna = True , ddof = 1 ):
207
260
if not isinstance (values .dtype .type , np .floating ):
208
261
values = values .astype ('f8' )
209
262
@@ -223,7 +276,8 @@ def _nanvar(values, axis=None, skipna=True, ddof=1):
223
276
return np .fabs ((XX - X ** 2 / count ) / (count - ddof ))
224
277
225
278
226
- def _nanmin (values , axis = None , skipna = True ):
279
+ @bottleneck_switch ()
280
+ def nanmin (values , axis = None , skipna = True ):
227
281
values , mask , dtype = _get_values (values , skipna , fill_value_typ = '+inf' )
228
282
229
283
# numpy 1.6.1 workaround in Python 3.x
@@ -247,7 +301,8 @@ def _nanmin(values, axis=None, skipna=True):
247
301
return _maybe_null_out (result , axis , mask )
248
302
249
303
250
- def _nanmax (values , axis = None , skipna = True ):
304
+ @bottleneck_switch ()
305
+ def nanmax (values , axis = None , skipna = True ):
251
306
values , mask , dtype = _get_values (values , skipna , fill_value_typ = '-inf' )
252
307
253
308
# numpy 1.6.1 workaround in Python 3.x
@@ -291,14 +346,8 @@ def nanargmin(values, axis=None, skipna=True):
291
346
result = _maybe_arg_null_out (result , axis , mask , skipna )
292
347
return result
293
348
294
- nansum = _bottleneck_switch ('nansum' , _nansum , zero_value = 0 )
295
- nanmean = _bottleneck_switch ('nanmean' , _nanmean )
296
- nanmedian = _bottleneck_switch ('nanmedian' , _nanmedian )
297
- nanvar = _bottleneck_switch ('nanvar' , _nanvar , ddof = 1 )
298
- nanmin = _bottleneck_switch ('nanmin' , _nanmin )
299
- nanmax = _bottleneck_switch ('nanmax' , _nanmax )
300
-
301
349
350
+ @disallow ('M8' )
302
351
def nanskew (values , axis = None , skipna = True ):
303
352
if not isinstance (values .dtype .type , np .floating ):
304
353
values = values .astype ('f8' )
@@ -332,6 +381,7 @@ def nanskew(values, axis=None, skipna=True):
332
381
return result
333
382
334
383
384
+ @disallow ('M8' )
335
385
def nankurt (values , axis = None , skipna = True ):
336
386
if not isinstance (values .dtype .type , np .floating ):
337
387
values = values .astype ('f8' )
@@ -365,6 +415,7 @@ def nankurt(values, axis=None, skipna=True):
365
415
return result
366
416
367
417
418
+ @disallow ('M8' )
368
419
def nanprod (values , axis = None , skipna = True ):
369
420
mask = isnull (values )
370
421
if skipna and not issubclass (values .dtype .type , np .integer ):
@@ -423,6 +474,7 @@ def _zero_out_fperr(arg):
423
474
return 0 if np .abs (arg ) < 1e-14 else arg
424
475
425
476
477
+ @disallow ('M8' )
426
478
def nancorr (a , b , method = 'pearson' , min_periods = None ):
427
479
"""
428
480
a, b: ndarrays
@@ -469,6 +521,7 @@ def _spearman(a, b):
469
521
return _cor_methods [method ]
470
522
471
523
524
+ @disallow ('M8' )
472
525
def nancov (a , b , min_periods = None ):
473
526
if len (a ) != len (b ):
474
527
raise AssertionError ('Operands to nancov must have same size' )
0 commit comments