Я пытаюсь проанализировать ландшафт карты, заданный StarCraft 2 bot API .
Задачей новичка для этого анализа было найти скалы для жнецов, которые являются специальными единицами в SC2, которые могут прыгать вверх и вниз по скалам.
Чтобы решить эту проблему, я анализирую точки, в которых сама точка не подлежит изменению (= обрыв), а северная и южная две точки являются изменяемыми. Пропускаемые точки помечаются как 1, а не как 0 в массиве.
Карта местности существует в виде двумерного массива. Ниже приведен небольшой отрывок из более крупного массива 200x200:
import numpy as np
example = np.array([[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]])
Здесь точки [2, 1] и [2, 2] будут соответствовать критериям, при которых сами точки не подлежат изменению (= 0), а точки выше и ниже их можно исправлять (= 1).
Это может быть достигнуто с помощью следующего кода:
above = np.roll(example, 1, axis=0) # Shift rows downwards
below = np.roll(example, -1, axis=0) # Shift rows upwards
result = np.zeros_like(example) # Create array with zeros
result[(example == 0) & (above == 1) & (below == 1)] = 1 # Set cells to 1 that match condition
print(repr(result))
# array([[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]])
Теперь мой вопрос: можно ли добиться того же с помощью меньшего количества кода?
Функция np.roll каждый раз создает новый объект np.array, поэтому анализ сотен соседних точек может привести к 100 строкам ненужного кода и высокому использованию памяти.
Я пытаюсь найти что-то похожее на
result = np.zeros_like(example)
result[(example == 0) & (example[-1, 0] == 1) & (example[1, 0 == 1)] = 1
# or
result[(example == 0) & ((example[-1:2, 0].sum() == 2)] = 1
Здесь числа в скобках отображают относительное положение к анализируемой в данный момент точке, но я не знаю, есть ли способ заставить это работать с numpy.
Кроме того, результат для нулевой строки будет неясным при проверке точки «над» ней: он может получить доступ либо к последней строке, вызвать ошибку или вернуть значение по умолчанию (0 или 1).
Edit:
Я нашел этот пост , и он указал мне на функцию scipy convolve2d , которую можно применить здесь, что может быть тем, что я ищу:
import numpy as np
from scipy import signal
example = np.array([[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]])
kernel = np.zeros((3, 3), dtype=int)
kernel[::2, 1] = 1
print(repr(kernel))
# array([[0, 1, 0],
# [0, 0, 0],
# [0, 1, 0]])
result2 = signal.convolve2d(example, kernel, mode="same")
print(repr(result2))
# array([[0, 1, 1, 0],
# [0, 0, 0, 0],
# [0, 2, 2, 0],
# [0, 0, 0, 0],
# [0, 1, 1, 0]])
result2[result2 < 2] = 0
result2[result2 == 2] = 1
print(repr(result2))
# array([[0, 0, 0, 0],
# [0, 0, 0, 0],
# [0, 1, 1, 0],
# [0, 0, 0, 0],
# [0, 0, 0, 0]])
Edit2:
Другое решение может быть scipy.ndimage.minimum_filter , которое, похоже, работает аналогично:
import numpy as np
from scipy import ndimage
example = np.array([[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]])
kernel = np.zeros((3, 3), dtype=int)
kernel[::2, 1] = 1
print(repr(kernel))
# array([[0, 1, 0],
# [0, 0, 0],
# [0, 1, 0]])
result3 = ndimage.minimum_filter(example, footprint=kernel_vertical, mode="constant")
print(repr(result3))
# array([[0, 0, 0, 0],
# [0, 0, 0, 0],
# [0, 1, 1, 0],
# [0, 0, 0, 0],
# [0, 0, 0, 0]])