numpy 由一维数组与自身的乘积构造一种对Tensor

ybzsozfc  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(80)

我有一个字符串数组,比如

arr = np.array(['A', 'B', 'B', 'B', 'C', 'C', 'C', 'C'])

我想创建一个 Torch Tensor矩阵,其类型为将由Carnival乘积创建的对。因此,结果将是一个8x8Tensor,其中:

  • 如果row == 'A'和列是col == 'B',则矩阵中的值例如是1;
  • 如果row == 'B'和列是col == 'C',则矩阵中的值例如是2
  • 0否则(即如果row与col相同)。

我怎么才能做到这一点?
到目前为止,我已经尝试做了如下事情:

arr = np.array(['A', 'B', 'B', 'B', 'C', 'C', 'C', 'C'])
_, arr = np.unique(arr, return_inverse=True)
arr = torch.tensor(arr)
arr += 1

out = torch.cartesian_prod(arr, arr).reshape(8, 8, 2)
out = out[:, :, 1] * out[:, :, 0]
torch.where(torch.sqrt(out) == torch.sqrt(out).int(), 0, out)

tensor([[0, 2, 2, 2, 3, 3, 3, 3],
        [2, 0, 0, 0, 6, 6, 6, 6],
        [2, 0, 0, 0, 6, 6, 6, 6],
        [2, 0, 0, 0, 6, 6, 6, 6],
        [3, 6, 6, 6, 0, 0, 0, 0],
        [3, 6, 6, 6, 0, 0, 0, 0],
        [3, 6, 6, 6, 0, 0, 0, 0],
        [3, 6, 6, 6, 0, 0, 0, 0]])

但它感觉有点笨拙(甚至值都是相当随机的,而不是0,1,2.),所以我想听听其他的想法。

72qzrwbm

72qzrwbm1#

结合合并网格和花式索引

对已发布解决方案的评论

原文中的方法有两个缺陷。
第一,两个 * 不相等 * 数的乘积的平方根可以是整数。例如,1 * 4的根是2,这是一个整数。因此,零将被插入作为像(1, 4), (2, 8), (3, 12), ...这样的对的标识符,这当然不是故意的。
接下来,将为一些不同的对设置相等的标识符。例如,在这个模型中,对(1, 6)(2, 3)的标识符为6。为了区分它们,有必要改变对它们的识别方式。

新增解决方案

让我们基于range(number_of_pairs).reshape(...)为所有可能的对创建一个标识符矩阵:

import numpy as np

data = np.array(['A', 'B', 'B', 'B', 'C', 'C', 'C', 'C'])
unique, codes = np.unique(data, return_inverse=True)

n = len(unique)
pairs_id = np.arange(n*n).reshape(n, n)   # assign unique id's to each pair
pairs_id = np.triu(pairs_id, k=1)         # put 0 on the diagonal and below it
pairs_id = pairs_id + pairs_id.T          # mirror the upper triangular matrix

# p.s. A hidden trick that is better expressed explicitly: 
# we can start the range from 0, because in this model
# the very first element is inevitably removed by replacing with zero

现在,我们为每对初始唯一值提供了唯一标识符,但标识符与对的顺序无关,对于两个值相等的对,标识符为零。
为了得到理想的答案,我们可以使用numpy.meshgrid来代替笛卡尔积和数组索引:

left, right = np.meshgrid(codes, codes)
answer = pairs_id[left, right]

汇总代码及输出

import numpy as np

arr = np.array(['A', 'B', 'B', 'B', 'C', 'C', 'C', 'C'])
unique, codes = np.unique(arr, return_inverse=True)

n = len(unique)
pairs = np.arange(n*n).reshape(n, n)
pairs = np.triu(pairs, k=1)
pairs = pairs + pairs.T

left, right = np.meshgrid(codes, codes)
answer = pairs[left, right]

print(
    f'{unique = }',
    f'{codes = }',
    f'{pairs = }',
    f'{answer = }',
    sep='\n' + '\N{Horizontal Line Extension}'*30 + '\n'
)

输出量:

unique = array(['A', 'B', 'C'], dtype='<U1')
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
codes = array([0, 1, 1, 1, 2, 2, 2, 2])
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
pairs = array([
       [0, 1, 2],
       [1, 0, 5],
       [2, 5, 0]])
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
answer = array([
       [0, 1, 1, 1, 2, 2, 2, 2],
       [1, 0, 0, 0, 5, 5, 5, 5],
       [1, 0, 0, 0, 5, 5, 5, 5],
       [1, 0, 0, 0, 5, 5, 5, 5],
       [2, 5, 5, 5, 0, 0, 0, 0],
       [2, 5, 5, 5, 0, 0, 0, 0],
       [2, 5, 5, 5, 0, 0, 0, 0],
       [2, 5, 5, 5, 0, 0, 0, 0]])
s71maibg

s71maibg2#

使用简单的广播和二进制编码:

a = 2**np.unique(arr, return_inverse=True)[1]
out = a[:,None] ^ a # easier than np.where(a[:,None]==a, 0, a[:,None]+a), thanks @Vitalizzare

输出量:

array([[0, 3, 3, 3, 5, 5, 5, 5],
       [3, 0, 0, 0, 6, 6, 6, 6],
       [3, 0, 0, 0, 6, 6, 6, 6],
       [3, 0, 0, 0, 6, 6, 6, 6],
       [5, 6, 6, 6, 0, 0, 0, 0],
       [5, 6, 6, 6, 0, 0, 0, 0],
       [5, 6, 6, 6, 0, 0, 0, 0],
       [5, 6, 6, 6, 0, 0, 0, 0]])

如果你想要从0到n的数字,再次因式分解:

out2 = np.unique(out, return_inverse=True)[1].reshape(out.shape)

输出量:

array([[0, 1, 1, 1, 2, 2, 2, 2],
       [1, 0, 0, 0, 3, 3, 3, 3],
       [1, 0, 0, 0, 3, 3, 3, 3],
       [1, 0, 0, 0, 3, 3, 3, 3],
       [2, 3, 3, 3, 0, 0, 0, 0],
       [2, 3, 3, 3, 0, 0, 0, 0],
       [2, 3, 3, 3, 0, 0, 0, 0],
       [2, 3, 3, 3, 0, 0, 0, 0]])

中间体:

# a
array([1, 2, 2, 2, 4, 4, 4, 4])

# a[:,None]==a
array([[ True, False, False, False, False, False, False, False],
       [False,  True,  True,  True, False, False, False, False],
       [False,  True,  True,  True, False, False, False, False],
       [False,  True,  True,  True, False, False, False, False],
       [False, False, False, False,  True,  True,  True,  True],
       [False, False, False, False,  True,  True,  True,  True],
       [False, False, False, False,  True,  True,  True,  True],
       [False, False, False, False,  True,  True,  True,  True]])

# a[:,None]+a
array([[2, 3, 3, 3, 5, 5, 5, 5],
       [3, 4, 4, 4, 6, 6, 6, 6],
       [3, 4, 4, 4, 6, 6, 6, 6],
       [3, 4, 4, 4, 6, 6, 6, 6],
       [5, 6, 6, 6, 8, 8, 8, 8],
       [5, 6, 6, 6, 8, 8, 8, 8],
       [5, 6, 6, 6, 8, 8, 8, 8],
       [5, 6, 6, 6, 8, 8, 8, 8]])
vaqhlq81

vaqhlq813#

使用广播和字符串连接:

# pairwise concatenation with broadcasting
pairs = np.char.add(arr[:, np.newaxis], arr[np.newaxis, :])

# replacing concatenation where elements are equal by an empty string
pairs[arr[:, np.newaxis] == arr[np.newaxis, :]] = ''

labels = (
    # get indices of unique array ( size=(len(arr)**2,) )
    np.unique(pairs, return_inverse=True)[1]
    # reshape to (len(arr), len(arr))
    .reshape(pairs.shape)
)

labels

输出量:

array([[0, 1, 1, 1, 2, 2, 2, 2],
       [3, 0, 0, 0, 4, 4, 4, 4],
       [3, 0, 0, 0, 4, 4, 4, 4],
       [3, 0, 0, 0, 4, 4, 4, 4],
       [5, 6, 6, 6, 0, 0, 0, 0],
       [5, 6, 6, 6, 0, 0, 0, 0],
       [5, 6, 6, 6, 0, 0, 0, 0],
       [5, 6, 6, 6, 0, 0, 0, 0]])

最后,当然,labels可以用来创建torch.array

相关问题