4

我有一个很大的域名列表(大约六千个),我想看看哪些词的趋势最高,以大致了解我们的投资组合。

我遇到的问题是列表被格式化为域名,例如:

examplecartrading.com

examplepensions.co.uk

示例交易.org

examplesummeroffers.com

+5996

只是运行字数统计会带来垃圾。所以我想最简单的方法是在整个单词之间插入空格然后运行字数统计。

为了我的理智,我更愿意编写这个脚本。

我知道(非常)小python 2.7,但我愿意接受任何建议来解决这个问题,代码示例真的会有所帮助。有人告诉我,使用简单的字符串 trie 数据结构将是实现这一目标的最简单方法,但我不知道如何在 python 中实现它。

4

3 回答 3

6

我们尝试从一组已知单词 ( ) 中将域名 ( s) 拆分为任意数量的单词(不仅仅是 2 个words)。递归ftw!

def substrings_in_set(s, words):
    if s in words:
        yield [s]
    for i in range(1, len(s)):
        if s[:i] not in words:
            continue
        for rest in substrings_in_set(s[i:], words):
            yield [s[:i]] + rest

这个迭代器函数首先产生调用它的字符串,如果它在words. 然后它以各种可能的方式将字符串分成两部分。如果第一部分不在 中words,它会尝试下一个拆分。如果是,则第一部分会添加到在第二部分调用自身的所有结果之前(可能没有,如 ["example", "cart", ...])

然后我们构建英语词典:

# Assuming Linux. Word list may also be at /usr/dict/words. 
# If not on Linux, grab yourself an enlish word list and insert here:
words = set(x.strip().lower() for x in open("/usr/share/dict/words").readlines())

# The above english dictionary for some reason lists all single letters as words.
# Remove all except "i" and "u" (remember a string is an iterable, which means
# that set("abc") == set(["a", "b", "c"])).
words -= set("bcdefghjklmnopqrstvwxyz")

# If there are more words we don't like, we remove them like this:
words -= set(("ex", "rs", "ra", "frobnicate"))

# We may also add words that we do want to recognize. Now the domain name
# slartibartfast4ever.co.uk will be properly counted, for instance.
words |= set(("4", "2", "slartibartfast")) 

现在我们可以把事情放在一起:

count = {}
no_match = []
domains = ["examplecartrading.com", "examplepensions.co.uk", 
    "exampledeals.org", "examplesummeroffers.com"]

# Assume domains is the list of domain names ["examplecartrading.com", ...]
for domain in domains:
    # Extract the part in front of the first ".", and make it lower case
    name = domain.partition(".")[0].lower()
    found = set()
    for split in substrings_in_set(name, words):
        found |= set(split)
    for word in found:
        count[word] = count.get(word, 0) + 1
    if not found:
        no_match.append(name)

print count
print "No match found for:", no_match

结果:{'ions': 1, 'pens': 1, 'summer': 1, 'car': 1, 'pensions': 1, 'deals': 1, 'offers': 1, 'trading': 1, 'example': 4}

使用 aset来包含英语词典可以进行快速的成员资格检查。-=从集合中删除项目,|=添加到它。

all函数与生成器表达式一起使用可以提高效率,因为all返回第一个False.

一些子字符串可能是一个整体或拆分的有效单词,例如“example”/“ex”+“ample”。对于某些情况,我们可以通过排除不需要的词来解决问题,例如上面代码示例中的“ex”。对于其他的,比如“pensions”/“pens”+“ions”,可能是无法避免的,当发生这种情况时,我们需要防止字符串中的所有其他单词被计算多次(一次用于“pensions”,一次用于对于“笔”+“离子”)。我们通过跟踪在集合中找到的每个域名的单词来做到这一点——集合忽略重复项——然后在找到所有单词后对单词进行计数。

编辑:重组并添加了很多评论。强制字符串为小写以避免因大写而丢失。还添加了一个列表来跟踪没有单词组合匹配的域名。

NECROMANCY 编辑:更改了子字符串函数,以便更好地扩展。对于超过 16 个字符左右的域名,旧版本变得异常缓慢。仅使用上面的四个域名,我将自己的运行时间从 3.6 秒提高到了 0.2 秒!

于 2011-08-01T11:36:44.197 回答
1

假设您只有几千个标准域,您应该能够在内存中完成这一切。

domains=open(domainfile)
dictionary=set(DictionaryFileOfEnglishLanguage.readlines())
found=[]
for domain in domains.readlines():
    for substring in all_sub_strings(domain):
        if substring in dictionary:
            found.append(substring)
from collections import Counter
c=Counter(found) #this is what you want

print c
于 2011-08-01T12:07:10.160 回答
1
with open('/usr/share/dict/words') as f:
  words = [w.strip() for w in f.readlines()]

def guess_split(word):
  result = []
  for n in xrange(len(word)):
    if word[:n] in words and word[n:] in words:
      result = [word[:n], word[n:]]
  return result


from collections import defaultdict
word_counts = defaultdict(int)
with open('blah.txt') as f:
  for line in f.readlines():
    for word in line.strip().split('.'):
      if len(word) > 3:
        # junks the com , org, stuff
        for x in guess_split(word):
          word_counts[x] += 1

for spam in word_counts.items():
  print '{word}: {count}'.format(word=spam[0],count=spam[1])

这是一种蛮力方法,它只尝试将域拆分为 2 个英文单词。如果域没有分成 2 个英文单词,它就会被垃圾。扩展它以尝试更多拆分应该很简单,但除非您很聪明,否则它可能不会随着拆分数量很好地扩展。幸运的是,我猜您最多只需要 3 或 4 次拆分。

输出:

deals: 1
example: 2
pensions: 1
于 2011-08-01T10:46:54.053 回答