Найти всех "соседей" для элемента в многомерном массиве, обвивая границы

Допустим, я использую двумерный / трехмерный массив для моделирования коробки, состоящей из ячеек. Ячейки помечены увеличивающимися числами, начиная с 0. До этого это могло быть сделано

box = np.arange(np.prod(n_cells))
box = box.reshape(n_cells)

где n_cells является массивом np.ar, в котором хранится количество ячеек, которое должно иметь поле в каждом измерении, в основном это размеры ячейки, подсчитанные в ячейках.

Теперь для меня самое сложное - найти разумный способ найти соседей для каждой ячейки. Моя цель состоит в том, чтобы создать массив или список, который содержит соседей для каждой ячейки - в идеале давайте рассмотрим небольшой 2D-пример.

0   1   2   3
4   5   6   7
8   9   10 11
12  13  14 15 

Здесь без периодических граничных условий соседи

0: 1, 4, 5
1: 0, 2, 4, 5, 6
2: 1, 5, 6, 7, 3
...

Но я хотел бы иметь его с периодическими граничными условиями, такими, что

0: 1, 4, 5, 3, 7, 12, 13, 15
1: 0, 2, 4, 5, 6, 12, 13, 14

Таким образом, каждый элемент имеет 8 соседей в 2D. В идеале я хотел бы иметь возможность создавать такой список / массив для любых измерений, но меня особенно интересует 2D/3D, если обобщенное решение недоступно.

Еще одна проблема, на которую я должен обратить внимание, заключается в том, что таким образом я подсчитываю все пары дважды, т. Е. 0 является соседом 1, а 1 - соседом 0. Это то, от чего мне нужно избавиться, но это не главная проблема.

1 ответ

Решение

Это можно сделать с помощью numpy.roll, который "катит" массив по заданным осям, с тем типом перехода, который вам нужен. Например, прокрутка по (-1, -1) сдвигает все влево и вверх, поэтому массив становится

  [[ 5,  6,  7,  4],
   [ 9, 10, 11,  8],
   [13, 14, 15, 12],
   [ 1,  2,  3,  0]]

Таким образом, мы просто нашли юго-восточного соседа для каждой точки. Осталось сгладить этот список (ravel), повторите процесс для каждого из 9 смещений (включая (0, 0), что означает само число) и суммируйте результаты. Решение работает для произвольной размерности массива b:

dim = len(b.shape)       # number of dimensions
offsets = [0, -1, 1]     # offsets, 0 first so the original entry is first 
columns = []
for shift in itertools.product(offsets, repeat=dim):   # equivalent to dim nested loops over offsets
    columns.append(np.roll(b, shift, np.arange(dim)).ravel())
neighbors = np.stack(columns, axis=-1)

Выход (значение neighbors):

  [[ 0,  1,  3,  4,  5,  7, 12, 13, 15],
   [ 1,  2,  0,  5,  6,  4, 13, 14, 12],
   [ 2,  3,  1,  6,  7,  5, 14, 15, 13],
   [ 3,  0,  2,  7,  4,  6, 15, 12, 14],
   [ 4,  5,  7,  8,  9, 11,  0,  1,  3],
   [ 5,  6,  4,  9, 10,  8,  1,  2,  0],
   [ 6,  7,  5, 10, 11,  9,  2,  3,  1],
   [ 7,  4,  6, 11,  8, 10,  3,  0,  2],
   [ 8,  9, 11, 12, 13, 15,  4,  5,  7],
   [ 9, 10,  8, 13, 14, 12,  5,  6,  4],
   [10, 11,  9, 14, 15, 13,  6,  7,  5],
   [11,  8, 10, 15, 12, 14,  7,  4,  6],
   [12, 13, 15,  0,  1,  3,  8,  9, 11],
   [13, 14, 12,  1,  2,  0,  9, 10,  8],
   [14, 15, 13,  2,  3,  1, 10, 11,  9],
   [15, 12, 14,  3,  0,  2, 11,  8, 10]]

В каждой строке первая запись является исходным номером, остальные - его соседями.

Чтобы каждая пара вход-сосед перечислялась только один раз, вы можете замаскировать избыточные записи, например, с помощью NaN:

np.where(neighbors >= neighbors[:, [0]], neighbors, np.nan)

  [[  0.,   1.,   3.,   4.,   5.,   7.,  12.,  13.,  15.],
   [  1.,   2.,  nan,   5.,   6.,   4.,  13.,  14.,  12.],
   [  2.,   3.,  nan,   6.,   7.,   5.,  14.,  15.,  13.],
   [  3.,  nan,  nan,   7.,   4.,   6.,  15.,  12.,  14.],
   [  4.,   5.,   7.,   8.,   9.,  11.,  nan,  nan,  nan],
   [  5.,   6.,  nan,   9.,  10.,   8.,  nan,  nan,  nan],
   [  6.,   7.,  nan,  10.,  11.,   9.,  nan,  nan,  nan],
   [  7.,  nan,  nan,  11.,   8.,  10.,  nan,  nan,  nan],
   [  8.,   9.,  11.,  12.,  13.,  15.,  nan,  nan,  nan],
   [  9.,  10.,  nan,  13.,  14.,  12.,  nan,  nan,  nan],
   [ 10.,  11.,  nan,  14.,  15.,  13.,  nan,  nan,  nan],
   [ 11.,  nan,  nan,  15.,  12.,  14.,  nan,  nan,  nan],
   [ 12.,  13.,  15.,  nan,  nan,  nan,  nan,  nan,  nan],
   [ 13.,  14.,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
   [ 14.,  15.,  nan,  nan,  nan,  nan,  nan,  nan,  nan],
   [ 15.,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan]])

Идея в том, что neighbors >= neighbors[:, [0]] перечисляет только тех, кто имеет большее число, чем сама клетка.

Другие вопросы по тегам