Скажем, у меня есть строка
s = 'AAABBBCAB'
Как я могу эффективно вычислить сумму префиксов частот каждого символа в строке, то есть:
psum = [{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1}, {'A': 3, 'B': 2}, {'A': 3, 'B': 3}, {'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1}, {'A': 4, 'B': 4, 'C': 1}]
@Vanjith Мне нужен счетчик частот символов.






это вариант:
from collections import Counter
c = Counter()
s = 'AAABBBCAB'
psum = []
for char in s:
c.update(char)
psum.append(dict(c))
# [{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1}, {'A': 3, 'B': 2},
# {'A': 3, 'B': 3}, {'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1},
# {'A': 4, 'B': 4, 'C': 1}]
я использую collections.Counter, чтобы сохранить «текущую сумму» и добавить (копию результата) в список psum. таким образом, я повторяю только один раз по строке s.
если вы предпочитаете иметь объекты collections.Counter в своем результате, вы можете изменить последнюю строку на
psum.append(c.copy())
чтобы получить
[Counter({'A': 1}), Counter({'A': 2}), ...
Counter({'A': 4, 'B': 4, 'C': 1})]
тот же результат может быть достигнут и с этим (сначала было предложено использовать accumulateв ответе Евгения Ярмаша; я просто избегаю map в пользу выражения генератора):
from itertools import accumulate
from collections import Counter
s = "AAABBBCAB"
psum = list(accumulate(Counter(char) for char in s))
просто для полноты (поскольку здесь пока нет «чистого dict» ответа). если вы не хотите использовать Counter или defaultdict, вы также можете использовать это:
c = {}
s = 'AAABBBCAB'
psum = []
for char in s:
c[char] = c.get(char, 0) + 1
psum.append(c.copy())
хотя defaultdict обычно более эффективен, чем dict.get(key, default).
Нам даже не нужно Counter здесь, достаточно простого defaultdict @hiro-protagonist , проверьте мой ответ ниже!
почему вы говорите, что defaultdict «проще», чем Counter? в чем проще?
Я не сказал проще, я сказал defaultdict — это более простая структура данных для представления словаря, чем Counter, имеет ли это смысл?
@DeveshKumarSingh они оба являются подклассами dict; структура данных счетчика не сложнее, чем у dict. или что я упускаю?
@DeveshKumarSingh, эти соображения неуместны. Я указал разницу во времени, но ОП должен принять собственное решение.
с небольшим риском неправильного использования вы можете сделать это генератором и выдавать исходный счетчик на каждом этапе, отправляя, возможно, дорогую копию dict потребителю, которому может не понадобиться
@WorldSEnder уверен, что это возможно. я создал список, поскольку вопрос предполагал, что это будет желаемый результат...
@DeveshKumarSingh: Ваш ответ пришел позже, чем этот, это точно такая же структура, но немного другого типа, она имеет ту же сложность, но с более подробным выводом. Вы не должны рекламировать это здесь.
О каком ответе вы говорите Эрику, я не вижу ни одного от вас в треде здесь! Кроме того, мой комментарий уже был отмечен как неуместное соображение!
@DeveshKumarSingh: я не написал никакого ответа, потому что Хиро и Юджин достаточно хороши. Если вы согласны с тем, что ваш комментарий был неуместен, вы всегда можете удалить его.
Использование dict было бы более эффективным подходом, чем использование defaultdict или Counter, поскольку преобразование в конце (в dict) требует больших усилий.
Проще всего было бы использовать объект Counter из коллекций.
from collections import Counter
s = 'AAABBBCAB'
[ dict(Counter(s[:i]) for i in range(1,len(s))]
Урожайность:
[{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1}, {'A': 3, 'B': 2},
{'A': 3, 'B': 3}, {'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1}]
Просто отметим, что Counter является подклассом dict, поэтому нет особых причин заменять Counter простым dict.
Я согласен, но это больше соответствует тому, что пользователь указал в качестве вывода. Я бы оставил объекты Counter у себя, так как у них есть полезные функции в дополнение к тому, что они являются диктовками.
Это элегантный 1-лайнер, поэтому +1, но он квадратичный, а не линейный. Подозреваю, что аналогичное решение от hiro protagonist более эффективно.
Вы можете сделать это в одной строке, используя itertools.accumulate и collections.Counter:
from collections import Counter
from itertools import accumulate
s = 'AAABBBCAB'
psum = list(accumulate(map(Counter, s)))
Это дает вам список Counter объектов. Теперь, чтобы получить частоты для любой подстроки s за время O(1), вы можете просто вычесть счетчики, например:
>>> psum[6] - psum[1] # get frequencies for s[2:7]
Counter({'B': 3, 'A': 1, 'C': 1})
На самом деле вам даже не нужен счетчик для этого, достаточно будет defaultdict!
from collections import defaultdict
c = defaultdict(int)
s = 'AAABBBCAB'
psum = []
#iterate through the character
for char in s:
#Update count for each character
c[char] +=1
#Add the updated dictionary to the output list
psum.append(dict(c))
print(psum)
Вывод выглядит так
[{'A': 1}, {'A': 2}, {'A': 3}, {'A': 3, 'B': 1},
{'A': 3, 'B': 2}, {'A': 3, 'B': 3},
{'A': 3, 'B': 3, 'C': 1}, {'A': 4, 'B': 3, 'C': 1},
{'A': 4, 'B': 4, 'C': 1}]
В Python 3.8 вы можете использовать понимание списка с выражение присваивания (он же «оператор моржа»):
>>> from collections import Counter
>>> s = 'AAABBBCAB'
>>> c = Counter()
>>> [c := c + Counter(x) for x in s]
[Counter({'A': 1}), Counter({'A': 2}), Counter({'A': 3}), Counter({'A': 3, 'B': 1}), Counter({'A': 3, 'B': 2}), Counter({'A': 3, 'B': 3}), Counter({'A': 3, 'B': 3, 'C': 1}), Counter({'A': 4, 'B': 3, 'C': 1}), Counter({'A': 4, 'B': 4, 'C': 1})]
Наконец, вам нужен один диктофон или вам нужен список диктовок для каждого символа во время чтения?