Создание диапазонов заданных массивов пусков и остановок - PullRequest
1 голос
/ 10 января 2020

Учитывая 2 массива стартов и остановок, есть ли способ генерировать массивы последовательных чисел в numpy, не прибегая к циклам? Например:

start = np.array([1, 3, 5])
stop  = np.array([4, 5, 7])

# Expected result
[
 [1, 2, 3, 4],
 [3, 4, 5],
 [5, 6, 7]
]

linspace подходит ближе всего к тому, что я хочу, но я должен использовать одинаковое количество шагов для каждой серии:

np.linspace(start, stop, num=3, axis=1)
array([[1. , 2.5, 4. ],
       [3. , 4. , 5. ],
       [5. , 6. , 7. ]])

Как изменить количество шаги для каждой отдельной серии?

Ответы [ 3 ]

2 голосов
/ 10 января 2020

Считается ли понимание списка "без циклов"?

[list(range(a,b+1)) for (a,b) in zip(start, stop)]
1 голос
/ 10 января 2020

Очевидное понимание списка:

In [92]: [np.arange(i,j) for i,j in zip([1,3,5],[5,6,8])]                                                 
Out[92]: [array([1, 2, 3, 4]), array([3, 4, 5]), array([5, 6, 7])]
In [93]: timeit [np.arange(i,j) for i,j in zip([1,3,5],[5,6,8])]                                          
4.46 µs ± 97.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Примечание. Я начну со списков, поскольку они выполняются быстрее.

frompyfunc - хороший инструмент для создания массивов dtype объектов:

In [94]: f = np.frompyfunc(np.arange, 2,1)                                                                
In [95]: f(np.array([1,3,5]), np.array([5,6,8]))                                                          
Out[95]: 
array([array([1, 2, 3, 4]), array([3, 4, 5]), array([5, 6, 7])],
      dtype=object)
In [96]: timeit f(np.array([1,3,5]), np.array([5,6,8]))                                                   
10.9 µs ± 36.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

но это медленнее, чем понимание.

но если мы генерируем списки вместо этого, понимание становится еще быстрее:

In [97]: timeit [list(range(i,j)) for i,j in zip([1,3,5],[5,6,8])]                                        
2.38 µs ± 44.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Но посмотрите на это волшебное, векторизованное linspace:

In [98]: np.linspace([1,3,5], [4,5,7], num=3, axis=1)                                                     
Out[98]: 
array([[1. , 2.5, 4. ],
       [3. , 4. , 5. ],
       [5. , 6. , 7. ]])
In [99]: timeit np.linspace([1,3,5], [4,5,7], num=3, axis=1)                                              
87.2 µs ± 260 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

медленно !!

Но это небольшой пример. Рейтинги могут измениться в гораздо больших случаях. В любом случае, не думайте, что «векторизация» всегда быстрее. Есть много случаев, когда операции со списком Python выполняются быстрее, особенно когда размеры скромны. Создание numpy массивов немного дорого - даже если они сделаны быстро,

0 голосов
/ 10 января 2020

Как отметил @hpaulj в комментариях, вы не можете иметь рваный массив, поэтому, по крайней мере, внешний контейнер должен быть списком. Вы можете сделать это без циклов, используя itertools.starmap и numpy.stack или zip:

result = list(starmap(np.arange, np.stack(start, stop + 1, axis=-1))

ИЛИ

result = list(starmap(np.arange, zip(start, stop + 1))

Это не совсем векторизовано, но не предоставляет циклических ключевых слов. Понимание списка, вероятно, будет быстрее, чем любой подход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...