2

I have the foll. python numpy array, arr:

([1L, 1L, 1L, 1L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 3L, 3L, 3L, 2L, 2L, 2L,
       2L, 2L, 2L, 1L, 1L, 1L, 1L])

I can find the first occurrence of 1 like this:

np.where(arr.squeeze() == 1)[0]

How do I find the position of the last 1 before either a 0 or 3.

user308827
  • 21,227
  • 87
  • 254
  • 417

2 Answers2

2

Here's one approach using np.where and np.in1d -

# Get the indices of places with 0s or 3s and this 
# decides the last index where we need to look for 1s later on
last_idx = np.where(np.in1d(arr,[0,3]))[0][-1]

# Get all indices of 1s within the range of last_idx and choose the last one
out = np.where(arr[:last_idx]==1)[0][-1]

Please note that for cases where no indices are found, using something like [0][-1] would complain about having no elements, so error-checking codes are needed to be wrapped around those lines.

Sample run -

In [118]: arr
Out[118]: array([1, 1, 3, 0, 3, 2, 0, 1, 2, 1, 0, 2, 2, 3, 2])

In [119]: last_idx = np.where(np.in1d(arr,[0,3]))[0][-1]

In [120]: np.where(arr[:last_idx]==1)[0][-1]
Out[120]: 9
Divakar
  • 218,885
  • 19
  • 262
  • 358
1

You can use a rolling window and search that for the values you want:

import numpy as np
arr = np.array([1L, 1L, 1L, 1L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 3L, 3L, 3L, 2L, 2L, 2L,
       2L, 2L, 2L, 1L, 1L, 1L, 1L])

def rolling_window(a, window):
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

match_1_0 = np.all(rolling_window(arr, 2) == [1, 0], axis=1)
match_1_3 = np.all(rolling_window(arr, 2) == [1, 3], axis=1)
all_matches = np.logical_or(match_1_0, match_1_3)
print(np.flatnonzero(all_matches)[-1])

Depending on your arrays, this might be good enough performance-wise. With that said, a less flexible (but simpler) solution might perform better even if it is a loop over indices that you usually want to avoid with numpy...:

for ix in xrange(len(arr) - 2, -1, -1):  # range in python3.x
    if arr[ix] == 1 and (arr[ix + 1] == 0 or arr[ix + 1] == 3):
        return ix

You might even be able to do something like which is probably a bit more flexible than the hard-coded solution above and (I would guess) still probably would out-perform the rolling window solution:

def rfind(haystack, needle):
    len_needle = len(needle)
    for ix in xrange(len(haystack) - len_needle, -1, -1):  # range in python3.x
        if (haystack[ix:ix + len_needle] == needle).all():
            return ix

Here, you'd do something like:

max(rfind(arr, np.array([1, 0])), rfind(arr, np.array([1, 3])))

And of course, with all of these answers, I haven't actually handled the case where the thing you are searching for isn't present since you didn't specify what you would want for that case...

Community
  • 1
  • 1
mgilson
  • 300,191
  • 65
  • 633
  • 696