Как сделать сокращение с помощью numpy.nditer на первой оси

Я пытаюсь понять, как работать с nditer, чтобы сделать в моем случае преобразование 3d-массива в массив 2d.

Я следил за помощью здесь http://docs.scipy.org/doc/numpy/reference/arrays.nditer.html и удалось создать функцию, которая применяет уменьшение по последней оси ввода. С помощью этой функции

def nditer_sum(data, red_axes):
    it = numpy.nditer([data, None],
            flags=['reduce_ok', 'external_loop'],
            op_flags=[['readonly'], ['readwrite', 'allocate']],
            op_axes=[None, red_axes])
    it.operands[1][...] = 0

    for x, y in it:
        y[...] = x.sum()

    return it.operands[1]

Я могу получить что-то эквивалентное data.sum(axis = 2)

>>> data = numpy.arange(2*3*4).reshape((2,3,4))
>>> nditer_sum(data, [0, 1, -1])
[[ 6 22 38]
[54 70 86]]
>>> data.sum(axis=2)
[[ 6 22 38]
[54 70 86]]

Итак, чтобы получить что-то эквивалентное data.sum(axis = 0) Я хотя это было достаточно, чтобы изменить аргумент red_axes на [-1, 0,1] Но результат совсем другой.

>>> data = numpy.arange(2*3*4).reshape((2,3,4))
>>> data.sum(axis=0)
[[12 14 16 18]
 [20 22 24 26]
 [28 30 32 34]]
>>> nditer_sum(data, [-1, 0, 1])
[[210 210 210 210]
 [210 210 210 210] 
 [210 210 210 210]]

В цикле for внутри nditer_sum (для x, y в нем:) итератор петля 2 раза и давая массив длиной 12 каждый раз, вместо цикл 12 раз и давая массив длины 2 каждый раз. у меня есть несколько раз прочитайте документацию по количеству писем и перейдите по этой ссылке в нет. Я использую numpy 1.6 и python 2.7

Ответ 1

Случай axis=0 работает правильно, если порядок nditer изменен на F. Теперь есть 12 шагов с массивами размера (2,), как вы хотели.

it = np.nditer([data, None],
        flags=['reduce_ok', 'external_loop'],
        op_flags=[['readonly'], ['readwrite', 'allocate']],
        order='F',     # ADDED to loop starting with the last dimension
        op_axes=[None, red_axes])

Но для среднего axis=1 не существует такого решения.


Другим подходом к итерации по выбранным измерениям является построение итератора 'multi_index' на уменьшенном размерном массиве. Я обнаружил в fooobar.com/questions/533771/..., что np.ndindex использует этот трюк для выполнения "мелкой итерации".

Для случая axis=0 эта функция работает:

def sum_1st(data):
    y = np.zeros(data.shape[1:], data.dtype)
    it = np.nditer(y, flags=['multi_index'])
    while not it.finished:
        xindex = tuple([slice(None)]+list(it.multi_index))
        y[it.multi_index] = data[xindex].sum()
        it.iternext()
    return y

Или обобщен на любую ось:

def sum_any(data, axis=0):
    yshape = list(data.shape)
    del yshape[axis]
    y = np.zeros(yshape, data.dtype)
    it = np.nditer(y, flags=['multi_index'])
    while not it.finished:
        xindex = list(it.multi_index)
        xindex.insert(axis, slice(None))
        y[it.multi_index] = data[xindex].sum()
        it.iternext()
    return y

Ответ 2

Изменение строки y[...] = x.sum() до y[...] += x исправляет ее (как в примере здесь).