4

正規表現のリストがあり、それらが到着したときに特定のアカウントに関連付けることができるように、それらのツイートと照合したいと思います。上記のようにルールの数が少ないと、非常に速くなりますが、ルールの量を増やすとすぐに遅くなります。

import string, re2, datetime, time, array

rules = [
    [[1],["(?!.*ipiranga).*((?=.*posto)(?=.*petrobras).*|(?=.*petrobras)).*"]],
    [[2],["(?!.*brasil).*((?=.*posto)(?=.*petrobras).*|(?=.*petrobras)).*"]],
]

#cache compile
compilled_rules = []
for rule in rules:
    compilled_scopes.append([[rule[0][0]],[re2.compile(rule[1][0])]])

def get_rules(text):
    new_tweet = string.lower(tweet)
    for rule in compilled_rules:
        ok = 1
        if not re2.search(rule[1][0], new_tweet): ok=0
        print ok

def test():
    t0=datetime.datetime.now()
    i=0
    time.sleep(1)
    while i<1000000:
        get_rules("Acabei de ir no posto petrobras. Moro pertinho do posto brasil")
        i+=1
        t1=datetime.datetime.now()-t0
        print "test"
        print i
        print t1
        print i/t1.seconds

550のルールでテストしたところ、50 reqs/sを超えることはできませんでした。これを行うためのより良い方法はありますか?少なくとも200reqs/sが必要です

編集:ジョナサンからのヒントの後、私は速度を約5倍向上させることができましたが、私のルールを少し入れ子にしました。以下のコードを参照してください。

scope_rules = {
    "1": {
        "termo 1" : "^(?!.*brasil)(?=.*petrobras).*",
        "termo 2" : "^(?!.*petrobras)(?=.*ipiranga).*",
        "termo 3" : "^(?!.*petrobras)(?=.*ipiranga).*",
        "termo 4" : "^(?!.*petrobras)(?=.*ipiranga).*",
        },
    "2": {
        "termo 1" : "^(?!.*ipiranga)(?=.*petrobras).*",
        "termo 2" : "^(?!.*petrobras)(?=.*ipiranga).*",
        "termo 3" : "^(?!.*brasil)(?=.*ipiranga).*",
        "termo 4" : "^(?!.*petrobras)(?=.*ipiranga).*",
        }
    }
compilled_rules = {}
for scope,rules in scope_rules.iteritems():
    compilled_rules[scope]={}
    for term,rule in rules.iteritems():
        compilled_rules[scope][term] = re.compile(rule)


def get_rules(text):
    new_tweet = string.lower(text)
    for scope,rules in compilled_rules.iteritems():
        ok = 1
        for term,rule in rules.iteritems():
            if ok==1:
                if re.search(rule, new_tweet):
                    ok=0
                    print "found in scope" + scope + " term:"+ term


def test():
    t0=datetime.datetime.now()
    i=0
    time.sleep(1)
    while i<1000000:
        get_rules("Acabei de ir no posto petrobras. Moro pertinho do posto ipiranga da lagoa")
        i+=1
        t1=datetime.datetime.now()-t0
        print "test"
        print i
        print t1
        print i/t1.seconds

cProfile.run('test()', 'testproof')
4

4 に答える 4

4

ここであなたのルールが原因のようです:.*先読みで区切られた 2 つのため、一致が成功する (または一致を除外する) ために非常に多くの順列をチェックする必要があります。re.search()これは、アンカーなしで使用するとさらに悪化します。また、その部分を含む代替は不要です-正規表現は文字列に含まれているpostoかどうかに関係なく一致するため、それを完全に削除することもできます。posto

たとえば、最初のルールは次のように書き換えることができます。

^(?!.*ipiranga)(?=.*petrobras)

結果に変化はありません。正確な単語を探している場合は、単語の境界でさらに最適化できます。

^(?!.*\bipiranga\b)(?=.*\petrobras\b)

いくつかの測定 ( RegexBuddyを使用):

文字列に適用された最初の正規表現はAcabei de ir no posto petrobras. Moro pertinho do posto brasil、一致を見つけるために正規表現エンジンに約 4700 ステップかかります。sinを取り出すと、petrobras不一致と判断するのに 100.000 ステップ以上かかります。

私の場合は 230 ステップで一致する (そして 260 ステップで失敗する) ため、正規表現を正しく構築するだけで 20 ~ 400 倍のスピードアップが得られます。

于 2012-10-04T12:17:53.757 に答える
1

さらに進んで、ルールを評価するための Cython 拡張機能を作成したところ、非常に高速になりました。約 3000 の正規表現ルールで 1 秒あたり約 70 のリクエストを実行できます

正規表現.pyx

import re2
import string as pystring

cpdef list match_rules(char *pytext, dict compilled_rules):
    cdef int ok, scope, term
    cdef list response = []
    text = pystring.lower(pytext)
    for scope, rules in compilled_rules.iteritems():
        ok = 1
        for term,rule in rules.iteritems():
            if ok==1:
                if re2.search(rule, text):
                    ok=0
                    response.append([scope,term])
    return response

パイソンコード

import re2 as re
import datetime, time, cProfile

scope_rules = {1: {1 : "^(?!.*brasil)(?=.*petrobras).*", 2: "^(?!.*petrobras)(?=.*ipiranga).*",3 : "^(?!.*petrobras)(?=.*ipiranga).*",4 : "^(?!.*petrobras)(?=.*ipiranga).*",},2: {1 : "^(?!.*brasil)(?=.*petrobras).*", 2: "^(?!.*petrobras)(?=.*ipiranga).*",3 : "^(?!.*petrobras)(?=.*ipiranga).*",4 : "^(?!.*petrobras)(?=.*ipiranga).*",},}

compilled_rules = {}
for scope,rules in scope_rules.iteritems():
    compilled_rules[scope]={}
    for term,rule in rules.iteritems():
        compilled_rules[scope][term] = re.compile(rule)

def test():
    t0=datetime.datetime.now()
    i=0
    time.sleep(1)
    while i<1000000:
        mregex.match_rules("Acabei de ir no posto petrobras. Moro pertinho do posto brasil",compilled_rules)
        i+=1
        t1=datetime.datetime.now()-t0
        print t1
        print i/t1.seconds

cProfile.run('test()', 'testproof')
于 2012-10-17T12:21:27.063 に答える
0

正規表現パターン自体を最適化するだけでなく (これは大きな違いになります)、Google の RE2を試すことができます。これは、Python の標準の正規表現モジュールよりも高速であるはずです。

これは C++ で行われますが、Facebook による RE2 の Python ラッパーであるPyRE2があります :)

PSあなたの質問のおかげで、私は正規表現マッチングに関する素晴らしい読み物を見つけました!

于 2012-10-04T12:24:49.460 に答える
0

@Tim Pietzcker の推奨事項に加えて

多数のルールがある場合は、共通点によってグループ化された小さなルールから構築された階層型アプローチを試すことが理にかなっている場合があります。

たとえば、上記のルールはどちらも「posto」と「petrobras」に一致します。これらの正規表現をまとめてリストにグループ化し、そのリストへのディスパッチを修飾すると、決して適用されない多くのルールを実行することを避けることができます。

疑似コードで....

# a dict of qualifier rules with list of rules 
rules = {
    "+ petrobras" : [
        "- ipiranga"
        "- brasil"
        "? posto"
    ] ,
}

for rule_qualifier in rules:
   if regex( rule_qualifier , text ):
       for rule in rule_qualifier:
           if regex( rule , text ):
               yay
于 2012-10-04T15:24:32.677 に答える