Параметры numpy из функции

Я еще не понял ключевые понятия в numpy.

Я хотел бы создать трехмерный массив и заполнить каждую ячейку результатом вызова функции - то есть функция будет вызываться много раз с разными индексами и возвращать разные значения.

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

Я мог бы создать его с нулями (или пустым), а затем перезаписать каждое значение с помощью цикла for, но кажется, что заполнение его непосредственно из функции кажется более чистым.

fromfunction звучит идеально. Читая документацию, звучит так, как будто функция вызывается один раз для каждой ячейки.

Но когда я действительно попробую это...

from numpy import *

def sum_of_indices(x, y, z):
    # What type are X, Y and Z ? Expect int or duck-type equivalent.
    # Getting 3 individual arrays
    print "Value of X is:"
    print x

    print "Type of X is:", type(x)
    return x + y + z

a = fromfunction(sum_of_indices, (2, 2, 2))

Я ожидаю получить что-то вроде:

Value of X is:
0
Type of X is: int
Value of X is:
1
Type of X is: int

повторяется 4 раза.

Я получил:

Value of X is:
[[[ 0.  0.]
  [ 0.  0.]]

 [[ 1.  1.]
  [ 1.  1.]]]
[[[ 0.  0.]
  [ 1.  1.]]

 [[ 0.  0.]
  [ 1.  1.]]]
[[[ 0.  1.]
  [ 0.  1.]]

 [[ 0.  1.]
  [ 0.  1.]]]
Type of X is: <type 'numpy.ndarray'>

Функция вызывается только один раз и, похоже, возвращает весь массив как результат.

Как правильно заполнить массив на основе нескольких вызовов функции индексов?

Ответ 1

Я, очевидно, не давал себе понять. Я получаю ответы, что fromfunc самом деле работает, как показывает мой тестовый код, что я уже знал, потому что мой тестовый код продемонстрировал это.

Ответ, который я искал, кажется, состоит из двух частей:


Документация fromfunc вводит в заблуждение. Это работает, чтобы заполнить весь массив сразу.

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

В частности, эта строка в документации была неверной (или, как минимум, вводящей в заблуждение)

Например, если shape была (2, 2), то параметры в свою очередь были бы (0, 0), (0, 1), (1, 0), (1, 1).

Нет. Если бы shape (т.е. из контекста, второй параметр функции fromfunction) была (2,2), параметры были бы (не "по очереди", а в единственном вызове):

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

Документация была обновлена, и в настоящее время читается более точно:

Функция вызывается с N параметрами, где N - ранг формы. Каждый параметр представляет координаты массива, изменяющиеся вдоль определенной оси. Например, если бы форма была (2, 2), то параметрами были бы массив ([[0, 0], [1, 1]]) и массив ([[0, 1], [0, 1]])

(Мой простой пример, полученный из примеров в руководстве, мог ввести в заблуждение, потому что + может работать как с массивами, так и с индексами. Эта неоднозначность является еще одной причиной, по которой документация неясна. Я хочу в конечном итоге использовать функцию, которая не ' • на основе массива, но на основе ячеек - например, каждое значение может быть получено из URL или базы данных на основе индексов или даже ввода от пользователя.)


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

Вы не можете сделать это в функциональном стиле.

Вы можете сделать это в императивном/итеративном стиле - т.е. писать вложенные циклы for и самостоятельно управлять длиной индекса.

Вы также можете сделать это как итератор, но итератору все еще нужно отслеживать свои собственные индексы.

Ответ 2

В этом отношении документация очень вводит в заблуждение. Это так же, как вы отмечаете: вместо выполнения f(0,0), f(0,1), f(1,0), f(1,1), numpy выполняет

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

Использование ndarrays, а не обещанных целых координат довольно сложно, когда вы пытаетесь использовать что-то вроде lambda i: l[i], где l - это другой массив или список (хотя действительно есть, вероятно, лучшие способы сделать это в numpy).

Функция numpy vectorize фиксирует это. Где у вас есть

m = fromfunction(f, shape)

Попробуйте использовать

g = vectorize(f)
m = fromfunction(g, shape)

Ответ 3

Я думаю, вы не понимаете, что из этой fromfunction.

Из исходного кода numpy.

def fromfunction(function, shape, **kwargs):
    dtype = kwargs.pop('dtype', float)
    args = indices(shape, dtype=dtype)
    return function(*args,**kwargs)

Где indices достаточно эквивалентны meshgrid где каждая переменная np.arange(x).

>>> side = np.arange(2)
>>> side
array([0, 1])
>>> x,y,z = np.meshgrid(side,side,side)
>>> x
array([[[0, 0],
        [1, 1]],

       [[0, 0],
        [1, 1]]])
>>> x+y+z #Result of your code.
array([[[0, 1],
        [1, 2]],

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

Ответ 4

Это дает вам неправильный результат? a должно быть как и ожидалось (и это когда я его протестировал) и кажется прекрасным способом делать то, что вы хотите.

>>> a
array([[[ 0.,  1.],    # 0+0+0, 0+0+1
        [ 1.,  2.]],   # 0+1+0, 0+1+1

       [[ 1.,  2.],    # 1+0+0, 1+0+1
        [ 2.,  3.]]])  # 1+1+0, 1+1+1

Поскольку fromfunction работает по индексам массивов для ввода, вы можете видеть, что ее нужно только один раз вызывать. Документация не делает это ясным, но вы можете видеть, что функция вызывается в массивах индексов в исходном коде (от numeric.py):

def fromfunction(function, shape, **kwargs):
    . . .
    args = indices(shape, dtype=dtype)
    return function(*args,**kwargs)

sum_of_indices вызывается на входы массива, где каждый массив содержит значения индекса для этого измерения.

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

       [[ 1.,  1.],
        [ 1.,  1.]]])

+

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

       [[ 0.,  0.],
        [ 1.,  1.]]])

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

       [[ 0.,  1.],
        [ 0.,  1.]]])

=

array([[[ 1.,  1.],
        [ 1.,  2.]],

       [[ 1.,  2.],
        [ 2.,  3.]]])