这是一种非确定性(阅读:详尽地)解决在标记化文本中查找日期位置的问题的尝试。它列举了划分句子的所有方式(作为标记列表),分区大小从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
在令牌 9 处发现日期 2015-01-04 00:00:00
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
请注意,这可能在计算上非常昂贵,因此您可能希望在单个短语上运行它,而不是在整个文档上运行。您甚至可能希望将长短语分成几块。
还有一点需要改进的是分区功能。如果你有先验信息,比如一个句子最多可以有多少个日期,那么它的划分方式可以大大减少。