Python 矢量化numpy对于子阵列是唯一的

Python 矢量化numpy对于子阵列是唯一的,python,numpy,Python,Numpy,我有一个形状为(N,20,20)的numpy数组数据,其中N是一个非常大的数字。 我想获得每个20x20子数组中唯一值的数量。 有一个循环是: values = [] for i in data: values.append(len(np.unique(i))) 我如何将这个循环矢量化?速度是个问题 如果我尝试np.unique(data),我会得到整个数据数组的唯一值,而不是单个20x20块,所以这不是我需要的 首先,您可以使用数据。重塑(N,-1),因为您对排序最后两个维度感兴趣

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

values = []
for i in data:
    values.append(len(np.unique(i)))
我如何将这个循环矢量化?速度是个问题


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

首先,您可以使用
数据。重塑(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
对您的数据有效,我的方法也应该有效


这是一个迭代解决方案,显然比我的
len(set(i))
版本快,并且与
diff…sort
竞争

在[585]中:data.shape Out[585]:(10000400)

我刚刚找到了一种更快的方法来使用
bincount
np.count\u 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\u nonzero
用于其他函数(例如
np.nonzero
)为其返回结果分配空间。因此,将此功能编码为最大速度是有意义的。(它在
diff…sort
情况下没有帮助,因为它不接受轴参数)

您是否考虑过为此编写一个Fortran函数并用f2py包装它?在fortran子例程中,您可以非常轻松地使用OpenMP进行并行化。当我需要加速计算密集型循环时,我经常使用这种方法。另一种方法可能是使用numba。它有一个矢量化装饰器,我相信它可能适用于这种情况。我不是麻木的专家,所以你可能想看看。谢谢迪帕克。我不懂Fortran,我宁愿尝试使用cython,因为我必须使用另一种语言。我可能会探索如何使用numba。谢谢。它确实有用!虽然我希望它跑得更快。我的数据大约需要7秒,我希望在不到1秒的时间内完成。如果没有其他更快的方法,我会接受你的答案。所以“矢量化”真的是“最快”的代码吗?:)<代码>np。排序大约需要3/4的时间;
diff
部分仅为1/4
sort
by row所用的时间与对整个展平数组进行排序所用的时间一样长。我发现一个
bincount
版本稍微快一点,但它会按行进行迭代。我尝试了bincount版本,当我将其改编为我的脚本时,它实际上比sort/diff(~7s)要慢(~11s),我需要将结果放在numpy数组的特定部分,我不确定这是否需要很多时间。在任何情况下,我认为sort/diff版本可能与纯python的速度一样快。所以我接受了答案。我要试试cython,因为我需要它在不到一秒钟内工作。我发现
count\u nonzero
可以大大加快
bincount
解决方案的速度。
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
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