2015-06-26 3 views
3

Кто-нибудь знает, есть ли текстовый парсер Python, который распознает введенные даты? Например, учитывая предложениеРазбор текста - распознаватель даты

«бла-бла-бла-бла 12 января 14 бла бла бла 01/04/15 бла бла бла»

анализатор может выбрать из двух дат вхождений. Я знаю некоторые инструменты Java, но есть ли Python? Неужели NTLK будет излишним?

Благодаря

+0

Возможный дубликат [парсер времени на естественном языке] (http://stackoverflow.com/questions/11340963/natural-language-time-parser) – danihp

+0

@ danihp Я так не думаю. Я ищу парсер, который извлекает даты, внедренные в произвольные строки, вместо того, чтобы анализировать строку даты в объекте. – Kar

+0

@Kar год они будут как 14, а не 2014 во всех случаях? – Ajay

ответ

3

Вот попытка недетерминированно (читайте: исчерпывающе) решить проблему поиска, где даты в токенизированном тексте. Он перечисляет все способы разделения предложения (как список токенов) с размером раздела от minps до maxps.

Каждое разбиение выполняется в синтаксический анализатор, который выводит список разобранных дат и диапазон токенов, где он был разобран.

Результат каждого парсера оценивается с суммой значений токенов в квадрате (поэтому, чтобы предпочесть дату, обработанную из 4 жетонов, а не 2 даты, разобранных по 2 жетонам).

Наконец, он находит и выводит синтаксис с лучшим результатом.

Три строительные блоки алгоритма:

from dateutil.parser import parse as parsedate 

def partition(lst, minps, maxps, i=0): 
    if lst == []: 
     yield [] 
    else: 
     try: 
      for l in range(minps, maxps+1): 
       if l > len(lst): continue 
       for z in partition(lst[l:], minps, maxps, i+l): 
        yield [(i, lst[:l])] + z 
     except: 
      pass 

def parsedates(p): 
    for x in p: 
     i, pi = x 
     try: 
      d = parsedate(' '.join(pi)) 
      # output: (startIndex, endIndex, parsedDate) 
      if d: yield i, i+len(pi), d 
     except: pass 

def score(p): 
    score = 0 
    for pi in p: 
     score += (pi[1]-pi[0])**2 
    return score 

Нахождение разбора с лучшим счетом:

def bestparse(toks, maxps=3): 
    bestscore = 0 
    bestparse = None 
    for ps in partition(toks, 1, maxps): 
     l = list(parsedates(ps)) 
     s = score(l) 
     if s > bestscore: 
      bestscore = s 
      bestparse = l 
    return bestparse 

Некоторые тесты:

l=['bla', 'bla', 'bla', '12', 'Jan', '14', 'bla', 'bla', 'bla', '01/04/15', 'bla', 'bla'] 
for bpi in bestparse(l): 
    print('found date %s at tokens %s' % (bpi[2], ','.join(map(str, range(*bpi[:2]))))) 

найдено Дата 2014-01 -12 00:00:00 в токенах 3,4,5

найдено дата 2015-01-04 00:00:00 знамений 9

l=['Fred', 'was', 'born', 'on', '23/1/99', 'at', '23:30'] 
for bpi in bestparse(l, 5): 
    print('found date %s at tokens %s' % (bpi[2], ','.join(map(str, range(*bpi[:2]))))) 

найдено дата 1999-01-23 23:30:00 на лексемы 3,4,5,6

Опасайтесь, что это может быть очень дорогостоящим, поэтому вы можете запускать его на отдельных коротких фразах, а не на весь документ. Вы даже можете разделить длинные фразы на куски.

Еще один момент для улучшения - функция разбиения. Если у вас есть предварительная информация, например, сколько дат может быть не более одного предложения, количество способов ее разделения может быть значительно уменьшено.

+0

Я не слишком уверен, насколько надежно полагаться на 'dateutil.parser' для идентификации даты. 'parsedate ('1')' дает 2015-06-01 и 'parsedate ('1 1')' дает 2015-01-01.Это кажется немного ухищренным, не так ли? – Kar

+0

@kar: похоже, вы не поняли, как работает алгоритм – fferri

+0

Учитывая '' bla 1 2 bla 1/14'', это даст два события. Я не думаю, что «1 2» следует рассматривать как дату. – Kar