在python中存储n-gram(字数可变的字符串)的最快方法

在python中存储n-gram(字数可变的字符串)的最快方法,python,n-gram,Python,N Gram,我有一个输入文件,由带有数字和单词序列的行组成,结构如下: \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

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

\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时,我想知道是否有一种更快的方法可以在不降低访问速度的情况下实现相同的结果。我是不是在做一些不太理想的事情?我在哪里可以改进

提前感谢,


Joris

就我个人而言,我会使用一个数据库(sqllite3内置于python中)。索引使查询快速进行。Python还支持

您还可以提供特殊名称:memory:来在中创建数据库 公羊


我会尽量少做regexp搜索

其他一些事情值得考虑:

  • 将所有数据存储在一个字典中可以加快速度;带有额外层的数据层次结构没有帮助,这可能与直觉相反

  • 存储元组可以避免调用
    .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:database来实现。)

关于代码的优化

1) 在循环之前编译正则表达式。有关重新编译的信息,请参阅帮助


2) 尽可能避免使用正则表达式。例如,可以通过简单的字符串比较来检查带有数字前缀的“-grams”字符串。all是一个内置函数。不要重复使用此名称。这很慢:从磁盘加载数据,还是使用它?两者都有?使用数据很慢。我不知道有什么方法可以优化“开放式(f)中的l”@埃拉扎尔:是的,我同意这种做法。@niefpaarschoenen:我的意思是问:你发布的解析数据并将其放入词典的代码哪种速度慢;或者之后使用字典的代码?这是一个很好的答案,但我会将if/else块改为简单的
if-not行[0]='\\':
,这样可以保存两行代码:)@l4mpi您可能是对的!我这样写是因为
pass
上的注释解释了这段代码与原始代码之间的巨大差异。这似乎值得一提。答案确实不错,现在就测试一下你的建议。我会尽快给出反馈。我已经可以说,出于我的目的,存储字符串比存储数字更可取(实际上是float,不是int,但我没有指定),因为解析速度慢,访问也可以。我设法减少了大约50%的处理时间,主要是通过去掉正则表达式。使用一本字典实际上会让它慢很多!而使用元组也没有帮助;如果有什么区别的话,它平均比字符串格式慢一点。令人沮丧的是,读取相同文件的C实现速度仍然是原来的10倍。这带来了巨大的不同!