1

I would like to get the following:

>>> import numpy as np
>>> import itertools
>>> a1 = np.random.randn(100)
>>> a2 = np.random.randn(100)
>>> l = [np.linspace(start=np.asscalar(min(a1[i],a2[i])), stop=np.asscalar(max(a1[i],a2[i])), num=30) for i in range(len(a1))]

However, the elements in l are now arrays. I would like to have a list of floats. If I do

l = [np.linspace(start=np.asscalar(min(a1[i],a2[i])), stop=np.asscalar(max(a1[i],a2[i])), num=30).tolist() for i in range(len(a1))]

I get a list of lists so I would need to unpack the inner one via

>>> list(chain.from_iterable(l))

The list I have is pretty large so that I would like to get the right result from the beginning. Is there a way to achieve that or is the second step via itertools chain necessary?

hpaulj
  • 221,503
  • 14
  • 230
  • 353
math
  • 1,868
  • 4
  • 26
  • 60
  • SInce `l` has 100 arrays of length 30, `np.array(l)` is a (100,30) array. That can be used as is, reshaped, raveled and/or turned into a list as needed. – hpaulj Oct 21 '17 at 18:59
  • In my time tests, generating the 100 arrays takes most time. Assembling them into a list takes a small portion of the total time, regardless of how you do. – hpaulj Oct 21 '17 at 23:04

3 Answers3

3

Since you know, that your final list has 100 * 30 values, you can generate a numpy.array of the correct size first hand:

import numpy as np
a1 = np.random.randn(100)
a2 = np.random.randn(100)
start = np.minimum(a1, a2)[:, None]
stop = np.maximum(a1, a2)[:, None]
values = (np.linspace(0,1,30)[None, :] * (stop-start) + start).ravel()
Daniel
  • 42,087
  • 4
  • 55
  • 81
  • This approach to scaling a linspace is advocated in https://stackoverflow.com/questions/46694167/vectorized-numpy-linspace-across-multi-dimensional-arrays – hpaulj Oct 22 '17 at 04:18
2

Not the most elegant answer, but since you're already using numpy you can cast your list comprehension as an array, flatten it, then make it a list. Borrowing from your code, it would look like this:

l = np.array([np.linspace(start=np.asscalar(min(a1[i],a2[i])), stop=np.asscalar(max(a1[i],a2[i])), num=30) for i in range(len(a1))]).flatten().tolist()
benten
  • 1,995
  • 2
  • 23
  • 38
0

You might be better off with a generator expression to avoid the list comprehension getting too big just to iterate over it once. Then you can append to list which is pretty cheap.

l = []
for x in (np.linspace(start=min(a1[i],a2[i]), stop=max(a1[i],a2[i]),
                      num=30).tolist() for i in range(len(a1))):
    l += x
percusse
  • 3,006
  • 1
  • 14
  • 28
  • You loop is the same as `list((np.linspace ...)`. It turns a generator comprehension into a list. If so why not use a list comprehension? – hpaulj Oct 21 '17 at 20:20
  • @hpaulj Becaue generator spits out the linspace one at a time without constructing everything at once and then iterating over them. This one appends to the existing list. – percusse Oct 21 '17 at 20:30
  • @hpaulj Try very high numbers of arrays/elements. And list((...)) is not flat – percusse Oct 21 '17 at 20:47
  • OK, your use of `l += x` is equivalent to `l.extend(x)`, where as `[x for x in ...] ` is a `l.append(x)` operation. – hpaulj Oct 21 '17 at 22:51
  • @hpaulj The point is not to create the iterator in its entirety but allow for lazy comprehension such that only the resulting list `l` is getting bigger. The generator emits the elements one by one as needed instead of creating all of them at once then emit them one by one. https://stackoverflow.com/q/47789/4950339 – percusse Oct 22 '17 at 11:45