【问题标题】:Python: Compare Elements of two arraysPython:比较两个数组的元素
【发布时间】:2017-08-29 14:34:02
【问题描述】:

如果坐标之间的欧几里德距离小于 1 并且时间相同,我想比较两个 numpy 数组的元素并删除其中一个数组的元素。 data_CD4 和 data_CD8 是数组。数组的元素是具有 3D 坐标和时间作为第 4 个元素的列表 (numpy.array([[x,y,z,time],[x,y,z,time].....])。是截止,这里是 1。

for i in data_CD8:
        for m in data_CD4:
            if distance.euclidean(tuple(i[:3]),tuple(m[:3])) < co and i[3]==m[3] :
                data_CD8=np.delete(data_CD8, i, 0)

有没有更快的方法来做到这一点?第一个数组有 5000 个元素,第二个有 2000 个元素,所以花了太多时间。

【问题讨论】:

  • 应该是[:3],而不是[3:]
  • 如果你想也可以使用 numpy 进行比较,请查看:stackoverflow.com/questions/10580676/…
  • 正如@trincot 指出的,它必须是distance.euclidean(tuple(i[:3]),tuple(m[:3])) 。你能确认一下吗?
  • 是的!你说的对。我改了。

标签: python arrays performance numpy


【解决方案1】:

如果您必须将data_CD4 中的所有项目与data_CD8 中的项目进行比较 在从data_CD8 中删除数据时,最好在每次迭代时使第二个可迭代的更小,这当然取决于您最常见的 案子。

for m in data_CD4:
    for i in data_CD8:
        if distance.euclidean(tuple(i[3:]),tuple(m[3:])) < co and i[3]==m[3] :
            data_CD8 = np.delete(data_CD8, i, 0)

基于大 O 符号 - 因为这是 O(n^2) - 我没有看到更快的 解决方案。

【讨论】:

    【解决方案2】:

    这应该是一个矢量化的方法。

    mask1 = np.sum((data_CD4[:, None, :3] - data_CD8[None, :, :3])**2, axis = -1) < co**2
    mask2 = data_CD4[:, None, 3] == data_CD8[None, :, 3]
    mask3 = np.any(np.logical_and(mask1, mask2), axis = 0)
    data_CD8 = data_CD8[~mask3]
    

    mask1 应该加快距离计算,因为它不需要平方根调用。 mask1mask2 是二维数组,我们通过 np.any 将它们压缩到 1d,最后执行所有删除操作可以防止读取/写入堆积。

    速度测试:

    a = np.random.randint(0, 10, (100, 3))
    
    b = np.random.randint(0, 10, (100, 3))
    
    %timeit cdist(a,b) < 5  #Divakar's answer
    10000 loops, best of 3: 133 µs per loop
    
    %timeit np.sum((a[None, :, :] - b[:, None, :]) ** 2, axis = -1) < 25  # My answer
    1000 loops, best of 3: 418 µs per loop
    

    C 编译的代码胜出,即使添加了不必要的平方根。

    【讨论】:

    • 感谢您的努力。尝试代码时出现此错误:IndexError: index 3284 is out of bounds for axis 0 with size 2587
    • 如果没有该行,很难判断错误是什么,但请尝试 axis = 0 in mask3
    • Aaand 和 Divakar 的回答一样,您需要反转 mask3
    • 啊,太好了!是的,现在你的两个代码都有相同的结果。你能解释一下你的代码是做什么的吗?我真的很想了解它:)
    • 如果原始数组在这两种情况下都有维度(n,4)(m,4) mask1mask2(n,m) 布尔数组,比较一个数组的每个“行”与每个“行”的另一个。然后,您使用np.any 为任何data_CD8“行”查找任何True 值。由于您想删除这些行,因此您反转 np.any 输出并将原始数据索引到它以获得您的输出。
    【解决方案3】:

    这是使用Scipy's cdist 的矢量化方法-

    from scipy.spatial import distance
    
    # Get eucliden distances between first three cols off data_CD8 and data_CD4
    dists = distance.cdist(data_CD8[:,:3], data_CD4[:,:3])
    
    # Get mask of those distances that are within co distance. This sets up the 
    # first condition requirement as posted in the loopy version of original code.
    mask1 = dists < co
    
    # Take the third column off the two input arrays that represent the time values.
    # Get the equality between all time values off data_CD8 against all time values
    # off data_CD4. This sets up the second conditional requirement.
    # We are adding a new axis with None, so that NumPY broadcasting
    # would let us do these comparisons in a vectorized manner.
    mask2 = data_CD8[:,3,None] == data_CD4[:,3]
    
    # Combine those two masks and look for any match correponding to any 
    # element off data_CD4. Since the masks are setup such that second axis
    # represents data_CD4, we need numpy.any along axis=1 on the combined mask.
    # A final inversion of mask is needed as we are deleting the ones that 
    # satisfy these requirements.
    mask3 = ~((mask1 & mask2).any(1))
    
    # Finally, using boolean indexing to select the valid rows off data_CD8
    out = data_CD8[mask3]
    

    【讨论】:

    • 嗯,在尝试您的代码时,不会从数组中删除任何内容。使用我的代码删除了 data_CD8 中的一半元素。现在我不能说为什么。
    • @Varlor 它创建一个新数组,删除为data_CD8_out。您是否验证了该数组中的值?或者只是用data_CD8 = data_CD8[~((mask1 &amp; mask2).any(1))] 分配回来?
    • 那么 data_CD8_out 是没有不满足条件的元素的原始数组吗?你能解释一下你的代码吗?它似乎真的很快,我想理解它:)
    • @Varlor 在那里添加了一些解释。
    • @Varlor 不过,为了回答您的问题,使用None,我们将data_CD8 的第四列切片从一维扩展到二维。因此,当将该扩展版本与data_CD4 进行比较时,它会将data_CD8 的第四列的所有元素与data_CD4 的所有元素进行比较,从而为我们提供了这些比较的二维数组。如果不使用None,我们将不会扩展data_CD8,它只会在两个数组之间进行元素比较,而不是all elemens against all elems 比较,这不是预期的操作。希望这是有道理的。
    猜你喜欢
    • 2016-06-19
    • 1970-01-01
    • 2021-05-06
    • 1970-01-01
    • 1970-01-01
    • 2021-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多