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

Fastest way to sorting a corpus dictionary into an OrderedDict - python

给定一个 corpus/texts 这样的:

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]
... 

哪个更快?

有没有其他更快的方法从 Counter 得到这样的 OrderedDict

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

假设内存不是问题。给定 120 GB 的 RAM,保留 10 亿个键值对应该没有太大问题吧?假设 10 亿个键的每个键平均有 20 个字符,每个值有一个整数。

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

例如,使用您的 txt(802 个字符)。

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 相同(根据您的 link)。我正在将已排序项目的列表直接传递给 OrderedDict 创建者。

当我查看 ipython 中的 mycounter 时,我得到的值是按排序顺序排列的:

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。同样,这是来自您的 link。

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 无济于事(时间方面)。只调用 argsortmost_common.

In [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:

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

或分段:

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

我正在从 items() 中创建一个结构化数组,按第二个字段对其进行排序,然后创建字典。虽然没有节省时间。有关使用 order 对结构化数组进行排序的另一个最新示例,请参阅

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()