11import sys
2+ import itertools
3+ import functools
24
35import numpy as np
46
57from pandas .core .common import isnull , notnull
68import pandas .core .common as com
7- import pandas .core .config as cf
89import pandas .lib as lib
910import pandas .algos as algos
1011import pandas .hashtable as _hash
1718 _USE_BOTTLENECK = False
1819
1920
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__
2549
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
3150 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 )
3577 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 ):
4678 result = alt (values , axis = axis , skipna = skipna , ** kwds )
47- else :
79+ except Exception :
4880 result = alt (values , axis = axis , skipna = skipna , ** kwds )
49- except Exception :
50- result = alt (values , axis = axis , skipna = skipna , ** kwds )
5181
52- return result
82+ return result
5383
54- return f
84+ return f
5585
5686
5787def _bn_ok_dtype (dt ):
@@ -166,13 +196,17 @@ def nanall(values, axis=None, skipna=True):
166196 values , mask , dtype = _get_values (values , skipna , True , copy = skipna )
167197 return values .all (axis )
168198
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 ):
170202 values , mask , dtype = _get_values (values , skipna , 0 )
171203 the_sum = values .sum (axis )
172204 the_sum = _maybe_null_out (the_sum , axis , mask )
173205 return the_sum
174206
175- def _nanmean (values , axis = None , skipna = True ):
207+ @disallow ('M8' )
208+ @bottleneck_switch ()
209+ def nanmean (values , axis = None , skipna = True ):
176210 values , mask , dtype = _get_values (values , skipna , 0 )
177211 the_sum = _ensure_numeric (values .sum (axis ))
178212 count = _get_counts (mask , axis )
@@ -186,8 +220,9 @@ def _nanmean(values, axis=None, skipna=True):
186220 the_mean = the_sum / count if count > 0 else np .nan
187221 return the_mean
188222
189-
190- def _nanmedian (values , axis = None , skipna = True ):
223+ @disallow ('M8' )
224+ @bottleneck_switch ()
225+ def nanmedian (values , axis = None , skipna = True ):
191226 def get_median (x ):
192227 mask = notnull (x )
193228 if not skipna and not mask .all ():
@@ -197,13 +232,31 @@ def get_median(x):
197232 if values .dtype != np .float64 :
198233 values = values .astype ('f8' )
199234
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
204236
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 ):
207260 if not isinstance (values .dtype .type , np .floating ):
208261 values = values .astype ('f8' )
209262
@@ -223,7 +276,8 @@ def _nanvar(values, axis=None, skipna=True, ddof=1):
223276 return np .fabs ((XX - X ** 2 / count ) / (count - ddof ))
224277
225278
226- def _nanmin (values , axis = None , skipna = True ):
279+ @bottleneck_switch ()
280+ def nanmin (values , axis = None , skipna = True ):
227281 values , mask , dtype = _get_values (values , skipna , fill_value_typ = '+inf' )
228282
229283 # numpy 1.6.1 workaround in Python 3.x
@@ -247,7 +301,8 @@ def _nanmin(values, axis=None, skipna=True):
247301 return _maybe_null_out (result , axis , mask )
248302
249303
250- def _nanmax (values , axis = None , skipna = True ):
304+ @bottleneck_switch ()
305+ def nanmax (values , axis = None , skipna = True ):
251306 values , mask , dtype = _get_values (values , skipna , fill_value_typ = '-inf' )
252307
253308 # numpy 1.6.1 workaround in Python 3.x
@@ -291,14 +346,8 @@ def nanargmin(values, axis=None, skipna=True):
291346 result = _maybe_arg_null_out (result , axis , mask , skipna )
292347 return result
293348
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-
301349
350+ @disallow ('M8' )
302351def nanskew (values , axis = None , skipna = True ):
303352 if not isinstance (values .dtype .type , np .floating ):
304353 values = values .astype ('f8' )
@@ -332,6 +381,7 @@ def nanskew(values, axis=None, skipna=True):
332381 return result
333382
334383
384+ @disallow ('M8' )
335385def nankurt (values , axis = None , skipna = True ):
336386 if not isinstance (values .dtype .type , np .floating ):
337387 values = values .astype ('f8' )
@@ -365,6 +415,7 @@ def nankurt(values, axis=None, skipna=True):
365415 return result
366416
367417
418+ @disallow ('M8' )
368419def nanprod (values , axis = None , skipna = True ):
369420 mask = isnull (values )
370421 if skipna and not issubclass (values .dtype .type , np .integer ):
@@ -423,6 +474,7 @@ def _zero_out_fperr(arg):
423474 return 0 if np .abs (arg ) < 1e-14 else arg
424475
425476
477+ @disallow ('M8' )
426478def nancorr (a , b , method = 'pearson' , min_periods = None ):
427479 """
428480 a, b: ndarrays
@@ -469,6 +521,7 @@ def _spearman(a, b):
469521 return _cor_methods [method ]
470522
471523
524+ @disallow ('M8' )
472525def nancov (a , b , min_periods = None ):
473526 if len (a ) != len (b ):
474527 raise AssertionError ('Operands to nancov must have same size' )
0 commit comments