Just adding some timings for @Antimon's answer. Using numpy.outer
is definitely the way to go IMO.
def list_version(shots, lens):
list_total = []
for shot in list_shot:
list_out = []
for lens in list_lens:
val = shot * lens
list_out.append(val)
list_total.append(list_out)
return list_total
def numpy_version1(shots, lens):
_shots = np.array(shots)
_lens = np.array(lens)
return np.outer(_shots, _lens)
def numpy_version2(shots, lens):
return np.outer(shots, lens)
Depending on the input type (meaning the original arrays are numpy arrays and don't need to be converted to lists), numpy.outer
can give a substantial (300 fold) increase in performance:
arr_shot = np.arange(0, 10000)
arr_lens = np.linspace(0, 13, 0.01)
list_shot = list(arr_shot)
list_lens = list(arr_lens)
%timeit list_version(list_shot, list_lens)
21.5 s ± 24.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit numpy_version1(list_shot, list_lens)
73 ms ± 75.2 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit numpy_version2(arr_shot, arr_lens)
72.2 ms ± 46.6 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Method |
Time (ms) |
Original |
21500 |
Native Numpy |
72.2 |
Numpy convert from list |
73 |
In response to @Nike's comment below, I timed the numpy version over several scales of input array sizes:
results = []
for m in np.logspace(1, 4, 10, base=10, dtype=int):
for n in np.logspace(1, 4, 10, base=10, dtype=int):
x = np.random.rand(m)
y = np.random.rand(n)
t = timeit.timeit("numpy_version2(x, y)", number=10, globals=globals())
results.append((m, n, t))
data = pd.DataFrame(results, columns=["size_shots", "size_lens", "time_numpy"])
data["size_total"] = data.size_shots * data.size_lens
This should produce 2 measurements of 10 iterations at every combination of array sizes (whose log10s are equally spaced) from 10 to 10,000.

TLDR: OP's arrays are of the order of 10^5 and 10^3 elements which should take <10s (10^8 output elements takes <10s above).