2
\$\begingroup\$

I create a 4-dimensional (x, y, z, t) array of zero values. I then set the initial values at t = Tmax. To do this, I use a nested for loop. I attempted to improve on the code by getting rid of the nested for loops, and replacing it with a single line of code. It works, however, I am not sure if the line

f[:, :, :, N] = np.array([[X_] * (J+1)] * (J+1)).T

is very optimised? It looks messy. Is there a more elegant way of writing this particular line? I have considered numpy's tile and repeat functions. My best attempt is as follows:

import numpy as np

I = 50
J = 25
K = 25
N = 50

Xmin = 0
Xmax = 200


Ymin = 0
Ymax = 1

Zmin = 0
Zmax = 1

dx = (Xmax - Xmin) / I
dy = (Ymax - Ymin) / J
dz = (Zmax - Zmin) / K

X_ = np.linspace(Xmin, Xmax, I + 1)

#Original approach    
f = np.zeros((I + 1, J + 1, K + 1, N + 1))
for i in range(0, I + 1):
    for j in range(0, J + 1):
        for k in range(0, K +1):
            f[i, j, k, N] = i * dx
print(f[:, :, :, :].sum())

#My attempt at a faster approach
f[:, :, :, N] = np.array([[X_] * (J+1)] * (J+1)).T
print(f[:, :, :, :].sum())

Thanks.

\$\endgroup\$
1
  • \$\begingroup\$ What is the "final output"? Your sum()? \$\endgroup\$
    – Reinderien
    Apr 4, 2022 at 21:48

1 Answer 1

4
\$\begingroup\$

You mentioned that you've considered np.tile. I think (subjective) it's a bit more readable than line you proposed. Objectively speaking np.tile method was created exactly for cases like your have. Moreover I've found np.tile-based solution at least 2 times faster.

I have found two solutions:

Generate replicas and put it into zeros-array slice

You can generate line of I+1 elements and replicate it using np.tile method. Then you can put in N'th slice of your np.zeros array

f = np.zeros((I + 1, J + 1, K + 1, N + 1))
line_i = np.arange(I + 1) * dx
f[:, :, :, N] = np.tile(line_i, (K + 1, J + 1, 1)).T

Over-replicate and assign zeros

You can generate an entire f array using np.tile method. Then you can assign over-replicated elements to zero.

line_i = np.arange(I + 1) * dx
f = np.tile(line_i, (N + 1, K + 1, J + 1, 1)).T
f[:, :, :, :-1] = 0

P.S. You are using f.sum() to validate results from two different solution. It's not the best practice for your case as sum of elements doesn't depend on the order of elements. You can use:

(f_nested == f_oneline).all()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.