2009-12-04 4 views
1

J'ai une table de base de données avec environ 1000 mots/phrases (un à quatre mots) - Cette table change rarement, donc je pourrais extraire les données en quelque chose de plus utile (comme une expression régulière? Je n'ai donc pas trouvé/deviné les mots-clés basés sur le traitement du langage naturel.correspondant mots-clés stockés/phrases dans le texte

J'ai alors un utilisateur qui saisit du texte dans un formulaire que je voudrais faire correspondre à mes mots-clés et expressions.

Le programme stockera alors un lien vers chaque expression correspondant au texte.

Donc, si nous avons couru l'algorithme sur ce texte de la question contre quelques phrases qui sont ici, nous obtiendrions un résultat comme ceci:

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

Quelles sont mes options?

  1. Dressez une expression régulière
  2. Une sorte de requête SQL
  3. Une troisième voie?

Compte tenu du fait qu'il ya 1000 phrases possibles ..

J'exécute Django/Python avec MySQL.

modifier: Je fais actuellement ceci:

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

Répondre

1

L'algorithme de ce travail est Aho-Corasick ... voir le lien en bas whch pointe vers un C-extension pour Python.

0

Si je vous comprends bien, vous avez un ensemble unique de chaînes que vous voulez comparer une chaînes d'entrée contre. Dans ce cas, vous pouvez utiliser set pour stocker à la fois les résultats de traitement et les valeurs db. La comparaison peut alors être effectuée comme suit:

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

La conversion ultérieure au dictionnaire est triviale.

+0

bien-genre de. J'ai un bloc de texte et de phrases que je cherche dans ce bloc. Je le fais actuellement via une expression régulière comme ceci: >>> text_input = "Ceci est quelque chose avec la première phrase et la troisième phrase" >>> regex = "première phrase | deuxième phrase | troisième phrase" > >> p = re.compile (regex, re.I) >>> p.findall (text_input) ['première phrase', 'deuxième phrase'] –

+0

FWIW, la syntaxe de compréhension est définie sur python 3.0 et supérieur. – hughdbrown

+0

@hughdbrown: Je n'utilisais pas la compréhension ensemble, j'utilisais un nouveau style set littéraux http://docs.python.org/3.1/whatsnew/3.0.html#new-syntax Tout ici pourrait être accompli dans py 2. x en utilisant 'set (lst)' – SilentGhost

0

Voici une légère variation sur la réponse de SilentGhost. Vous chargez les mots-clés ligne par ligne. Rangez-les dans un ensemble. pour chaque mot clé que vous trouvez dans l'entrée utilisateur, augmentez l'entrée correspondante dans les résultats.

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 

Espérons que cela vous dirige dans la bonne direction. Pas tout à fait sûr que c'est ce que vous demandiez, mais c'est ma meilleure estimation.

Vous souciez-vous de la vitesse? Pourquoi n'aimez-vous pas la méthode que vous utilisez maintenant? Est-ce que votre méthode fonctionne?

0

Une fois que vous avez formé votre motif tel que (first phrase)|(the second)|(and another) (avec entre parenthèses j'indiquerai) et compilé dans un objet d'expression régulière r, un bon moyen de boucle sur les matches et identifier qui correspondent à ce WAS:

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

Il serait également raisonnable que l'instance de GroupCounter contienne également l'objet regex, car il a besoin de la liste de phrases dans le même ordre que celui apparaissant dans la regex elle-même.

0

Si vous avez 1000 phrases, et vous recherchez une chaîne d'entrée pour trouver de ces phrases sont des sous-chaînes, vous allez probablement pas être heureux avec les performances que vous obtenez d'utiliser une grande expression régulière. Un trie est un peu plus de travail à implémenter, mais c'est beaucoup plus efficace: l'expression régulière a|b|c|d|e fait cinq tests sur chaque caractère dans une chaîne d'entrée donnée, alors qu'un trie n'en fait qu'un. Vous pourriez également utiliser un lexeur, comme Plex, qui produit un DFA.

Edit:

Je semble être temporisant ce matin. Essayez ceci:

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) 

Un test rapide (words.txt est le fichier de mots BSD, une chose très pratique d'avoir autour - il contient environ 240 000 mots):

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

qui prend environ 15 secondes sur mon machine. Ceci, cependant, est presque instantanée:

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

Oui, unken est en words.txt. Je ne sais pas pourquoi.

Oh, et j'ai essayé de comparer avec des expressions régulières:

>>> 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 
+0

semble être similaire à la chose Aho-Corasick proposée par @johnmachin - serait intéressé par les différences de vitesse entre les deux ... –

+0

Aho-Corasick est plus rapide. Mais la différence de vitesse entre les deux est une fonction de la longueur de la chaîne recherchée, pas de la taille du dictionnaire. Si vous recherchez des chaînes relativement longues, cela en vaut probablement la peine. –

Questions connexes