我需要有关任何标准python包的信息,这些包可用于URL上的“最长前缀匹配”。我已经完成了两个标准包http://packages.python.org/PyTrie/#pytrie.StringTrie& 'http://pypi.python.org/pypi/trie/0.1.1'但它们似乎对URL上的最长前缀匹配任务没有用。
检查,如果我的设置包含以下网址1-> http://www.google.com/mail,2-> http://www.google.com/document,3-> http:/ /www.facebook.com等。
现在,如果我搜索“http://www.google.com/doc”,则应返回2并搜索“http://www.face”应返回3.
我想确认是否有任何标准的python包可以帮助我这样做,或者我应该为前缀匹配实现Trie。
我不是在寻找一种正则表达式的解决方案,因为随着URL数量的增加它不可扩展。
非常感谢。
答案 0 :(得分:13)
suffixtree
与pytrie
对比trie
与datrie
对比startswith
-functions 记录的时间是3次重复1000次搜索中的最短时间。包括建筑时间并在所有搜索中传播。搜索是在1到1000000个项目的主机名集合上执行的。
三种类型的搜索字符串:
non_existent_key
- 字符串rare_key
- 约百万分之20 frequent_key
- 出现次数与集合大小相当| function | memory, | ratio |
| | GiB | |
|-------------+---------+-------|
| suffix_tree | 0.853 | 1.0 |
| pytrie | 3.383 | 4.0 |
| trie | 3.803 | 4.5 |
| datrie | 0.194 | 0.2 |
| startswith | 0.069 | 0.1 |
#+TBLFM: $3=$2/@3$2;%.1f
要重现结果run the trie benchmark code。
rare_key / nonexistent_key case
如果网址数小于10000,那么datrie是最快的
N> 10000 - suffixtree
更快,startwith
平均显着较慢。
轴:
frequent_key
最高N = 100000 datrie
是最快的(时间为百万网址)
由建造时间占主导地位。)
找到匹配项中最长的匹配时间最长。所以所有函数的行为都与预期的相似。
startswith
- 时间表现与密钥类型无关。
trie
和pytrie
表现得彼此相似。
datrie
- 最快,最体面的内存消耗
startswith
在这里处于劣势,因为其他方法不会因为构建特里的时间而受到惩罚。
datrie
,pytrie
,trie
- 稀有/非现有密钥几乎为O(1)(常数时间)
拟合(近似)已知函数的多项式以进行比较(与图中相同的对数/对数刻度):
| Fitting polynom | Function |
|------------------------------+-------------------|
| 0.15 log2(N) + 1.583 | log2(N) |
| 0.30 log2(N) + 3.167 | log2(N)*log2(N) |
| 0.50 log2(N) + 1.111e-15 | sqrt(N) |
| 0.80 log2(N) + 7.943e-16 | N**0.8 |
| 1.00 log2(N) + 2.223e-15 | N |
| 2.00 log2(N) + 4.446e-15 | N*N |
答案 1 :(得分:12)
此示例适用于小网址列表但不能很好地扩展。
def longest_prefix_match(search, urllist):
matches = [url for url in urllist if url.startswith(search)]
if matches:
return max(matches, key=len)
else:
raise Exception("Not found")
使用trie模块的实现。
import trie
def longest_prefix_match(prefix_trie, search):
# There may well be a more elegant way to do this without using
# "hidden" method _getnode.
try:
return list(node.value for node in prefix_trie._getnode(search).walk())
except KeyError:
return list()
url_list = [
'http://www.google.com/mail',
'http://www.google.com/document',
'http://www.facebook.com',
]
url_trie = trie.Trie()
for url in url_list:
url_trie[url] = url
searches = ("http", "http://www.go", "http://www.fa", "http://fail")
for search in searches:
print "'%s' ->" % search, longest_prefix_match(url_trie, search)
结果:
'http' -> ['http://www.facebook.com', 'http://www.google.com/document', 'http://www.google.com/mail']
'http://www.go' -> ['http://www.google.com/document', 'http://www.google.com/mail']
'http://www.fa' -> ['http://www.facebook.com']
'http://fail' -> []
或使用PyTrie给出相同的结果,但列表的排序方式不同。
from pytrie import StringTrie
url_list = [
'http://www.google.com/mail',
'http://www.google.com/document',
'http://www.facebook.com',
]
url_trie = StringTrie()
for url in url_list:
url_trie[url] = url
searches = ("http", "http://www.go", "http://www.fa", "http://fail")
for search in searches:
print "'%s' ->" % search, url_trie.values(prefix=search)
从内存使用的角度来看,我开始认为radix tree / patricia tree会更好。这就是基数树的样子:
而trie看起来更像:
答案 2 :(得分:1)
以下函数将返回最长匹配的索引。其他有用的信息也可以很容易地被提取出来。
from os.path import commonprefix as oscp
def longest_prefix(s, slist):
pfx_idx = ((oscp([s, url]), i) for i, url in enumerate(slist))
len_pfx_idx = map(lambda t: (len(t[0]), t[0], t[1]), pfx_idx)
length, pfx, idx = max(len_pfx_idx)
return idx
slist = [
'http://www.google.com/mail',
'http://www.google.com/document',
'http://www.facebook.com',
]
print(longest_prefix('http://www.google.com/doc', slist))
print(longest_prefix('http://www.face', slist))
答案 3 :(得分:1)
如果您愿意为时间性能交易RAM,那么SuffixTree
可能会有用。它具有很好的算法属性,例如它允许在线性时间内解决最长的常见子串问题。
如果您总是搜索前缀而不是任意子字符串,那么您可以在填充SubstringDict()
时添加唯一的前缀:
from SuffixTree import SubstringDict
substr_dict = SubstringDict()
for url in URLS: # urls must be ascii (valid urls are)
assert '\n' not in url
substr_dict['\n'+url] = url #NOTE: assume that '\n' can't be in a url
def longest_match(url_prefix, _substr_dict=substr_dict):
matches = _substr_dict['\n'+url_prefix]
return max(matches, key=len) if matches else ''
SuffixTree
的这种用法似乎不是最理想的,但它比@StephenPaulger's solution [基于SubstringDict()
]快20到20倍(没有.startswith()
的构建时间)关于我试过的数据,它可能已经足够了。
要安装SuffixTree
,请运行:
pip install SuffixTree -f https://hkn.eecs.berkeley.edu/~dyoo/python/suffix_trees