按argsort索引排序的numpy累加和

3

背景

我正在尝试计算一组点之间的“社交旅行者”距离。对于两个点ab,我们定义ST(a,b)为从a到所有比b更接近a的点的欧几里得距离的累积和,包括到b的距离。

(假设a正在拜访各种人。他们首先看见最近的人,然后是下一个最近的人,以此类推。社交旅行者距离是他们抵达b时已经走过的路程(仅计算出发距离)。)

社交旅行者距离在某个任意半径内定义;超出该范围,“访问”的点被定义为不可到达。这是因为某些原因。 ;)

到目前为止我做了什么

我使用numpy数组存储了一些点。以下是一个示例:

>>> import numpy as np
>>> x = np.array([(0,0), (-1,2), (-2,-2), (6,-2), (4,0)])
>>> x
array([[ 0,  0],
       [-1,  2],
       [-2, -2],
       [ 6, -2],
       [ 4,  0]])

我生成了一个数组,其中包含每个点之间的成对距离。在这个例子中,我考虑的上限距离为4。

>>> from sklearn.metrics import pairwise_distances
>>> y = pairwise_distances(x)
>>> y
array([[0.  , 2.24, 2.83, 6.32, 4.  ],
       [2.24, 0.  , 4.12, 8.06, 5.39],
       [2.83, 4.12, 0.  , 8.  , 6.32],
       [6.32, 8.06, 8.  , 0.  , 2.83],
       [4.  , 5.39, 6.32, 2.83, 0.  ]])
>>> import numpy.ma as ma
>>> ym = ma.masked_greater(y, 4)
>>> ym
masked_array(
  data=[[0.0 , 2.24, 2.83,   --, 4.0 ],
        [2.24, 0.0 ,   --,   --,   --],
        [2.83,   --, 0.0 ,   --,   --],
        [  --,   --,   --, 0.0 , 2.83],
        [4.0 ,   --,   --, 2.83, 0.0 ]],
  mask=[[False, False, False,  True, False],
        [False, False,  True,  True,  True],
        [False,  True, False,  True,  True],
        [ True,  True,  True, False, False],
        [False,  True,  True, False, False]],
  fill_value=1e+20)

(我已经缩短了数字以保证大家的理智。)

我的目标是用该行非缺失值的累积总和来替换每个行元素,直到包括该元素为止。(为简化起见,忽略并列的问题。)也就是说,我想得到这个数组

>>> hypothetical_new_y
array([[0.  , 2.24, 5.06,  nan, 9.06],
       [2.24, 0.  ,  nan,  nan,  nan],
       [2.83,  nan, 0.  ,  nan,  nan],
       [nan ,  nan,  nan, 0.  , 2.83],
       [6.83,  nan,  nan, 2.83, 0.  ]])

我知道如何沿着行获取累计总和:

>>> np.cumsum(ym, axis=1)
masked_array(
  data=[[0.0 , 2.24, 5.06,   --, 9.06],
        [2.24, 2.24,   --,   --,   --],
        [2.83,   --, 2.83,   --,   --],
        [  --,   --,   --, 0.0 , 2.83],
        [4.0 ,   --,   --, 6.83, 6.83]],
...

由于这是按行顺序添加而不是排序,因此它会给出错误的值(除了这里的第一行巧合)。我可以做同样的事情,首先对行进行排序:
>>> np.cumsum(np.sort(ym, axis=1), axis=1)
masked_array(
  data=[[0.0 , 2.24, 5.06, 9.06,   --],
        [0.0 , 2.24,   --,   --,   --],
        [0.0 , 2.83,   --,   --,   --],
        [0.0 , 2.83,   --,   --,   --],
        [0.0 , 2.83, 6.83,   --,   --]],
  mask=[[False, False, False, False,  True],
        [False, False,  True,  True,  True],
        [False, False,  True,  True,  True],
        [False, False,  True,  True,  True],
        [False, False, False,  True,  True]],
  fill_value=1e+20)

这使我获得了正确的值,但它们按升序排列而不是我想要的顺序。我希望这些累加和根据原始数组的argsort值定位:
>>> np.argsort(ym)
array([[0, 1, 2, 4, 3],
       [1, 0, 2, 3, 4],
       [2, 0, 1, 3, 4],
       [3, 4, 0, 1, 2],
       [4, 3, 0, 1, 2]])

看起来我需要一种方法来对这些行进行排序,做累积求和,然后将按照 argsort 向量排序的累积求和返回。请注意,如果按照 np.argsort(ym) 中的向量呈现 np.cumsum(np.sort(ym, axis=1), axis=1) 的结果,则得到了我的 hypothethical_new_y 数组。但就是在最后一步,我卡住了。

我感觉自己有三分之二的 Venn 图表了。你有什么想法可以帮我完成最后这一步吗?我的希望是这只是我发现的 numpy 语法知识中相对简单的一个漏洞。

编辑:发布后继续查找后,我认为已经找到了 np.take_along_axis() 的“显而易见”的答案 - 但并不是。请考虑以下代码:

>>> foo = np.argsort(ym)
>>> bar = np.cumsum(np.sort(ym, axis=1), axis=1)
>>> np.take_along_axis(bar, foo, axis=1)
masked_array(
  data=[[0.0 , 2.24, 5.06,   --, 9.06],
        [2.24, 0.0 ,   --,   --,   --],
        [  --, 0.0 , 2.83,   --,   --],
        [  --,   --, 0.0 , 2.83,   --],
        [  --,   --, 0.0 , 2.83, 6.83]],
...

例如,如果您查看最后一行,很明显该命令正在获取bar的最后一行的第四个元素并将其放入第一个位置,获取bar的最后一行的第三个元素并将其放入第二个位置等等 (回想一下foo的最后一行是[4, 3, 0, 1, 2])。相反,我想要的是获取bar的最后一行的第一个元素并将其放入第四个位置,获取bar的第二个元素并将其放入第三个位置等等。而np.put_along_axis对我来说也不太适用。
1个回答

1

非常抱歉没有理解要求。我再试了一下,得到了这个结果。我无法想象如何使用for循环来实现这个,但输出似乎与您的hypothetical_new_y相匹配,除了第一行中的四舍五入问题。但我认为逻辑应该是正确的。

>>> x
array([[ 0.  ,  2.24,  2.83,  6.32,  4.  ],
       [ 2.24,  0.  ,  4.12,  8.06,  5.39],
       [ 2.83,  4.12,  0.  ,  8.  ,  6.32],
       [ 6.32,  8.06,  8.  ,  0.  ,  2.83],
       [ 4.  ,  5.39,  6.32,  2.83,  0.  ]])
>>> ym
masked_array(data =
 [[0.0 2.24 2.83 -- 4.0]
 [2.24 0.0 -- -- --]
 [2.83 -- 0.0 -- --]
 [-- -- -- 0.0 2.83]
 [4.0 -- -- 2.83 0.0]],
             mask =
 [[False False False  True False]
 [False False  True  True  True]
 [False  True False  True  True]
 [ True  True  True False False]
 [False  True  True False False]],
       fill_value = 1e+20)

>>> g=np.cumsum(np.sort(ym, axis=1), axis=1)
>>> g
masked_array(data =
 [[0.0 2.24 5.07 9.07 --]
 [0.0 2.24 -- -- --]
 [0.0 2.83 -- -- --]
 [0.0 2.83 -- -- --]
 [0.0 2.83 6.83 -- --]],
             mask =
 [[False False False False  True]
 [False False  True  True  True]
 [False False  True  True  True]
 [False False  True  True  True]
 [False False False  True  True]],
       fill_value = 1e+20)
>>> n = np.zeros_like(x, dtype=float).view(np.ma.masked_array)
>>> for i in range(n.shape[0]):
...         n[i][x[i].argsort(axis=0)] = g.data[i]
...         
>>> 
>>> n.mask = ym.mask
>>> n
masked_array(data =
 [[0.0 2.24 5.07 -- 9.07]
 [2.24 0.0 -- -- --]
 [2.83 -- 0.0 -- --]
 [-- -- -- 0.0 2.83]
 [6.83 -- -- 2.83 0.0]],
             mask =
 [[False False False  True False]
 [False False  True  True  True]
 [False  True False  True  True]
 [ True  True  True False False]
 [False  True  True False False]],
       fill_value = 1e+20)

有点笨拙,如果我再错一次,我就会举起白旗


抱歉没有及时回复——如果我只说“全球大流行”,希望这能解释清楚? - JP Ferguson
这确实产生了所需的数组!现在的问题是是否有一种方式可以为了效率而删除其中的for循环。但这将把问题带到一个新阶段,对此我非常感激。 - JP Ferguson
@JPFerguson,是的,现在整个世界都以不同的速度前进。也许你的代码能帮助我们更快地回归正常!我试图摆脱for循环,但没有成功。很高兴能帮忙。 - Ethan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接