Здесь пара методов, требующих ввода 2D. Может потребоваться корректировка, чтобы гарантировать, что они работают с правильной стороны и в правильном размере, но основная идея верна.
- с использованием
np.where()
и циклическим перемещением по одной яркости с нарезкой:
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
- с использованием полного явного зацикливания (аналогично вашему, но как-то чище), но ускорено с
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
Чтобы проверить это мы получаем правильный результат, мы генерируем некоторый ввод, используя:
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
и тест гласит:
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]]
Теперь для времени прибл. ваш входной размер:
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
Как вы можете видеть, с подходом NumPy (который настолько векторизован, насколько я мог подумать) вы получите прибл. Ускорение на 2 порядка, с ускоренным кодом Numba вы получаете прибл. Ускорение на 3 порядка.