Оптимизация счетчик слов


Я взял сегодня класс Python в код Google, и это то, что я сделал для задачи о построении счетчик слов.

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

import sys

def make_dict(filename):
    """returns a word/count dictionary for the given input file"""
    myFile=open(filename,'rU')
    text=myFile.read()
    words=text.lower().split()
    wordcount_dict={}   #map each word to its count
    for word in words:
        wordcount_dict[word]=0
    for word in words:
        wordcount_dict[word]=wordcount_dict[word]+1
    myFile.close()
    return  wordcount_dict

def print_words(filename):
    """prints each word in the file followed by its count"""
    wordcount_dict=make_dict(filename)
    for word in wordcount_dict.keys():
        print word, "  " , wordcount_dict[word]

def print_top(filename):
    """prints the words with the top 20 counts"""
    wordcount_dict=make_dict(filename)
    keys = wordcount_dict.keys()
    values = sorted(wordcount_dict.values())
    for x in xrange (1,21): #for the top 20 values
        for word in keys :
            if wordcount_dict[word]==values[-x]:
                print word, "       ",wordcount_dict[word]

def main():
  if len(sys.argv) != 3:
    print 'usage: ./wordcount.py {--count | --topcount} file'
    sys.exit(1)

  option = sys.argv[1]
  filename = sys.argv[2]
  if option == '--count':
    print_words(filename)
  elif option == '--topcount':
    print_top(filename)
  else:
    print 'unknown option: ' + option
    sys.exit(1)

if __name__ == '__main__':
  main()


Комментарии
3 ответа

Мне нравится, как вы разбить код на функции, я думаю, что есть смысл и избегает повторения или дублирования кода и функциональности.

Одно небольшое улучшение, чтобы рассмотреть здесь, чтобы print_words и print_top возьмите словарь вернулся из make_dict нежели называя его как первый шаг. Кроме того, чтобы избежать небольшого количества дублирующегося кода, такого рода "композиция функций" при эффективном использовании может быть очень мощным шаблон проектирования, позволяющий очень выразительный и читаемый код для более сложных задач, чем этот.

Несколько других конкретных мыслей:

В make_dict, Вы читаете весь файл в память, а затем обрабатывая каждое слово дважды. Это работает достаточно хорошо для небольших файлов, но представьте, что у вас текстовый файл 500 гигабайт? В этом случае чтение файла в память ("буферизации" это) приведет к аварийному завершению программы, или, по крайней мере, очень плохой производительности.

Вместо этого, вы можете перебирать файл в для петли, которая будет читать только небольшими порциями файл в память сразу. Для этого требуется небольшое изменение в вашей логике в make_dict:

myFile=open(filename,'rU')
wordcount_dict={} #map each word to its count
for line in myFile:
# .strip() removes the newline character from the
# end of the line, as well as any leading or trailing
# white-space characters (spaces, tabs, etc)
words = line.strip().lower().split()
for word in words:
if word not in wordcount_dict:
wordcount_dict[word] = 0
wordcount_dict[word] += 1

В print_words, вы используете .клавиши() , чтобы получить слова из wordcount_dict, то доступ к этим ключам из словаря. Это прекрасно работает, но Python словари позволяют получать элементы (то есть пары ключ-значение) непосредственно при переборе по словарю, используя .элементы() вместо .ключи():

for word, count in wordcount_dict.items():
print word, " " , count

элементы() возвращает список длиной-2 кортежей, и для слова, граф в ... синтаксис "распаковывает" эти кортежи в переменные Слово и считать.

Опираясь на эту технику, мы можем упростить print_top , а также. Один раз у нас в руках список всех (слова, счет) пары (Продолжительность-2 кортежей), мы можем отсортировать этот список, а затем распечатать только первые 20 слов (по их сосчитать):

wordcount_dict=make_dict(filename)
word_count_pairs = wordcount_dict.items()
word_count_pairs.sort(
# use a "key" function to return the sort key
# for each item in the list we are sorting; here
# we return index 1 -- that is, the count -- to
# sort by that
key=lambda pair: pair[1],

# reverse the sort so that the words with the
# highest counts come first in the sorted list
reverse=True
)

# make a copy of the first 20 elements of the
# list, using slice notation
top_word_count_pairs = word_count_pairs[:20]

for word, count in top_word_count_pairs:
print word, " ",wordcount_dict[word]

1
ответ дан 15 декабря 2011 в 01:12 Источник Поделиться

Если вам нужно подготовить счетчик слов, тогда более адекватные контейнер коллекции.defaultdict

Тогда ваш make_dict функцию можно написать гораздо проще:

def make_dict(filename):
"""returns a word/count dictionary for the given input file"""
wordcount_dict = defaultdict(int)
with open(filename, 'rU') as myFile:
for line in myFile:
words = line.strip().lower().split()
for word in words:
wordcount_dict[word] += 1
return wordcount_dict

Обратите внимание, что вам не нужно заботиться об инициализации словарь для записи новых ключей для Word считая, как defaultdict заботится о ней.

Другой подход заключается в использовании ООП. То есть, чтобы создать слово счетчик объекта в состояние инициализации, методы и все прочее. Код становится упрощенной, капсулированные и готова быть продлен.

Ниже, есть предложение рабочей ООП. Есть некоторые улучшения, которые могут быть реализованы также в вашем функциональная версия, Если вам не нравится ООП:

1) я упростил свои методы. Теперь есть только один способ print_words(самовыдвижение, количество=нет). Если вы хотите лучше 20, то просто укажите количество слов.

2) я включил некоторые оптимизации, чтобы очистить слова, которые разделяются с помощью знаков препинания (в противном случае дом, дом. и дом будет расценено как разные), используя константы из строки модуля.

non_chars = string.punctuation + string.whitespace
words = [item.strip(non_chars).lower() for item in line.split()]

3) я использовал оператор.itemgetter для сортировки ключей (вместо лямбды. Более читабельный, имхо)

4) я использовал форматирование для печати, чтобы лучше рассмотреть. Использовали классическую %.

import operator
import string
from collections import defaultdict

class WordCounter(defaultdict):
def __init__(self, filename):
defaultdict.__init__(self, int)
self.file = filename
self._fill_it()

def _fill_it(self):
"fill dictionary"
non_chars = string.punctuation + string.whitespace
with open(self.file, 'rU') as myFile:
for line in myFile:
words = [item.strip(non_chars).lower() for item in line.split()]
for word in words:
self[word] += 1

def print_words(self, number=None):
"""prints the words with the top <number> counts"""
wc_pairs = self.items()
wc_pairs.sort(key=operator.itemgetter(1), reverse=True)
number = number or len(wc_pairs)
for word, count in wc_pairs[:number]:
print "%-20s%5s" % (word, count)

my_wc = WordCounter('testme.txt')

print my_wc['aword'] # print 'aword' counts
my_wc.print_words() # print all (sorted by counts)
my_wc.print_words(3) # print top 3

И последнее замечание: оставляя пустое пространство перед и после операторов и после запятых в списках, повышает читабельность текста и является хорошей практикой.

4
ответ дан 17 декабря 2011 в 09:12 Источник Поделиться

Я напишу make_dict функцию таким образом:

def make_dict(filename):
wordcount_dict={}
with open(filename,'rU') as myFile:
for line in myFile:
words = line.strip().lower().split()
for word in words:
wordcount_dict.setdefault(word, 0)
wordcount_dict[word] += 1
return wordcount_dict

С сайта файл закрывается автоматически, если исключение происходит.
{}.значение setdefault () - это более подходящие для Python, чем условие, которое @dcrosta предложил.

Насчет главной функции, есть отличная библиотека Python optparse, который помогает разбора параметров командной строки. Проверьте это, если вам нужна более сложный пользовательский интерфейс.

2
ответ дан 16 декабря 2011 в 05:12 Источник Поделиться