The numpy, a Hadmard product on a vertical stack of 1D arrays is significantly faster than looping through the list of 1D arrays and performing the Hadamard (element-wise) product on each (which makes sense, and I tested it anyways).
I have a situation in which I need to perform the Hadamard product between one set of numpy arrays and another as such:
stacked_arrays = np.vstack([1D-arrays...])
stacked_arrays *= np.power(factor, np.arange(1, num_arrays))
However, I need this operation to mutate each component 1D array in list, and this operation needs to happen a lot. I know this sounds like a weird feature, but is there any way to do this without a loop like:
factors = factor ** np.arange(1, num_arrays)
for array, f in zip([1D..arrays], factors):
array *= f
or without unstacking the result of the operation?
Also map
can't be used since map
creates copies of the numpy arrays as such:
result = map(lambda x, y: x * y, zip([1D..arrays], factors))
since you can't do *=
with lambda
a list of numpy arrays is returned leaving the originals unmutated.
Is there a way to get np.vstack
to still reference the old component arrays somehow, or an alternative way to achieve the speed of the Hadamard product between arrays that are stacked
while mutating the unstacked ones? Since some time can be saved if unstacking (np.split
) doesn't need to occur.
TimeIt results:
m = []
for _ in range(100):
m.append(np.array([1, 2, 4, 5], dtype=np.float64))
factors = np.expand_dims(np.power(2, np.arange(100, dtype=np.float64)), axis=1)
def split_and_unstack():
l = np.vstack(m)
l *= factors
result = np.split(l, 100)
def split_only():
l = np.vstack(m)
l *= factors
print(timeit.timeit(split_and_unstack, number=10000))
# 1.8569015570101328
print(timeit.timeit(split_only, number=10000))
# 0.9328480050317012
# makes sense that with unstacking it is about double the time
Clarification:
The list of [1D arrays] mentioned above is a sublist of a larger list of 1D arrays.
This larger list is a collections.deque
. And this deque
needs
to be shuffled before the sublist is extracted (i.e this is an Experience Replay Buffer for Stochastic Gradient Descent).
Buffer pop
and append
speed:
times = int(1e4)
tgt = np.array([1, 2, 3, 4])
queue = collections.deque([tgt] * times, maxlen=times)
reg_list = [tgt] * times
numpy_list = np.array([tgt] * times)
def pop():
queue.pop()
def pop_list():
reg_list.pop()
def pop_np():
global numpy_list
numpy_list = numpy_list[1:]
print(timeit.timeit(pop, number=times))
# 0.0008135469979606569
print(timeit.timeit(pop_list, number=times))
# 0.000994370027910918
print(timeit.timeit(pop_np, number=times))
# 0.0016436030273325741
def push():
queue.append(tgt)
def push_list():
reg_list.append(tgt)
numpy_list = np.array([tgt] * 1)
def push_np():
numpy_list[0] = tgt
print(timeit.timeit(push, number=times))
# 0.0008797429618425667
print(timeit.timeit(push_list, number=times))
# 0.00097957398975268
print(timeit.timeit(push_np, number=times))
# 0.003331452957354486