Numpy применяет функцию в парах строк и накапливает результаты

#python #function #numpy #list-comprehension

#python #функция #numpy #список-понимание

Вопрос:

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

Предполагая, что у меня есть 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]])
  

Я хочу применить следующую функцию к каждой паре строк и накапливать результаты , т.е. (row0 с row1) -> (вывод предыдущего шага со строкой 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])
  

Комментарии:

1.Используйте for цикл. Или используйте functools.reduce функцию (py3). numpy ufunc также есть reduce метод.

2. Привет. Цикл for был бы в порядке, но я попытался использовать цикл for, и снова я не нахожу способа его обобщить.

3. Уточните, что вы хотите с более высоким N, 4 или 5 и т.д.

4. См. Редактирование 1 для N = 4

Ответ №1:

Вот наивный способ ее решения с использованием 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] . Также обратите внимание, что нет простого способа распараллелить это, поскольку вычисления выполняются последовательно.

Комментарии:

1. это именно то, что мне было нужно.

Ответ №2:

Упрощенная версия вашей функции и 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.]])