将数组中的重复分组?

Cup*_*tor 2 python numpy repeat scipy

我正在寻找一个函数,它获取一个一维排序数组并返回一个包含两列的二维数组,第一列包含非重复项,第二列包含项的重复次数。现在我的代码如下:

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]))    
Run Code Online (Sandbox Code Playgroud)

我的输出如下:

[[ 0.  0.]
 [ 1.  1.]
 [ 2.  2.]
 [ 3.  1.]]
Run Code Online (Sandbox Code Playgroud)

首先,我无法摆脱我的 [0,0]。其次,我想知道是否有一个 numpy 或 scipy 函数,或者我的好吗?

谢谢。

unu*_*tbu 5

您可以使用np.unique获取 中的唯一值x,以及索引数组(称为inverse)。该inverse可被认为是“标签”中的元素x。与x它本身不同的是,标签总是整数,从 0 开始。

然后,您可以对标签进行分类计数。由于标签从 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]])
Run Code Online (Sandbox Code Playgroud)

有时,当数组很小时,事实证明使用纯 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
Run Code Online (Sandbox Code Playgroud)

对于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()
Run Code Online (Sandbox Code Playgroud)