对包含字符串的 scipy.stats.itemfreq 结果进行排序

Sorting a scipy.stats.itemfreq result containing strings

问题

我正在尝试计算字符串列表的频率并按降序对其进行排序。 scipy.stats.itemfreq 生成频率结果,输出为字符串元素的 numpy 数组。这就是我难过的地方。我该如何排序?

到目前为止,我已经尝试了 operator.itemgetter,它似乎适用于一个小列表,直到我意识到它是按第一个字符串字符排序而不是将字符串转换为整数,所以 '5' > '11' 作为它正在比较 51 而不是 511.

我正在使用 python 2.7、numpy 1.8.1、scipy 0.14.0。

示例代码:

from scipy.stats import itemfreq
import operator as op

items = ['platypus duck','platypus duck','platypus duck','platypus duck','cat','dog','platypus duck','elephant','cat','cat','dog','bird','','','cat','dog','bird','cat','cat','cat','cat','cat','cat','cat']
items = itemfreq(items)
items = sorted(items, key=op.itemgetter(1), reverse=True)
print items
print items[0]

输出:

[array(['platypus duck', '5'], 
      dtype='|S13'), array(['dog', '3'], 
      dtype='|S13'), array(['', '2'], 
      dtype='|S13'), array(['bird', '2'], 
      dtype='|S13'), array(['cat', '11'], 
      dtype='|S13'), array(['elephant', '1'], 
      dtype='|S13')]
['platypus duck' '5']

预期输出:

我在订购之后,像这样:

[array(['cat', '11'], 
      dtype='|S13'), array(['platypus duck', '5'], 
      dtype='|S13'), array(['dog', '3'], 
      dtype='|S13'), array(['', '2'], 
      dtype='|S13'), array(['bird', '2'], 
      dtype='|S13'), array(['elephant', '1'], 
      dtype='|S13')]
['cat', '11']

总结

我的问题是:如何按计数降序对数组(在本例中为字符串数组)进行排序?请随时为我上面的代码示例建议替代方法和 faster/improved 方法。

不幸的是,itemfreq returns 唯一项 它们在同一个数组中的计数。对于您的情况,这意味着计数被转换为字符串,这很愚蠢。

如果你可以将 numpy 升级到版本 1.9,那么你可以使用 numpy.unique 和参数 return_counts=True 而不是使用 itemfreq(请参阅下面的旧版本如何完成此操作麻木):

In [29]: items = ['platypus duck','platypus duck','platypus duck','platypus duck','cat','dog','platypus duck','elephant','cat','cat','dog','bird','','','cat','dog','bird','cat','cat','cat','cat','cat','cat','cat']

In [30]: values, counts = np.unique(items, return_counts=True)

In [31]: values
Out[31]: 
array(['', 'bird', 'cat', 'dog', 'elephant', 'platypus duck'], 
      dtype='|S13')

In [32]: counts
Out[32]: array([ 2,  2, 11,  3,  1,  5])

获取将 counts 降序排列的索引:

In [38]: idx = np.argsort(counts)[::-1]

In [39]: values[idx]
Out[39]: 
array(['cat', 'platypus duck', 'dog', 'bird', '', 'elephant'], 
      dtype='|S13')

In [40]: counts[idx]
Out[40]: array([11,  5,  3,  2,  2,  1])

对于老版本的numpy,可以结合np.uniquenp.bincount,如下:

In [46]: values, inv = np.unique(items, return_inverse=True)

In [47]: counts = np.bincount(inv)

In [48]: values
Out[48]: 
array(['', 'bird', 'cat', 'dog', 'elephant', 'platypus duck'], 
      dtype='|S13')

In [49]: counts
Out[49]: array([ 2,  2, 11,  3,  1,  5])

In [50]: idx = np.argsort(counts)[::-1]

In [51]: values[idx]
Out[51]: 
array(['cat', 'platypus duck', 'dog', 'bird', '', 'elephant'], 
      dtype='|S13')

In [52]: counts[idx]
Out[52]: array([11,  5,  3,  2,  2,  1])

其实上面就是itemfreq所做的。这是 scipy 源代码中 itemfreq 的定义(没有文档字符串):

def itemfreq(a):
    items, inv = np.unique(a, return_inverse=True)
    freq = np.bincount(inv)
    return np.array([items, freq]).T

一个更简单的方法来完成你的任务 - 获取一个项目的频率并按频率对项目进行排序 - 是使用 pandas 函数 value_counts (对于原始 post 和更多建议见 here):

import pandas as pd
import numpy as np
x = np.array(["bird","cat","dog","dog","cat","cat"])
pd.value_counts(x)

cat     3
dog     2
bird    1
dtype: int64

仅获取出现次数,排序:

y = pd.value_counts(x).values

array([3, 2, 1])

仅获取要计数的项目的唯一名称,已排序:

z = pd.value_counts(x).index

Index(['cat', 'dog', 'bird'], dtype='object')