Разбиение строки на отдельные слова в Python - PullRequest
4 голосов
/ 01 августа 2011

У меня большой список доменных имен (около шести тысяч), и я хотел бы посмотреть, какие слова имеют тенденцию к наивысшему значению для приблизительного обзора нашего портфеля.

Проблема, с которой я столкнулся, заключается в том, что список форматируется как доменные имена, например:

examplecartrading.com

examplepensions.co.uk

exampledeals.org

examplesummeroffers.com

+ 5996

Просто подсчет слов поднимает мусор. Поэтому я думаю, что самый простой способ сделать это - вставить пробелы между целыми словами, а затем выполнить подсчет слов.

Для здравомыслия я бы предпочел написать это.

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

Ответы [ 3 ]

6 голосов
/ 01 августа 2011

Мы пытаемся разбить доменное имя (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}

Использование set для хранения английскогословарь делает для быстрой проверки членства.-= удаляет элементы из набора, |= добавляет к нему.

Использование функции all вместе с выражением генератора повышает эффективность, поскольку all возвращает первоеFalse.

Некоторые подстроки могут быть допустимыми словами как целыми, так и разделенными, например, "example" / "ex" + "достаточно".В некоторых случаях мы можем решить проблему, исключив нежелательные слова, такие как «ex» в приведенном выше примере кода.Для других, таких как «пенсии» / «ручки» + «ионы», это может быть неизбежно, и когда это происходит, мы должны предотвратить подсчет всех других слов в строке несколько раз (один раз для «пенсии» и один раздля "ручек" + "ионы").Мы делаем это, отслеживая найденные слова каждого доменного имени в наборе - наборы игнорируют дубликаты - и затем подсчитываем слова, как только все найдены.

РЕДАКТИРОВАТЬ: Реструктурироватьи добавил много комментариев.Вынуждает строки в нижний регистр, чтобы избежать пропусков из-за заглавных букв.Также добавлен список для отслеживания доменных имен, в которых не найдено ни одной комбинации слов.

РЕДАКТИРОВАНИЕ НЕКОРМАНТИЙ: Изменена функция подстроки, чтобы она лучше масштабировалась.Старая версия стала смехотворно медленной для доменных имен длиннее 16 символов или около того.Используя только четыре доменных имени, я увеличил свое время выполнения с 3,6 до 0,2 секунды!

1 голос
/ 01 августа 2011

при условии, что у вас всего несколько тысяч стандартных доменов, вы должны делать все это в памяти.

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
1 голос
/ 01 августа 2011
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
...