Reputation: 31
If inputed an numpy 2d array such as
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
an output like this should be obtained
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
where every number except the border becomes the mean of its adjacent numbers.
My current code works but I need to use it without for loops and vectorize it using numpy.
My current code:
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
Upvotes: 2
Views: 401
Reputation: 29635
Not better than other methods, but you can also use np.roll
in each direction to do the same thing:
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
now with u2 = evolve_heat_slow(u)
you get
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]])
Upvotes: -2
Reputation: 30200
Although Amadan's scipy answer makes the most sense for this case, here's another approach doing it "manually":
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)
Here, we define components by first taking the central 3x3 "window" and shifting it by 1 in each direction. We then stack the shifted windows, take the mean, and replace the central window with those values.
After 1 iteration:
[[ 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.]]
After 2 iterations:
[[ 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. ]]
Upvotes: 2
Reputation: 198294
That's pretty much the definition of 2D convolution. scipy
has you covered. I copy a
to preserve the borders; the convolution in valid
mode will make a smaller array (without the borders), which I then paste inside the prepared "frame".
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]])
Upvotes: 6