2015-05-21 8 views
1

Каждая ячейка моей матрицы должна быть оценена дорогой функцией. Матрица симметрична, это лучший метод, который я мог бы придумать для заполнения каждой ячейки.Лучший способ вычисления половины симметричной матрицы numpy?

num_cases = len(case_dictionary.keys()) # num_cases = 10 
SmallMatrix = np.zeros((num_cases,num_cases)) 

for CasesX in range(0,num_cases): 
    for CasesY in range(CasesX,num_cases): 
     SmallMatrix[CasesX,CasesY] = 1 

возвращается:

array([[ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], 
     [ 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.], 
     [ 0., 0., 1., 1., 1., 1., 1., 1., 1., 1.], 
     [ 0., 0., 0., 1., 1., 1., 1., 1., 1., 1.], 
     [ 0., 0., 0., 0., 1., 1., 1., 1., 1., 1.], 
     [ 0., 0., 0., 0., 0., 1., 1., 1., 1., 1.], 
     [ 0., 0., 0., 0., 0., 0., 1., 1., 1., 1.], 
     [ 0., 0., 0., 0., 0., 0., 0., 1., 1., 1.], 
     [ 0., 0., 0., 0., 0., 0., 0., 0., 1., 1.], 
     [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]]) 

достаточно легко ...

Однако, когда матрица больше и вычисление дорого: ли вложенная цикл наиболее эффективным решением?

num_cases = len(case_dictionary.keys()) # 100000 
BigMatrix = np.zeros((num_cases,num_cases)) 

for CasesX in range(0,num_cases): 
    for CasesY in range(CasesX,num_cases): 
     BigMatrix[CasesX,CasesY] = ExpensiveFunction() 

медленный ... из-за моей функции или цикла?

EDIT

Непрерывно работает с парными данными, поэтому я вернулся и пытался работать с @hpaulj раствором. Я недостаточно осведомлен, чтобы понять, почему testUpper() быстрее?

def testUpper(func): 
    num_cases = 100 
    BigMatrix = np.zeros((num_cases,num_cases)) 

    upper = np.triu_indices_from(BigMatrix) 

    BigMatrix[upper] = ExpensiveFunction() 

протестированные @unutbu test функции снизу, против Numpy версии:

In [8]: %timeit test(ExpensiveFunction) 
     1 loops, best of 3: 11.1 s per loop 

In [9]: %timeit testUpper(ExpensiveFunction) 
     1000 loops, best of 3: 2.03 ms per loop 
+1

Трудно сказать, без каких-либо подробностей, что делает код медленным. Петли Python медленны, но это может быть не узким местом. С этим словом, что для вас «медленное»? милисекунды, секунды, минуты ...? Как вы оцениваете 'ExpensiveFunction()'? Имеет ли он какие-либо параметры или является одним и тем же вызовом функции для любой записи? –

+2

Какая у вас задание? – YXD

+0

ExpensiveFunction имеет 5 параметров (3 определены за пределами циклов 2 условны на CasesX и CasesY), он вычисляет оценки подобия, оценивая R-код (используя пакет python rpy2). Медленный, относительный, согласованный, вопрос был скорее об эффективности вложенного цикла, как о лучшем методе устранения половины вычислений в симметричной матрице. Не знал, имели ли панды или numpy пятнистый вызов, который обрабатывал это для вас. – Kevin

ответ

5

Вот простой эксперимент, который показывает, что горлышко бутылки, скорее всего, будет ExpensiveFunction:

import time 

def SimpleFunction(): 
    return 1 

def ExpensiveFunction(): 
    time.sleep(0.001) 
    return 1 

def test(func): 
    num_cases = 100 
    BigMatrix = np.zeros((num_cases,num_cases)) 

    for CasesX in range(0,num_cases): 
     for CasesY in range(CasesX,num_cases): 
      BigMatrix[CasesX,CasesY] = func() 

In [84]: %timeit test(ExpensiveFunction) 
1 loops, best of 3: 5.48 s per loop 

In [85]: %timeit test(SimpleFunction) 
1000 loops, best of 3: 890 µs per loop 

Два цикла timeit одинаковы, за исключением вызываемой функции. Когда func является SimpleFunction, заполняется BigMatrix занимает менее 1 мс. Но когда func является ExpensiveFunction, заселение BigMatrix занимает 5 секунд.

Таким образом, двойной for-loop, вероятно, не является горлом бутылки; ExpensiveFunction есть. Вы можете попробовать его с помощью своего фактического кода, чтобы убедиться. Если окажется, что ExpensiveFunction является узким местом, вам не нужно беспокоиться о оптимизации двойной петли, так как даже если есть более быстрый способ заполнить BigMatrix - даже если вы можете сократить стоимость времени до нуля - вы бы (в приведенном выше случае) сохранить только самое большее 890 us, в то время как общая программа все равно займет 5 секунд.

+0

Спасибо, это объясняет, что циклы не являются проблема. Вызов функции - это горлышко бутылки, заставившее меня поверить, что @MrE может предложить лучшее решение, векторизовав мой R-код. Я буду продвигать, как только смогу (n00b, sry) – Kevin

1

Я хотел бы предложить, чтобы применить «дорогой» вычисления на половину вашей матрицы и чем сделать свой NumPy массив симметричным, используя symmetrize() функцию, которая должна быть с минимальным временем цене

def symmetrize(a): 
    return a + a.T - numpy.diag(a.diagonal()) 
Смежные вопросы