任意维数组中任意维坐标的笛卡尔积

问题描述 投票:0回答:2

这与 Numpy 有一定关系:x 和 y 数组点的笛卡尔乘积为单个 2D 点数组

我正在寻找一种简洁的方法来创建两个任意维度数组的笛卡尔积。

示例:

与相关主题类似,我想要

x = numpy.array([1,2,3]) #ndim 1
y = numpy.array([4,5])  #ndim 1
cartesian_product(x,y) == numpy.array([[[1, 4],
                                        [2, 4],
                                        [3, 4]],
                                       [[1, 5],
                                        [2, 5],
                                        [3, 5]]]) #ndim "2" = ndim x + ndim y

生成的数组是二维的,因为 [1, 4]、[2, 4] 等是坐标,因此不是真正的维度。概括而言,将 x/y 写为 [[1], [2], [3]] 可能会更好。

以上等于

numpy.dstack(numpy.meshgrid(x,y))

但是我也想要

x2 = numpy.array([[1,1], [2,2], [3,3]]) #ndim "1", since [1, 1] is a coordinate
cartesian_product(x2,y) == numpy.array([[[1, 1, 4],
                                         [2, 2, 4],
                                         [3, 3, 4]],

                                        [[1, 1, 5],
                                         [2, 2, 5],
                                         [3, 3, 5]]]) #ndim 2 = ndim x2 + ndim y


y2 = numpy.array([[10, 11], [20, 21]]) #ndim 1
(cartesian_product(x2, y2) ==
numpy.array([[[1, 1, 10, 11],
              [2, 2, 10, 11],
              [3, 3, 10, 11]],

             [[1, 1, 20, 21],
              [2, 2, 20, 21],
              [3, 3, 20, 21]]])) #ndim x2 + ndim y2

x3 = numpy.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) #ndim 2
(cartesian_product(x3, y) ==
numpy.array([[[[1, 2, 4], [3, 4, 4]], [[5, 6, 4], [7, 8, 4]]],
             [[[1, 2, 5], [3, 4, 5]], [[5, 6, 5], [7, 8, 5]]]]) #ndim 3

想象一下我正在尝试做的事情: 正如我所说, [[0, 0], [0, 1], [1, 1], [1, 0]] 应该被解释为一维坐标列表,对应于一条线。如果我用 [1, 2, 3, 4] 做笛卡尔积,我会在 z 方向上挤压这条线,变成一个表面(即二维)。但现在数组当然是 3 维的了。

我想我可以找到用循环来解决这个问题的方法,但是有没有办法用 numpy/scipy 工具来实现这个目标?

python numpy multidimensional-array cartesian-product n-dimensional
2个回答
2
投票

一种节省内存的方法是广播分配:

def cartesian_product(x, y):
    if x.ndim < 2:
        x = np.atleast_2d(x).T
    if y.ndim < 2:
        y = np.atleast_2d(y).T

    sx, sy = x.shape, y.shape
    sz = sy[:-1] + sx[:-1] + (sy[-1] + sx[-1],)
    z = np.empty(sz, np.result_type(x, y))

    # Broadcasted assignment
    z[...,:sx[-1]] = x
    z[...,sx[-1]:] = y.reshape(sy[:-1] + (x.ndim-1)*(1,) + (sy[-1],))

    return z

如果您需要有关广播的详细信息,此页面可以满足您的要求。


0
投票

基于 @user6758673 的答案,一种更通用的方法(适用于任何维度的数组)是:

import numpy as np

def cartesian_product(x, y, *other_arrays):
    if x.ndim < 2:
        x = np.atleast_2d(x).T
    if y.ndim < 2:
        y = np.atleast_2d(y).T

    sx, sy = x.shape, y.shape
    sz = sy[:-1] + sx[:-1] + (sy[-1] + sx[-1],)
    z = np.empty(sz, np.result_type(x, y))

    # Broadcasted assignment
    z[...,:sx[-1]] = x
    z[...,sx[-1]:] = y.reshape(sy[:-1] + (x.ndim-1)*(1,) + (sy[-1],))

    answer = z.reshape((x.shape[0]*y.shape[0], x.shape[1]+y.shape[1]))
    if len(other_arrays) > 0:
        return cartesian_product(answer, *other_arrays)

    return answer

# Usage example:
array1 = np.array([[1,2], [3,4], [5,6]])
array2 = np.array([['a'], ['b']])
array3 = np.array([[8], [9]])
arrays = [ array1, array2, array3 ]
cartesian_product(*arrays) # or simply: cartesian_product(array1, array2, array3)
# array([['1', '2', 'a', '8'],
#     ['3', '4', 'a', '8'],
#     ['5', '6', 'a', '8'],
#     ['1', '2', 'b', '8'],
#     ['3', '4', 'b', '8'],
#     ['5', '6', 'b', '8'],
#     ['1', '2', 'a', '9'],
#     ['3', '4', 'a', '9'],
#     ['5', '6', 'a', '9'],
#     ['1', '2', 'b', '9'],
#     ['3', '4', 'b', '9'],
#     ['5', '6', 'b', '9']], dtype='<U21')
© www.soinside.com 2019 - 2024. All rights reserved.