Перепишите частоту символов строки как понимание

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

text = "asampletextstring"
char_count = {}
for char in text:
    if char_count.get(char):
        char_count[char] += 1
    else:
        char_count[char] = 1

Мой вопрос: можно ли переписать приведенный выше фрагмент как comprehension?


person Stefan    schedule 31.10.2018    source источник
comment
Почему понимание?   -  person mad_    schedule 31.10.2018
comment
Не понимание, а короче: char_count = Counter(text)   -  person dvnguyen    schedule 31.10.2018
comment
@mad_ Я хотел написать фрагмент более лаконично, а также изучить pythonicспособ ведения дел. @dvnguyen Да, я знаю о Counter, но я подумал, что может быть подход без импорта чего-либо ...   -  person Stefan    schedule 31.10.2018
comment
dictsи lists также питонические. Понимание может сократить некоторые строки кода, но каждый раз может быть не таким производительным.   -  person mad_    schedule 31.10.2018


Ответы (4)


Это возможно, но неэффективно:

text = "asampletextstring"

char_count = { char : text.count(char) for char in text }

print(char_count)

Вывод

{'s': 2, 'x': 1, 'p': 1, 'm': 1, 'e': 2, 'r': 1, 'n': 1, 'g': 1, 'a': 2, 'i': 1, 'l': 1, 't': 3}

Вы можете написать более короткую версию своего кода:

char_count = {}
for char in text:
    char_count[char] = char_count.get(char, 0) + 1
person Dani Mesejo    schedule 31.10.2018
comment
счетчик неэффективен для короткой строки, заданной автором темы, но отлично подходит для длинного текста (скажем, 10 000) см. мои обсуждения в теме измерения - person Serge; 01.11.2018
comment
предоставил одну итерацию по набору (тексту), как было предложено выше - person Serge; 01.11.2018

Здесь можно использовать set(), чтобы избежать встречи с персонажем 2 или более раз.

text = "asampletextstring"
dict1 = {ch: text.count(ch) for ch in set(text)}

print(dict1)
{'s': 2, 'r': 1, 'i': 1, 'n': 1, 'a': 2, 'e': 2, 'p': 1, 't': 3, 'x': 1, 'l': 1, 'g': 1, 'm': 1}
person Chris Charley    schedule 31.10.2018
comment
Счетчик довольно эффективен для длинных строк и алфавитов, в соответствии с измерениями в stackoverflow.com/questions/41594940/ - person Serge; 31.10.2018

Было любопытно изучить производительность различных подходов и доказать, что понимание не очень хорошо каждый раз, когда я проводил некоторый анализ с использованием понимания словаря, понимания словаря путем преобразования ввода в наборы и традиционных циклов for. Имеет смысл, почему понимание здесь дорого обходится, поскольку .count() перебирает все text каждый раз, чтобы подсчитать частоту одного char

from timeit import timeit

print('Approach 1 without set compehrension: {}'.format(timeit ('{ch: text.count(ch) for ch in text}',setup='text = "asampletextstring"',number=1000000)))
print('Approach 2 with set compehrension: {}'.format(timeit ('{ch: text.count(ch) for ch in set(text)}',setup='text = "asampletextstring"',number=1000000)))
print('Approach 3 simple loops :{}'.format(timeit('for c in text:char_count[c] = char_count.get(c, 0) + 1',setup='text = "asampletextstring";char_count={};',number=1000000)))
print('Approach 4 Counter :{}'.format(timeit('Counter(text)',setup='text = "asampletextstring";from collections import Counter;',number=1000000)))

Выход:

Approach 1 without set compehrension: 4.43441867505
Approach 2 with set compehrension: 3.98101747791
Approach 3 simple loops :2.60219633984
Approach 4 Counter :7.54261124884
person mad_    schedule 31.10.2018
comment
Счетчик довольно эффективен для длинных строк и алфавитов, в соответствии с измерениями в stackoverflow.com/questions/41594940/ - person Serge; 31.10.2018
comment
с setup='text = asampletextstring * 1000;из коллекций импортировать счетчик;',number=100))) время - person Serge; 31.10.2018
comment
Подход 1 без набора, объем: 16.287531096022576 Подход 2 с набором и объемом: 0.02602811809629202 Подход 3 простых цикла: 0.19724294892512262 Подход 4 Счетчик: 0.05493389000184834 - person Serge; 31.10.2018
comment
Я не понимаю вашу точку зрения здесь. Моя цель состояла в том, чтобы сравнить данный текст, а не большую и маленькую последовательность. И значение времени может отличаться в зависимости от операционной системы и вычислительной мощности, но относительное время все равно имеет значение. Дайте мне знать, если я пропустил что-то очевидное - person mad_; 31.10.2018
comment
Мне было любопытно изучить производительность различных подходов и доказать, что наивный цикл не всегда так хорош, как назначенные стандартные инструменты или модули. - person Serge; 31.10.2018
comment
Думаю, для мелкого текста производительность не имеет большого значения, а для длинного может быть важнее. Также существует немалая вероятность того, что автор темы предоставил короткую строку, следуя правилам, требующим «минимального» примера. - person Serge; 01.11.2018
comment
И, конечно же, хотя точное время различается на разных машинах, результат показывает, что для подсчета длинных строк и счетчика превосходит наивный цикл (хотя подсчет без набора не является хорошей идеей) - person Serge; 01.11.2018
comment
вы сделали опечатку в asampletextstring * 1000, вместо этого вы использовали asampletextstring * 1000 - person Serge; 01.11.2018

Переписывать - не очень, не вижу простого пути. Лучшее, что я получил, требует дополнительного словаря.

d = {}
{ c: d.get(c, 0)  for c in text if d.update( {c: d.get(c,0) + 1} ) or True}

В Python 3.8 можно будет получить однострочник, но с помощью (ab)использования выражений присваивания

person Serge    schedule 31.10.2018