如何在数组中分组重复项?
我在找一个函数,它可以接收一个一维的已排序数组,然后返回一个二维数组,这个二维数组有两列。第一列是没有重复的项,第二列是每个项出现的次数。目前我的代码是这样的:
def priorsGrouper(priors):
if priors.size==0:
ret=priors;
elif priors.size==1:
ret=priors[0],1;
else:
ret=numpy.zeros((1,2));
pointer1,pointer2=0,0;
while(pointer1<priors.size):
counter=0;
while(pointer2<priors.size and priors[pointer2]==priors[pointer1]):
counter+=1;
pointer2+=1;
ret=numpy.row_stack((ret,[priors[pointer1],pointer2-pointer1]))
pointer1=pointer2;
return ret;
print priorsGrouper(numpy.array([1,2,2,3]))
我的输出结果是这样的:
[[ 0. 0.]
[ 1. 1.]
[ 2. 2.]
[ 3. 1.]]
首先,我无法去掉我的 [0,0]。其次,我想知道有没有 numpy 或 scipy 的函数可以做到这一点,还是说我的方法可以?
谢谢。
4 个回答
1
如果你想计算某个物品出现的次数,可以使用字典。
l = [1, 2, 2, 3]
d = {}
for i in l:
if i not in d:
d[i] = 1
else:
d[i] += 1
result = [[k, v] for k, v in d.items()]
对于你的例子,返回的结果是:
[[1, 1],
[2, 2],
[3, 1]]
祝你好运。
3
如果顺序不重要,可以使用Counter。
from collections import Counter
% Counter([1,2,2,3])
= Counter({2: 2, 1: 1, 3: 1})
% Counter([1,2,2,3]).items()
[(1, 1), (2, 2), (3, 1)]
如果想要保持顺序(按照第一次出现的顺序),你可以自己实现一个Counter的版本:
from collections import OrderedDict
def OrderedCounter(seq):
res = OrderedDict()
for x in seq:
res.setdefault(x, 0)
res[x] += 1
return res
% OrderedCounter([1,2,2,3])
= OrderedDict([(1, 1), (2, 2), (3, 1)])
% OrderedCounter([1,2,2,3]).items()
= [(1, 1), (2, 2), (3, 1)]
5
你可以使用 np.unique 来获取 x
中的唯一值,同时还会得到一个索引数组(叫做 inverse
)。这个 inverse
可以理解为 x
中元素的“标签”。与 x
不同,这些标签总是整数,从0开始。
接着,你可以对这些标签使用 bincount。因为标签是从0开始的,所以这个 bincount 不会填满很多你不关心的零。
最后,column_stack 会把 y
和 bincount 合并成一个二维数组:
In [84]: x = np.array([1,2,2,3])
In [85]: y, inverse = np.unique(x, return_inverse=True)
In [86]: y
Out[86]: array([1, 2, 3])
In [87]: inverse
Out[87]: array([0, 1, 1, 2])
In [88]: np.bincount(inverse)
Out[88]: array([1, 2, 1])
In [89]: np.column_stack((y,np.bincount(inverse)))
Out[89]:
array([[1, 1],
[2, 2],
[3, 1]])
有时候,当数组很小的时候,使用普通的 Python 方法会比 NumPy 函数更快。我想检查一下这里是否也是这样,如果是的话,x
需要多大时 NumPy 方法才会更快。
下面是一个图表,展示了不同方法的性能与 x
大小之间的关系:

In [173]: x = np.random.random(1000)
In [174]: x.sort()
In [156]: %timeit using_unique(x)
10000 loops, best of 3: 99.7 us per loop
In [180]: %timeit using_groupby(x)
100 loops, best of 3: 3.64 ms per loop
In [157]: %timeit using_counter(x)
100 loops, best of 3: 4.31 ms per loop
In [158]: %timeit using_ordered_dict(x)
100 loops, best of 3: 4.7 ms per loop
当 len(x)
为 1000 时,using_unique
比测试的任何普通 Python 方法快超过 35 倍。
所以看起来 using_unique
是最快的,即使对于非常小的 len(x)
。
下面是用来生成这个图表的程序:
import numpy as np
import collections
import itertools as IT
import matplotlib.pyplot as plt
import timeit
def using_unique(x):
y, inverse = np.unique(x, return_inverse=True)
return np.column_stack((y, np.bincount(inverse)))
def using_counter(x):
result = collections.Counter(x)
return np.array(sorted(result.items()))
def using_ordered_dict(x):
result = collections.OrderedDict()
for item in x:
result[item] = result.get(item,0)+1
return np.array(result.items())
def using_groupby(x):
return np.array([(k, sum(1 for i in g)) for k, g in IT.groupby(x)])
fig, ax = plt.subplots()
timing = collections.defaultdict(list)
Ns = [int(round(n)) for n in np.logspace(0, 3, 10)]
for n in Ns:
x = np.random.random(n)
x.sort()
timing['unique'].append(
timeit.timeit('m.using_unique(m.x)', 'import __main__ as m', number=1000))
timing['counter'].append(
timeit.timeit('m.using_counter(m.x)', 'import __main__ as m', number=1000))
timing['ordered_dict'].append(
timeit.timeit('m.using_ordered_dict(m.x)', 'import __main__ as m', number=1000))
timing['groupby'].append(
timeit.timeit('m.using_groupby(m.x)', 'import __main__ as m', number=1000))
ax.plot(Ns, timing['unique'], label='using_unique')
ax.plot(Ns, timing['counter'], label='using_counter')
ax.plot(Ns, timing['ordered_dict'], label='using_ordered_dict')
ax.plot(Ns, timing['groupby'], label='using_groupby')
plt.legend(loc='best')
plt.ylabel('milliseconds')
plt.xlabel('size of x')
plt.show()