Как сделать для цикла быстрее с NumPy

1

Если введен массив numd 2d, такой как

                           100   100   100   100   100
                           100    0     0     0    100
                           100    0     0     0    100
                           100    0     0     0    100
                           100   100   100   100   100

такой выход должен быть получен

                            100   100   100   100   100
                            100    50    25    50   100
                            100    25     0    25   100
                            100    50    25    50   100
                            100   100   100   100   100

где каждое число, кроме границы, становится средним из его смежных чисел.

Мой текущий код работает, но мне нужно использовать его без циклов и векторизовать его с помощью numpy.

Мой текущий код:

import numpy as np
def evolve_heat_slow(u):      
    u2 = np.copy(u)
    x=u2.shape[0]
    y=u2.shape[1]
    for i in range(1,x-1):
        for s in range(1,y-1):
            u2[i,s]=(u[i-1,s]+u[i+1,s]+u[i,s+1]+u[i,s-1])/4
    return u2
Теги:
numpy
python-3.x

3 ответа

0
Лучший ответ

Не лучше, чем другие методы, но вы также можете использовать np.roll в каждом направлении, чтобы сделать то же самое:

def evolve_heat_slow(u):
    u2 = u.copy()
    u2[1:-1, 1:-1] = ((np.roll(u2,1,0) + np.roll(u2,-1,0) 
                        + np.roll(u2,1,1) + np.roll(u2,-1,1))/4)[1:-1, 1:-1]
    return u2

теперь с u2 = evolve_heat_slow(u) вы получаете

u2 =
array([[100, 100, 100, 100, 100],
       [100,  50,  25,  50, 100],
       [100,  25,   0,  25, 100],
       [100,  50,  25,  50, 100],
       [100, 100, 100, 100, 100]])
  • 0
    @akhilrastogi Твоя неприязнь к ответу Амадана очень странная для меня. Этот ответ в 7 раз медленнее, чем Амадан для меня на вашем примере массива, и более чем на порядок медленнее на больших массивах.
  • 0
    @akhilrastogi Это в 122 раза быстрее?
6

Это в значительной степени определение двумерной свертки. scipy вы покрыли. Я копирую a чтобы сохранить границы; свертка в valid режиме сделает меньший массив (без границ), который затем вставляю внутри подготовленного "кадра".

import numpy as np
from scipy.signal import convolve2d

a = np.array([[100, 100, 100, 100, 100], [100, 0, 0, 0, 100],  [100, 0, 0, 0, 100], [100, 0, 0, 0, 100], [100, 100, 100, 100, 100]])
b = np.array([[0, 0.25, 0], [0.25, 0, 0.25], [0, 0.25, 0]])
r = np.copy(a)
r[1:-1, 1:-1] = convolve2d(a, b, mode='valid')
r
# => array([[100, 100, 100, 100, 100],
#           [100,  50,  25,  50, 100],
#           [100,  25,   0,  25, 100],
#           [100,  50,  25,  50, 100],
#           [100, 100, 100, 100, 100]])
  • 0
    я не понимаю, как это работает, так как каждое число должно быть средним из его смежных чисел, а не просто вводить значение также, если нужно было поместить это в определение только с вводом исходного массива numpy, который не будет работать.
  • 0
    Массив b делает это. Свертывание с b говорит: «Для каждого элемента умножьте элемент непосредственно выше на 0,25, элемент прямо влево на 0,25, элемент прямо вправо на 0,25, элемент непосредственно ниже на 0,25, а затем сложите их». Что в основном совпадает с вашим (u[i-1,s]+u[i+1,s]+u[i,s+1]+u[i,s-1])/4 . Это не «ввод значения» - b эквивалент вашего усреднения формулы. Думайте об этом как о коде, а не как о данных.
Показать ещё 6 комментариев
2

Хотя Amadan scipy ответ имеет наибольший смысл для этого случая, здесь другой подход делает это "вручную":

import numpy as np

# Create your array
data = np.ones((5,5)) * 100
data[1:-1,1:-1] = 0

def evolve_heat_slow(m, should_copy=True):
    if should_copy: m = m.copy()

    components = (
        m[:-2,  1:-1],  # N
        m[2:,   1:-1],  # S
        m[1:-1, 2:],    # E
        m[1:-1, :-2],   # W
    )

    m[1:-1, 1:-1] = np.mean(np.stack(components), axis=0)
    return m

for _ in range(2):
    data = evolve_heat_slow(data)
    print(data)

Здесь мы определяем компоненты, сначала беря центральное 3x3 "окно" и сдвигая его на 1 в каждом направлении. Затем мы складываем сдвинутые окна, принимаем среднее значение и заменяем центральное окно этими значениями.

После 1-й итерации:

[[ 100.  100.  100.  100.  100.]
 [ 100.   50.   25.   50.  100.]
 [ 100.   25.    0.   25.  100.]
 [ 100.   50.   25.   50.  100.]
 [ 100.  100.  100.  100.  100.]]

После 2 итераций:

[[ 100.   100.   100.   100.   100. ]
 [ 100.    62.5   50.    62.5  100. ]
 [ 100.    50.    25.    50.   100. ]
 [ 100.    62.5   50.    62.5  100. ]
 [ 100.   100.   100.   100.   100. ]]

Ещё вопросы

Сообщество Overcoder
Наверх
Меню