我正在使用numpy在python中工作(也可能是熊猫系列),并且正在尝试进行以下计算:
让我们说我有一个与x轴上的点相对应的数组:
2, 9, 5, 6, 55, 8
对于此数组中的每个元素,我都希望获得到最接近的元素的距离,因此输出应如下所示:
3, 1, 1, 1, 46, 1
我正在尝试找到一种可以缩放到2D(到最近的XY点的距离)的解决方案,理想情况下会避免for循环。有可能吗?
答案 0 :(得分:3)
这里似乎有一个O(N ^ 2)解决方案的主题。对于1D,获得O(N log N)很简单:
x = np.array([2, 9, 5, 6, 55, 8])
i = np.argsort(x)
dist = np.diff(x[i])
min_dist = np.r_[dist[0], np.minimum(dist[1:], dist[:-1]), dist[-1]])
min_dist = min_dist[np.argsort(i)]
这显然不能很好地扩展到多个维度,因此请改用scipy.special.KDTree
。假设您的数据是N维的并且形状为(M, N)
,则可以
k = KDTree(data)
dist = k.query(data, k=2)[0][:, -1]
Scipy具有KDTree
,cKDTree
的Cython实现。 Sklearn的sklearn.neighbors.KDTree
也具有相似的界面。
答案 1 :(得分:2)
方法1
您可以使用广播来获取距离矩阵:
>>> data = np.array([2,9,5,6,55,8])
>>> dst_matrix = data - data[:, None]
>>> dst_matrix
array([[ 0, 7, 3, 4, 53, 6],
[ -7, 0, -4, -3, 46, -1],
[ -3, 4, 0, 1, 50, 3],
[ -4, 3, -1, 0, 49, 2],
[-53, -46, -50, -49, 0, -47],
[ -6, 1, -3, -2, 47, 0]])
然后我们可以按照建议的in this post消除对角线:
dst_matrix = dst_matrix[~np.eye(dst_matrix.shape[0],dtype=bool)].reshape(dst_matrix.shape[0],-1)
>>> dst_matrix
array([[ 7, 3, 4, 53, 6],
[ -7, -4, -3, 46, -1],
[ -3, 4, 1, 50, 3],
[ -4, 3, -1, 49, 2],
[-53, -46, -50, -49, -47],
[ -6, 1, -3, -2, 47]])
最后,可以找到最少的物品:
>>> np.min(np.abs(dst_matrix), axis=1)
array([ 3, 1, 1, 1, 46, 1])
方法2
如果您正在寻找节省时间和内存的有效解决方案,那么最好的选择是scipy.spatial.cKDTree
,它将(任意维度的)点打包到为查询最近点而优化的特定数据结构中。也可以扩展为2D或3D。
import scipy.spatial
data = np.array([2,9,5,6,55,8])
ckdtree = scipy.spatial.cKDTree(data[:,None])
distances, idx = ckdtree.query(data[:,None], k=2)
output = distances[:,1] #distances to not coincident points
对于每个点,此处需要查询前两个最近的点,因为它们中的第一个应该是重合的。这是我在所有提出的答案中找到的唯一的解决方案,而且没有时效(平均性能为100万点为4秒)。 警告:在应用此方法之前,您需要过滤重复的点。
答案 2 :(得分:2)
有很多方法可以实现它。一些可读且可概括的方法是:
方法1 :
dist = np.abs(a[:,None]-a)
np.min(dist, where=~np.eye(len(a),dtype=bool), initial=dist.max(), axis=1)
#[ 3 1 1 1 46 1]
方法2 :
dist = np.abs(np.subtract.outer(a,a))
np.min(dist, where=~np.eye(len(a),dtype=bool), initial=dist.max(), axis=1)
对于2D情况,方法1 (假定欧几里德距离。也可以是其他距离):
from scipy.spatial.distance import cdist
dist = cdist(a,a)
np.min(dist, where=~np.eye(len(a),dtype=bool), initial=dist.max(), axis=1)
对于仅适用于numpy的二维案例方法2 :
dist=np.sqrt(((a[:,None]-a)**2).sum(-1))
np.min(dist, where=~np.eye(len(a),dtype=bool), initial=dist.max(), axis=1)
答案 3 :(得分:0)
您可以对熊猫系列进行一些列表理解:
s = pd.Series([2,9,5,6,55,8])
s.apply(lambda x: min([abs(x - s[y]) for y in s.index if s[y] != x]))
Out[1]:
0 3
1 1
2 1
3 1
4 46
5 1
然后,您只需在末尾添加.to_list()
或.to_numpy()
即可摆脱系列索引:
s.apply(lambda x: min([abs(x - s[y]) for y in s.index if s[y] != x])).to_numpy()
array([ 3, 1, 1, 1, 46, 1], dtype=int64)