Переназначить значения словаря

у меня есть словарь, как

{'A': 0, 'B': 1, 'C': 2, 'D': 3, etc}

Как удалить элементы из этого словаря без создания пробелов в значениях, если словарь не упорядочен?

пример:

у меня есть большая матрица, где строки представляют слова, а столбцы представляют документы, где эти слова встречаются. Я храню слова и соответствующие им индексы как словарь. Е. Г. для этой матрицы

2 0 0
1 0 3
0 5 1
4 1 2

в словарь будет выглядеть так:

words = {'apple': 0, 'orange': 1, 'banana': 2, 'pear': 3}

если я удалю слова 'apple' и 'banana' матрица будет содержать всего две строки. Так что значение 'orange' в словаре должно теперь равняться 0, а не 1 и значение 'pear' должно быть 1 вместо 3.

в Python 3.6+ словари упорядочены, поэтому я могу просто написать что-то вроде этого, чтобы переназначить значения:

i = 0
for k, v in words.items():
  v = i
  i += 1

или, в качестве альтернативы

words = dict(zip(terms.keys(), range(0, matrix.shape[0])))

Я думаю, это далеко не самый эффективный способ изменить значения, и он не будет работать с неупорядоченными словари. Как это сделать эффективно? Есть ли способ легко переназначить значения в случае, если словарь не упорядочен?

5 ответов


включите дикт в отсортированном списке, а затем построить новый дикт без слов, которые вы хотите удалить:

import itertools

to_remove = {'apple', 'banana'}

# Step 1: sort the words
ordered_words = [None] * len(words)
for word, index in words.items():
    ordered_words[index] = word
# ordered_words: ['apple', 'orange', 'banana', 'pear']

# Step 2: Remove unwanted words and create a new dict
counter = itertools.count()
words = {word: next(counter) for word in ordered_words if word not in to_remove}
# result: {'orange': 0, 'pear': 1}

это имеет время выполнения O (n), потому что ручное упорядочение списка с операциями индексирования является линейной операцией, в отличие от sorted который будет O (N log n).

см. также документацию для itertools.count и next.


вы можете использовать существующую логику, используя представление сортируемого словаря:

import operator

words = {'apple': 0, 'orange': 1, 'banana': 2, 'pear': 3}
sorted_words = sorted(words.items(), key=operator.itemgetter(1))

for i, (k, v) in enumerate(sorted_words):
    words[k] = i

изначально у нас есть

words = {'apple': 0, 'orange': 1, 'banana': 2, 'pear': 3}

чтобы изменить порядок на основе от минимального до максимального, вы можете использовать sorted и понимание словаря.

std = sorted(words, key=lambda x: words[x])

newwords = { word : std.index(word) for word in std }

это хорошо..?


вы всегда можете сохранить перевернутый словарь, который отображает индексы в слова, и использовать его в качестве ссылки для сохранения порядка исходного словаря. Затем вы можете удалить слова и снова перестроить словарь:

words = {'apple': 0, 'orange': 1, 'banana': 2, 'pear': 3}

# reverse dict for index -> word mappings
inverted = {i: word for word, i in words.items()}

remove = {'apple', 'banana'}

# sort/remove the words
new_words = [inverted[i] for i in range(len(inverted)) if inverted[i] not in remove]

# rebuild new dictionary
new_dict = {word: i for i, word in enumerate(new_words)}

print(new_dict)

Выходы:

{'orange': 0, 'pear': 1}

Примечание: как и принятый ответ, это также O(n).


вы используете неправильный инструмент (dict) для задания вы должны использовать list

class vocabulary:
    def __init__(self, *words):
        self.words=list(words)
    def __getitem__(self, key):
        try:
             return self.words.index(key)
        except ValueError:
            print (key + " is not in vocabulary")
    def remove(self, word):
        if type(word)==int:
           del self.words[word]
           return
        return self.remove(self[word])

words = vocabulary("apple" ,"banana", "orange")
print (words["banana"]) # outputs 1
words.remove("apple")
print (words["banana"]) # outputs 0

примечание по сложности

у меня было несколько комментариев, упоминая, что dict более эффективно, потому что это время поиска O(1) и время поиска list и O(n).

это просто не так в этом случае.

на O(1) гарантия хэш-таблицей (dict в python), является результатом амортизированного сложность, означающая, что вы усредняете общее использование таблицы поиска, которая является генерируется один раз, предполагая, что ваша хэш-функция сбалансирована.

этот амортизированный расчет не учитывает удаление всего словаря и его регенерацию каждый раз, когда вы удаляете элемент, как предлагают некоторые другие ответы.

на list реализация и dict реализация имеет ту же наихудшую сложность O(n).

тем не менее,list реализация может быть оптимизирована с помощью двух строк python (bisect) в худшем случае сложность O(log(n))