我有一个键列表['foo_a','foo_b','foo_c','fnord']
此处所有类似的解决方案都假设您的文字中没有fnord
。
我有这个代码来完成这项工作:
def detect_prefix(keys):
PCT = 0.70 # cutof
pre = ''
l = len(keys)
for i in range(0, len(max(keys, key=len))):
keys = filter(lambda k: k.startswith(pre), keys)
cnt = dict()
for k in map(lambda k: k[i], keys):
cnt.setdefault(k,0)
cnt[k] +=1
if cnt[max(cnt)] / float(l) >= PCT:
pre += max(cnt)
else:
break
return pre
我怀疑强烈怀疑这可以做得更优雅,但我的python-fu现在还不够强大。
我很想听到一些建议。
修改即可。
补充背景和澄清。
这些是其他开发人员在应用程序中用于翻译的密钥。
他们应该有一个共同的前缀,但人们会忘记,并且他们会从其他代码剪切和粘贴。 “_”作为前缀分隔符只是一种约定。最好不要假设甚至使用了分隔符。 70%是一个完全任意的门槛。 “最普遍”或“占主导地位”也会起作用
是的,这是python 2.7,引号内的空间只是一个视觉假象。
答案 0 :(得分:1)
查找哪些内容具有特定前缀的好方法是trie。我使用了一个名为pytrie的实现,但它们都以相同的方式工作。唯一有趣的是你仍然需要以另一种方式生成所有前缀,因为询问trie“foo_a的所有前缀”只会给你“foo_a”以及它的所有前缀字符串你的数据的一部分,但你似乎关心“foo_”,即使它不是它自己的关键。但是,可以反过来这样做,告诉你所有以给定前缀开头的键,即使它没有明确存储。
除此之外,它一切都相当简单。包括导入,它有五行:
from pytrie import StringTrie as trie
data = trie.fromkeys(['foo_a','foo_b','foo_c','fnord'])
PCT = 0.70
prefixes = (k[:i] for k in data for i,_ in enumerate(k, start=1))
print(max(filter(lambda x: len(data.keys(x)) >= PCT * len(data), prefixes), key=len))
打印foo_
。
答案 1 :(得分:1)
如果您知道公共前缀的所需阈值频率:
#!/usr/bin/env python
from collections import Counter
from itertools import izip_longest
strings = ['foo_a','foo_b','foo_c','fnord']
threshold = .7 * len(strings)
prefix = []
for chars in izip_longest(*strings, fillvalue=''):
char, count = Counter(chars).most_common(1)[0]
if count < threshold:
break
prefix.append(char)
print(''.join(prefix))
# -> foo_
或者您可以收集所有常用前缀及其频率,然后再决定:
#!/usr/bin/env python
from collections import Counter
from itertools import izip_longest
strings = ['foo_a', 'foo_b','foo_c','fnord']
assert len(strings) > 1
threshold = len(strings)
prefix = []
prefixes = []
for chars in izip_longest(*strings, fillvalue=''):
char, count = Counter(chars).most_common(1)[0]
if count == 1:
break
elif count < threshold:
if prefix:
prefixes.append((''.join(prefix), threshold))
threshold = count
prefix.append(char)
if prefix:
prefixes.append((''.join(prefix), threshold))
print(prefixes)
# -> [('f', 4), ('foo_', 3)]
两个代码示例都假设存在主要前缀,即每个位置最常见的字符属于最常见的前缀。
答案 2 :(得分:0)
def det_pref(words):
cnt = {'':len(words)}
for w_pfx in itertools.chain.from_iterable([[w[:i] for i in range(1,len(w)+1)] for w in words]):
cnt[w_pfx] = 1 + cnt.get(w_pfx,0)
return max([w_pfx for (w_pfx,n) in cnt.items() if n/len(words)>0.7])
警告:由于此解决方案在循环期间没有提前输出和输入减少,因此效率低于原始代码。
这是一种更有效的方法,恕我直言仍然是pythonic,但比我的第一个更难理解和更长时间:
def det_pref(words):
cnt = dict()
pfx_len = [len(w) for w in words]
while any(pfx_len):
for i,w_pfx in [(i,words[i][:l]) for i,l in enumerate(pfx_len)]:
pfx_len[i] -= pfx_len[i] and 1 or 0
n = 1 + cnt.get(w_pfx,0)
if n/len(words)> 0.7:
return w_pfx
cnt[w_pfx] = n
return ''