【问题标题】:Efficiently compute pairwise equal for NumPy arrays高效地计算 NumPy 数组的成对相等
【发布时间】:2018-01-08 20:18:27
【问题描述】:

给定两个 NumPy 数组,比如说:

import numpy as np
import numpy.random as rand

n = 1000
x = rand.binomial(n=1, p=.5, size=(n, 10))
y = rand.binomial(n=1, p=.5, size=(n, 10))

下面有没有更有效的方法来计算X

X = np.zeros((n, n))
for i in range(n):
    for j in range(n):
        X[i, j] = 1 * np.all(x[i] == y[j])

【问题讨论】:

  • 是的。查看broadcasting,然后尝试(x[:, None, :] == y).all(axis=-1)。然而,这是节省时间的,但不是特别节省空间(尽管在这种特殊情况下,我们只讨论需要大约 10MB 的空间)。您主要担心空间效率还是时间效率? n 在实践中会有多大?
  • 仅供参考:X = np.zeros((n, n)) 创建的默认数据类型是浮点数。您真的希望 X 成为浮点值数组吗?如果您只关心相等,则可以将其设为小整数数组 (X = np.zeros((n, n), dtype=np.int8)) 或布尔值数组 (X = np.zeros((n, n), dtype=np.bool_))。
  • @MarkDickinson 我认为n 在实践中最多为10^6
  • 嗯。然后你将很难将X 放入内存,除非你有 TB 或更多或 RAM。你用X 你创建它之后做什么?
  • 这是一个好点...我希望X 将是一个稀疏矩阵。之后我需要使用 X 执行一些 mat-vec 产品。

标签: python arrays numpy


【解决方案1】:

方法#1:输入数组0s & 1s

对于只有0s1s 的输入数组,我们可以将它们的每一行减少为标量,从而将输入数组减少为1D,然后利用broadcasting,就像这样 -

n = x.shape[1]        
s = 2**np.arange(n)
x1D = x.dot(s)
y1D = y.dot(s)
Xout = (x1D[:,None] == y1D).astype(float)

方法 #2:一般案例

对于一般情况,我们可以使用views -

# https://stackoverflow.com/a/45313353/ @Divakar
def view1D(a, b): # a, b are arrays
    a = np.ascontiguousarray(a)
    b = np.ascontiguousarray(b)
    void_dt = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
    return a.view(void_dt).ravel(),  b.view(void_dt).ravel()

x1D, y1D = view1D(x, y)
Xout = (x1D[:,None] == y1D).astype(float)

运行时测试

# Setup
In [287]: np.random.seed(0)
     ...: n = 1000
     ...: x = rand.binomial(n=1, p=.5, size=(n, 10))
     ...: y = rand.binomial(n=1, p=.5, size=(n, 10))

# Original approach
In [288]: %%timeit
     ...: X = np.zeros((n, n))
     ...: for i in range(n):
     ...:     for j in range(n):
     ...:         X[i, j] = 1 * np.all(x[i] == y[j])
1 loop, best of 3: 4.69 s per loop

# Approach #1
In [290]: %%timeit
     ...: n = x.shape[1]        
     ...: s = 2**np.arange(n)
     ...: x1D = x.dot(s)
     ...: y1D = y.dot(s)
     ...: Xout = (x1D[:,None] == y1D).astype(float)
1000 loops, best of 3: 1.42 ms per loop

# Approach #2
In [291]: %%timeit
     ...: x1D, y1D = view1D(x, y)
     ...: Xout = (x1D[:,None] == y1D).astype(float)
100 loops, best of 3: 18.5 ms per loop

【讨论】:

  • 感谢您的全面回答!我是视图概念的新手,我在理解方法 #2 时遇到了一些麻烦。您介意对这种方法以及它如此高效的原因提供更多解释吗?
  • @Hilbert 这个想法之前已经探索过,并且在这篇文章中有一些 cmets - stackoverflow.com/a/43481447
  • 对不起,我不熟悉使用视图...我浏览了其他帖子,但我仍然不明白为什么我们需要 void_dt 以及 a.view(void_dt) 做了什么(另外,一个np.void 数据类型做)。你能详细说明一下吗?
  • @Hilbert void_dt 是我们用来将每一行视为标量的数据类型,基本上将更多数据打包到一个元素中。换一种方式是解包,如本例中所示 - stackoverflow.com/a/48163550/3293881,因为我们将一个元素解包为三个元素。
猜你喜欢
  • 2018-03-02
  • 1970-01-01
  • 1970-01-01
  • 2019-05-17
  • 1970-01-01
  • 2017-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多