Какой самый быстрый способ разбить словарные ключи на кортежи строкового типа и добавить еще одну строку к последним элементам кортежей?

1

Учитывая словарь строковых ключей и целочисленных значений, какой самый быстрый способ

  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

Есть лучший способ сделать это?

Относительно добавления кортежа и приведения его к внешнему кортежу. Почему это разрешено? Разве кортеж не должен быть неизменным?

  • 0
    Похоже, ваш вопрос принадлежит CodeReview.
  • 0
    Это возможно, потому что вы создаете НОВЫЙ словарь, а его ключи - РАЗНЫЕ кортежи. Оригинальные ключи словаря действительно неизменны, и вы не меняете их.
Теги:
string
dictionary
tuples

5 ответов

2

Я бы предложил слегка модифицированную версию вашего решения. Вместо использования конструктора кортежей вы можете использовать распаковку кортежей:

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

Преимущество использования распаковки кортежей в том, что вы получите лучшую производительность по сравнению с конструктором tuple. Я расскажу об этом timeit используя timeit. Я буду использовать случайно сгенерированный dict. Каждый ключ в dict будет иметь 2 случайно выбранных символа из строчных букв, и каждое значение будет целым числом в диапазоне 0-100. Для всех этих тестов я использую Python 3.7.0

Тест с 100 элементами в dict

$ 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

Тест с 1000 элементами в dict

$ 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}
0

В Python 3 вы можете использовать помеченные слова в кортежах.

Ты можешь попробовать:

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

Или используйте str.split, и str.join и '</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,}
>>> {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
0

Я бы пошел на что-то вроде этого:

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}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню