Создайте массив из лямбда-функции, размерной формы и dtype - PullRequest
0 голосов
/ 11 мая 2019

Мне нужно создать функцию, которая принимает лямбду, размерную форму и тип Numpy d и производит массив. Я знаю, что fromfunction это сделает, но я не могу это использовать. Я думаю, один из способов взглянуть на это - мне нужно жестко кодировать fromfunction. Проблема в том, что лямбда-функция не может быть передана как функция. Я пытался использовать циклы и индексы, и я новичок в последней идее, поэтому я мог бы не делать это должным образом. В основном мне нужно создать функцию. Входные данные приведены в виде (ожидаемого) результата в комментарии.

import numpy as np
def array_function(f, d, dtype=None):

    return x

print(array_function(lambda i,j: (i - j)**2, [4, 4]))

# Expected Result
#[[0. 1. 4. 9.]
# [1. 0. 1. 4.]
# [4. 1. 0. 1.]
# [9. 4. 1. 0.]]

Ответы [ 3 ]

0 голосов
/ 11 мая 2019

Как насчет создания np.ufunc из вашего lambda и использования reduce для его применения к нескольким измерениям?

from functools import reduce
import numpy as np

def apply(f, shape, dtype=None):
    ufunc = np.frompyfunc(f, 2, 1)
    ranges = (np.arange(dim) for dim in shape)
    return reduce(ufunc.outer, ranges).astype(dtype)

print(apply(lambda i, j: (i - j) ** 2, (4, 4)))

Выход:

[[0. 1. 4. 9.]
 [1. 0. 1. 4.]
 [4. 1. 0. 1.]
 [9. 4. 1. 0.]]
0 голосов
/ 11 мая 2019

Для этого lambda, fromfunction отлично работает:

In [1]: foo = lambda i,j: (i-j)**2                                              
In [2]: np.fromfunction(foo,(4,4))                                              
Out[2]: 
array([[0., 1., 4., 9.],
       [1., 0., 1., 4.],
       [4., 1., 0., 1.],
       [9., 4., 1., 0.]])

fromfunction генерирует «сетку» индексов из формы:

In [7]: np.indices((4,4))                                                       
Out[7]: 
array([[[0, 0, 0, 0],
        [1, 1, 1, 1],
        [2, 2, 2, 2],
        [3, 3, 3, 3]],

       [[0, 1, 2, 3],
        [0, 1, 2, 3],
        [0, 1, 2, 3],
        [0, 1, 2, 3]]])

и передаетдве плоскости (1-е измерение) для вашей функции.Ваша функция, как написано, работает с массивами, такими как эти 2-мерные сетки.meshgrid и mgridogrid) генерируют аналогичные индексы.

Но я также мог бы создать два массива напрямую и передать их foo:

In [8]: foo(np.arange(4)[:,None], np.arange(4))                                 
Out[8]: 
array([[0, 1, 4, 9],
       [1, 0, 1, 4],
       [4, 1, 0, 1],
       [9, 4, 1, 0]])

Эти два входных массива транслируются друг против друга так же, как это делают 2 плоскости в Out[7].По сути, они являются (4,1) и (4) -образными эквивалентами.

Обратите внимание, что в Python lambda - это просто анонимная функция.Здесь я присвоил его переменной, дав ей имя (своего рода).Функция def также может быть использована.

Так что, пока ваша функция работает с необходимыми массивами двумерных индексов, вам не нужно никакого специального кодирования.

Если функция работает только со скалярными значениями i и j, тогда вам придется прибегнуть к чему-то, что повторяется на уровне Python (в отличие от использования скомпилированных numpy функций).

Версия со списком:

In [6]: np.array([[foo(i,j) for j in range(4)] for i in range(4)])              
Out[6]: 
array([[0, 1, 4, 9],
       [1, 0, 1, 4],
       [4, 1, 0, 1],
       [9, 4, 1, 0]])

Мне скорее нравитсяfrompyfunc, который будет использоваться как:

In [9]: f = np.frompyfunc(foo, 2,1)                                             
In [10]: f(np.arange(4)[:,None], np.arange(4))                                  
Out[10]: 
array([[0, 1, 4, 9],
       [1, 0, 1, 4],
       [4, 1, 0, 1],
       [9, 4, 1, 0]], dtype=object)

Обратите внимание, что он возвращает объект dtype.Это можно изменить с помощью astype.Он также может быть передан в fromfunction, если вы слишком 'ленивы', чтобы писать свои собственные передаваемые массивы I и J.

По моему опыту, подход frompyfunc несколько быстрее, чем списокпонимание (примерно до 2х).С другой стороны, если foo работает с массивами, как в [8], то соотношение скоростей больше похоже на 10x.Поэтому с точки зрения производительности вы будете счастливы, если сможете писать функции, работающие с целыми массивами, а не со скалярными индексами.

0 голосов
/ 11 мая 2019

Лямбда-функция не может быть передана

Лямбда-функция - это функция, которая является первоклассным объектом в python. Нетрудно передать его в качестве аргумента другой функции. Вы можете просто взглянуть или разобраться в двух измерениях, создать и массив, а затем изменить форму:

import numpy as np
def array_function(f, d, dtype=None):
    a = np.array([f(i, j) for i in range(d[0]) for j in range(d[1])], dtype)
    return a.reshape(d)

print(array_function(lambda i,j: (i - j)**2, [4, 4]))

результат:

[[0 1 4 9]  
 [1 0 1 4]  
 [4 1 0 1]  
 [9 4 1 0]]  

немного сложнее, если вы хотите, чтобы array_function брал массивы произвольного размера. Один из вариантов - создать массив размера, а затем перечислить все элементы для вызова функции:

import numpy as np
def array_function(f, d, dtype=None):
    a = np.zeros(d)
    for coord, val in np.ndenumerate(a):
        a[coord] = f(*coord)    
    return a

# three dimensions    
print(array_function(lambda i,j, k: k+(i - j)**2, [4, 5,2], np.float))

Редактировать на основе комментария

Вы можете создать итератор с starmap и product из itertools. Я не уверен, что итератор много покупает с NumPy, так как вы обычно хотите узнать размер массива при создании. Вы можете указать длину, которая не обязательна, но повышает производительность:

from itertools import product, starmap
import numpy as np
from operator import mul
from functools import reduce

def array_function(f, d, dtype=None):
    length = reduce(mul, d)
    iterator = starmap(f, product(*[range(x) for x in d]))

    a = np.fromiter(iterator, dtype, length)
    return a.reshape(d)

print(array_function(lambda i,j: (i - j)**2, [4, 4], np.float))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...