使用 numpy 广播减去二维数组

Subtracting Two dimensional arrays using numpy broadcasting

总的来说,我是 numpy 的新手,所以这是一个简单的问题,但我不知道如何解决它。
我正在尝试实现 K 最近邻算法来对数据集进行分类

有数组名为 new_pointspoint 分别具有 (30,4) 的形状 和 (120,4)(4 是每个元素的属性总数)
所以我正在尝试使用 numpy.broadcasting

计算每个新点与所有旧点之间的距离
def calc_no_loop(new_points, points):
  return np.sum((new_points-points)**2,axis=1)
#doesn't work here is log 

ValueError: operands could not be broadcast together with shapes (30,4) (120,4)

但是根据广播规则,两个形状数组 (30,4) 和 (120,4) 是不兼容的 所以我很感激任何关于如何解决这个问题的见解(使用 .reshape prehaps - 不确定)

请注意:我已经使用一个和两个循环实现了相同的功能,但没有一个就无法实现它

def calc_two_loops(new_points, points):
m, n = len(new_points), len(points)
d = np.zeros((m, n))
for i in range(m):
    for j in range(n):
        
        d[i, j] = np.sum((new_points[i] - points[j])**2)
return d


def calc_one_loop(new_points, points):
m, n = len(new_points), len(points)    
d = np.zeros((m, n))
print(d)
for i in range(m):
    d[i] = np.sum((new_points[i] - points)**2)
return d

让我们创建一个更小的示例:

nNew = 3; nOld = 5    # Number of new / old points
# New points
new_points = np.arange(100, 100 + nNew * 4).reshape(nNew, 4)
# Old points
points = np.arange(10, 10 + nOld * 8, 2).reshape(nOld, 4)

要单独计算差异,运行:

dfr = new_points[:, np.newaxis, :] - points[np.newaxis, :, :]

到目前为止,我们在每个点的每个 属性 中都有差异(每个新点与每个旧点)。

dfr的形状是(3, 5, 4):

  • 第一维:新点的个数,
  • 第二维度:老点数,
  • 第三维度:每个维度的差异属性。

然后,按点求差的平方和,运行:

d = np.power(dfr, 2).sum(axis=2)

这是你的结果。

对于我的示例数据,结果是:

array([[31334, 25926, 21030, 16646, 12774],
       [34230, 28566, 23414, 18774, 14646],
       [37254, 31334, 25926, 21030, 16646]], dtype=int32)

所以你有 30 个新点和 120 个旧点,所以如果我理解正确的话你想要一个距离的 shape(120,30) 数组结果。

你可以

import numpy as np

points = np.random.random(120*4).reshape(120,4)
new_points = np.random.random(30*4).reshape(30,4)

def calc_no_loop(new_points, points):
    res = np.zeros([len(points[:,0]),len(new_points[:,0])])
    for idx in range(len(points[:,0])):
        res[idx,:] = np.sum((points[idx,:]-new_points)**2,axis=1)
    return np.sqrt(res)

test = calc_no_loop(new_points,points)
print(np.shape(test))
print(test)

给出

(120, 30)
[[0.67166838 0.78096694 0.94983683 ... 1.00960301 0.48076185 0.56419991]
 [0.88156338 0.54951826 0.73919191 ... 0.87757896 0.76305462 0.52486626]
 [0.85271938 0.56085692 0.73063341 ... 0.97884167 0.90509791 0.7505591 ]
 ...
 [0.53968258 0.64514941 0.89225849 ... 0.99278462 0.31861253 0.44615026]
 [0.51647526 0.58611128 0.83298535 ... 0.86669406 0.64931403 0.71517123]
 [1.08515826 0.64626221 0.6898687  ... 0.96882542 1.08075076 0.80144746]]

但是从你上面的函数名我知道你不需要循环?那么你可以这样做:

def calc_no_loop(new_points, points):
    new_points1 = np.repeat(new_points[np.newaxis,...],len(points),axis=0)
    points1 = np.repeat(points[:,np.newaxis,:],len(new_points),axis=1)
    return np.sqrt(np.sum((new_points-points1)**2 ,axis=2))

test = calc_no_loop(new_points,points)
print(np.shape(test))
print(test)

有输出:

(120, 30)
[[0.67166838 0.78096694 0.94983683 ... 1.00960301 0.48076185 0.56419991]
 [0.88156338 0.54951826 0.73919191 ... 0.87757896 0.76305462 0.52486626]
 [0.85271938 0.56085692 0.73063341 ... 0.97884167 0.90509791 0.7505591 ]
 ...
 [0.53968258 0.64514941 0.89225849 ... 0.99278462 0.31861253 0.44615026]
 [0.51647526 0.58611128 0.83298535 ... 0.86669406 0.64931403 0.71517123]
 [1.08515826 0.64626221 0.6898687  ... 0.96882542 1.08075076 0.80144746]]

即同样的结果。请注意,我将 np.sqrt() 添加到您在上面的示例中可能忘记的结果中。