【问题标题】:What's the fastest way to split dictionary keys into a string-type tuples and append another string to last items in the tuples?将字典键拆分为字符串类型元组并将另一个字符串附加到元组中的最后一项的最快方法是什么?
【发布时间】:2019-05-29 18:18:12
【问题描述】:

给定一个字符串键和整数值的字典,最快的方法是什么

  1. 将每个键拆分成一个字符串类型的键元组
  2. 然后将一个特殊的子字符串</w> 附加到元组中的最后一项

给定:

counter = {'The': 6149,
     'Project': 205,
     'Gutenberg': 78,
     'EBook': 5,
     'of': 39169,
     'Adventures': 2,
     'Sherlock': 95,
     'Holmes': 198,
     'by': 6384,
     'Sir': 30,
     'Arthur': 18,
     'Conan': 3,
     'Doyle': 2,}

目标是实现:

counter = {('T', 'h', 'e</w>'): 6149,
 ('P', 'r', 'o', 'j', 'e', 'c', 't</w>'): 205,
 ('G', 'u', 't', 'e', 'n', 'b', 'e', 'r', 'g</w>'): 78,
 ('E', 'B', 'o', 'o', 'k</w>'): 5,
 ('o', 'f</w>'): 39169,
 ('A', 'd', 'v', 'e', 'n', 't', 'u', 'r', 'e', 's</w>'): 2,
 ('S', 'h', 'e', 'r', 'l', 'o', 'c', 'k</w>'): 95,
 ('H', 'o', 'l', 'm', 'e', 's</w>'): 198,
 ('b', 'y</w>'): 6384,
 ('S', 'i', 'r</w>'): 30,
 ('A', 'r', 't', 'h', 'u', 'r</w>'): 18,
 ('C', 'o', 'n', 'a', 'n</w>'): 3,
 ('D', 'o', 'y', 'l', 'e</w>'): 2,}

一种方法是

  • 遍历计数器并
  • 将除最后一个字符之外的所有字符都转换为元组
  • 添加到元组并创建一个外部元组
  • 并将元组键分配给计数

我试过了

{(tuple(k[:-1])+(k[-1]+'</w>',) ,v) for k,v in counter.items()}

更详细的形式:

new_counter = {}
for k, v in counter.items():
    left = tuple(k[:-1])
    right = tuple(k[-1]+'w',)
    new_k = (left + right,)
    new_counter[new_k] = v

有更好的方法吗?

关于添加元组并将其转换为外部元组。 为什么允许这样做?元组不应该是不可变的吗?

【问题讨论】:

  • 听起来您的问题属于 CodeReview。
  • 这是可能的,因为您创建了一个新字典,并且它的键是不同的元组。字典的原始键确实是不可变的,您不要更改它们。

标签: python string dictionary tuples


【解决方案1】:

我建议对您的解决方案稍作修改。而不是使用 元组构造函数你可以使用元组解包:

>>> {(*a[:-1],f'a[-1]</w>',):b for a,b in counter.items()}

tuple 构造函数相比,使用元组解包的好处是您将获得更好的性能。我将使用timeit 对此进行更多说明。我将使用随机生成的dictdict 中的每个键都有 2 个从小写字母中随机选择的字符,每个值都是 0-100 范围内的整数。对于所有这些基准测试,我使用的是 Python 3.7.0

在 dict 中使用 100 个元素进行基准测试

$ python -m timeit -s "import random" -s "import string" -s "counter = {''.join(random.sample(string.ascii_lowercase,2)): random.randint(0,100) for _ in range(100)}" "{(*a[:-1],f'a[-1]</w>',):b for a,b in counter.items()}
$ 10000 loops, best of 5: 36.6 usec per loop

$ python -m timeit -s "import random" -s "import string" -s "counter = {''.join(random.sample(string.ascii_lowercase,2)): random.randint(0,100) for _ in range(100)}" "{tuple(key[:-1])+(key[-1]+'</w>',):value for key,value in counter.items()}"
$ 5000 loops, best of 5: 59.7 usec per loop

在 dict 中使用 1000 个元素进行基准测试

$ python -m timeit -s "import random" -s "import string" -s "counter = {''.join(random.sample(string.ascii_lowercase,2)): random.randint(0,100) for _ in range(1000)}" "{(*a[:-1],f'a[-1]</w>',):b for a,b in counter.items()}"
$ 1000 loops, best of 5: 192 usec per loop

$ python -m timeit -s "import random" -s "import string" -s "counter = {''.join(random.sample(string.ascii_lowercase,2)): random.randint(0,100) for _ in range(1000)}" "{tuple(key[:-1])+(key[-1]+'</w>',):value for key,value in counter.items()}"
$ 1000 loops, best of 5: 321 usec per loop

用有问题的 dict 进行基准测试

$ python -m timeit -s "import random" -s "import string" -s "counter = counter = {'The': 6149, 'Project': 205, 'Gutenberg': 78, 'EBook': 5, 'of': 39169, 'Adventures': 2, 'Sherlock': 95, 'Holmes': 198, 'by': 6384, 'Sir': 30, 'Arthur': 18, 'Conan': 3,'Doyle': 2}" "{(*a[:-1],f'a[-1]</w>',):b for a,b in counter.items()}"
$ 50000 loops, best of 5: 7.28 usec per loop

$ python -m timeit -s "import random" -s "import string" -s "counter = counter = {'The': 6149, 'Project': 205, 'Gutenberg': 78, 'EBook': 5, 'of': 39169, 'Adventures': 2, 'Sherlock': 95, 'Holmes': 198, 'by': 6384, 'Sir': 30, 'Arthur': 18, 'Conan': 3,'Doyle': 2}" "{tuple(key[:-1])+(key[-1]+'</w>',):value for key,value in counter.items()}"
$ 20000 loops, best of 5: 11 usec per loop

【讨论】:

    【解决方案2】:

    您即将使用tuple 对您的代码进行一些更改。你不能修改一个元组的元素,但是你可以用另一个元组替换一个元组::

    {tuple(key[:-1])+(key[-1]+'</w>',):value for key,value in counter.items()}
    
    {('T', 'h', 'e</w>'): 6149,
     ('P', 'r', 'o', 'j', 'e', 'c', 't</w>'): 205,
     ('G', 'u', 't', 'e', 'n', 'b', 'e', 'r', 'g</w>'): 78,
     ('E', 'B', 'o', 'o', 'k</w>'): 5,
     ('o', 'f</w>'): 39169,
     ('A', 'd', 'v', 'e', 'n', 't', 'u', 'r', 'e', 's</w>'): 2,
     ('S', 'h', 'e', 'r', 'l', 'o', 'c', 'k</w>'): 95,
     ('H', 'o', 'l', 'm', 'e', 's</w>'): 198,
     ('b', 'y</w>'): 6384,
     ('S', 'i', 'r</w>'): 30,
     ('A', 'r', 't', 'h', 'u', 'r</w>'): 18,
     ('C', 'o', 'n', 'a', 'n</w>'): 3,
     ('D', 'o', 'y', 'l', 'e</w>'): 2}
    

    【讨论】:

      【解决方案3】:

      或者使用str.split,并预先添加str.join'&lt;/w&gt;'

      >>> counter = {'The': 6149,
           'Project': 205,
           'Gutenberg': 78,
           'EBook': 5,
           'of': 39169,
           'Adventures': 2,
           'Sherlock': 95,
           'Holmes': 198,
           'by': 6384,
           'Sir': 30,
           'Arthur': 18,
           'Conan': 3,
           'Doyle': 2,}
      >>> {tuple((' '.join(k)+'</w>').split()):v for k,v in counter.items()}
      {('T', 'h', 'e</w>'): 6149, ('P', 'r', 'o', 'j', 'e', 'c', 't</w>'): 205, ('G', 'u', 't', 'e', 'n', 'b', 'e', 'r', 'g</w>'): 78, ('E', 'B', 'o', 'o', 'k</w>'): 5, ('o', 'f</w>'): 39169, ('A', 'd', 'v', 'e', 'n', 't', 'u', 'r', 'e', 's</w>'): 2, ('S', 'h', 'e', 'r', 'l', 'o', 'c', 'k</w>'): 95, ('H', 'o', 'l', 'm', 'e', 's</w>'): 198, ('b', 'y</w>'): 6384, ('S', 'i', 'r</w>'): 30, ('A', 'r', 't', 'h', 'u', 'r</w>'): 18, ('C', 'o', 'n', 'a', 'n</w>'): 3, ('D', 'o', 'y', 'l', 'e</w>'): 2}
      >>> 
      

      时间安排:

      import timeit
      print('bro-grammer:',timeit.timeit(lambda: [{(*a[:-1],f'a[-1]</w>',):b for a,b in counter.items()} for i in range(1000)],number=10))
      print('Sandeep Kadapa:',timeit.timeit(lambda: [{tuple(key[:-1])+(key[-1]+'</w>',):value for key,value in counter.items()} for i in range(1000)],number=10))
      print('U9-Forward:',timeit.timeit(lambda: [{tuple((' '.join(k)+'</w>').split()):v for k,v in counter.items()} for i in range(1000)],number=10))
      

      输出:

      bro-grammer: 0.1293355557653911
      Sandeep Kadapa: 0.20885866344797197
      U9-Forward: 0.3026948357193003
      

      【讨论】:

        【解决方案4】:

        我会选择这样的:

        def f(string):
            l = list(string)
            l[-1] = l[-1] + '</w>'
            return tuple(l)
        dict((f(k), v) for k, v in counter.items())
        

        输出:

        {('A', 'd', 'v', 'e', 'n', 't', 'u', 'r', 'e', 's</w>'): 2,
         ('A', 'r', 't', 'h', 'u', 'r</w>'): 18,
         ('C', 'o', 'n', 'a', 'n</w>'): 3,
         ('D', 'o', 'y', 'l', 'e</w>'): 2,
         ('E', 'B', 'o', 'o', 'k</w>'): 5,
         ('G', 'u', 't', 'e', 'n', 'b', 'e', 'r', 'g</w>'): 78,
         ('H', 'o', 'l', 'm', 'e', 's</w>'): 198,
         ('P', 'r', 'o', 'j', 'e', 'c', 't</w>'): 205,
         ('S', 'h', 'e', 'r', 'l', 'o', 'c', 'k</w>'): 95,
         ('S', 'i', 'r</w>'): 30,
         ('T', 'h', 'e</w>'): 6149,
         ('b', 'y</w>'): 6384,
         ('o', 'f</w>'): 39169}
        

        【讨论】:

          【解决方案5】:

          使用 Python 3,您可以在元组中使用星号表达式。

          你可以试试:

          >>> {(*key[:-1], key[-1] + '</w>'): value for key, value in counter.items()}
          

          【讨论】:

            【解决方案6】:

            您还可以从迭代中删除 .items(),然后像这样:

            {tuple(i[:-1]) + (i[-1]+'</w>',):counter[i] for i in counter}
            

            这有点快。

             timeit.timeit(lambda: {tuple(i[:-1]) + (i[-1]+'w',):counter[i] for i in counter}, number=10)
             0.000192291005179286
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-09-03
              • 2023-03-12
              • 2014-10-05
              • 2013-01-02
              • 1970-01-01
              • 2023-03-22
              • 2010-10-13
              相关资源
              最近更新 更多