【问题标题】:matching stored keywords/phrases in text匹配文本中存储的关键字/短语
【发布时间】:2009-12-04 13:06:09
【问题描述】:

我有一个包含大约 1000 个关键字/短语(一到四个字长)的数据库表 - 该表很少更改,因此我可以将数据提取到更有用的内容中(比如正则表达式?) - 所以这没有找到/ 基于自然语言处理的关键字猜测..

然后,我让用户将一些文本输入到我想与我的关键字和短语匹配的表单中。

然后程序会在文本旁边存储一个链接到每个匹配的短语。

因此,如果我们针对此处的几个短语对该问题文本运行算法,我们会得到如下结果:

{"inputting some text" : 1,
 "extract the data" : 1,
 "a phrase not here" : 0}

我有什么选择?

  1. 编译正则表达式
  2. 某种 SQL 查询
  3. 第三种方式?

请记住,有 1000 个可能的短语..

我正在使用 MySQL 运行 Django/Python。

编辑:我目前正在这样做:

>>> text_input = "This is something with first phrase in and third phrase" 
>>> regex = "first phrase|second phrase|third phrase" 
>>> p = re.compile(regex, re.I) 
>>> p.findall(text_input)
['first phrase','third phrase']

【问题讨论】:

    标签: python mysql regex django


    【解决方案1】:

    这项工作的算法是Aho-Corasick ...请参阅底部指向 Python 的 C 扩展的链接。

    【讨论】:

      【解决方案2】:

      如果我理解正确,您有一组独特的字符串,您想将输入字符串与之进行比较。在这种情况下,您可以使用set 来存储处理结果和数据库值。然后可以进行如下比较:

      >>> db = {'abc', 'def', 'jhi', 'asdf'}
      >>> inpt = {'abc', 'tmp'}
      >>> db & inpt
      {'abc'}
      

      到字典的进一步转换是微不足道的。

      【讨论】:

      • 好吧 - 有点。我有一块要在该块中查找的文本和短语。我目前正在通过这样的正则表达式进行操作: >>> text_input = "This is something with first phrase in and third phrase" >>> regex = "first phrase|second phrase|third phrase" >>> p = re.compile(regex, re.I) >>> p.findall(text_input) ['第一个短语','第二个短语']
      • FWIW,集合理解语法为 python 3.0 及更高版本。
      • @hughdbrown:我没有使用集合理解,我使用的是新式集合文字docs.python.org/3.1/whatsnew/3.0.html#new-syntax 这里的一切都可以通过使用set(lst)在py 2.x 中完成
      • @SilentGhost:没那么简单。他不会将他的输入字符串解析成短语。
      【解决方案3】:

      请注意...您可能对django's support for regex in queries感兴趣

      来自链接的 django 文档的示例:

      Entry.objects.get(title__regex=r'^(An?|The) +')
      

      【讨论】:

        【解决方案4】:

        这里是 SilentGhost 答案的一个细微变化。您逐行加载关键字。将它们存储在一组中。对于您在用户输入中找到的每个关键字,在结果中增加相应的条目。

        keyword_file = StringIO("""inputting some text
            extract the data
            a phrase not here""")
        
        keywords = set(line.strip() for line in keyword_file)
        
        results = defaultdict(int)
        for phrase in keywords:
            if userinput.find(phrase) != -1:
                results[phrase] += 1
        
        print results
        

        希望这会为您指明正确的方向。不完全确定这是您要问的,但这是我最好的猜测。

        你关心速度吗?你为什么不喜欢你现在使用的方法?你的方法有效吗?

        【讨论】:

          【解决方案5】:

          一旦你形成了你的模式,比如(first phrase)|(the second)|(and another)我指出的括号)并将它编译成一个正则表达式对象r,这是一个循环匹配和识别的好方法这是哪场比赛:

          class GroupCounter(object):
            def __init__(self, phrases):
              self.phrases = phrases
              self.counts = [0] * len(phrases)
            def __call__(self, mo):
              self.counts[mo.lastindex - 1] += 1
              return ''
            def asdict(self):
              return dict(zip(self.phrases, self.counts))
          
          g = GroupCounter(['first phrase', 'the second', 'and another'])
          r.sub(g, thetext)
          print g.asdict()
          

          让 GroupCounter 实例也构建 regex 对象也是合理的,因为它确实需要与它在 regex 本身中出现的顺序相同的短语列表。

          【讨论】:

            【解决方案6】:

            如果您有 1000 个短语,并且您正在搜索输入字符串以查找其中哪些短语是子字符串,那么您可能不会对使用大型正则表达式获得的性能感到满意。 trie 实现起来要多一些工作,但效率要高得多:正则表达式 a|b|c|d|e 对给定输入字符串中的每个字符进行五次测试,而 trie 只进行一次测试。您也可以使用生成 DFA 的词法分析器,例如 Plex

            编辑:

            我今天早上似乎在拖延。试试这个:

                class Trie(object):
                    def __init__(self):
                        self.children = {}
                        self.item = None
                    def add(self, item, remainder=None):
                        """Add an item to the trie."""
                        if remainder == None:
                            remainder = item
                        if remainder == "":
                            self.item = item
                        else:
                            ch = remainder[0]
                            if not self.children.has_key(ch):
                                self.children[ch] = Trie()
                            self.children[ch].add(item, remainder[1:])
                    def find(self, word):
                        """Return True if word is an item in the trie."""
                        if not word:
                            return True
                        ch = word[0]
                        if not self.children.has_key(ch):
                            return False
                        return self.children[ch].find(word[1:])
                    def find_words(self, word, results=None):
                        """Find all items in the trie that word begins with."""
                        if results == None:
                            results = []
                        if self.item:
                            results.append(self.item)
                        if not word:
                            return results
                        ch = word[0]
                        if not self.children.has_key(ch):
                            return results
                        return self.children[ch].find_words(word[1:], results)
            

            快速测试(words.txt 是 BSD 单词文件,非常方便 - 它包含大约 240,000 个单词):

            >>> t = Trie()
            >>> with open(r'c:\temp\words.txt', 'r') as f:
                    for word in f:
                        t.add(word.strip())
            

            这在我的机器上大约需要 15 秒。然而,这几乎是瞬间完成的:

            >>> s = "I played video games in a drunken haze."
            >>> r = []
            >>> for i in range(len(s)):
                    r.extend(t.find_words(s[i:]))
            >>> r
            ['I', 'p', 'play', 'l', 'la', 'lay', 'a', 'ay', 'aye', 'y', 'ye', 'yed', 'e', 'd', 'v', 'video', 'i', 'id', 'ide', 'd', 'de', 'e', 'o', 'g', 'ga', 'gam', 'game', 'a', 'am', 'ame', 'm', 'me', 'e', 'es', 's', 'i', 'in', 'n', 'a', 'd', 'drunk', 'drunken', 'r', 'run', 'u', 'un', 'unken', 'n', 'k', 'ken', 'e', 'en', 'n', 'h', 'ha', 'haze', 'a', 'z', 'e']
            

            是的,unken 在 words.txt 中。我不知道为什么。

            哦,我确实尝试过与正则表达式进行比较:

             >>> import re
             >>> with open(r'c:\temp\words.txt', 'r') as f:
                     p = "|".join([l.strip() for l in f])
            
             >>> p = re.compile(p)
            
             Traceback (most recent call last):
              File "<pyshell#250>", line 1, in <module>
                p = re.compile(p)
              File "C:\Python26\lib\re.py", line 188, in compile
                return _compile(pattern, flags)
              File "C:\Python26\lib\re.py", line 241, in _compile
                p = sre_compile.compile(pattern, flags)
              File "C:\Python26\lib\sre_compile.py", line 529, in compile
                groupindex, indexgroup
            OverflowError: regular expression code size limit exceeded
            

            【讨论】:

            • 似乎与@johnmachin 建议的 Aho-Corasick 类似 - 会对两者之间的速度差异感兴趣......
            • Aho-Corasick 更快。但是两者之间的速度差异是被搜索字符串长度的函数,而不是字典的大小。如果您要搜索相对较长的字符串,那么额外的复杂性可能值得。
            猜你喜欢
            • 2017-04-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-11-10
            • 2010-12-11
            • 1970-01-01
            相关资源
            最近更新 更多