From 75ae820c74e927679af82ef634a8f3ef0f97503f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 14 Apr 2014 08:43:20 -0400 Subject: BUG: many functions silently drop `keepdims` kwarg change test from `type(a) is not mu.ndarray` to `not isinstance(a, mu.ndarray)` Because every sub-class of ndarray is not guaranteed to implement `keepdims` as a kwarg, when wrapping these methods care must be taken. The previous behavior was to silently eat the kwarg when dealing with a sub-class of ndarray. Now, if `keepdims=np._NoValue` (the new default) it is not passed through to the underlying function call (so the default value of `keepdims` is now controlled by the sub-class). If `keepdims` is not `np._NoValue` then it is passed through and will raise an exception if the sub-class does not support the kwarg. A special case in nanvar was required to deal with `matrix` that previously relied on `fromnumeric` silently dropping `keepdims`. --- numpy/core/fromnumeric.py | 195 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 136 insertions(+), 59 deletions(-) (limited to 'numpy/core/fromnumeric.py') diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 4faeb557a..bb89adbe1 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -17,7 +17,6 @@ from . import _methods _dt_ = nt.sctype2char - # functions that are methods __all__ = [ 'alen', 'all', 'alltrue', 'amax', 'amin', 'any', 'argmax', @@ -1380,6 +1379,7 @@ def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None): return asanyarray(a).trace(offset, axis1, axis2, dtype, out) + def ravel(a, order='C'): """Return a contiguous flattened array. @@ -1740,7 +1740,7 @@ def clip(a, a_min, a_max, out=None): return clip(a_min, a_max, out) -def sum(a, axis=None, dtype=None, out=None, keepdims=False): +def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Sum of array elements over a given axis. @@ -1770,9 +1770,15 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=False): the same shape as the expected output, but the type of the output values will be cast if necessary. keepdims : bool, optional - If this is set to True, the axes which are reduced are left in the - result as dimensions with size one. With this option, the result - will broadcast correctly against the input array. + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `arr`. + + If the default value is passed, then `keepdims` will not be + passed through to the `sum` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. Returns ------- @@ -1821,6 +1827,9 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=False): -128 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if isinstance(a, _gentype): res = _sum_(a) if out is not None: @@ -1832,15 +1841,14 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=False): sum = a.sum except AttributeError: return _methods._sum(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) - # NOTE: Dropping the keepdims parameters here... - return sum(axis=axis, dtype=dtype, out=out) + out=out, **kwargs) + return sum(axis=axis, dtype=dtype, out=out, **kwargs) else: return _methods._sum(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + out=out, **kwargs) -def product(a, axis=None, dtype=None, out=None, keepdims=False): +def product(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Return the product of array elements over a given axis. @@ -1849,11 +1857,13 @@ def product(a, axis=None, dtype=None, out=None, keepdims=False): prod : equivalent function; see for details. """ - return um.multiply.reduce(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return um.multiply.reduce(a, axis=axis, dtype=dtype, out=out, **kwargs) -def sometrue(a, axis=None, out=None, keepdims=False): +def sometrue(a, axis=None, out=None, keepdims=np._NoValue): """ Check whether some values are true. @@ -1865,14 +1875,13 @@ def sometrue(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) - - try: - return arr.any(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.any(axis=axis, out=out) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.any(axis=axis, out=out, **kwargs) -def alltrue(a, axis=None, out=None, keepdims=False): +def alltrue(a, axis=None, out=None, keepdims=np._NoValue): """ Check if all elements of input array are true. @@ -1882,14 +1891,13 @@ def alltrue(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.all(axis=axis, out=out, **kwargs) - try: - return arr.all(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.all(axis=axis, out=out) - -def any(a, axis=None, out=None, keepdims=False): +def any(a, axis=None, out=None, keepdims=np._NoValue): """ Test whether any array element along a given axis evaluates to True. @@ -1915,11 +1923,18 @@ def any(a, axis=None, out=None, keepdims=False): (e.g., if it is of type float, then it will remain so, returning 1.0 for True and 0.0 for False, regardless of the type of `a`). See `doc.ufuncs` (Section "Output arguments") for details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `any` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- any : bool or ndarray @@ -1963,14 +1978,13 @@ def any(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) - - try: - return arr.any(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.any(axis=axis, out=out) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.any(axis=axis, out=out, **kwargs) -def all(a, axis=None, out=None, keepdims=False): +def all(a, axis=None, out=None, keepdims=np._NoValue): """ Test whether all array elements along a given axis evaluate to True. @@ -1994,11 +2008,18 @@ def all(a, axis=None, out=None, keepdims=False): type is preserved (e.g., if ``dtype(out)`` is float, the result will consist of 0.0's and 1.0's). See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `all` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- all : ndarray, bool @@ -2037,11 +2058,10 @@ def all(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) - - try: - return arr.all(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.all(axis=axis, out=out) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.all(axis=axis, out=out, **kwargs) def cumsum(a, axis=None, dtype=None, out=None): @@ -2177,7 +2197,7 @@ def ptp(a, axis=None, out=None): return ptp(axis, out) -def amax(a, axis=None, out=None, keepdims=False): +def amax(a, axis=None, out=None, keepdims=np._NoValue): """ Return the maximum of an array or maximum along an axis. @@ -2197,11 +2217,18 @@ def amax(a, axis=None, out=None, keepdims=False): Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `amax` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- amax : ndarray or scalar @@ -2255,20 +2282,22 @@ def amax(a, axis=None, out=None, keepdims=False): 4.0 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if type(a) is not mu.ndarray: try: amax = a.max except AttributeError: return _methods._amax(a, axis=axis, - out=out, keepdims=keepdims) - # NOTE: Dropping the keepdims parameter - return amax(axis=axis, out=out) + out=out, **kwargs) + return amax(axis=axis, out=out, **kwargs) else: return _methods._amax(a, axis=axis, - out=out, keepdims=keepdims) + out=out, **kwargs) -def amin(a, axis=None, out=None, keepdims=False): +def amin(a, axis=None, out=None, keepdims=np._NoValue): """ Return the minimum of an array or minimum along an axis. @@ -2288,11 +2317,18 @@ def amin(a, axis=None, out=None, keepdims=False): Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `amin` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- amin : ndarray or scalar @@ -2346,17 +2382,19 @@ def amin(a, axis=None, out=None, keepdims=False): 0.0 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if type(a) is not mu.ndarray: try: amin = a.min except AttributeError: return _methods._amin(a, axis=axis, - out=out, keepdims=keepdims) - # NOTE: Dropping the keepdims parameter - return amin(axis=axis, out=out) + out=out, **kwargs) + return amin(axis=axis, out=out, **kwargs) else: return _methods._amin(a, axis=axis, - out=out, keepdims=keepdims) + out=out, **kwargs) def alen(a): @@ -2392,7 +2430,7 @@ def alen(a): return len(array(a, ndmin=1)) -def prod(a, axis=None, dtype=None, out=None, keepdims=False): +def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Return the product of array elements over a given axis. @@ -2427,6 +2465,12 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=False): result as dimensions with size one. With this option, the result will broadcast correctly against the input array. + If the default value is passed, then `keepdims` will not be + passed through to the `prod` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- product_along_axis : ndarray, see `dtype` parameter above. @@ -2484,16 +2528,19 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=False): True """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if type(a) is not mu.ndarray: try: prod = a.prod except AttributeError: return _methods._prod(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) - return prod(axis=axis, dtype=dtype, out=out) + out=out, **kwargs) + return prod(axis=axis, dtype=dtype, out=out, **kwargs) else: return _methods._prod(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + out=out, **kwargs) def cumprod(a, axis=None, dtype=None, out=None): @@ -2793,7 +2840,7 @@ def round_(a, decimals=0, out=None): return round(decimals, out) -def mean(a, axis=None, dtype=None, out=None, keepdims=False): +def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Compute the arithmetic mean along the specified axis. @@ -2823,11 +2870,18 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=False): is ``None``; if provided, it must have the same shape as the expected output, but the type will be cast if necessary. See `doc.ufuncs` for details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `mean` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- m : ndarray, see dtype parameter above @@ -2874,18 +2928,21 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=False): 0.55000000074505806 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if type(a) is not mu.ndarray: try: mean = a.mean - return mean(axis=axis, dtype=dtype, out=out) + return mean(axis=axis, dtype=dtype, out=out, **kwargs) except AttributeError: pass return _methods._mean(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + out=out, **kwargs) -def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): """ Compute the standard deviation along the specified axis. @@ -2922,6 +2979,12 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `std` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- standard_deviation : ndarray, see dtype parameter above. @@ -2981,19 +3044,23 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): 0.44999999925494177 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + if type(a) is not mu.ndarray: try: std = a.std - return std(axis=axis, dtype=dtype, out=out, ddof=ddof) + return std(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) except AttributeError: pass return _methods._std(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + **kwargs) def var(a, axis=None, dtype=None, out=None, ddof=0, - keepdims=False): + keepdims=np._NoValue): """ Compute the variance along the specified axis. @@ -3031,6 +3098,12 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `var` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- variance : ndarray, see dtype parameter above @@ -3089,12 +3162,16 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, 0.2025 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + if type(a) is not mu.ndarray: try: var = a.var - return var(axis=axis, dtype=dtype, out=out, ddof=ddof) + return var(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) except AttributeError: pass return _methods._var(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + **kwargs) -- cgit v1.2.1 From 010d17ee8167196ea90c24c57b4ea34badfc11ae Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 5 Feb 2016 12:09:29 -0500 Subject: STY: pep8 only --- numpy/core/fromnumeric.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'numpy/core/fromnumeric.py') diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index bb89adbe1..1c97b7c4f 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -3059,8 +3059,7 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): **kwargs) -def var(a, axis=None, dtype=None, out=None, ddof=0, - keepdims=np._NoValue): +def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): """ Compute the variance along the specified axis. @@ -3174,4 +3173,4 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, pass return _methods._var(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - **kwargs) + **kwargs) -- cgit v1.2.1 From 08fb5807118423e314f324b9bcafdbaab9316f4d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 5 Feb 2016 12:10:26 -0500 Subject: MNT: move std, var, mean calls out of try block Move the calls to user-provided versions of std, var, and mean on non mu.ndarray objects out of the `try` block so that numpy will not mask AttributeError raised during the execution of the function rather than due to the object not having the required method. --- numpy/core/fromnumeric.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'numpy/core/fromnumeric.py') diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 1c97b7c4f..00b2dbae0 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -2934,12 +2934,13 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): if type(a) is not mu.ndarray: try: mean = a.mean - return mean(axis=axis, dtype=dtype, out=out, **kwargs) except AttributeError: pass + else: + return mean(axis=axis, dtype=dtype, out=out, **kwargs) return _methods._mean(a, axis=axis, dtype=dtype, - out=out, **kwargs) + out=out, **kwargs) def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): @@ -3051,12 +3052,13 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): if type(a) is not mu.ndarray: try: std = a.std - return std(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) except AttributeError: pass + else: + return std(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) return _methods._std(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - **kwargs) + **kwargs) def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): @@ -3168,9 +3170,11 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): if type(a) is not mu.ndarray: try: var = a.var - return var(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) + except AttributeError: pass + else: + return var(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) return _methods._var(a, axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) -- cgit v1.2.1 From f473fe4418a98e2e0edb357f23b74964b09d6a7a Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 5 Feb 2016 12:15:50 -0500 Subject: MNT: reduce number of return statements In sum, amax, amin, and prod simplify the logic to remove an identical return statement / call to `_methods._xxx`. This removes several elif/else pairs and reduces the number of exit points from the functions but makes the code path a bit more complicated to trace. --- numpy/core/fromnumeric.py | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'numpy/core/fromnumeric.py') diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 00b2dbae0..52a15e30d 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -1836,16 +1836,15 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): out[...] = res return out return res - elif type(a) is not mu.ndarray: + if type(a) is not mu.ndarray: try: sum = a.sum except AttributeError: - return _methods._sum(a, axis=axis, dtype=dtype, - out=out, **kwargs) - return sum(axis=axis, dtype=dtype, out=out, **kwargs) - else: - return _methods._sum(a, axis=axis, dtype=dtype, - out=out, **kwargs) + pass + else: + return sum(axis=axis, dtype=dtype, out=out, **kwargs) + return _methods._sum(a, axis=axis, dtype=dtype, + out=out, **kwargs) def product(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): @@ -2285,16 +2284,17 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims + if type(a) is not mu.ndarray: try: amax = a.max except AttributeError: - return _methods._amax(a, axis=axis, - out=out, **kwargs) - return amax(axis=axis, out=out, **kwargs) - else: - return _methods._amax(a, axis=axis, - out=out, **kwargs) + pass + else: + return amax(axis=axis, out=out, **kwargs) + + return _methods._amax(a, axis=axis, + out=out, **kwargs) def amin(a, axis=None, out=None, keepdims=np._NoValue): @@ -2389,12 +2389,12 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue): try: amin = a.min except AttributeError: - return _methods._amin(a, axis=axis, - out=out, **kwargs) - return amin(axis=axis, out=out, **kwargs) - else: - return _methods._amin(a, axis=axis, - out=out, **kwargs) + pass + else: + return amin(axis=axis, out=out, **kwargs) + + return _methods._amin(a, axis=axis, + out=out, **kwargs) def alen(a): @@ -2535,12 +2535,12 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): try: prod = a.prod except AttributeError: - return _methods._prod(a, axis=axis, dtype=dtype, - out=out, **kwargs) - return prod(axis=axis, dtype=dtype, out=out, **kwargs) - else: - return _methods._prod(a, axis=axis, dtype=dtype, - out=out, **kwargs) + pass + else: + return prod(axis=axis, dtype=dtype, out=out, **kwargs) + + return _methods._prod(a, axis=axis, dtype=dtype, + out=out, **kwargs) def cumprod(a, axis=None, dtype=None, out=None): -- cgit v1.2.1