Как рассчитать все комбинации разницы между элементами массива в 2d? - PullRequest
1 голос
/ 26 марта 2019

Учитывая массив arr = [10, 11, 12] Я хочу вычислить все способы, которыми один элемент может быть вычтен из другого. Для массива 1xN желаемый результат - массив NxN, где output[i, j] = arr[i] - arr[j]. Мой подход состоял в том, чтобы сгенерировать все возможные пары из двух чисел, вычесть и изменить форму. Следующим образом

opts = np.array(list(product(arr, arr)))
[[10 10] 
 [10 11]
 [10 12]
 [11 10]
 [11 11]
 [11 12]
 [12 10]
 [12 11]
 [12 12]]
 diffs = (opts[:, 0] - opts[:, 1]).reshape(len(arr), -1)
 [[ 0 -1 -2]
  [ 1  0 -1]
  [ 2  1  0]]

Это работает довольно хорошо, что я хотел бы сделать дальше, это обобщить это на 2d вход. По сути, я хотел бы получить массив MxN для вывода массива MxNxN, и для каждого слоя (по глубине) выполнить вышеуказанные функции для каждой строки.

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

Моя первая мысль - инициализировать выходные данные соответствующей формы и выполнить цикл по строкам и установить значения «вручную», но я надеялся на векторизованный подход. Кто-нибудь знает, как я могу сделать это в двух измерениях, не зацикливаясь на тысячи строк?

Ответы [ 2 ]

2 голосов
/ 26 марта 2019

Вот общий векторизованный способ охвата как одномерных, так и двумерных случаев, используя broadcasting после преобразования входного массива в транслируемые shpaes друг против друга -

def permute_axes_subtract(arr, axis):
    # Get array shape
    s = arr.shape

    # Get broadcastable shapes by introducing singleton dimensions
    s1 = np.insert(s,axis,1)
    s2 = np.insert(s,axis+1,1)

    # Perform subtraction after reshaping input array to 
    # broadcastable ones against each other
    return arr.reshape(s1) - arr.reshape(s2)

Для выполнения любых другихпоэлементно ufunc операция, просто замените на нее операцию вычитания.

Пример выполнения -

In [184]: arr = np.random.rand(3)

In [185]: permute_axes_subtract(arr, axis=0).shape
Out[185]: (3, 3)

In [186]: arr = np.random.rand(3,4)

In [187]: permute_axes_subtract(arr, axis=0).shape
Out[187]: (3, 3, 4)

In [188]: permute_axes_subtract(arr, axis=1).shape
Out[188]: (3, 4, 4)

Временные параметры @ Опубликованное решение ClimbingTheCurve func - permute_difference и тот, который размещен в этом на больших 2D массивах -

In [189]: arr = np.random.rand(100,100)

In [190]: %timeit permute_difference(arr, axis=0)
     ...: %timeit permute_axes_subtract(arr, axis=0)
1 loop, best of 3: 295 ms per loop
1000 loops, best of 3: 1.17 ms per loop

In [191]: %timeit permute_difference(arr, axis=1)
     ...: %timeit permute_axes_subtract(arr, axis=1)
1 loop, best of 3: 303 ms per loop
1000 loops, best of 3: 1.12 ms per loop
1 голос
/ 26 марта 2019

Решение состоит в том, чтобы написать функцию для случая 1d, а для обобщения использовать функцию np.apply_along_axis(), которая принимает функцию, ось, которую нужно применить вдоль, и входной массив.Это работает точно так, как задумано.Код, который я использовал:

from itertools import product

import numpy as np


def permute_difference(arr, axis=1):
    """
    applies the _permute_difference to a 2d array
    along the specified axis

    Parameters
    ----------
    arr numpy.array

    Returns
    -------
    numpy.array
        a 3d array, each 2d array the i^th along the depth
        contains the permuted difference of the i^th row
        in the input array
    """
    def _permute_difference(arr):
        """
        calculates all the differences between all combinations
        terms in the input array. output[i,j] = arr[i] - arr[j]
        for every combination if ij.

        Parameters
        ----------
        arr numpy.array
            a 1d input array

        Returns
        -------
        numpy.array
            a 2d array

        Examples
        --------
        arr = [10, 11, 12]

        diffs = [[ 0 -1 -2]
                [ 1  0 -1]
                [ 2  1  0]]
        """
        opts = np.array(list(product(arr, arr)))
        d = (opts[:, 0] - opts[:, 1]).reshape(len(arr), -1)
        return d

    if arr.ndim == 1:
        diffs = _permute_difference(arr)
    else:
        diffs = np.apply_along_axis(permute_difference, axis, arr)
    return diffs
...