【问题标题】:Python strings: quickly summarize the character count in order of appearancePython字符串:按出现顺序快速汇总字符数
【发布时间】:2018-09-17 14:23:10
【问题描述】:

假设我在 Python3.x 中有以下字符串

string1 = 'AAAAABBBBCCCDD'
string2 = 'CCBADDDDDBACDC'
string3 = 'DABCBEDCCAEDBB'

我想创建一个摘要“频率字符串”,以下列格式计算字符串中的字符数:

string1_freq = '5A4B3C2D'  ## 5 A's, followed by 4 B's, 3 C's, and 2D's
string2_freq = '2C1B1A5D1B1A1C1D1C' 
string3_freq = '1D1A1B1C1B1E1D2C1A1E1D2B' 

我的问题:

我将如何快速创建这样一个摘要字符串?

我的想法是:创建一个空列表来跟踪计数。然后创建一个检查下一个字符的 for 循环。如果匹配,将计数增加 +1 并移动到下一个字符。否则,追加到字符串 'count' + 'character identity' 的末尾。

这在 Python 中是非常低效的。有没有更快的方法(也许使用下面的功能)?

在python中有几种计算字符串元素的方法。我喜欢collections.Counter,例如

from collections import Counter
counter_str1 = Counter(string1)
print(counter_str1['A']) # 5
print(counter_str1['B']) # 4
print(counter_str1['C']) # 3
print(counter_str1['D']) # 2

还有str.count(sub[, start[, end]

返回子字符串 sub 中非重叠出现的次数 范围 [开始,结束]。可选参数 start 和 end 是 解释为切片符号。

举个例子:

print(string1.count('A'))  ## 5

【问题讨论】:

    标签: python string python-3.x frequency


    【解决方案1】:

    以下代码无需导入任何模块即可完成任务。

    def freq_map(s):
        num = 0         # number of adjacent, identical characters
        curr = s[0]     # current character being processed
        result = ''     # result of function
    
        for i in range(len(s)):
            if s[i] == curr:
                num += 1
            else:
                result += str(num) + curr
                curr = s[i]
                num = 1
    
        result += str(num) + curr
    
        return result
    

    注意:由于您要求基于性能的解决方案,我建议您使用此代码或其修改版本。

    我已经对 CoryKramer 提供的代码进行了粗略的性能测试以供参考。此代码在 58% 的时间内执行相同的功能,而无需使用外部模块。 sn-p 可以找到here

    【讨论】:

    • 性能测试让我吃惊,尤其是针对 for 循环。你知道这背后有什么原因吗? itertools 是否有一些开销?
    • 我鼓励您执行类似的测试,以了解您自己和您的实现是否相同。我不确定为什么使用itertools 的实施成本更高。我的猜测是,这是itertools 模块提供的广泛功能的结果。
    • 看来这个方法还是最快的。
    【解决方案2】:

    我会使用itertools.groupby 对同一字母的连续 次运行进行分组。然后使用join 中的生成器表达式为每次运行创建计数和字母的字符串表示形式。

    from itertools import groupby
    def summarize(s):
        return ''.join(str(sum(1 for _ in i[1])) + i[0] for i in groupby(s))
    

    示例

    >>> summarize(string1)
    '5A4B3C2D'
    >>> summarize(string2)
    '2C1B1A5D1B1A1C1D1C'
    >>> summarize(string3)
    '1D1A1B1C1B1E1D2C1A1E1D2B'
    

    【讨论】:

    • 感谢您的回答。很抱歉我迷路了,但您能否提供更多关于summarize() 工作原理的详细信息?这对学习很有用。所以i[0] in i[0] for i in groupby(s))s 内的字母。 i[1] 是一个 itertools._grouper 对象...
    • 是的,完全正确。 i[0] 是字母,i[1] 是一组连续的字母,我们只是计算。剩下的就是把所有东西都变成字符串并连接起来
    • 更 Python 化(也更高效),解压缩二元组并按名称使用它,而不是重复索引:''.join(str(sum(1 for _ in grp)) + key for key, grp in groupby(s))。对于除最大群体之外的所有群体,使用''.join(str(len(list(grp))) + key for key, grp in groupby(s)) 会更快
    • @ShadowRanger "对于除了最大的群体之外的所有群体,它甚至更快"。如果您可以对此进行量化,我会很感兴趣。您在什么时候注意到减速,或者它与组的大小成线性关系?
    • @ShanZhengYang:len(list(grp)) 基本上总是最快的,除非它导致您超出 RAM 限制并最终分页到磁盘(在这种情况下似乎不太可能)。 my old answer on this topic 有更多详细信息。
    猜你喜欢
    • 1970-01-01
    • 2017-03-30
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 2015-11-01
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    相关资源
    最近更新 更多