【问题标题】:Fast way to apply function to each row of a numpy array将函数应用于 numpy 数组的每一行的快速方法
【发布时间】:2017-05-06 22:53:08
【问题描述】:

假设我有一些最近邻分类器。对于新观察,它计算新观察与“已知”数据集中所有观察之间的距离。它返回与新观察距离最小的观察的类标签。

import numpy as np

known_obs = np.random.randint(0, 10, 40).reshape(8, 5)
new_obs = np.random.randint(0, 10, 80).reshape(16, 5)
labels = np.random.randint(0, 2, 8).reshape(8, )

def my_dist(x1, known_obs, axis=0):
    return (np.square(np.linalg.norm(x1 - known_obs, axis=axis)))

def nn_classifier(n, known_obs, labels, axis=1, distance=my_dist):
    return labels[np.argmin(distance(n, known_obs, axis=axis))]

def classify_batch(new_obs, known_obs, labels, classifier=nn_classifier, distance=my_dist):
    return [classifier(n, known_obs, labels, distance=distance) for n in new_obs]

print(classify_batch(new_obs, known_obs, labels, nn_classifier, my_dist))

出于性能原因,我想避免在classify_batch 函数中使用for 循环。有没有办法使用 numpy 操作将 nn_classifier 函数应用于 new_obs 的每一行? 我已经尝试过 apply_along_axis,但正如经常提到的,它方便但不快。

【问题讨论】:

  • 循环本身并不是减速的原因。 任何涉及直接调用 python 函数的事情都会很慢。寻找使用broadcastingufuncs的方法。
  • 你试过k-means吗?
  • 另请参阅this question(几乎是重复的)和this message 链接到最佳答案。

标签: python numpy


【解决方案1】:

避免循环的关键是在“距离”的 (16,8) 数组上表达动作。 labels[]argmin 步骤只会掩盖问题。

如果我设置labels = np.arange(8),那么这个

arr = np.array([my_dist(n, known_obs, axis=1) for n in new_obs])
print(arr)
print(np.argmin(arr, axis=1))

产生同样的东西。它仍然具有列表理解,但我们更接近“源”。

[[  32.  115.   22.  116.  162.   86.  161.  117.]
 [ 106.   31.  142.  164.   92.  106.   45.  103.]
 [  44.  135.   94.   18.   94.   50.   87.  135.]
 [  11.   92.   57.   67.   79.   43.  118.  106.]
 [  40.   67.  126.   98.   50.   74.   75.  175.]
 [  78.   61.  120.  148.  102.  128.   67.  191.]
 [  51.   48.   57.  133.  125.   35.  110.   14.]
 [  47.   28.   93.   91.   63.   49.   32.   88.]
 [  61.   86.   23.  141.  159.   85.  146.   22.]
 [ 131.   70.  155.  149.  129.  127.   44.  138.]
 [  97.  138.   87.  117.  223.   77.  130.  122.]
 [ 151.   78.  211.  161.  131.  115.   46.  164.]
 [  13.   50.   31.   69.   59.   43.   80.   40.]
 [ 131.  108.  157.  161.  207.   85.  102.  146.]
 [  39.  106.   67.   23.   61.   67.   70.   88.]
 [  54.   51.   74.   68.   42.   86.   35.   65.]]
[2 1 3 0 0 1 7 1 7 6 5 6 0 5 3 6]

print((new_obs[:,None,:] - known_obs[None,:,:]).shape)

我得到一个 (16,8,5) 数组。那么我可以在最后一个轴上应用linalg.norm 吗?

这似乎可以解决问题

np.square(np.linalg.norm(diff, axis=-1))

所以在一起:

diff = (new_obs[:,None,:] - known_obs[None,:,:])
dist = np.square(np.linalg.norm(diff, axis=-1))
idx = np.argmin(dist, axis=1)
print(idx)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-20
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 2018-01-18
    相关资源
    最近更新 更多