将语料库字典排序为OrderedDict的最快方法 - python

时间:2015-08-02 10:45:03

标签: python numpy dictionary counter ordereddictionary

给出语料库/文本:

Resumption of the session
I declare resumed the session of the European Parliament adjourned on Friday 17 December 1999 , and I would like once again to wish you a happy new year in the hope that you enjoyed a pleasant festive period .
Although , as you will have seen , the dreaded ' millennium bug ' failed to materialise , still the people in a number of countries suffered a series of natural disasters that truly were dreadful .
You have requested a debate on this subject in the course of the next few days , during this part @-@ session .
In the meantime , I should like to observe a minute ' s silence , as a number of Members have requested , on behalf of all the victims concerned , particularly those of the terrible storms , in the various countries of the European Union .

我可以简单地这样做来获得一个包含单词频率的词典:

>>> word_freq = Counter()
>>> for line in text.split('\n'):
...     for word in line.split():
...             word_freq[word]+=1
... 

但如果目标是从最高频率到最低频率实现有序字典,我将不得不这样做:

>>> from collections import OrderedDict
>>> sorted_word_freq = OrderedDict()
>>> for word, freq in word_freq.most_common():
...     sorted_word_freq[word] = freq
... 

想象一下,我在Counter对象中有10亿个密钥,迭代most_common()会复杂​​地经历语料库(非唯一实例)一次和词汇表(唯一键) 。

注意:Counter.most_common()会调用临时sorted(),请参阅https://hg.python.org/cpython/file/e38470b49d3c/Lib/collections.py#l472

鉴于此,我看到以下使用numpy.argsort()的代码:

>>> import numpy as np
>>> words = word_freq.keys()
>>> freqs = word_freq.values()
>>> sorted_word_index = np.argsort(freqs) # lowest to highest
>>> sorted_word_freq_with_numpy = OrderedDict()
>>> for idx in reversed(sorted_word_index):
...     sorted_word_freq_with_numpy[words[idx]] = freqs[idx]
... 

哪个更快?

还有其他更快捷的方式从OrderedDict获得Counter

除了OrderedDict之外,还有其他python对象可以实现相同的排序键值对吗?

假设内存不是问题。鉴于120 GB的内存,保持10亿个键值对不应该有太多问题吗?假设10亿个密钥每个密钥平均有20个字符,每个值都有一个整数。

2 个答案:

答案 0 :(得分:3)

Pandas中的Series对象是一组键值对(可以具有非唯一键),这可能是有意义的。它有一个sort方法,按值排序并在Cython中实现。这是一个排序长度为一百万的数组的例子:

In [39]:
import pandas as pd
import numpy as np

arr = np.arange(1e6)
np.random.shuffle(arr)
s = pd.Series(arr, index=np.arange(1e6))
%timeit s.sort()
%timeit sorted(arr)

1 loops, best of 3: 85.8 ms per loop
1 loops, best of 3: 1.15 s per loop

给定普通的Python dict,您可以通过调用

来构造Series
my_series = pd.Series(my_dict)

然后按值按

排序
my_series.sort()

答案 1 :(得分:2)

提高速度的一步是以最佳方式填充计数器。

例如,使用txt(802 char)。

mycounter=Counter(txt.split())

生成与word_counter相同的内容,但时间为1/3。

或者如果您必须逐行从文件中读取文本,请使用:

word_freq=Counter()
for line in txt.splitlines():
    word_freq.update(line.split())

类似地,可以在没有循环的情况下创建有序字典:

mydict = OrderedDict(sorted(mycounter.items(), key=operator.itemgetter(1), reverse=True))

我在sorted调用most_common的方式与OrderedDict相同(根据您的链接)。我将已排序项目列表直接传递给mycounter创建者。

当我查看ipython中的In [160]: mycounter Out[160]: Counter({'the': 13, ',': 10, 'of': 9, 'a': 7, '.': 4, 'in': 4, 'to': 3, 'have': 3, 'session': 3, ''': 3, 'on': 3, 'you': 3, 'I': 3, 'that': 2, 'requested': 2, 'like': 2, 'European': 2, 'this': 2, 'countries': 2, 'as': 2, 'number': 2, 's': 1, 'various': 1, 'wish': 1, 'will': 1, 'Parliament': 1, 'meantime': 1, 'Resumption': 1, 'natural': 1, 'days': 1, 'debate': 1, 'You': 1, 'Members': 1, 'next': 1, '@-@': 1, 'hope': 1, 'enjoyed': 1, 'December': 1, 'victims': 1, 'particularly': 1, 'millennium': 1, .... 'behalf': 1, 'were': 1, 'failed': 1}) 时,我会按排序顺序获取值:

__repr__

那是因为它的most_common方法调用了items = ', '.join(map('%r: %r'.__mod__, self.most_common())) 。这也是你的链接。

sorted

在进一步测试中,我发现直接调用In [166]: timeit mycounter.most_common() 10000 loops, best of 3: 31.1 µs per loop In [167]: timeit sorted(mycounter.items(),key=operator.itemgetter(1),reverse=True) 10000 loops, best of 3: 30.5 µs per loop In [168]: timeit OrderedDict(mycounter.most_common()) 1000 loops, best of 3: 225 µs per loop 并不能节省时间:

In [174]: %%timeit 
   .....: sorteddict=OrderedDict()
   .....: for word,freq in word_freq.most_common():
    sorteddict[word]=freq
   .....: 
1000 loops, best of 3: 224 µs per loop

在这种情况下,直接加载字典也不会节省时间。您的迭代也同样如此:

np.argsort

对于此示例,使用argsort无效(按时间)。只是调用most_commonIn [178]: timeit np.argsort(list(mycounter.values())) 10000 loops, best of 3: 34.2 µs per loop 慢。

x=np.array(list(mycounter.values()))

大部分时间是将列表转换为数组np.argsort(x)numpy要快得多。许多numpy功能都是如此。在数组numpy上运行时速度很快。但是在将列表转换为数组时会有很多开销。

我可以通过OrderedDict(np.sort(np.array(list(mycounter.items()), dtype='a12,i'), order='f1')[::-1]) 在一行中创建OrderedDict:

lla = np.array(list(mycounter.items()),dtype='a12,i')
lla.sort(order='f1')
OrderedDict(lla[::-1])

或分段:

items()

我正在从order创建一个结构化数组,在第二个字段中对其进行排序,然后创建字典。虽然没有节省时间。有关使用Long.parseLong(String s) 对结构化数组进行排序的最新示例,请参阅https://stackoverflow.com/a/31837513/901925