Here a couple of methods requiring a 2D input.
May need to be adjusted to ensure they are working on the correct side and on the correct dimension, but the core idea holds.
- using
np.where() and looping over one dim with slicing:
import numpy as np
def side_fill_np(arr, value, flag):
rows, cols = arr.shape
idx = np.where(arr == flag)
for i in range(cols):
arr[idx[0][i]:, idx[1][i]] = value
return arr
- using full explicit looping (similar to yours, but somehow cleaner), but accelerated with
numba
import numba as nb
@nb.jit
def side_fill_nb(arr, value, flag):
rows, cols = arr.shape
for j in range(cols):
found = False
for i in range(rows):
if found:
arr[i, j] = value
elif arr[i, j] == flag:
found = True
To test that we are getting the correct result, we generate some input using:
def gen_data(shape):
rows, cols = shape
arr = np.zeros(shape, dtype=bool)
indexes = (np.random.randint(0, rows - 1, cols), np.arange(cols))
arr[indexes] = True
return arr
and the test reads:
np.random.seed(0) # for reproducible results
arr = gen_data((10, 20))
print(arr.astype(int))
# [[0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0]
# [0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0]
# [0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
# [0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0]
# [1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1]
# [0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0]
# [0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0]
# [0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0]
# [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
side_fill_np(arr, True, True)
print(arr.astype(int))
# [[0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
# [0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0]
# [0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0]
# [0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 0 0 1 0]
# [0 1 1 1 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 0]
# [1 1 1 1 0 1 1 1 1 0 0 0 0 1 0 0 0 0 1 1]
# [1 1 1 1 0 1 1 1 1 0 1 0 0 1 1 0 0 0 1 1]
# [1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 0 1 1]
# [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
# [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]]
Now for the timings for approx. your input size:
arr = gen_data((2000, 2000))
%timeit side_fill(arr, True, True)
# 1 loop, best of 3: 2.48 s per loop
%timeit side_fill_np(arr, True, True)
# 10 loops, best of 3: 52.6 ms per loop
%timeit side_fill_nb(arr, True, True)
# 100 loops, best of 3: 6.14 ms per loop
As you can see, with the NumPy approach (which is as much vectorized as I could think of) you get approx. 2 orders of magnitude speed-up, with the Numba accelerated code you get approx. 3 orders of magnitude speed-up.