【问题标题】:numpy cumulative sum ordered on argsort indices在 argsort 索引上排序的 numpy 累积总和
【发布时间】:2020-07-26 17:00:34
【问题描述】:

背景

我正在尝试计算一组点之间的“社交旅行者”距离。对于两点ab,我们定义ST(a,b)a之间的欧式距离之和em> 以及比 ba 的所有点 更接近,直到并包括 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 数组。但这是我跌倒在我脸上的最后一步。

我觉得这里有三分之二的维恩图。关于如何迈出最后一步的任何想法?我希望这是我发现的 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 的最后一行的第4 个元素放在第一个位置,即@ 最后一行的第3 个元素987654333@ 并将其放在第二个位置,依此类推(回想一下 foo 的最后一行是 [4, 3, 0, 1, 2]。)我想要的是取 @987654335 最后一行的 1st 元素@ 放在第四点,bar2nd元素放在第三点,以此类推@987654337 @ 也不是我的果酱。

【问题讨论】:

    标签: python arrays numpy


    【解决方案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)
    

    有点笨拙,如果我又错了,我会打白旗?

    【讨论】:

    • 抱歉没有尽快回复——如果我只是说“全球大流行病”,希望可以解释一下吗?
    • 这确实产生了所需的数组!现在的问题是,为了提高效率,是否有办法去除其中的 for 循环。但这将问题带到了一个新的阶段,对此我非常感激。
    • @JPFerguson,是的,现在全世界都在以不同的速度发展?。也许您的代码将帮助我们更快地恢复正常!我努力摆脱 for 循环无济于事。很高兴能提供帮助。
    猜你喜欢
    • 1970-01-01
    • 2011-04-06
    • 1970-01-01
    • 1970-01-01
    • 2014-11-20
    • 2019-04-07
    • 1970-01-01
    • 2020-09-13
    • 2016-10-27
    相关资源
    最近更新 更多