【问题标题】:Best way to count char occurences in a string计算字符串中 char 出现次数的最佳方法
【发布时间】:2012-01-21 09:55:53
【问题描述】:

您好,我正在尝试将这些 python 行写在一行中,但由于代码正在执行的字典修改而出现一些错误。

for i in range(len(string)):
    if string[i] in dict:
        dict[string[i]] += 1

我认为的一般语法是

abc = [i for i in len(x) if x[i] in array]

考虑到我将字典中的值加 1,是否有人可以告诉我这可能如何工作

谢谢

【问题讨论】:

  • 不清楚,dict 在循环之前有什么?如果为空,则不计算任何字符。

标签: python string optimization performancecounter


【解决方案1】:

您想要做的事情可以通过dict生成器表达式str.count()来完成:

abc = dict((c, string.count(c)) for c in string)

替代使用set(string) (来自soulcheck下方的评论)

abc = dict((c, string.count(c)) for c in set(string))

时间

看到下面的 cmets,我在这个和其他答案中进行了一些测试。 (使用 python-3.2)

测试函数:

@time_me
def test_dict(string, iterations):
    """dict((c, string.count(c)) for c in string)"""
    for i in range(iterations):
        dict((c, string.count(c)) for c in string)

@time_me
def test_set(string, iterations):
    """dict((c, string.count(c)) for c in set(string))"""
    for i in range(iterations):
        dict((c, string.count(c)) for c in set(string))

@time_me
def test_counter(string, iterations):
    """Counter(string)"""
    for i in range(iterations):
        Counter(string)

@time_me
def test_for(string, iterations, d):
    """for loop from cha0site"""
    for i in range(iterations):
        for c in string:
            if c in d:
                d[c] += 1

@time_me
def test_default_dict(string, iterations):
    """defaultdict from joaquin"""
    for i in range(iterations):
        mydict = defaultdict(int)
        for mychar in string:
            mydict[mychar] += 1

测试执行:

d_ini = dict((c, 0) for c in string.ascii_letters)
words = ['hand', 'marvelous', 'supercalifragilisticexpialidocious']

for word in words:
    print('-- {} --'.format(word))
    test_dict(word, 100000)
    test_set(word, 100000)
    test_counter(word, 100000)
    test_for(word, 100000, d_ini)
    test_default_dict(word, 100000)
    print()

print('-- {} --'.format('Pride and Prejudcie - Chapter 3 '))

test_dict(ch, 1000)
test_set(ch, 1000)
test_counter(ch, 1000)
test_for(ch, 1000, d_ini)
test_default_dict(ch, 1000)

测试结果:

-- hand --
389.091 ms -  dict((c, string.count(c)) for c in string)
438.000 ms -  dict((c, string.count(c)) for c in set(string))
867.069 ms -  Counter(string)
100.204 ms -  for loop from cha0site
241.070 ms -  defaultdict from joaquin

-- marvelous --
654.826 ms -  dict((c, string.count(c)) for c in string)
729.153 ms -  dict((c, string.count(c)) for c in set(string))
1253.767 ms -  Counter(string)
201.406 ms -  for loop from cha0site
460.014 ms -  defaultdict from joaquin

-- supercalifragilisticexpialidocious --
1900.594 ms -  dict((c, string.count(c)) for c in string)
1104.942 ms -  dict((c, string.count(c)) for c in set(string))
2513.745 ms -  Counter(string)
703.506 ms -  for loop from cha0site
935.503 ms -  defaultdict from joaquin

# !!!: Do not compare this last result with the others because is timed
#      with 1000 iterations instead of 100000
-- Pride and Prejudcie - Chapter 3  --
155315.108 ms -  dict((c, string.count(c)) for c in string)
982.582 ms -  dict((c, string.count(c)) for c in set(string))
4371.579 ms -  Counter(string)
1609.623 ms -  for loop from cha0site
1300.643 ms -  defaultdict from joaquin

【讨论】:

  • 准确地说,第一个解决方案是传递给dict()构造函数的生成器表达式,第二个是dict理解
  • 这不是有点糟糕,复杂吗?我认为是O(n*n),而原来是O(n)(好吧,O(n*n) 是哈希表查找的最坏情况,但平均而言是O(n))...
  • @cha0site true,但您可以轻松修改它以具有相同的运行时间:abc = dict((c, string.count(c)) for c in set(string))
  • @cha0site 啊,没注意到。真的
  • @RikPoggi 它本质上使 O(n^2) 算法成为 O(n*d) 算法,其中 n = 字符串的长度,d = 字母表的大小。这里 d 是常数,所以你没问题。您仍然应该更喜欢 reclosedev 或 joaquin 的解决方案,具体取决于您使用的 python 版本,因为它是 O(n) 并且肯定更通用。
【解决方案2】:

Python 2.7+ 的替代方案:

from collections import Counter

abc = Counter('asdfdffa')
print abc
print abc['a']

输出:

Counter({'f': 3, 'a': 2, 'd': 2, 's': 1})
2

【讨论】:

    【解决方案3】:

    这是集合模块的工作:


    选项 1.- collections. defaultdict:

    >>> from collections import defaultdict
    >>> mydict = defaultdict(int)
    

    然后你的循环变成:

    >>> for mychar in mystring: mydict[mychar] += 1
    

    选项 2.- collections.Counter(来自 Felix 评论):

    一种更适合这种特定情况的替代方法,来自同一个collections 模块:

    >>> from collections import Counter
    

    那么你只需要 (!!!):

    >>> mydict = Counter(mystring)
    

    Counter 仅在 python 2.7 中可用。所以对于 python

    【讨论】:

    • 我将您的解决方案与其他解决方案进行了比较,这是最好的,也许您想看看它。 (+1)
    【解决方案4】:

    这不是列表理解的好选择。您通常希望使用列表推导式来制作列表,并且在其中产生副作用(更改全局状态)并不是一个好主意。

    另一方面,您的代码可能会像这样更好:

    for c in string:
        if c in dict:
            dict[c] += 1
    

    或者,如果你真的想获得功能(我已将 dict 重命名为 d,因为我需要 python 的内置 dict 函数):

    d.update(dict([ (c, d[c]+1, ) for c in string ]))
    

    注意我是如何没有在列表理解中更改d,而是在列表理解之外更新d

    【讨论】:

    • -1 看起来很糟糕而且不起作用。 d = {}; d.update(dict([ (c, d[c]+1, ) for c in 'fubar' ])) 产生 KeyError: 'f' 。将 d[c] 更改为 d.get(c, 0) 可以避免 KeyError 但它仍然不会因为 d 在循环中没有更新,所以 d[c] 永远不会大于 0,所以 所有计数都是 1 .
    • @John:取决于你想做什么。最初的问题假设dict 中有东西,所以我也这样做了。
    • 第一个建议要求将所有预期字符的 dict 初始化为 0 - 这并不可靠。
    【解决方案5】:

    你原来的循环是无可救药的非 Pythonic。如果您只想遍历string 中的字母,则无需遍历range(len(string))。改为这样做:

    for c in my_string:
        if c in my_dict:
            my_dict[c] += 1
    

    【讨论】:

    • -1 您忘记了末尾的 else: my_dict[c] = 1 或开头的 defaultdict 设置。所有计数都将为零。
    【解决方案6】:
    >>> def count(s):
        global k
        list =[]
        for i in s:
            k=0
            if i not in list:
                list.append(i)      
                for j in range(len(s)):
                    if i == s[j]:
                        k +=1
    
                print 'count of char {0}:{1}'.format(i,k)
    
    
    >>> count('masterofalgorithm')
    count of char m:2
    count of char a:2
    count of char s:1
    count of char t:2
    count of char e:1
    count of char r:2
    count of char o:2
    count of char f:1
    count of char l:1
    count of char g:1
    count of char i:1
    count of char h:1
    >>> 
    

    【讨论】:

      猜你喜欢
      • 2019-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-13
      • 2022-09-22
      • 2016-03-29
      • 1970-01-01
      相关资源
      最近更新 更多