Dis, j'ai une chaîne
s = 'AAABBBCAB'
Comment puis-je calculer efficacement la somme des fréquences de préfixe de chaque caractère de la chaîne, à savoir:
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}]
Vous pouvez le faire sur une seule ligne en utilisant itertools.accumulate
et collections.Counter
:
from collections import Counter
from itertools import accumulate
s = 'AAABBBCAB'
psum = list(accumulate(map(Counter, s)))
Cela vous donne une liste d'objets Counter
. Maintenant, pour obtenir des fréquences pour toute sous-chaîne de s
en O(1) temps, vous pouvez simplement soustraire des compteurs, par exemple:
>>> psum[6] - psum[1] # get frequencies for s[2:7]
Counter({'B': 3, 'A': 1, 'C': 1})
c'est une option:
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}]
j'utilise collections.Counter
afin de garder une "somme courante" et d'ajouter (une copie du résultat) à la liste psum
. de cette façon, je répète une seule fois sur la chaîne s
.
si vous préférez avoir collections.Counter
objets dans votre résultat, vous pouvez changer la dernière ligne en
psum.append(c.copy())
afin d'obtenir
[Counter({'A': 1}), Counter({'A': 2}), ...
Counter({'A': 4, 'B': 4, 'C': 1})]
le même résultat pourrait également être obtenu avec cela (en utilisant accumulate
a été proposé pour la première fois dans la réponse d'Eugene Yarmash ; j'évite simplement map
en faveur d'une expression de générateur):
from itertools import accumulate
from collections import Counter
s = "AAABBBCAB"
psum = list(accumulate(Counter(char) for char in s))
juste pour être complet (car il n'y a pas encore de réponse 'pure dict
' ici). si vous ne souhaitez pas utiliser Counter
ou defaultdict
, vous pouvez également utiliser ceci:
c = {}
s = 'AAABBBCAB'
psum = []
for char in s:
c[char] = c.get(char, 0) + 1
psum.append(c.copy())
bien que defaultdict
soit généralement plus performant que dict.get(key, default)
.
En fait, vous n'avez même pas besoin d'un compteur pour cela, un simple défaut suffit!
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)
La sortie ressemble à
[{'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}]
Le plus simple serait d'utiliser l'objet Counter des collections.
from collections import Counter
s = 'AAABBBCAB'
[ dict(Counter(s[:i]) for i in range(1,len(s))]
Rendements:
[{'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}]
Dans Python 3.8 vous pouvez utiliser une compréhension de liste avec un expression d'affectation (aka "l'opérateur morse"):
>>> 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})]