计算对象数据类型的重新排列中的值的频率

时间:2017-01-28 19:37:21

标签: object numpy types frequency recarray

这是我的意见:

data = np.array ([( 'a1' , np.NaN , 'a2' ), 
                  ( 'a1' , 'b2' , 'b1' ),  
                  ( 'c1' , 'c1' , np.NaN )], 
                 dtype = [( 'A' , object ), 
                          ( 'B' , object ), 
                          ( 'C' , object )] ).view (np.recarray)

我想计算一个变量所取的每个值的频率,我希望输出看起来像(比如输入freq('A')):

array [ ( 'a1' , 2 ) , ( 'c1' , 1 ) ]

我已经尝试np.bincounts()但显然它不适用于对象数据类型。有没有办法使用NumPy实现这一目标?

1 个答案:

答案 0 :(得分:1)

您可以使用np.unique分配一个整数"标签" data['A']中的每个对象。然后,您可以将np.bincount应用于label s:

In [18]: uniq, label = np.unique(data['A'], return_inverse=True)

In [19]: np.column_stack([uniq, np.bincount(label)])
Out[19]: 
array([['a1', 2],
       ['c1', 1]], dtype=object)

请注意,对于dtype object的NumPy数组的操作并不比列表上的等效操作更快(并且通常更慢)。 (您需要使用具有本机NumPy(即非对象)dtypes的数组来享受比纯Python更快的速度优势。)例如,如果您使用data列表的dict,并且计数,您的计算可能会更快频率collections.Counter

In [21]: data = {'A':['a1','a1','c1']}

In [22]: import collections

In [23]: collections.Counter(data['A'])
Out[23]: Counter({'a1': 2, 'c1': 1})

正如hpaulj所指出的,当collection.Counter(data['A'])也是一个混乱的时候,你可以使用data。它比上面显示的np.unique / np.bincount方法快。因此,如果您必须使用对象的重新排列,那么这可能是您的最佳选择。

以下是显示相对速度的基准:

data = np.random.choice(['a','b','c'], size=(300,)).astype(
    [('A', object), ('B', object), ('C', object)]).view(np.recarray)
data2 = {key:data[key].tolist() for key in ['A','B','C']}

在列表的字典上使用Counter是最快的:

In [92]: %timeit collections.Counter(data2['A'])
100000 loops, best of 3: 13.7 µs per loop

在dtype Counter数组上使用object是下一个最快的:

In [91]: %timeit collections.Counter(data['A'])
10000 loops, best of 3: 29.1 µs per loop

我的原始建议是彻头彻尾的慢(虽然这是一个苹果到橙子的比较,因为这会返回一个数组,而不是一个字典):

In [93]: %%timeit 
   ....: uniq, label = np.unique(data['A'], return_inverse=True)
   ....: np.column_stack([uniq, np.bincount(label)])
   ....: 
10000 loops, best of 3: 118 µs per loop