diff options
author | ahaldane <ealloc@gmail.com> | 2016-10-20 18:07:17 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-20 18:07:17 -0400 |
commit | 8a9b04cbe7d7d45989500b6aad51e636523e305e (patch) | |
tree | 2a6ef534f04ad64aa9daa78da075ddd9b0f95419 | |
parent | 88a66d8d0464caf57f40cf85a24156042e7ff0d7 (diff) | |
parent | 4c2ad8a4a23524f422f476c1596c973c4308e5da (diff) | |
download | python-numpy-8a9b04cbe7d7d45989500b6aad51e636523e305e.tar.gz python-numpy-8a9b04cbe7d7d45989500b6aad51e636523e305e.tar.bz2 python-numpy-8a9b04cbe7d7d45989500b6aad51e636523e305e.zip |
Merge pull request #7922 from eric-wieser/np-ma-convolve
ENH: Add ma.convolve and ma.correlate for #6458
-rw-r--r-- | doc/release/1.12.0-notes.rst | 7 | ||||
-rw-r--r-- | numpy/ma/core.py | 83 | ||||
-rw-r--r-- | numpy/ma/tests/test_core.py | 19 |
3 files changed, 105 insertions, 4 deletions
diff --git a/doc/release/1.12.0-notes.rst b/doc/release/1.12.0-notes.rst index 05ad4c152..0baa33049 100644 --- a/doc/release/1.12.0-notes.rst +++ b/doc/release/1.12.0-notes.rst @@ -254,6 +254,13 @@ context manager will work as expected. Additionally, it is possible to use the context manager as a decorator which can be useful when multiple tests give need to hide the same warning. +New masked array functions ``ma.convolve`` and ``ma.correlate`` added +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +These functions wrapped the non-masked versions, but propagate through masked +values. There are two different propagation modes. The default causes masked +values to contaminate the result with masks, but the other mode only outputs +masks if there is no alternative. + Improvements ============ diff --git a/numpy/ma/core.py b/numpy/ma/core.py index c4a54acb4..4466dc0af 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -56,10 +56,10 @@ __all__ = [ 'argmax', 'argmin', 'argsort', 'around', 'array', 'asanyarray', 'asarray', 'bitwise_and', 'bitwise_or', 'bitwise_xor', 'bool_', 'ceil', 'choose', 'clip', 'common_fill_value', 'compress', 'compressed', - 'concatenate', 'conjugate', 'copy', 'cos', 'cosh', 'count', 'cumprod', - 'cumsum', 'default_fill_value', 'diag', 'diagonal', 'diff', 'divide', - 'dump', 'dumps', 'empty', 'empty_like', 'equal', 'exp', 'expand_dims', - 'fabs', 'filled', 'fix_invalid', 'flatten_mask', + 'concatenate', 'conjugate', 'convolve', 'copy', 'correlate', 'cos', 'cosh', + 'count', 'cumprod', 'cumsum', 'default_fill_value', 'diag', 'diagonal', + 'diff', 'divide', 'dump', 'dumps', 'empty', 'empty_like', 'equal', 'exp', + 'expand_dims', 'fabs', 'filled', 'fix_invalid', 'flatten_mask', 'flatten_structured_array', 'floor', 'floor_divide', 'fmod', 'frombuffer', 'fromflex', 'fromfunction', 'getdata', 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'harden_mask', 'hypot', @@ -7366,6 +7366,81 @@ outer.__doc__ = doc_note(np.outer.__doc__, outerproduct = outer +def _convolve_or_correlate(f, a, v, mode, propagate_mask): + """ + Helper function for ma.correlate and ma.convolve + """ + if propagate_mask: + # results which are contributed to by either item in any pair being invalid + mask = ( + f(getmaskarray(a), np.ones(np.shape(v), dtype=np.bool), mode=mode) + | f(np.ones(np.shape(a), dtype=np.bool), getmaskarray(v), mode=mode) + ) + data = f(getdata(a), getdata(v), mode=mode) + else: + # results which are not contributed to by any pair of valid elements + mask = ~f(~getmaskarray(a), ~getmaskarray(v)) + data = f(filled(a, 0), filled(v, 0), mode=mode) + + return masked_array(data, mask=mask) + + +def correlate(a, v, mode='valid', propagate_mask=True): + """ + Cross-correlation of two 1-dimensional sequences. + + Parameters + ---------- + a, v : array_like + Input sequences. + mode : {'valid', 'same', 'full'}, optional + Refer to the `np.convolve` docstring. Note that the default + is 'valid', unlike `convolve`, which uses 'full'. + propagate_mask : bool + If True, then a result element is masked if any masked element contributes towards it. + If False, then a result element is only masked if no non-masked element + contribute towards it + + Returns + ------- + out : MaskedArray + Discrete cross-correlation of `a` and `v`. + + See Also + -------- + numpy.correlate : Equivalent function in the top-level NumPy module. + """ + return _convolve_or_correlate(np.correlate, a, v, mode, propagate_mask) + + +def convolve(a, v, mode='full', propagate_mask=True): + """ + Returns the discrete, linear convolution of two one-dimensional sequences. + + Parameters + ---------- + a, v : array_like + Input sequences. + mode : {'valid', 'same', 'full'}, optional + Refer to the `np.convolve` docstring. + propagate_mask : bool + If True, then if any masked element is included in the sum for a result + element, then the result is masked. + If False, then the result element is only masked if no non-masked cells + contribute towards it + + Returns + ------- + out : MaskedArray + Discrete, linear convolution of `a` and `v`. + + See Also + -------- + numpy.convolve : Equivalent function in the top-level NumPy module. + """ + return _convolve_or_correlate(np.convolve, a, v, mode, propagate_mask) + + def allequal(a, b, fill_value=True): """ Return True if all entries of a and b are equal, using diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index a4887aaf0..9b65643ed 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -4086,6 +4086,25 @@ class TestMaskedArrayFunctions(TestCase): test = np.ma.compressed(M(shape=(0,1,2))) assert_equal(test, 42) + def test_convolve(self): + a = masked_equal(np.arange(5), 2) + b = np.array([1, 1]) + test = np.ma.convolve(a, b) + assert_equal(test, masked_equal([0, 1, -1, -1, 7, 4], -1)) + + test = np.ma.convolve(a, b, propagate_mask=False) + assert_equal(test, masked_equal([0, 1, 1, 3, 7, 4], -1)) + + test = np.ma.convolve([1, 1], [1, 1, 1]) + assert_equal(test, masked_equal([1, 2, 2, 1], -1)) + + a = [1, 1] + b = masked_equal([1, -1, -1, 1], -1) + test = np.ma.convolve(a, b, propagate_mask=False) + assert_equal(test, masked_equal([1, 1, -1, 1, 1], -1)) + test = np.ma.convolve(a, b, propagate_mask=True) + assert_equal(test, masked_equal([-1, -1, -1, -1, -1], -1)) + class TestMaskedFields(TestCase): |