【问题标题】:how to replace patterns sequentially in python when pattern includes duplicates当模式包含重复项时如何在python中按顺序替换模式
【发布时间】:2019-01-21 17:16:53
【问题描述】:

我有一个模式列表和替换列表。该模式包含重复元素,但它们对应于不同的替换。

txt=132GOasmHOMEwokdslNOWsdwkGO239NOW
pattern=['GO','HOME','NOW','GO','NOW']
REPLACEMENT=['why','nope','later','aha','genes']

期望的输出是 132whyasmnopewokdsllatersdwkaha239genes

完成顺序替换最有效的方法是什么?

【问题讨论】:

  • 如果你的字符串是“HOME GO NOW GO NOW”,输出应该是什么? (切换模式中的前两个元素)
  • 不用担心模式列表是顺序提取的
  • 不是“不,为什么后来啊哈基因”?澄清一下,您只是在寻找某些单词,它们的顺序无关紧要吗?如果字符串是“GO GO GO GO GO”,输出是否也一样?
  • 所以我事先知道了 txt 并且知道它将包含模式中的元素按该顺序和相同的长度,并且它不会包含 GO * 5 模式中的其他元素
  • @santoku - 请注意,随着输入的变化,最有效的变化是什么。对于这个简单的数据集,所有给出的解决方案都差不多。随着输入的增长,最有效的方式发生了巨大的变化。

标签: python list replace


【解决方案1】:
txt='132GOasmHOMEwokdslNOWsdwkGO239NOW'
pattern=['GO','HOME','NOW','GO','NOW']
REPLACEMENT=['why','nope','later','aha','genes']

for i,x in enumerate(pattern):
    txt = txt.replace(x,REPLACEMENT[i], 1)

为了好玩,这里是时间测试,因为问题要求最有效。

pattern=['GO','HOME','NOW','GO','NOW']
REPLACEMENT=['why','nope','later','aha','genes']

t = time.time()
for z in xrange(1000000):
    txt = '132GOasmHOMEwokdslNOWsdwkGO239NOW'
    for a,b in zip(pattern,REPLACEMENT):
        txt=txt.replace(a,b,1)
print time.time() - t

t = time.time()
for z in xrange(1000000):
    txt2 = '132GOasmHOMEwokdslNOWsdwkGO239NOW'
    for i,x in enumerate(pattern):
        txt2 = txt2.replace(x,REPLACEMENT[i], 1)
print time.time() - t

t = time.time()
for z in xrange(1000000):
    txt3 = '132GOasmHOMEwokdslNOWsdwkGO239NOW'
    x = dict(zip(reversed(pattern), reversed(REPLACEMENT)))
    for k in x:
      txt3 = txt3.replace(k,x[k], 1)
print time.time() - t


t = time.time()
for z in xrange(1000000):
    txt = '132GOasmHOMEwokdslNOWsdwkGO239NOW'
    new_d = iter(REPLACEMENT)
    new_result = re.sub('\b' + '|'.join(pattern) + '\b', lambda _: next(new_d), txt)
print time.time() - t

哪些结果:

2.57099986076
2.48500013351
3.50499987602
4.23699998856

如您所见,enumerate 比 zip 效率稍高,另外两个不在同一个范围内。

【讨论】:

  • 在测试了我的和zip替换方法后,我发现我的确实更快。
  • 这并不总是与速度有关。 zip replace 方法更像 Pythonic IMO。
  • 他实际上说出了最有效的方法。你怎么能这么说呢,它们本质上是相同的代码,除了我只解析一个列表,你正在解析 2。如果我的任何东西实际上更简单和最有效。
  • 不是在这里争论,但我很确定收益是微不足道的,因为 enumeratezip 都返回迭代器。此外,效率可能会转化为可读性(从长远来看),这取决于您的理念。速度只是另一个指标。
【解决方案2】:

可以同时循环遍历两个列表,每次只替换模式的第一个实例:

for a,b in zip(pattern,REPLACEMENT):
    txt=txt.replace(a,b,1)

【讨论】:

    【解决方案3】:

    我认为你应该试试这个:

    import re
    txt = "132GOasmHOMEwokdslNOWsdwkGO239NOW"
    pattern = ['GO','HOME','NOW','GO','NOW']
    REPLACEMENT = ['why','nope','later','aha','genes'] 
    txt1 = re.sub(pattern[1], REPLACEMENT[1], txt)
    txt2 = re.sub(pattern[2], REPLACEMENT[2], txt1)
    txt3 = re.sub(pattern[3], REPLACEMENT[3], txt2)
    txt4 = re.sub(pattern[4], REPLACEMENT[4], txt3)
    FINAL_TEXT = re.sub(pattern[5], REPLACEMENT[5], txt4)
    print(FINAL_TEXT)
    

    还有输出:

    "132whyasmnotwokdsllatersdwkaha239genes"
    

    【讨论】:

    • 好的,你能告诉我我做错了什么吗,或者请编辑。这可以帮助我理解你的意见。
    • 用户(询问者)在他的第二条评论中写道:“我事先知道 txt,并且知道它将包含模式中的元素按该顺序和长度相同,并且不会包含其他GO * 5"图案中的元素
    【解决方案4】:

    使用dict 可以减少您需要迭代的项目数量,这对于一些长输入可能很有价值。

    txt = '132GOasmHOMEwokdslNOWsdwkGO239NOW'
    pattern = ['GO','HOME','NOW','GO','NOW']
    REPLACEMENT = ['why','nope','later','aha','genes']
    
    x = dict(zip(reversed(pattern), reversed(REPLACEMENT)))
    for k in x:
      txt = txt.replace(k,x[k], 1)
    print(txt)
    

    编辑:为了好玩,我在备份中添加了一个基准,以说明减少需要迭代的项目数量对于一些长输入可能很有价值。当您使用琐碎的测试数据集时,最高效并不总是显而易见的。

     #! /usr/bin/env python
    # -*- coding: UTF8 -*- 
    
    def alpha(pattern, REPLACEMENT, txt):
      for a,b in zip(pattern,REPLACEMENT):
        txt=txt.replace(a,b,1)
    
    def beta(pattern, REPLACEMENT, txt):
      for i,x in enumerate(pattern):
        txt = txt.replace(x,REPLACEMENT[i], 1)
    
    def gamma(pattern, REPLACEMENT, txt):
      x = dict(zip(reversed(pattern), reversed(REPLACEMENT)))
      for k in x:
        txt = txt.replace(k,x[k], 1)
    
    def delta(pattern, REPLACEMENT, txt):
      new_d = iter(REPLACEMENT)
      new_result = re.sub('\b' + '|'.join(pattern) + '\b', lambda _: next(new_d), txt)
    
    if __name__ == '__main__':
      import timeit, re
    
      txt = '132GOasmHOMEwokdslNOWsdwkGO239NOW'
      pattern = ['GO','HOME','NOW','GO','NOW']
      REPLACEMENT = ['why','nope','later','aha','genes']
    
      print("Trivial inputs:  len(pattern): {}, len(REPLACEMENT): {}, len(txt): {}".format(len(pattern), len(REPLACEMENT), len(txt)));
      print("alpha: ", timeit.timeit("alpha(pattern, REPLACEMENT, txt)", setup="from __main__ import alpha, txt, pattern, REPLACEMENT"))
      print("beta:  ", timeit.timeit("beta( pattern, REPLACEMENT, txt)", setup="from __main__ import beta,  txt, pattern, REPLACEMENT"))
      print("gamma: ", timeit.timeit("gamma(pattern, REPLACEMENT, txt)", setup="from __main__ import gamma, txt, pattern, REPLACEMENT"))
      print("delta: ", timeit.timeit("delta(pattern, REPLACEMENT, txt)", setup="from __main__ import delta, txt, pattern, REPLACEMENT"))
      print("")
    
      txtcopy = txt
      patterncopy = pattern.copy()
      REPLACEMENTcopy = REPLACEMENT.copy()
    
      for _ in range(3):
        txt = txt + txtcopy
        pattern.extend(patterncopy)
        REPLACEMENT.extend(REPLACEMENTcopy)
    
      print("Small inputs: len(pattern): {}, len(REPLACEMENT): {}, len(txt): {}".format(len(pattern), len(REPLACEMENT), len(txt)));
      print("alpha: ", timeit.timeit("alpha(pattern, REPLACEMENT, txt)", setup="from __main__ import alpha, txt, pattern, REPLACEMENT"))
      print("beta:  ", timeit.timeit("beta( pattern, REPLACEMENT, txt)", setup="from __main__ import beta,  txt, pattern, REPLACEMENT"))
      print("gamma: ", timeit.timeit("gamma(pattern, REPLACEMENT, txt)", setup="from __main__ import gamma, txt, pattern, REPLACEMENT"))
      print("delta: ", timeit.timeit("delta(pattern, REPLACEMENT, txt)", setup="from __main__ import delta, txt, pattern, REPLACEMENT"))
      print("")
    
      txt = txtcopy
      pattern = patterncopy.copy()
      REPLACEMENT = REPLACEMENTcopy.copy()
    
      for _ in range(300):
        txt = txt + txtcopy
        pattern.extend(patterncopy)
        REPLACEMENT.extend(REPLACEMENTcopy)
    
      print("Larger inputs: len(pattern): {}, len(REPLACEMENT): {}, len(txt): {}".format(len(pattern), len(REPLACEMENT), len(txt)));
      print("alpha: ", timeit.timeit("alpha(pattern, REPLACEMENT, txt)", setup="from __main__ import alpha, txt, pattern, REPLACEMENT"))
      print("beta:  ", timeit.timeit("beta(pattern, REPLACEMENT, txt)", setup="from __main__ import beta,  txt, pattern, REPLACEMENT"))
      print("gamma: ", timeit.timeit("gamma(pattern, REPLACEMENT, txt)", setup="from __main__ import gamma, txt, pattern, REPLACEMENT"))
      print("delta: ", timeit.timeit("delta(pattern, REPLACEMENT, txt)", setup="from __main__ import delta, txt, pattern, REPLACEMENT"))
    

    结果:

    Trivial inputs:  len(pattern): 5, len(REPLACEMENT): 5, len(txt): 33
    alpha:  4.60048107800003
    beta:   4.169088881999869
    gamma:  5.7612637450001785
    delta:  11.371387353000046
    
    Small inputs: len(pattern): 20, len(REPLACEMENT): 20, len(txt): 132
    alpha:  17.281149661999734
    beta:   15.131949634000193
    gamma:  7.339897444000144
    delta:  26.50896787900001
    
    Larger inputs: len(pattern): 1505, len(REPLACEMENT): 1505, len(txt): 9933
    alpha:  18766.660852467998
    beta:   17640.960064803
    gamma:  64.01868645999639
    delta:  901.3577002189995
    

    因此,对于微不足道的输入,enumerate 解决方案比 zip 快一点,比 iter 快很多。当输入的长度略微增加时,不删除重复项的成本开始显现,我的解决方案运行时间不到一半。当运行具有大量重复项的长输入时,@eatmeimadish 解决方案的完成时间比删除重复项时长 27555%。哎哟。

    【讨论】:

    • 也慢了很多。
    • 对于所有数据集?
    • 是的,这比前两个答案慢得多,也更难阅读,前两个答案在性能和可读性上相似。
    • 我会在真正缓慢的案例完成后立即发布我的基准测试。
    • @eatmeimadanish - 基准测试并不能支持您关于慢得多的说法。您的解决方案甚至不在同一个球场。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-31
    • 2013-08-22
    • 2012-09-16
    • 2013-07-21
    • 2012-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多