Наиболее эффективный способ создания массива cos и sin в Numpy

мне нужно сохранить массив в размере n С cos(x) и sin(x) допустим

array[[cos(0.9), sin(0.9)],
      [cos(0.35),sin(0.35)],
      ...]

аргументы каждой пары cos и sin задаются случайным выбором. Мой код, насколько я улучшаю, выглядит так:

def randvector():
""" Generates random direction for n junctions in the unitary circle """
    x = np.empty([n,2])
    theta = 2 * np.pi * np.random.random_sample((n))
    x[:,0] = np.cos(theta)
    x[:,1] = np.sin(theta)
    return x

есть ли более короткий путь или более эффективный способ достичь этого?

3 ответов


ваш код достаточно эффективен. И я думаю, что ответ justhalf неплохой.

для эффективного и короткого, как насчет этого кода?

def randvector(n):
    theta = 2 * np.pi * np.random.random_sample((n))
    return np.vstack((np.cos(theta), np.sin(theta))).T

обновление

добавить результат cProfile.

justhalf это!--10-->

      5 function calls in 4.707 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.001    0.001    4.707    4.707 <string>:1(<module>)
     1    2.452    2.452    4.706    4.706 test.py:6(randvector1)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.010    0.010    0.010    0.010 {method 'random_sample' of 'mtrand.RandomState' objects}
     1    2.244    2.244    2.244    2.244 {numpy.core.multiarray.array}

ОП!--10-->

      5 function calls in 0.088 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.088    0.088 <string>:1(<module>)
     1    0.079    0.079    0.088    0.088 test.py:9(randvector2)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.009    0.009    0.009    0.009 {method 'random_sample' of 'mtrand.RandomState' objects}
     1    0.000    0.000    0.000    0.000 {numpy.core.multiarray.empty}

шахты

      21 function calls in 0.087 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.087    0.087 <string>:1(<module>)
     2    0.000    0.000    0.000    0.000 numeric.py:322(asanyarray)
     1    0.000    0.000    0.002    0.002 shape_base.py:177(vstack)
     2    0.000    0.000    0.000    0.000 shape_base.py:58(atleast_2d)
     1    0.076    0.076    0.087    0.087 test.py:17(randvector3)
     6    0.000    0.000    0.000    0.000 {len}
     1    0.000    0.000    0.000    0.000 {map}
     2    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.009    0.009    0.009    0.009 {method 'random_sample' of 'mtrand.RandomState' objects}
     2    0.000    0.000    0.000    0.000 {numpy.core.multiarray.array}
     1    0.002    0.002    0.002    0.002 {numpy.core.multiarray.concatenate}

ваш код выглядит нормально для меня, но вот еще несколько мыслей.

вот однострочный. Это немного медленнее, чем ваша версия.

def randvector2(n):
    return np.exp((2.0j * np.pi) * np.random.rand(n, 1)).view(dtype=np.float64)

Я получаю эти тайминги для n=10000

С уважением:

1000 loops, best of 3: 716 µs per loop

мой сокращенный вариант:

1000 loops, best of 3: 834 µs per loop

теперь, если скорость вызывает беспокойство, ваш подход действительно очень хорош. Другой ответ показывает, как использовать hstack. Это хорошо работает. Вот еще одна версия, которая немного отличается от ваш и немного быстрее.

def randvector3(n):
    x = np.empty([n,2])
    theta = (2 * np.pi) * np.random.rand(n)
    np.cos(theta, out=x[:,0])
    np.sin(theta, out=x[:,1])
    return x

это дает мне время:

1000 loops, best of 3: 698 µs per loop

если у вас есть доступ к numexpr, следующие быстрее (по крайней мере на моей машине).

import numexpr as ne
def randvector3(n):
    sample = np.random.rand(n, 1)
    c = 2.0j * np.pi
    return ne.evaluate('exp(c * sample)').view(dtype=np.float64)

это дает мне время:

1000 loops, best of 3: 366 µs per loop

честно говоря, если бы я писал это для чего-то, что не было чрезвычайно интенсивным, я бы сделал почти то же самое, что и вы. Это делает ваши намерения довольно ясными для читателя. Версия с hstack работает хорошо тоже.

еще одно краткое Примечание: Когда я запускаю тайминги для n=10, моя однострочная версия самая быстрая. Когда я делаю n=10000000, быстрая версия pure-numpy является самой быстрой.


вы можете использовать список понимание, чтобы сделать код немного короче:

def randvector(n):
    return np.array([(np.cos(theta), np.sin(theta)) for theta in 2*np.pi*np.random.random_sample(n)])

но, как упоминалось в комментариях IanH, это медленнее. На самом деле, благодаря моему эксперименту, это 5X медленнее, потому что это не использует преимущества векторизации NumPy.

Итак, чтобы ответить на ваш вопрос:

есть ли более короткий путь?

Да, что я и даю в этом ответе, хотя он только короче на несколько символов (но он сохраняет много строк!)

есть ли более эффективный (я считаю, что вы имели в виду "эффективный") способ?

Я считаю, что ответ на этот вопрос, не слишком усложняя код, - нет, так как numpy уже оптимизирует векторизацию (присвоение значений cos и sin массиву)

времени

сравнение различных методов:

ОП randvector: 0.002131 s

мой randvector: 0.013218 s

mskimm по randvector: 0.003175 s

так кажется, что мскимм randvector выглядит хорошо с точки зрения эффективности конца длины кода =D