numpy: вычисление среднего и стандартного по нескольким непоследовательным осям (2-я попытка) - PullRequest
1 голос
/ 04 января 2012

[Более ранняя версия этого поста не получила абсолютно никакого ответа, поэтому, на случай, если это произошло из-за недостатка ясности, я переработал его с дополнительными пояснениями и комментариями к коду.]

Я хочу вычислить среднее и стандартное отклонение для элементов числового n -мерного массива, который не соответствует одной оси (а скорее k > 1 непоследовательные оси) и получить результаты, собранные в новый ( n - k + 1) -мерный массив.

Включает ли numpy стандартные конструкции для эффективного выполнения этой операции?

Функция mu_sigma, скопированная ниже, является моей лучшей попыткой решения этой проблемы, но у нее есть две явные неэффективности: 1) она требует создания копии исходных данных; 2) оно вычисляет среднее значение дважды (поскольку для вычисления стандартного отклонения требуется вычисление среднего значения).

Функция mu_sigma принимает два аргумента: box и axes. box является n -мерным массивом numpy (он же "ndarray"), а axes является k -трудой целых чисел, представляющих (не обязательно последовательные) измерения box. Функция возвращает новый ( n - k + 1) -мерный ndarray, содержащий среднее и стандартное отклонение, вычисленное по «гиперслоям» box, представленным k указанные оси.

Приведенный ниже код также включает пример mu_sigma в действии. В этом примере аргумент box представляет собой массив чисел с плавающей запятой 4 x 2 x 4 x 3 x 4, а аргумент axes - кортеж (1, 3). (Следовательно, у нас есть n == len(box.shape) == 5 и k == len(axes) == 2.) Результат (который здесь я назову outbox) для этого примера ввода возвращается 4 x 4 x 4 x 2 ndarray чисел с плавающей запятой. Для каждого триплета индексов i , k , j (где каждый индекс находится в пределах набора {0, 1, 2, 3}) элемент outbox[i, j, k, 0] - это среднее из 6 элементов, указанных в числовом выражении box[i, 0:2, j, 0:3, k]. Точно так же outbox[i, j, k, 1] является стандартным отклонением тех же 6 элементов. Это означает, что первые n - k == 3 измерения диапазона результатов по тем же индексам, что и n - k неосевые размеры ввода ndarray box, которые в данном случае равны размерам 0, 2 и 4.

Стратегия, используемая в mu_sigma, заключается в

  1. переставляет размеры (используя метод transpose), так что все оси, указанные во втором аргументе функции, ставятся в конце; остальные (неосевые) размеры остаются в начале (в исходном порядке);
  2. сворачивать размеры осей в одну (с помощью метода reshape); новое «свернутое» измерение теперь является последним измерением измененного ndarray;
  3. вычисление ndarray средних значений с использованием последнего «свернутого» измерения в качестве оси;
  4. вычислить ndarray стандартных отклонений, используя последнее «свернутое» измерение в качестве оси;
  5. возвращает ndarray, полученный в результате объединения ndarrays, полученных в (3) и (4)


import numpy as np

def mu_sigma(box, axes):
    inshape = box.shape

    # determine the permutation needed to put all the dimensions given in axes
    # at the end (otherwise preserving the relative ordering of the dimensions)
    nonaxes = tuple([i for i in range(len(inshape)) if i not in set(axes)])

    # permute the dimensions
    permuted = box.transpose(nonaxes + axes)

    # determine the shape of the ndarray after permuting the dimensions and
    # collapsing the axes-dimensions; thanks to Bago for the "+ (-1,)"
    newshape = tuple(inshape[i] for i in nonaxes) + (-1,)

    # collapse the axes-dimensions
    # NB: the next line results in copying the input array
    reshaped = permuted.reshape(newshape)

    # determine the shape for the mean and std ndarrays, as required by
    # the subsequent call to np.concatenate (this reshaping is not necessary
    # if the available mean and std methods support the keepdims keyword;
    # instead, just set keepdims to True in both calls).
    outshape = newshape[:-1] + (1,)

    # compute the means and standard deviations
    mean = reshaped.mean(axis=-1).reshape(outshape)
    std = reshaped.std(axis=-1).reshape(outshape)

    # collect the results in a single ndarray, and return it
    return np.concatenate((mean, std), axis=-1)

inshape = 4, 2, 4, 3, 4
inbuf = np.array(map(float, range(np.product(inshape))))
inbox = np.ndarray(inshape, buffer=inbuf)
outbox = mu_sigma(inbox, tuple(range(len(inshape))[1::2]))

# "inline tests"
assert all(outbox[..., 1].ravel() ==
           [inbox[0, :, 0, :, 0].std()] * outbox[..., 1].size)
assert all(outbox[..., 0].ravel() == [float(4*(v + 3*w) + x)
                                      for v in [8*y - 1
                                                for y in [3*z + 1
                                                          for z in range(4)]]
                                      for w in range(4)
                                      for x in range(4)])

1 Ответ

1 голос
/ 19 января 2012

Похоже, с numpy 2.0 это стало немного проще.

http://projects.scipy.org/numpy/ticket/1234

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...