Способы ускорения itertools.product в python - PullRequest
0 голосов
/ 31 марта 2020

Я пытаюсь создать массив numpy, состоящий из всех возможных распределений активов, используя itertools.product. Условия состоят в том, что ассигнования для каждого актива могут находиться в диапазоне от нуля до 100% и могут увеличиваться с шагом (100% / количество активов). Общая сумма ассигнований должна составлять 100%.

Расчеты занимают очень много времени при увеличении количества активов (10 секунд для 7 активов, 210 секунд для 8 активов и т. Д.). Есть ли способ как-нибудь ускорить код? Может быть, я должен попробовать использовать его. Таким образом, или многопроцессорность?

import itertools as it
import numpy as np

def CreateMatrix(Increments):

    inputs = it.product(np.arange(0, 1 + Increments, Increments), repeat = int(1/Increments));
    matrix = np.ndarray((1, int(1/Increments)));
    x = 0;
    for i in inputs:
        if np.sum(i, axis = 0) == 1:
            if x > 0:
                matrix = np.r_[matrix, np.ndarray((1, int(1/Increments)))]
            matrix[x] = i
            x = x + 1

    return matrix

Assets = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
Increments = 1.0 / len(Assets)
matrix = CreateMatrix(Increments);
print matrix

1 Ответ

1 голос
/ 31 марта 2020

Используйте stdlib sum вместо numpy.sum. Этот код тратит большую часть своего времени на вычисление этой суммы, согласно cProfile.

Код профилирования

import cProfile, pstats, StringIO
import itertools as it
import numpy as np


def CreateMatrix(Increments):
    inputs = it.product(np.arange(0, 1 + Increments, Increments), repeat = int(1/Increments));
    matrix = np.ndarray((1, int(1/Increments)));
    x = 0
    for i in inputs:
        if np.sum(i, axis=0) == 1:
            if x > 0:
                matrix = np.r_[matrix, np.ndarray((1, int(1/Increments)))]
            matrix[x] = i
            x += 1
    return matrix

pr = cProfile.Profile()
pr.enable()
Assets = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
Increments = 1.0 / len(Assets)
matrix = CreateMatrix(Increments);
print matrix
pr.disable()
s = StringIO.StringIO()
sortby = 'cumulative'
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print s.getvalue()

Усеченный вывод

         301565912 function calls (301565864 primitive calls) in 294.255 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1   26.294   26.294  294.254  294.254 product.py:7(CreateMatrix)
 43046721   41.948    0.000  267.762    0.000 Library/Python/2.7/lib/python/site-packages/numpy/core/fromnumeric.py:1966(sum)
 43046723   60.071    0.000  217.863    0.000 Library/Python/2.7/lib/python/site-packages/numpy/core/fromnumeric.py:69(_wrapreduction)
 43046723  124.341    0.000  124.341    0.000 {method 'reduce' of 'numpy.ufunc' objects}
 43046723   14.630    0.000   14.630    0.000 Library/Python/2.7/lib/python/site-packages/numpy/core/fromnumeric.py:70(<dictcomp>)
 43046721   12.629    0.000   12.629    0.000 {getattr}
 43098200    7.958    0.000    7.958    0.000 {isinstance}
 43046724    6.191    0.000    6.191    0.000 {method 'items' of 'dict' objects}
     6434    0.047    0.000    0.199    0.000 Library/Python/2.7/lib/python/site-packages/numpy/lib/index_tricks.py:316(__getitem__)

Временные эксперименты

numpy.sum

import itertools as it
import numpy as np

def CreateMatrix(Increments):

    inputs = it.product(np.arange(0, 1 + Increments, Increments), repeat = int(1/Increments));
    matrix = np.ndarray((1, int(1/Increments)));
    x = 0;
    for i in inputs:
        if np.sum(i, axis = 0) == 1:
            if x > 0:
                matrix = np.r_[matrix, np.ndarray((1, int(1/Increments)))]
            matrix[x] = i
            x = x + 1

    return matrix

Assets = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
Increments = 1.0 / len(Assets)
matrix = CreateMatrix(Increments);
$ python -m timeit --number=3 --verbose "$(cat product.py)"
raw times: 738 696 697
3 loops, best of 3: 232 sec per loop

Stdlib sum

import itertools as it
import numpy as np

def CreateMatrix(Increments):

    inputs = it.product(np.arange(0, 1 + Increments, Increments), repeat = int(1/Increments));
    matrix = np.ndarray((1, int(1/Increments)));
    x = 0;
    for i in inputs:
        if sum(i) == 1:
            if x > 0:
                matrix = np.r_[matrix, np.ndarray((1, int(1/Increments)))]
            matrix[x] = i
            x = x + 1

    return matrix

Assets = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
Increments = 1.0 / len(Assets)
matrix = CreateMatrix(Increments);
$ python -m timeit --number=3 --verbose "$(cat product.py)"
raw times: 90.5 84.3 85.3
3 loops, best of 3: 28.1 sec per loop

Есть много способов ускорить ваше решение, как сказали другие люди в своих комментариях. Взгляните на Как «мультипроцессировать» модуль продукта itertools? , чтобы узнать, как использовать multiprocessing для ускорения этого процесса. Независимо от того, что вы делаете: умный алгоритм, параллелизм или оба, замените функцию суммы; это большая скорость при очень небольших усилиях.

...