4

Is there any method in python to compute elementwise OR or AND operations for 2D arrays across rows or columns?

For example, for the following array, elementwise OR operations across row would result in a vector [1, 0, 0, 0, 0, 0, 0, 0].

array([[1, 0, 0, 0, 0, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 0, 0],
       [1, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
Alex Riley
  • 169,130
  • 45
  • 262
  • 238
thetna
  • 6,903
  • 26
  • 79
  • 113
  • 3
    Yes: see [here](http://stackoverflow.com/a/20528566/3923281), specifically the details about the `.reduce` method of ufuncs. – Alex Riley Feb 12 '17 at 16:18
  • 1
    You may use `numpy.apply_along_axis()` https://docs.scipy.org/doc/numpy/reference/generated/numpy.apply_along_axis.html – ZdaR Feb 12 '17 at 16:18

1 Answers1

8

numpy has logical_or, logical_xor and logical_and which have a reduce method

>> np.logical_or.reduce(a, axis=0)
array([ True, False, False, False, False, False, False, False], dtype=bool)

as you see in the example they coerce to bool dtype, so if you require uint8 you have to cast back in the end.

since bools are stored as bytes you can use cheap viewcasting for that.

with the axis keyword you can select along which axis to reduce. it is possible to select multiple axes

>> np.logical_or.reduce(a, axis=1)
array([ True,  True,  True,  True], dtype=bool)
>>> np.logical_or.reduce(a, axis=(0, 1))
True

the keepdims keyword is useful for broadcasting, for example to find all "crosses" of rows and columns >= 2 in array b

>>> b = np.random.randint(0,10, (4, 4))
>>> b
array([[0, 5, 3, 4],
       [4, 1, 5, 4],
       [4, 5, 5, 5],
       [2, 4, 6, 1]])
>>> rows = np.logical_and.reduce(b >= 2, axis=1, keepdims=True)
# keepdims=False (default) -> rows.shape==(4,)  keepdims=True -> rows.shape==(4, 1)
>>> cols = np.logical_and.reduce(b >= 2, axis=0, keepdims=True)
# keepdims=False (default) -> cols.shape==(4,)  keepdims=True -> cols.shape==(1, 4)
>>> rows & cols # shapes (4, 1) and (1, 4) are broadcast to (4, 4)
array([[False, False, False, False],
       [False, False, False, False],
       [False, False,  True, False],
       [False, False, False, False]], dtype=bool)

notice the slight abuse of the & operator which stands for bitwise_and. since the effect is the same on bools (in fact trying to use and in this place would have thrown an exception) this is common practice

as @ajcr points out the popular np.any and np.all are shorthand for np.logical_or.reduce and np.logical_and.reduce. note, however, that there are subtle differences

>>> np.logical_or.reduce(a)
array([ True, False, False, False, False, False, False, False], dtype=bool)
>>> np.any(a)
True

OR:

if you want to stick with uint8 and know for certain all your entries will be 0 and 1 you can use bitwise_and, bitwise_or and bitwise_xor

>>> np.bitwise_or.reduce(a, axis=0)
array([1, 0, 0, 0, 0, 0, 0, 0], dtype=uint8)
Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • 3
    For the OP's particular `and`/`or` case, it might also be worth noting that `logical_or.reduce` is just equivalent to `np.any` or `ndarray.any`, and that the `logical_and.reduce` is exactly the same as `np.all` or `ndarray.all` (this is how they are defined in the [source](https://github.com/numpy/numpy/blob/f737b354e6ab2aef8b4c05bca0011de46a4b53b0/numpy/core/_methods.py#L20-L21)). – Alex Riley Feb 12 '17 at 16:36