Подсчитайте частоту слов в Python с помощью словаря

Моя задача - найти частоту каждого слова в списке. Есть два пути к этому.

Метод1

def f(words):
    wdict = {}
    for word in words:
        if word not in wdict:
            wdict[word] = 0
        wdict[word] += 1
    return wdict

Метод 2

def g(words):
    wdict = {}
    for word in words:
        try:
            wdict[word] += 1
        except KeyError:
            wdict[word] = 1

Почему метод 2 эффективен? Разве в обоих случаях количество вызовов хеш-функций не совпадает с этим http://blackecho.github.io/blog/programming/2016/03/23/python-underlying-data-structures.html?

Наиболее эффективным, вероятно, является Counter из стандартной библиотеки: from collections import Counter; c = Counter(words).

mehdix 19.12.2018 09:23

есть много способов сделать это. Некоторые из них более эффективны, чем Method2. См. collections.defaultDict или даже лучше collections.Counter.

Ma0 19.12.2018 09:24

Ev.kounis. Мой вопрос в том, почему Method2 эффективнее Method1 в случае количества вызовов хеш-функции?

Sheikh Arbaz 19.12.2018 10:21

Просто проверьте это сами: gist.github.com/BrunoDesthuilliers/…

bruno desthuilliers 19.12.2018 10:29
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
4 606
4

Ответы 4

Смоделируем несколько случаев.

Пример: «Птица летит»

words = ["A", "bird", "is", "flying"]

В вашем первом методе: для каждого слова он будет искать в словаре 3 раза, поэтому будет найдено всего 3 * len(words) или 3 * 4 = 12

Во втором способе: он будет искать только 2 раза, если не будет найден; иначе 1 раз: так 2 * 4 = 8

Теоретически оба имеют одинаковую временную сложность.

Обновлять:

Спасибо Тьерри Латюий за указание. Действительно, метод 1 должен быть более эффективным, чем метод 2. В словаре Python используется хэш-карта, поэтому доступ к ключевой сложности будет O (n), но в среднем случае это O (1). и реализация CPython довольно эффективна. С другой стороны, обработка исключений try / catch выполняется медленно.

вы можете использовать defaultdict в своем методе 1 для более чистого кода.

Нет, это не правильно. Доступ к ключу в dict или проверка того, существует ли он, составляют примерно O (1), поэтому оба метода примерно O (n). В вашем примере я получаю 1,36 мкс для метода 1 и 2 мкс для метода 2, что на самом деле намного медленнее. Причина в том, что обрабатывать исключения намного дороже, чем тесты, поэтому метод 1 начнет работать быстрее, только если будет много повторяющихся слов.

Thierry Lathuille 19.12.2018 10:23

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

triplee, видишь это хоть раз: blackecho.github.io/blog/programming/2016/03/23/…

Sheikh Arbaz 19.12.2018 10:06

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

tripleee 19.12.2018 10:09

В блоге говорится, что количество вызовов функций в методе 1 больше, чем в методе 2. Я не могу его переварить. Мне кажется, что количество вызовов функций в обоих случаях одинаково.

Sheikh Arbaz 19.12.2018 10:12

В нем говорится, что он выполняет много ненужных вычислений функции хэш, но это похоже на ложное утверждение. Почему это комментарий к моему ответу, а не, например, новый вопрос или комментарий автору блога?

tripleee 19.12.2018 10:14

@SheikhArbaz, вы можете проверить правильность утверждения этого сообщения в блоге, настроив быстрый и простой тест вроде этого: gist.github.com/BrunoDesthuilliers/… - как вы узнаете, на достаточно длинном реальном тексте (с большим количеством разных слов) проверка на содержание выполняется значительно быстрее (в 2 раза в Python 3.6 и в четыре в Python 2.7). Для фиктивного текста, в котором одни и те же три слова повторяются снова и снова.

bruno desthuilliers 19.12.2018 10:29

Есть несколько подходов к этому ответу. Вы можете использовать цикл и при этом получить ожидаемый ответ. Я сосредотачиваюсь на двух методах:

Понимание списка

wordstring = 'it was the best of times it was the worst of times '
wordstring += 'it was the age of wisdom it was the age of foolishness'
wordlist = wordstring.split()
# Count each word
wordfreq = [wordlist.count(w) for w in wordlist] # a list comprehension
# Convert to set to remove repetitions
frequencies=set(zip(wordlist, wordfreq))
print(frequencies)

Вывод:

{('of', 4), ('best', 1), ('the', 4), ('worst', 1), ('age', 2), ('wisdom', 1), ('it', 4), ('was', 4), ('times', 2), ('foolishness', 1)}

Метод второй: стандартная библиотека

import collections
wordstring = 'it was the best of times it was the worst of times '
wordstring += 'it was the age of wisdom it was the age of foolishness'
wordlist = wordstring.split()
# Count frequency
freq=collections.Counter(wordlist)
print(freq)

Вывод:

Counter({'it': 4, 'was': 4, 'the': 4, 'of': 4, 'times': 2, 'age': 2, 'best': 1, 'worst': 1, 'wisdom': 1, 'foolishness': 1})

Выбор метода зависит от размера текста, с которым вы работаете. Приведенные выше методы подходят для текста небольшого размера.

это "понимание списка", а не "сжатие списка";) (исправлено).

bruno desthuilliers 19.12.2018 10:07

Должно быть сжатие списка в python, а не понимание

MUNGAI NJOROGE 19.12.2018 13:44

Пожалуйста, проверьте свои факты: docs.python.org/3/tutorial/…

bruno desthuilliers 19.12.2018 13:48

Не связано, но ваше первое решение будет работать лучше, если сначала наберет набор слов и только потом будет их подсчитывать, то есть: frequencies = [(word, wordlist.count(word)) for word in set(wordlist)]

bruno desthuilliers 19.12.2018 15:44

Конечно, я хотел, чтобы это было просто.

MUNGAI NJOROGE 19.12.2018 16:06

Есть два основных отличия:

  • Метод 1 будет выполнять операцию in для каждого слова, а метод 2 будет обновлять напрямую, когда это возможно.
  • Каждый раз, когда Method1 вставляет новое слово, счетчик позже обновляется. Method2 начинает отсчет с 1.

В конечном итоге это зависит от ввода, но если будет достаточное количество повторений, будет меньше операций.

Пример:
Давайте просто рассмотрим код здесь, чтобы получить общее представление (а не фактические операции).

['a', 'a']

Метод1
1 - 'а' не в wdict - True
2 - присвоить 'a'
3 - обновить 'a'
4 - 'a' не в слове - False
5 - обновить 'а'

Метод 2
1 - доступ 'a'
2 - ошибка
3 - присвоить "a" непосредственно 1
4 - обновить 'а' (второй 'а')

Хотя эти шаги не совсем соответствуют количеству операций, выполняемых при выполнении, они указывают на то, что Method2 более компактный и проходит меньше «шагов».

Другие вопросы по теме