1

I have 2 NumPy array as follow:

import numpy as np
a = np.array([1, 4, 2, 6, 4, 4, 6, 2, 7, 6, 2, 8, 9, 3, 6, 3, 4, 4, 5, 8])
b = np.array([2, 8, 3, 9, 9, 9, 7, 5, 4, 8, 6, 5, 4, 4, 7, 2, 1, 1, 9, 9])

and 2 constant numbers:

c = 6
d = 3

Based on a previous question, I can extract an array each times the elements in a are less than c, 2 or more times consecutively:

array = np.append(a, -np.inf)  # padding so we don't lose last element
mask = array >= c  # values to be removed
split_indices = np.where(mask)[0]
for subarray in np.split(array, split_indices + 1):
    if len(subarray) > 2:
        print(subarray[:-1])

which output:

[1. 4. 2.]
[4. 4.]
[3. 4. 4. 5.]

Now, I would like to change my condition for a multiple condition where, 2 or more times consecutively:

  1. elements in a are less than c,

AND

  1. elements in b are less than d

Using the following code:

mask = ((a< c) & (b< d))

I know that my conditions (2 times or more consecutively) are just meet 1 time at indices 15,16 and 17.

Now I would like to extract the value of a corresponding to those indices where my conditions are meet.

Based on the link answer, I tried:

a1= np.append(a, -np.inf)
a2=np.append(b, -np.inf)  # padding so we don't lose last element
mask = ((a1< c) & (a2< d))  # values to be removed
split_indices = np.where(mask)[0]
for subarray in np.split(a, split_indices + 1):
    if len(subarray) > 2:
        print(subarray[:-1])

Which surprisingly, return an array where my coonditions are not meet...

[4 2 6 4 4 6 2 7 6 2 8 9 3 6]

I also tried the np.extract as follow:

np.extract((len(list(g))>=2 for i, g in ((a < c) & (b < d)) if i), a)

which return me a value of 1 and not the value of the array a...

The desired output array should be the one of indice 15,16,17 corresponding to the value [3 4 4] in array a.

Could someone point me to the python tools I could use in order to extract the array fulfilling my multiple conditions?

NOTE: this is a minimal example of my problem, in my "real life" I need to find arrays that meet my conditions 14 times or more consecutively!

steve
  • 511
  • 1
  • 9
  • 33

3 Answers3

4

Note that in your previous question when you looked for the elements in array that are less than the threshold, your mask was defined not as mask = array < threshold but as an inverse of it: mask = array >= threshold. This is because it was used later to get elements that would be removed.

So, in your new example, you also have to get the inverse of your mask. Instead of mask = (a1 < c) & (a2 < d) you need mask = ~((a1 < c) & (a2 < d)):

a1= np.append(a, -np.inf)
a2 = np.append(b, -np.inf)
mask = ~((a1 < c) & (a2 < d))
split_indices = np.where(mask)[0]
for subarray in np.split(a, split_indices + 1):
    if len(subarray) > 2:
        print(subarray[:-1])

gives:

[3 4 4]

which is 15-17th elements of a.

Georgy
  • 12,464
  • 7
  • 65
  • 73
0

Following the conditions, your desired output after placing the 2 conditions are: [3,4,4] from a or [2,1,1] from b right?

Try:

a = [1, 4, 2, 6, 4, 4, 6, 2, 7, 6, 2, 8, 9, 3, 6, 3, 4, 4, 5, 8]
b = [2, 8, 3, 9, 9, 9, 7, 5, 4, 8, 6, 5, 4, 4, 7, 2, 1, 1, 9, 9]
c = 6
d = 3
condition_met = []
a_extract = []
b_extract = []

for i in range(0, len(a)):
    if a[i] < c and b[i] < d:
        condition_met.append(True)
    else:
        condition_met.append(False)

printing condition_met list gives [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, True, True, False, False]

Using this, we now check for your conditions:

for i in range(0, len(condition_met)):
    if i == 0 and condition_met[i] == True and condition_met[i+1] == True:
        a_extract.append(a[i])
        b_extract.append(b[i])
    elif condition_met[i] == True and condition_met[i+1] == True and i != len(condition_met) - 1 and i > 0 or condition_met[i] == True and condition_met[i-1] == True and i != len(condition_met) - 1 and i > 0:
        a_extract.append(a[i])
        b_extract.append(b[i])
    elif condition_met[i] == True and condition_met[i-1] == True and i == len(condition_met) - 1:
        a_extract.append(a[i])
        b_extract.append(b[i])

You'll get [3,4,4] for your a_extract list and [2,1,1] for your b_extract list.

Is this what you need?

Joe
  • 879
  • 2
  • 6
  • 15
  • The `a_extract` list is the one you are looking for, I just added the `b_extract` to show versatility :)). Let me know if this helps you. – Joe Jul 05 '19 at 17:55
  • It actually works! thank you. But could you please explain me where and how the `2 times or more consecutively` condition meet, is applied in this code? Cause this is a minimal example of the task I need to do , and in my real life problem the condition need to be meet 14 times or more consecutively... :) – steve Jul 05 '19 at 18:31
  • I see, well I did use your condition (`a < c and b < d`) to populate the `codition_met` list with boolean values (`True or False`). Now the determination of which list element to get is that positioning of those `True` or `False` value right? Basically, two straight `True`'s will meet your condition, this straight `True` is either to the left or to the right if your `i` in the second for loop is greater than 0 or less that `len(condition_met) - 1`. if `i` equals 0 check the `0`th position in the list and the `i+1`th position. And check the `i-1`th position if `i` equals `len - 1` – Joe Jul 05 '19 at 18:43
0

You can create a mask using SciKit image like

import numpy as np
import skimage

N = 2
mask = ((a < c) & (b < d))
mask2 = np.zeros_like(mask)

tmp = skimage.util.view_as_windows(mask, N).all(axis=1)
mask2[N - 1:-N + 1] = skimage.util.view_as_windows(tmp, N).any(axis=1)
mask2
# array([False, False, False, False, False, False, False, False, False,
#        False, False, False, False, False, False,  True,  True,  True,
#        False, False])

and get the indices and values using

np.where(mask2)[0]  # array([15, 16, 17])
a[mask2]  # array([3, 4, 4])
Nils Werner
  • 34,832
  • 7
  • 76
  • 98
  • Thanks for your answer. I don't really understand where is the "2 times or more consecutively" implemented in this code. I need for my problem to make this code work for 14 times or more consecutively. – steve Jul 08 '19 at 14:44
  • `view_as_windows()` creates an overlapping view on the array, in this case 2 consecutive values per row. Do an `.all()` on that axis and you will know whether the condition was true for 2 consecutive values. – Nils Werner Jul 08 '19 at 14:49
  • Thanks! But then I cannot adapt this code for 14 times or more consecutively? – steve Jul 08 '19 at 15:37
  • Yes, just change the 2 to 14? – Nils Werner Jul 08 '19 at 17:01
  • It does not work when I changed the 2 by any other values. Let say that I modified my arrays `a` and `b` as: `a = np.array([1, 4, 2, 6, 4, 4, 6, 2, 7, 6, 2, 8, 9, 3, 2, 3, 4, 4, 5, 8])` `b = np.array([2, 8, 3, 9, 9, 9, 7, 5, 4, 8, 6, 5, 4, 4, 2, 2, 1, 1, 9, 9])` And that I am now searching when my conditions are meet 4 times or more consecutively, by changing the 2 by a 4. It always return me `ValueError: could not broadcast input array from shape (14) into shape (18)` – steve Jul 08 '19 at 17:06
  • I have updated the code so it works with `N`. Note that for `N > len(a)`, it stops working – Nils Werner Jul 08 '19 at 20:35