【问题标题】:vectorize numpy unique for subarrays向量化子数组唯一的numpy
【发布时间】:2015-09-03 15:33:39
【问题描述】:

我有一个形状为 (N, 20, 20) 的 numpy 数组数据,其中 N 是一个非常大的数字。 我想获取每个 20x20 子数组中唯一值的数量。 循环如下:

values = []
for i in data:
    values.append(len(np.unique(i)))

如何矢量化这个循环?速度是一个问题。

如果我尝试 np.unique(data) 我会得到整个数据数组的唯一值,而不是单个 20x20 块,所以这不是我需要的。

【问题讨论】:

  • 您是否考虑过为此编写一个 Fortran 函数并用 f2py 包装它?在 fortran 子例程中,您可以很容易地使用 OpenMP 进行并行化。当我需要加速计算密集型循环时,我经常采用这种方法。
  • 另一种方法可能是使用 numba numba.pydata.org 。它有一个矢量化装饰器,我相信它可能适用于这种情况。我不是 numba 方面的专家,所以你可能想看看它。
  • 谢谢迪帕克。我不知道 Fortran,我宁愿尝试使用 cython,因为我必须使用另一种语言。我可能会探索如何使用 numba。

标签: python numpy


【解决方案1】:

首先,您可以使用data.reshape(N,-1),因为您有兴趣对最后两个维度进行排序。

获取每行唯一值数量的简单方法是将每行转储到一个集合中并让它进行排序:

[len(set(i)) for i in data.reshape(data.shape[0],-1)]

但这是一个迭代,可能是一个快速的迭代。

“向量化”的一个问题是每行中唯一值的集合或列表的长度会有所不同。当涉及到“矢量化”时,“不同长度的行”是一个危险信号。您不再拥有使大多数矢量化成为可能的“矩形”数据布局。

您可以对每一行进行排序:

np.sort(data.reshape(N,-1))

array([[1, 2, 2, 3, 3, 5, 5, 5, 6, 6],
       [1, 1, 1, 2, 2, 2, 3, 3, 5, 7],
       [0, 0, 2, 3, 4, 4, 4, 5, 5, 9],
       [2, 2, 3, 3, 4, 4, 5, 7, 8, 9],
       [0, 2, 2, 2, 2, 5, 5, 5, 7, 9]])

但是如何在不迭代的情况下识别每一行中的唯一值?计算非零差异的数量可能就可以解决问题:

In [530]: data=np.random.randint(10,size=(5,10))

In [531]: [len(set(i)) for i in data.reshape(data.shape[0],-1)]
Out[531]: [7, 6, 6, 8, 6]

In [532]: sdata=np.sort(data,axis=1)
In [533]: (np.diff(sdata)>0).sum(axis=1)+1            
Out[533]: array([7, 6, 6, 8, 6])

我打算添加一个关于浮点数的警告,但如果 np.unique 对您的数据有效,那么我的方法应该同样有效。


[(np.bincount(i)>0).sum() for i in data]

这是一个迭代解决方案,明显比我的len(set(i)) 版本更快,并且与diff...sort 相比具有竞争力。

在 [585] 中:data.shape 输出[585]: (10000, 400)

In [586]: timeit [(np.bincount(i)>0).sum() for i in data]
1 loops, best of 3: 248 ms per loop

In [587]: %%timeit                                       
sdata=np.sort(data,axis=1)
(np.diff(sdata)>0).sum(axis=1)+1
   .....: 
1 loops, best of 3: 280 ms per loop

我刚刚找到了一种更快的方法来使用bincountnp.count_nonzero

In [715]: timeit np.array([np.count_nonzero(np.bincount(i)) for i in data])
10 loops, best of 3: 59.6 ms per loop

我对速度的提高感到惊讶。但后来我想起count_nonzero 用于其他函数(例如np.nonzero)来为其返回结果分配空间。因此,将此函数编码为最大速度是有道理的。 (它在diff...sort 的情况下没有帮助,因为它不采用轴参数)。

【讨论】:

  • 谢谢。它确实有效!虽然我希望让它跑得更快。我的数据大约需要 7 秒,我希望在不到 1 秒的时间内完成。如果没有其他更快方法的答案,我会接受你的。
  • 那么'vectorize'真的是'fastest'的代码吗? :) np.sort 大约需要 3/4 的时间; diff 部分只有 1/4。 sort 按行排序与对整个展平数组进行排序一样长。
  • 我找到了一个稍快一点的bincount 版本 - 但它会逐行迭代。
  • 我尝试了 bincount 版本,当我将它调整到我的脚本时,它实际上比排序/差异 (~7s) 慢 (~11s),我需要将结果放在 a 的特定部分numpy 数组,我不确定这是否需要很多时间。无论如何,我认为 sort/diff 版本可能与纯 python 一样快。所以我接受这个答案。我将尝试 cython,因为我需要它在不到一秒的时间内工作。
  • 我发现count_nonzero可以大大加快bincount的解决方案。
猜你喜欢
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-01
  • 1970-01-01
  • 2018-03-23
  • 2017-03-16
  • 1970-01-01
相关资源
最近更新 更多