For example, for
a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
我想要获取
[2, 2, 3]
有没有一种方法可以不使用for循环或np.vectorize
来完成这个任务?
编辑:实际数据由1000行每行100个元素组成,每个元素的范围为1到365。最终目标是确定具有重复项的行的百分比。这是一个作业问题,我已经用for循环解决了它,但我只是想知道是否有更好的方法可以用numpy完成。
For example, for
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
对于不是非常大的ints
,另一种方法是通过给每行偏移量来区分每个元素,然后进行分组求和,并计算每行中非零分组的数量 -
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,并沿着通用轴获取独特计数的数量。我们将利用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])
如果您正在使用浮点数并希望基于某个容差值而不是绝对匹配来确定独特性,我们可以使用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
进行输入。
bincount
,感觉有些被忽视了。现在给它加上计时,对这个也要公平一些。 - Divakar这个解决方案通过 np.apply_along_axis
并不是矢量化的,需要使用 Python 循环。但是使用 len
+ np.unique
函数相对直观。
import numpy as np
from toolz import compose
a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
np.apply_along_axis(compose(len, np.unique), 1, a) # [2, 2, 3]
toolz
еЊ…пәЊдҢ еЏҮд»ӨйЂљиү‡lambda
е‡Ңж•°дҢүз”Ёд»Өдё‹ж›үд»Әд»Әз Ѓпәљnp.apply_along_axis(lambda x: len(np.unique(x)), axis=1, arr=a)
гЂ‚ - aysljcIn [6]: np.count_nonzero(np.diff(np.sort(a)), axis=1)+1
Out[6]: array([2, 2, 3])
>>> a = np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]])
>>> df = pd.DataFrame(a.T)
>>> print(*df.nunique())
2 2 3