У меня есть список слов l = [10,10,20,15,10,20]
. Я хочу присвоить каждому уникальному значению определенный «индекс» для получения [1,1,2,3,1,2]
.
Это мой код:
a = list(set(l))
res = [a.index(x) for x in l]
Который оказывается очень медленным.
l
имеет 1M элементов и 100K уникальных элементов. Я также попробовал карту с лямбдой и сортировкой, которая не помогла. Какой идеальный способ сделать это?
6 ответов
Медлительность вашего кода возникает из-за того, что a.index(x)
выполняет линейный поиск, и вы выполняете этот линейный поиск для каждого из элементов l
. Таким образом, для каждого из 1М элементов вы выполняете (до) 100К сравнений.
Самый быстрый способ преобразовать одно значение в другое - найти его на карте. Вам нужно будет создать карту и заполнить отношения между исходными значениями и значениями, которые вы хотите. Затем извлеките значение из карты, когда вы встретите другое в списке то же значение.
Вот пример, который делает один проход через l
. Может быть место для дальнейшей оптимизации, чтобы исключить необходимость многократного перераспределения res
при добавлении к нему.
res = []
conversion = {}
i = 0
for x in l:
if x not in conversion:
value = conversion[x] = i
i += 1
else:
value = conversion[x]
res.append(value)
Ну, я думаю, это зависит от того, хотите ли вы вернуть индексы в указанном порядке или нет. Если вы хотите, чтобы пример вернулся:
[1,1,2,3,1,2]
Затем вы можете посмотреть другие ответы. Однако, если вы заботитесь только о получении уникального индекса для каждого уникального номера, то у меня есть быстрое решение для вас
import numpy as np
l = [10,10,20,15,10,20]
a = np.array(l)
x,y = np.unique(a,return_inverse = True)
И для этого примера вывод у:
y = [0,0,2,1,0,2]
Я проверил это на 1000000 записей, и это было сделано практически сразу.
Ваше решение медленное, потому что его сложность O(nm)
, где m
является числом уникальных элементов в l
: a.index()
равно O(m)
, и вы вызываете его для каждого элемента в l
.
Чтобы сделать это O(n)
, избавьтесь от index()
и сохраните индексы в словаре:
>>> idx, indexes = 1, {}
>>> for x in l:
... if x not in indexes:
... indexes[x] = idx
... idx += 1
...
>>> [indexes[x] for x in l]
[1, 1, 2, 3, 1, 2]
Если l
содержит только целые числа в известном диапазоне, вы также можете хранить индексы в списке вместо словаря для более быстрого поиска.
Вы можете использовать collections.OrderedDict()
, чтобы сохранить в порядке уникальные предметы, и, перебрать перечисление этих упорядоченных уникальных предметов, чтобы получить набор предметов и эти индексы (в зависимости от их порядка), а затем передать этот словарь с помощью основной список operator.itemgetter()
, чтобы получить соответствующий индекс для каждого элемента:
>>> from collections import OrderedDict
>>> from operator import itemgetter
>>> itemgetter(*lst)({j:i for i,j in enumerate(OrderedDict.fromkeys(lst),1)})
(1, 1, 2, 3, 1, 2)
Вы можете сделать это за O(N)
время с помощью {{X1} } и понимание списка:
>>> from itertools import count
>>> from collections import defaultdict
>>> lst = [10, 10, 20, 15, 10, 20]
>>> d = defaultdict(count(1).next)
>>> [d[k] for k in lst]
[1, 1, 2, 3, 1, 2]
В Python 3 используйте __next__
вместо next
.
Если вам интересно, как это работает?
default_factory
(т.е. count(1).next
в данном случае), переданный в defaultdict
, вызывается только тогда, когда Python встречает отсутствующий ключ, поэтому для 10 значение будет равно 1, затем для следующих десяти он больше не является пропущенным ключом, поэтому используется ранее вычисленный 1, теперь 20 снова является пропущенным ключом, и Python снова вызовет default_factory
, чтобы получить его значение и так далее.
d
в конце будет выглядеть так:
>>> d
defaultdict(<method-wrapper 'next' of itertools.count object at 0x1057c83b0>,
{10: 1, 20: 2, 15: 3})
Для полноты вы также можете сделать это с нетерпением:
from itertools import count wordid = dict(zip(set(list_), count(1)))
Это использует набор для получения уникальных слов в парах
list_
каждое из этих уникальных слов со следующим значением изcount()
(которое считает вверх), и строит словарь из результатов.
Оригинальный ответ, написанный nneonneo.
Похожие вопросы
Связанные вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.