【问题标题】:(Python) Find the indices of the values in two arrays that are equal to the values in two other arrays(Python) 查找两个数组中与其他两个数组中的值相等的值的索引
【发布时间】:2017-12-29 20:27:40
【问题描述】:

我有以下 4 个数组,我想获取数组 A 和 X 中相等的值的索引,对应于 B 和 Y 中相同位置的值。因此对于以下示例,

    import numpy as np
    A = np.asarray([400.5, 100,  700,   200,  15, 900])
    B = np.asarray([500.5, 200,  500, 600.5,   8, 999])
    X = np.asarray([400.5, 700,  100,   300,  15, 555, 900])
    Y = np.asarray([500.5, 500,600.5,   100,   8, 555, 999])

我想得到两个带有索引的数组:

indAB = [0 2 4 5]

  • 0 因为 A&B 中的 400.5 和 500.5 也在 X&Y 的位置 0
  • 2 因为 A&B 中的 700 和 500 也在位置 2 的 X&Y 中
  • 4 因为 A&B 中的 15 和 8 也在 X&Y 中的位置 4
  • 5 因为 A&B 中的 900 和 999 也在 X&Y 中的位置 5

indXY = [0 1 4 6]

  • 0、1、4 和 6 与 indAB 类似,但与 X&Y 相关。

其中 indAB 是 A 和 B 中的值的索引,它们等于 X 和 Y 中的值,indXY 是 X 和 Y 中的值的索引,它们等于 A 和 B 中的值。

这是我目前的尝试:

    def indices(a,b):
        setb = set(b)
        ind = [i for i, x in enumerate(a) if x in setb]
        return ind

    iA = np.asarray(indices(A,X))
    iB = np.asarray(indices(X,A))
    iX = np.asarray(indices(B,Y))
    iY = np.asarray(indices(Y,B))

    def CommonIndices(a,b):
        return np.asarray(list(set(a) & set(b)))

    indAB = CommonIndices(iA,iX)
    indXY = CommonIndices(iB,iY)

    print(indAB) # returns = [0 2 4 5]
    print(indXY) # returns = [0 1 2 4 6]

我不断得到 [0 1 2 4 6] 的 indXY 不正确。 2 不应该包括在内,因为即使 600.5 在 Y 和 B 中,200 和 100 在 A 和 B 中(分别)也不相等。

如果有人能提供解决方案,我将不胜感激。非常感谢!

【问题讨论】:

    标签: python arrays numpy equals indices


    【解决方案1】:

    试试这个:

    import numpy as np
    
    A = np.asarray([400.5, 100,  700,   200,  15, 900])
    B = np.asarray([500.5, 200,  500, 600.5,   8, 999])
    X = np.asarray([400.5, 700,  100,   300,  15, 555, 900])
    Y = np.asarray([500.5, 500,600.5,   100,   8, 555, 999])
    
    AB = np.stack([A, B], axis=-1)
    XY = np.stack([X, Y], axis=-1)
    
    eq = AB[:, np.newaxis, :] == XY[np.newaxis, :, :]
    eq = np.logical_and.reduce(eq, axis=-1)
    
    indAB, = np.where(np.logical_or.reduce(eq, axis=1))
    indXY, = np.where(np.logical_or.reduce(eq, axis=0))
    
    print("indAB", indAB)
    print("indXY", indXY)
    

    输出:

    indAB [0 2 4 5]
    indXY [0 1 4 6]
    

    说明

    ABXY 只是数组 ABXY 分别“堆叠”成二维数组。 eq 保存 ABXY 中元素的全对全比较; np.newaxis 用于向ABXY 添加尺寸(注意AB 在位置1 和XY 在位置0 中获得新尺寸)。相等运算符== 通过它们的新维度广播数组。第一个np.logical_and.reduce 是确保两个“组件”相等(AXBY),np.logical_or.reduce 操作检查是否有从ABXY 和从XYAB 的任何完全等式。最后,np.where 获取索引。

    不利的一面是,请注意,这需要一个大小为 len(A) x len(X) x 2 的布尔数组,因此如果原始数组非常很大,您可能会遇到内存问题。

    更新

    如前所述,非常大的数组可能是个问题。如果您想“一次性”进行所有比较,则没有真正的解决方法(中间数组的大小只是比较的数量)。但是,您也可以“分段”运行算法,例如:

    import numpy as np
    
    MAX_SIZE = 2  # Biggest array will be MAX_SIZE x MAX_SIZE x 2
    
    A = np.asarray([400.5, 100,  700,   200,  15, 900])
    B = np.asarray([500.5, 200,  500, 600.5,   8, 999])
    X = np.asarray([400.5, 700,  100,   300,  15, 555, 900])
    Y = np.asarray([500.5, 500,600.5,   100,   8, 555, 999])
    
    AB = np.stack([A, B], axis=-1)
    XY = np.stack([X, Y], axis=-1)
    
    maskAB = np.full(len(AB), False, dtype=bool)
    maskXY = np.full(len(XY), False, dtype=bool)
    
    for iAB in range(0, len(AB), MAX_SIZE):
        pAB = np.expand_dims(AB[iAB:iAB + MAX_SIZE], axis=1)
        for iXY in range(0, len(XY), MAX_SIZE):
            pXY = np.expand_dims(XY[iXY:iXY + MAX_SIZE], axis=0)
            eq = pAB == pXY
            eq = np.logical_and.reduce(eq, axis=-1)
            maskAB[iAB:iAB + MAX_SIZE] |= np.logical_or.reduce(eq, axis=1)
            maskXY[iXY:iXY + MAX_SIZE] |= np.logical_or.reduce(eq, axis=0)
    
    indAB, = np.where(maskAB)
    indXY, = np.where(maskXY)
    
    print("indAB", indAB)
    print("indXY", indXY)
    

    而且输出仍然是:

    indAB [0 2 4 5]
    indXY [0 1 4 6]
    

    我使用 2 的 MAX_SIZE 只是为了表明它在示例中有效,但实际上您可以根据您愿意使用的最大内存量来选择它(例如 MAX_SIZE = 10000 它应该大约为数百兆字节)。 MAX_SIZE 不需要小于数组的大小,也不必是数组大小的除数。

    【讨论】:

    • 非常感谢!!这正是我所需要的,我自己永远不会到达那里哈哈。非常感谢:D
    • @TimeExplorer 没问题。我添加了一些解释,以防您(或任何找到答案的人)觉得它有用。
    • 数组确实很大35182*2044207*2=143838581348。有没有一种成本不会太高的方法?
    • @TimeExplorer 我已经添加了一个“分段”运行算法的代码变体,它应该允许您限制使用的内存量。
    【解决方案2】:

    numpy_indexed 包(免责声明:我是它的作者)包含高效优雅地完成此类事情的功能。此方法的内存要求是线性的,计算要求为 NlogN。对于您正在考虑的大量数组,相对于当前接受的蛮力方法的速度优势很容易达到数量级:

    import numpy as np
    import numpy_indexed as npi
    
    A = np.asarray([400.5, 100,  700,   200,  15, 900])
    B = np.asarray([500.5, 200,  500, 600.5,   8, 999])
    X = np.asarray([400.5, 700,  100,   300,  15, 555, 900])
    Y = np.asarray([500.5, 500,600.5,   100,   8, 555, 999])
    
    AB = np.stack([A, B], axis=-1)
    XY = np.stack([X, Y], axis=-1)
    
    # casting the AB and XY arrays to npi.index first is not required, but a performance optimization; without this each call to npi.indices would have to re-index the arrays, which is the expensive part
    AB = npi.as_index(AB)
    XY = npi.as_index(XY)
    # npi.indices(list, items) is a vectorized nd-equivalent of list.index(item)
    indAB = npi.indices(AB, XY, missing='mask').compressed()
    indXY = npi.indices(XY, AB, missing='mask').compressed()
    

    请注意,您也可以选择如何处理缺失值。还要看一下集合操作,例如 npi.intersection(XY, AB);他们可能会提供一条更简单的途径,以实现您在更高层次上的目标。

    【讨论】:

    • 不错。它确实看起来比公认的答案更有效。我不知道你是如何实现npi 的。据我所知,我的答案与您的答案有关,但与普通 Python 对象有关。
    • 有点不同; npi 是用 'pure numpy' 编写的,因此执行集合类型操作的技巧是对数组进行(arg)排序并将相关项放在一起。因此,O(NlogN) 性能而不是您的方法应该具有的 O(N)。但是对于许多现实世界的案例,排序结果比 O(NlogN) 更好,因为数据很少是纯随机的;当然,矢量化很难被击败。
    • numpy_indexed 包运行良好。非常感谢! :D
    • @EelcoHoogendoorn:非常感谢您的回答。与作者交流关于 SO 的第一手经验总是很高兴。
    【解决方案3】:

    这是另一种方法。我敢说比较清楚了,应该是因为使用了集合才高效,而且只需要O( len(A) + len(X) )内存。

    numpy 甚至不需要,但可以用于数组。

    from collections import defaultdict
    
    A = [400.5, 100, 700, 200, 15, 900]
    B = [500.5, 200, 500, 600.5, 8, 999]
    X = [400.5, 700, 100, 300, 15, 555, 900]
    Y = [500.5, 500, 600.5, 100, 8, 555, 999]
    
    def get_indices(values):
        d = defaultdict(set)
        for i, value in enumerate(values):
            d[value].add(i)
        return d
    
    iA, iB, iX, iY = [get_indices(values) for values in [A, B, X, Y]]
    print(iA)
    # {400.5: {0}, 100: {1}, 200: {3}, 900: {5}, 700: {2}, 15: {4}}
    print(iX)
    # {400.5: {0}, 100: {2}, 300: {3}, 900: {6}, 555: {5}, 700: {1}, 15: {4}}
    
    for i, (a, b) in enumerate(zip(A, B)):
        common_indices = iX[a] & iY[b]
        if common_indices:
            print("A B : %d" % i)
            print("X Y : %d" % common_indices.pop())
            print()
    
    #   A B : 0
    #   X Y : 0
    
    #   A B : 2
    #   X Y : 1
    
    #   A B : 4
    #   X Y : 4
    
    #   A B : 5
    #   X Y : 6
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-05
      • 2015-12-15
      • 2020-06-21
      • 1970-01-01
      • 2021-12-28
      • 2017-08-04
      • 1970-01-01
      • 2012-08-12
      相关资源
      最近更新 更多