0

我有一个输入文件,由带有数字和单词序列的行组成,结构如下:

\1-grams:
number   w1    number
number   w2    number
\2-grams:
number   w1 w2   number
number   w1 w3   number
number   w2 w3   number
\end\

我想以这样一种方式存储单词序列(所谓的 n-gram),以便我可以轻松地检索每个唯一 n-gram 的两个数字。我现在做的事情如下:

all = {}
ngrams = {}
for line in open(file):
    m = re.search('\\\([1-9])-grams:',line.strip()) # find nr of words in sequence
    if m != None:
        n = int(m.group(1))
        ngrams = {} # reinitialize dict for new n
    else:
        m = re.search('(-[0-9]+?[\.]?[0-9]+)\t([^\t]+)\t?(-[0-9]+\.[0-9]+)?',line.strip()) #find numbers and word sequence
        if m != None:
            ngrams[m.group(2)] = '{0}|{1}'.format(m.group(1), m.group(3))
        elif "\end\\" == line.strip():
            all[int(n)] = ngrams

通过这种方式,我可以通过这种方式轻松快速地找到例如序列 s='w1 w2' 的数字:

all[2][s]

问题是这个存储过程相当慢,特别是当有很多(> 100k)的n-gram时,我想知道是否有更快的方法来实现相同的结果而不会降低访问速度。我在这里做的不是最理想的吗?我在哪里可以改进?

提前致谢,

乔里斯

4

3 回答 3

6

我会尝试做更少的正则表达式搜索。

值得考虑其他一些事情:

  • 将所有数据存储在一个字典中可能会加快速度;具有额外层的数据层次结构无济于事,也许违反直觉。

  • 存储元组可以避免调用.format().

  • 在 CPython 中,函数中的代码比全局代码更快。

下面是它的样子:

def load(filename):
    ngrams = {}
    for line in open(filename):
        if line[0] == '\\':
            pass  # just ignore all these lines
        else:
            first, rest = line.split(None, 1)
            middle, last = rest.rsplit(None, 1)
            ngrams[middle] = first, last
    return ngrams

ngrams = load("ngrams.txt")

我想存储int(first), int(last)而不是first, last. 这将加快访问速度,但会减慢加载时间。所以这取决于你的工作量。

我不同意 johnthexii:只要数据集适合内存,在 Python 中执行此操作应该比与数据库(甚至是 sqlite)对话要快得多。(如果您使用数据库,这意味着您可以加载一次而不必重复加载,因此 sqlite 最终可能正是您想要的——但您不能使用 :memory: 数据库来做到这一点。)

于 2013-06-27T14:47:53.313 回答
3

关于代码的优化。

1)在循环之前编译正则表达式。请参阅 re.compile 的帮助。

2) 尽可能避免使用正则表达式。例如,前面有数字的“-grams”字符串可以通过简单的字符串比较来检查

于 2013-06-27T14:51:07.730 回答
0

就我个人而言,我会转移到带有索引的数据库(sqllite3 内置于 python) 。索引使查询快速进行。Python 还支持内存中的 sqllite 数据库

您还可以提供特殊名称 :memory: 在 RAM 中创建数据库。

于 2013-06-27T14:40:09.310 回答