方法一
基于排序的一种方法是 -
def group_into_dict(a):
sidx = a.argsort()
sorted_a = a[sidx]
cut_idx = np.flatnonzero(np.r_[True,sorted_a[1:] != sorted_a[:-1],True])
return {sorted_a[i]:sidx[i:j] for i,j in zip(cut_idx[:-1], cut_idx[1:])}
示例运行 -
In [55]: a
Out[55]: array([1, 1, 5, 5, 1])
In [56]: group_into_dict(a)
Out[56]: {1: array([0, 1, 4]), 5: array([2, 3])}
对具有 1000000
元素和不同比例唯一数字的数组进行计时,以便将提出的算法与原始算法进行比较 -
In [75]: a = np.random.randint(0,10000,(1000000))
In [76]: %timeit {val: np.where(a==val)[0] for val in np.unique(a)}
1 loop, best of 3: 6.62 s per loop
In [77]: %timeit group_into_dict(a)
10 loops, best of 3: 121 ms per loop
In [78]: a = np.random.randint(0,1000,(1000000))
In [79]: %timeit {val: np.where(a==val)[0] for val in np.unique(a)}
1 loop, best of 3: 720 ms per loop
In [80]: %timeit group_into_dict(a)
10 loops, best of 3: 92.1 ms per loop
In [81]: a = np.random.randint(0,100,(1000000))
In [82]: %timeit {val: np.where(a==val)[0] for val in np.unique(a)}
10 loops, best of 3: 120 ms per loop
In [83]: %timeit group_into_dict(a)
10 loops, best of 3: 75 ms per loop
In [84]: a = np.random.randint(0,20,(1000000))
In [85]: %timeit {val: np.where(a==val)[0] for val in np.unique(a)}
10 loops, best of 3: 60.8 ms per loop
In [86]: %timeit group_into_dict(a)
10 loops, best of 3: 60.3 ms per loop
所以,如果你只涉及到不超过20
个唯一数,那就坚持使用原始方法继续往下看;否则,基于排序的方案似乎效果很好。
方法 #2
基于Pandas
适用于极少量唯一数 -
In [142]: a
Out[142]: array([1, 1, 5, 5, 1])
In [143]: import pandas as pd
In [144]: {u:np.flatnonzero(a==u) for u in pd.Series(a).unique()}
Out[144]: {1: array([0, 1, 4]), 5: array([2, 3])}
在具有 1000000
元素和 20
个唯一元素的数组上的时间 -
In [146]: a = np.random.randint(0,20,(1000000))
In [147]: %timeit {u:np.flatnonzero(a==u) for u in pd.Series(a).unique()}
10 loops, best of 3: 35.6 ms per loop
In [148]: %timeit {val: np.where(a==val)[0] for val in np.unique(a)}
10 loops, best of 3: 58 ms per loop
对于较少的唯一元素 -
In [149]: a = np.random.randint(0,10,(1000000))
In [150]: %timeit {u:np.flatnonzero(a==u) for u in pd.Series(a).unique()}
10 loops, best of 3: 25.3 ms per loop
In [151]: %timeit {val: np.where(a==val)[0] for val in np.unique(a)}
10 loops, best of 3: 44.9 ms per loop
In [152]: a = np.random.randint(0,5,(1000000))
In [153]: %timeit {u:np.flatnonzero(a==u) for u in pd.Series(a).unique()}
100 loops, best of 3: 17.9 ms per loop
In [154]: %timeit {val: np.where(a==val)[0] for val in np.unique(a)}
10 loops, best of 3: 34.4 ms per loop
如何使用pandas处理少量元素的问题?
基于排序的方法 #1
,对于20个唯一元素的情况,获取argsort索引是瓶颈所在-
In [164]: a = np.random.randint(0,20,(1000000))
In [165]: %timeit a.argsort()
10 loops, best of 3: 51 ms per loop
现在,基于
pandas
的函数可以给我们提供唯一的元素,无论是负数还是其他任何类型,我们仅需要将其与输入数组中的元素进行比较,而不需要进行排序。让我们看看在这方面的改进:
In [166]: %timeit pd.Series(a).unique()
100 loops, best of 3: 3.17 ms per loop
当然,接下来它需要获取
np.flatnonzero
的索引,这仍然使其相对更有效。
numpy.where()
可能更好。这并不像听起来那么浪费——无论如何都需要遍历结果。 - Sven Marnachwhere
,我至少需要找出哪些值存在,所以似乎无法避免使用unique
。 - Nico Schlömernp.unique
函数的return_inverse=True
选项可能对您有用。 - Mad Physicist