From 69b0c42bca27dd5d5522de306bcd7db7deccbfad Mon Sep 17 00:00:00 2001 From: B R S Recht Date: Thu, 4 May 2017 20:03:09 -0400 Subject: ENH: Add isin, genereralizing in1d to ND arrays (#8423) This fixes gh-8331 Also update the docs for arraysetops to remove the outdated "1D" from the description, which was already incorrect for np.unique. --- numpy/lib/arraysetops.py | 102 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 3 deletions(-) (limited to 'numpy/lib/arraysetops.py') diff --git a/numpy/lib/arraysetops.py b/numpy/lib/arraysetops.py index fae3e3cbc..9a1448991 100644 --- a/numpy/lib/arraysetops.py +++ b/numpy/lib/arraysetops.py @@ -1,9 +1,10 @@ """ -Set operations for 1D numeric arrays based on sorting. +Set operations for arrays based on sorting. :Contains: - ediff1d, unique, + isin, + ediff1d, intersect1d, setxor1d, in1d, @@ -31,7 +32,7 @@ import numpy as np __all__ = [ 'ediff1d', 'intersect1d', 'setxor1d', 'union1d', 'setdiff1d', 'unique', - 'in1d' + 'in1d', 'isin' ] @@ -380,6 +381,7 @@ def setxor1d(ar1, ar2, assume_unique=False): flag2 = flag[1:] == flag[:-1] return aux[flag2] + def in1d(ar1, ar2, assume_unique=False, invert=False): """ Test whether each element of a 1-D array is also present in a second array. @@ -387,6 +389,8 @@ def in1d(ar1, ar2, assume_unique=False, invert=False): Returns a boolean array the same length as `ar1` that is True where an element of `ar1` is in `ar2` and False otherwise. + We recommend using :func:`isin` instead of `in1d` for new code. + Parameters ---------- ar1 : (M,) array_like @@ -411,6 +415,8 @@ def in1d(ar1, ar2, assume_unique=False, invert=False): See Also -------- + isin : Version of this function that preserves the + shape of ar1. numpy.lib.arraysetops : Module with a number of other functions for performing set operations on arrays. @@ -481,6 +487,96 @@ def in1d(ar1, ar2, assume_unique=False, invert=False): else: return ret[rev_idx] + +def isin(element, test_elements, assume_unique=False, invert=False): + """ + Calculates `element in test_elements`, broadcasting over `element` only. + Returns a boolean array of the same shape as `element` that is True + where an element of `element` is in `test_elements` and False otherwise. + + Parameters + ---------- + element : array_like + Input array. + test_elements : array_like + The values against which to test each value of `element`. + This argument is flattened if it is an array or array_like. + See notes for behavior with non-array-like parameters. + assume_unique : bool, optional + If True, the input arrays are both assumed to be unique, which + can speed up the calculation. Default is False. + invert : bool, optional + If True, the values in the returned array are inverted, as if + calculating `element not in test_elements`. Default is False. + ``np.isin(a, b, invert=True)`` is equivalent to (but faster + than) ``np.invert(np.isin(a, b))``. + + Returns + ------- + isin : ndarray, bool + Has the same shape as `element`. The values `element[isin]` + are in `test_elements`. + + See Also + -------- + in1d : Flattened version of this function. + numpy.lib.arraysetops : Module with a number of other functions for + performing set operations on arrays. + Notes + ----- + + `isin` is an element-wise function version of the python keyword `in`. + ``isin(a, b)`` is roughly equivalent to + ``np.array([item in b for item in a])`` if `a` and `b` are 1-D sequences. + + `element` and `test_elements` are converted to arrays if they are not + already. If `test_elements` is a set (or other non-sequence collection) + it will be converted to an object array with one element, rather than an + array of the values contained in `test_elements`. This is a consequence + of the `array` constructor's way of handling non-sequence collections. + Converting the set to a list usually gives the desired behavior. + + .. versionadded:: 1.13.0 + + Examples + -------- + >>> element = 2*np.arange(4).reshape((2, 2)) + >>> element + array([[0, 2], + [4, 6]]) + >>> test_elements = [1, 2, 4, 8] + >>> mask = np.isin(element, test_elements) + >>> mask + array([[ False, True], + [ True, False]], dtype=bool) + >>> element[mask] + array([2, 4]) + >>> mask = np.isin(element, test_elements, invert=True) + >>> mask + array([[ True, False], + [ False, True]], dtype=bool) + >>> element[mask] + array([0, 6]) + + Because of how `array` handles sets, the following does not + work as expected: + + >>> test_set = {1, 2, 4, 8} + >>> np.isin(element, test_set) + array([[ False, False], + [ False, False]], dtype=bool) + + Casting the set to a list gives the expected result: + + >>> np.isin(element, list(test_set)) + array([[ False, True], + [ True, False]], dtype=bool) + """ + element = np.asarray(element) + return in1d(element, test_elements, assume_unique=assume_unique, + invert=invert).reshape(element.shape) + + def union1d(ar1, ar2): """ Find the union of two arrays. -- cgit v1.2.1