在Python中查找字符串中字符的数量

12

我想要创建一个单词和其在字符串中出现次数的字典。假设字符串如下:

str1 = "aabbaba"

我想创建一个像这样的字典

word_count = {'a':4,'b':3}

我试图使用字典推导来做到这一点。 我已经做了

dic = {x:dic[x]+1 if x in dic.keys() else x:1 for x in str}

这最终会导致一个错误提示

  File "<stdin>", line 1
    dic = {x:dic[x]+1 if x in dic.keys() else x:1 for x in str}
                                               ^
SyntaxError: invalid syntax

有人能告诉我这个语法哪里出了问题吗?还有,我该如何使用字典推导式创建这样一个字典?


你看过“计数器”吗? - dawg
移除第二个x:第一个x是两者的关键,if条件语句解析为值的一部分。 - Erotemic
1
@dawg 我知道计数器。我不想使用计数器。如果可能的话,我想使用字典推导来完成这个任务。 - Chiyaan Suraj
字典/列表/集合推导式很酷,但并不意味着它们是所有问题的最佳解决方案,这是其中之一。 - Copperfield
请查看此答案:https://dev59.com/rF8d5IYBdhLWcg3wgye4 - dawg
5个回答

16

像其他人所说,最好使用 Counter 进行计数。

你也可以这样做:

>>> {e:str1.count(e) for e in set(str1)}
{'a': 4, 'b': 3}

但是对于每个唯一的字符,它会遍历该字符串1+n次(一次用于创建集合,一次用于计算每个唯一字母出现的次数。也就是说,这具有二次运行时复杂度)。如果你有很多唯一字符在一个长字符串中,结果很糟糕... 一个计数器只遍历一次字符串。

如果你想要一个比使用.count更高效的无导入版本,你可以使用.setdefault来创建一个计数器:

>>> count={}
>>> for c in str1:
...    count[c]=count.setdefault(c, 0)+1
... 
>>> count
{'a': 4, 'b': 3}

无论字符串有多长或包含多少个不同的字符,它只会遍历一次。


如果您喜欢,也可以使用defaultdict

>>> from collections import defaultdict
>>> count=defaultdict(int)
>>> for c in str1:
...    count[c]+=1
... 
>>> count
defaultdict(<type 'int'>, {'a': 4, 'b': 3})
>>> dict(count)
{'a': 4, 'b': 3}

但如果你要导入collections模块 -- 使用Counter!


为什么不在第一次使用时就执行count = collections.defaultdict(int),而不是在每次迭代中都使用setdefault - Michael Kohl
1
@MichaelKohl 避免导入吗?否则计数器是更好的选择。 - Copperfield

9
最理想的方法是使用 collections.Counter 进行操作:
>>> from collections import Counter
>>> str1 = "aabbaba"
>>> Counter(str1)
Counter({'a': 4, 'b': 3})

你无法通过简单的字典推导表达式来实现这一点,因为你需要引用先前元素计数的值。如Dawg's answer中所述,作为解决方法,你可以在字典推导表达式中使用list.count(e)来查找字符串set中每个元素的计数。但是时间复杂度将为n*m,因为它将为每个唯一元素(其中m是唯一元素)遍历整个字符串,而使用计数器则只需n

4

这是一个适合使用collections.Counter的好案例:

>>> from collections import Counter
>>> Counter(str1)
Counter({'a': 4, 'b': 3})

这是一个字典子类,因此您可以像使用标准字典一样使用该对象:

>>> c = Counter(str1)
>>> c['a']
4

您可以不使用Counter类来完成此操作。这个简单而高效的Python代码如下:

>>> d = {}
>>> for x in str1:
...     d[x] = d.get(x, 0) + 1
... 
>>> d
{'a': 4, 'b': 3}

2
请注意,这不是正确的方法,因为它不会将重复的字符计算多次(除了丢失原始字典中的其他字符),但这回答了最初的问题,即推导式中是否可能有if-else,并演示了如何实现。
回答您的问题,是的,它是可能的,但方法如下:
dic = {x: (dic[x] + 1 if x in dic else 1) for x in str1}

该条件仅适用于值,而不适用于键值映射。

可以使用 dict.get 更清晰地表达上述内容:

dic = {x: dic.get(x, 0) + 1 for x in str1}

如果x不在dic中,则返回0。

示例:

In [78]: s = "abcde"

In [79]: dic = {}

In [80]: dic = {x: (dic[x] + 1 if x in dic else 1) for x in s}

In [81]: dic 
Out[81]: {'a': 1, 'b': 1, 'c': 1, 'd': 1, 'e': 1}

In [82]: s = "abfg"

In [83]: dic = {x: dic.get(x, 0) + 1 for x in s}

In [84]: dic
Out[84]: {'a': 2, 'b': 2, 'f': 1, 'g': 1}

你的意思是只需要 dic = {x: dic.get(x, 1) for x in str} 吗?你在外面定义过 dic 吗?因为在字典推导式中它将没有引用。 - Moinuddin Quadri
一旦你修复了str到str1,这将失败并显示“NameError: name 'dic' is not defined”。 - Copperfield
@MoinuddinQuadri 是的,我刚刚注意到了,如果你指出来会更有帮助。现在已经修复了。 - sirfz
现在如果你想重新开始,你需要重置字典。 - Copperfield
@Copperfield 这不是正确的做法,但它回答了OP关于理解推导式中if-else的问题。 - sirfz
显示剩余2条评论

0
请查看下面的简单解决方案。如果在字典中找不到键,它将创建一个新的键,并将当前值加1。
text = 'ABCDEEEEFED'

d = {}

for x in text:
    if x in d:
        d[x] = d[x] + 1
    else:
        d[x] = 1


欢迎来到SO。请通过显示使用OP提供的输入数据获得的输出来验证您的答案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接