将字符串拆分为连续的计数?

Splitting a string into consecutive counts?

例如,如果给定的字符串是这样的:

"aaabbbbccdaeeee"

我想说的是:

3 a, 4 b, 2 c, 1 d, 1 a, 4 e

在 Python 中使用强力循环很容易做到,但我想知道是否有更 Pythonic / 更简洁的单行方法。

我的蛮力:

while source!="":
    leading = source[0]
    c=0
    while source!="" and source[0]==leading:
        c+=1
        source=source[1:]
    print(c, leading)

无论位置如何,使用 Counter 计算字符串中每个不同字母的数量:

>>> s="aaabbbbccdaeeee"
>>> from collections import Counter
>>> Counter(s)
Counter({'a': 4, 'b': 4, 'e': 4, 'c': 2, 'd': 1})

如果字符串中的位置有意义,您可以使用groupby

from itertools import groupby
li=[]
for k, l in groupby(s):
    li.append((k, len(list(l))))

print li

打印:

[('a', 3), ('b', 4), ('c', 2), ('d', 1), ('a', 1), ('e', 4)]

可以简化为列表理解:

[(k,len(list(l))) for k, l in groupby(s)]

您甚至可以使用正则表达式:

>>> [(m.group(0)[0], len(m.group(0))) for m in re.finditer(r'((\w)*)', s)] 
[('a', 3), ('b', 4), ('c', 2), ('d', 1), ('a', 1), ('e', 4)]

有许多不同的方法可以解决这个问题。 @dawg 已经发布了最佳解决方案,但如果由于某种原因不允许您使用 Counter()(可能是工作面试或学校作业),那么您实际上可以通过几种方式解决问题。

from collections import Counter, defaultdict

def counter_counts(s):
    """ Preferred method using Counter()


    Arguments:
        s {string} -- [string to have each character counted]

    Returns:
        [dict] -- [dictionary of counts of each char]
    """

    return Counter(s)

def default_counts(s):
    """ Alternative solution using defaultdict


    Arguments:
        s {string} -- [string to have each character counted]

    Returns:
        [dict] -- [dictionary of counts of each char]
    """

    counts = defaultdict(int)  # each key is initalized to 0
    for char in s:
        counts[char] += 1  # increment the count of each character by 1

    return counts

def vanilla_counts_1(s):
    """ Alternative solution using a vanilla dicitonary


    Arguments:
        s {string} -- [string to have each character counted]

    Returns:
        [dict] -- [dictionary of counts of each char]
    """

    counts = {}
    for char in s:
        # we have to manually check that each value is in the dictionary before attempting to increment it
        if char in counts:
            counts[char] += 1
        else:
            counts[char] = 1

    return counts

def vanilla_counts_2(s):
    """ Alternative solution using a vanilla dicitonary
    This version uses the .get() method to increment instead of checking if a key already exists


    Arguments:
        s {string} -- [string to have each character counted]

    Returns:
        [dict] -- [dictionary of counts of each char]
    """

    counts = {}
    for char in s:
         # the second argument in .get() is the default value if we dont find the key
        counts[char] = counts.get(char, 0) + 1 

    return counts

为了好玩,让我们来看看每种方法的执行情况。

对于 s = "aaabbbbccdaeeee" 和 10,000 次运行:

Counter: 0.0330204963684082s
defaultdict: 0.01565241813659668s
vanilla 1: 0.01562952995300293s
vanilla 2: 0.015581130981445312s

(实际上是相当令人惊讶的结果)

现在让我们测试一下如果我们将字符串设置为创世记的完整纯文本版本并运行 1,000 次会发生什么:

Counter: 8.500739336013794s
defaultdict: 14.721554040908813s
vanilla 1: 18.089043855667114s
vanilla 2: 27.01840090751648s

看起来创建 Counter() 对象的开销变得不那么重要了!

(这些不是很科学的测试,但是有点有趣)。