web-dev-qa-db-fra.com

Comment calculer efficacement la somme de préfixe des fréquences de caractères dans une chaîne?

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}]
22
planetp

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})
21
Eugene Yarmash

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).

19
hiro protagonist

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}]
7
Devesh Kumar Singh

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}]
6
Christian Sloper

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})]
1
Eugene Yarmash