Skip to main content
 首页 » 编程设计

python之NumPy 数组中每行的唯一元素数

2025年05月04日11开发

例如,对于

a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]]) 

我想得到

[2, 2, 3] 

有没有办法不用 for 循环或使用 np.vectorize 来做到这一点?

编辑:实际数据由 1000 行组成,每行 100 个元素,每个元素的范围从 1 到 365。最终目标是确定具有重复行的百分比。这是我已经解决的家庭作业问题(使用 for 循环),但我只是想知道是否有更好的方法用 numpy 来解决。

请您参考如下方法:

方法 #1

一种带排序的矢量化方法 -

In [8]: b = np.sort(a,axis=1) 
 
In [9]: (b[:,1:] != b[:,:-1]).sum(axis=1)+1 
Out[9]: array([2, 2, 3]) 

方法 #2

对于不是很大的 int 的另一种方法是用一个偏移量来偏移每一行,该偏移量将每一行的元素与其他元素区分开来,然后进行合并求和并计算非每行零箱 -

n = a.max()+1 
a_off = a+(np.arange(a.shape[0])[:,None])*n 
M = a.shape[0]*n 
out = (np.bincount(a_off.ravel(), minlength=M).reshape(-1,n)!=0).sum(1) 

运行时测试

作为函数的方法 -

def sorting(a): 
    b = np.sort(a,axis=1) 
    return (b[:,1:] != b[:,:-1]).sum(axis=1)+1 
 
def bincount(a): 
    n = a.max()+1 
    a_off = a+(np.arange(a.shape[0])[:,None])*n 
    M = a.shape[0]*n 
    return (np.bincount(a_off.ravel(), minlength=M).reshape(-1,n)!=0).sum(1) 
 
# From @wim's post    
def pandas(a): 
    df = pd.DataFrame(a.T) 
    return df.nunique() 
 
# @jp_data_analysis's soln 
def numpy_apply(a): 
    return np.apply_along_axis(compose(len, np.unique), 1, a)  

案例#1:方形

In [164]: np.random.seed(0) 
 
In [165]: a = np.random.randint(0,5,(10000,10000)) 
 
In [166]: %timeit numpy_apply(a) 
     ...: %timeit sorting(a) 
     ...: %timeit bincount(a) 
     ...: %timeit pandas(a) 
1 loop, best of 3: 1.82 s per loop 
1 loop, best of 3: 1.93 s per loop 
1 loop, best of 3: 354 ms per loop 
1 loop, best of 3: 879 ms per loop 

案例 #2:大量行

In [167]: np.random.seed(0) 
 
In [168]: a = np.random.randint(0,5,(1000000,10)) 
 
In [169]: %timeit numpy_apply(a) 
     ...: %timeit sorting(a) 
     ...: %timeit bincount(a) 
     ...: %timeit pandas(a) 
1 loop, best of 3: 8.42 s per loop 
10 loops, best of 3: 153 ms per loop 
10 loops, best of 3: 66.8 ms per loop 
1 loop, best of 3: 53.6 s per loop 

扩展到每列的唯一元素数

为了扩展,我们只需要对所提出的两种方法沿另一个轴进行切片和 ufunc 操作,就像这样 -

def nunique_percol_sort(a): 
    b = np.sort(a,axis=0) 
    return (b[1:] != b[:-1]).sum(axis=0)+1 
 
def nunique_percol_bincount(a): 
    n = a.max()+1 
    a_off = a+(np.arange(a.shape[1]))*n 
    M = a.shape[1]*n 
    return (np.bincount(a_off.ravel(), minlength=M).reshape(-1,n)!=0).sum(1) 

具有通用轴的通用 ndarray

让我们看看如何扩展到通用维度的 ndarray 并获得沿通用轴的唯一计数的数量。我们将使用 np.diff 及其 axis 参数来获取那些连续的差异,从而使其通用,就像这样 -

def nunique(a, axis): 
    return (np.diff(np.sort(a,axis=axis),axis=axis)!=0).sum(axis=axis)+1 

样本运行-

In [77]: a 
Out[77]:  
array([[1, 0, 2, 2, 0], 
       [1, 0, 1, 2, 0], 
       [0, 0, 0, 0, 2], 
       [1, 2, 1, 0, 1], 
       [2, 0, 1, 0, 0]]) 
 
In [78]: nunique(a, axis=0) 
Out[78]: array([3, 2, 3, 2, 3]) 
 
In [79]: nunique(a, axis=1) 
Out[79]: array([3, 3, 2, 3, 3]) 

如果您正在处理 float pt 数字,并希望根据一些公差值而不是绝对匹配来制作唯一性案例,我们可以使用 np.isclose。两个这样的选项是 -

(~np.isclose(np.diff(np.sort(a,axis=axis),axis=axis),0)).sum(axis)+1 
a.shape[axis]-np.isclose(np.diff(np.sort(a,axis=axis),axis=axis),0).sum(axis) 

对于自定义公差值,请使用 np.isclose 提供它们。