Numpy применять функцию в парах строк и накапливать результаты - PullRequest
1 голос
/ 20 апреля 2019

Я хочу сделать что-то простое, но я не нашел умного способа сделать это.

Предполагая, что у меня есть массив numpy с 3 строками, подобный этому:

import numpy as np

a = np.array([[0.514, 0.966, 0.443, 0.95 , 0.524, 0.423, 0.75 , 0.463, 0.721, 0.089],
              [0.929, 0.014, 0.275, 0.521, 0.739, 0.612, 0.366, 0.469, 0.575, 0.533],
              [0.235, 0.084, 0.994, 0.713, 0.421, 0.851, 0.66 , 0.231, 0.699, 0.216]])

Я хочу применить следующую функцию к каждой паре строк и накапливать результаты т.е. (строка0 с строкой1) -> (вывод предыдущего шага) со строкой 3) и т. д. :

def myfunc(x,y):
    return x**2 + y**2 - x*y

Вручную это будет выглядеть примерно так:

tmp1 = myfunc(a[0],a[1])
results = myfunc(tmp1,a[2])

Теперь я хочу обобщить это умным способом для общего N (N = a.shape[0]).

Я пробовал подходы, основанные на понимании списков, но я не могу обобщить их для любого N.


Редактировать 1:

Пример для N = 4:

tmp1 = myfunc(a[0],a[1])
tmp2 = myfunc(tmp1,a[2])
results = myfunc(tmp2,a[3])

Ответы [ 2 ]

1 голос
/ 20 апреля 2019

Упрощенная версия вашей функции и a, который должен выделять действие:

In [344]: def myfunc(x,y): 
     ...:     return 2*x + y 
     ...: a = np.eye(5)                                                              
In [345]: a                                                                          
Out[345]: 
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 1., 0.],
       [0., 0., 0., 0., 1.]])
In [346]: res = myfunc(a[0],a[1])                                                    
In [347]: res                                                                        
Out[347]: array([2., 1., 0., 0., 0.])
In [348]: for i in a[2:]: 
     ...:     res = myfunc(res,i) 
     ...:                                                                            
In [349]: res                                                                        
Out[349]: array([16.,  8.,  4.,  2.,  1.])

В Python есть функция reduce для многократного применения функции к списку.В Py3 это в functools:

In [355]: functools.reduce(myfunc, a)                                                
Out[355]: array([16.,  8.,  4.,  2.,  1.])

Или начиная с нуля res и применяется ко всему массиву:

In [357]: res = np.zeros(a.shape[1])                                                 
In [358]: for i in a: 
     ...:     res = myfunc(res,i) 
     ...:                                                                            
In [359]: res                                                                        
Out[359]: array([16.,  8.,  4.,  2.,  1.])

Для сохранения промежуточных результатов:

In [361]: res = [np.zeros(a.shape[1])] 
     ...: for i in a: 
     ...:     temp = myfunc(res[-1],i) 
     ...:     res.append(temp)                                                                            
In [362]: res                                                                        
Out[362]: 
[array([0., 0., 0., 0., 0.]),
 array([1., 0., 0., 0., 0.]),
 array([2., 1., 0., 0., 0.]),
 array([4., 2., 1., 0., 0.]),
 array([8., 4., 2., 1., 0.]),
 array([16.,  8.,  4.,  2.,  1.])]

Это понятие accumulate.numpy ufunc имеют оба значения reduce и accumulate и будут быстрее, если с ними можно написать myfunc.Но это не работает в общем случае.

In [363]: np.add.accumulate(a,axis=0)                                                
Out[363]: 
array([[1., 0., 0., 0., 0.],
       [1., 1., 0., 0., 0.],
       [1., 1., 1., 0., 0.],
       [1., 1., 1., 1., 0.],
       [1., 1., 1., 1., 1.]])
1 голос
/ 20 апреля 2019

Вот наивный способ решения проблемы с использованием цикла for по первому измерению (то есть по оси 0):

# your custom function; slightly rewritten because
# * based multiplication is faster than `pow()`
In [93]: def myfunc(x,y):
    ...:     return x*x + y*y - x*y


# to be replenished after each iteration
In [95]: res = a[0]

# go over rows and compute the results using `myfunc()`
In [96]: for i in range(a.shape[0]-1):
    ...:    curr_step_res = myfunc(res, a[i+1])
    ...:    res = curr_step_res[:]


# final result
In [97]: res
Out[97]: 
array([0.32468859, 0.775874  , 0.861402  , 0.4852772 , 0.18264236,
       0.56028635, 0.33515591, 0.05036018, 0.37391415, 0.05364418])

проверка работоспособности с помощью ручных вызовов

In [99]: tmp1 = myfunc(a[0],a[1])
In [100]: results = myfunc(tmp1,a[2])

In [101]: np.allclose(results, res)
Out[101]: True

результаты для вашего случая с N = 4

# sample array to work with
In [102]: a = np.random.random_sample((4, 6))

# to be replenished after each iteration
In [103]: res = a[0]

In [104]: for i in range(a.shape[0]-1):
     ...:    curr_step_res = myfunc(res, a[i+1])
     ...:    res = curr_step_res[:]

In [105]: res
Out[105]: 
array([0.51971283, 0.61377465, 0.0838452 , 0.2201938 , 0.54028219,
       0.19318569])

# compute using manual calls
In [106]: tmp1 = myfunc(a[0],a[1])
     ...: tmp2 = myfunc(tmp1,a[2])
     ...: results = myfunc(tmp2,a[3])

# sanity check for equality of both results
In [107]: np.allclose(results, res)
Out[107]: True

PS это должно обобщаться на любой N, где N = arr.shape[0].Кроме того, обратите внимание, что не существует прямого способа распараллелить это, потому что вычисление является последовательным.

...