#python #unordered-map
#python #stl
Вопрос:
Я пишу некоторый код, который требует от меня извлечения нижней границы ключа (для простоты игнорируйте ключи, которые лежат ниже наименьшего ключа в коллекции).
В C используется std::map (как наиболее сопоставимый тип данных) Я бы просто использовал lower_bound() для возврата итератора.
Мой Pythonfoo не настолько хорош, но я предполагаю, что (в случае, если в Python еще нет способа сделать это), это было бы хорошим использованием лямбда-функции …
Каков Pythonic способ получения ключа нижней границы для данного индекса?
На случай, если вопрос слишком абстрактный, это то, что я на самом деле пытаюсь сделать:
У меня есть Python dict, проиндексированный по дате. Я хочу иметь возможность использовать дату для поиска dict и возвращать значение, связанное с нижней границей указанного ключа.
Следующий фрагмент:
mymap = { datetime.date(2007, 1, 5): 'foo',
datetime.date(2007, 1, 10): 'foofoo',
datetime.date(2007, 2, 2): 'foobar',
datetime.date(2007, 2, 7): 'foobarbar' }
mydate = datetime.date(2007, 1, 7)
# fetch lbound key for mydate from mymap
def mymap_lbound_key(orig):
pass # return the lbound for the key
Я действительно не хочу перебирать ключи в поисках первого ключа <= предоставленный ключ, если только нет лучшей альтернативы …
Ответ №1:
dict
Класс Python не обладает такой функциональностью; вам нужно было бы написать его самостоятельно. Конечно, было бы удобно, если бы ключи были уже отсортированы, не так ли, чтобы вы могли выполнять двоичный поиск по ним и избегать перебора их всех? В этом ключе я бы взглянул на sorteddict
класс в blist
пакете. http://pypi.python.org/pypi/blist /
Ответ №2:
если у вас каким-то образом перегружена дата, чтобы она могла сравнивать, загляните в модуль bisect.
пример минимального целочисленного кодирования:
from bisect import bisect_left
data = {
200 : -100,
-50 : 0,
51 : 100,
250 : 200
}
keys = list(data.keys())
print data[ keys[ bisect_left(keys, -79) ] ]
Ответ №3:
Когда мне нужно что-то, напоминающее карту c , я использую SortedDict. Вы можете использовать irange
для получения итератора по элементам, превышающим заданный ключ — я думаю, именно так std::lower_bound
и работает.
код:
from sortedcontainers import SortedDict
sd = SortedDict()
sd[105] = 'a'
sd[102] = 'b'
sd[101] = 'c'
#SortedDict is sorted on insert, like std::map
print(sd)
# sd.irange(minimum=<key>) returns an iterator beginning with the first key not less than <key>
print("min = 100", list(sd.irange(minimum=100)))
print("min = 102", list(sd.irange(minimum=102)))
print("min = 103", list(sd.irange(minimum=103)))
print("min = 106", list(sd.irange(minimum=106)))
вывод:
SortedDict(None, 1000, {101: 'c', 102: 'b', 105: 'a'})
min = 100 [101, 102, 105]
min = 102 [102, 105]
min = 103 [105]
min = 106 []
Комментарии:
1. Я не хочу создавать список; просто хочу выяснить, насколько «далеко» находится итератор, возвращаемый
sd.irange(minimum=<key>)
, с самого начала. В C я бы сделал что-то вродеsd.lower_bound(key)-sd.begin()
. Есть ли способ сделать это ? Заранее благодарю.2.
next(sd.irange(minimum=103)) - sd.keys()[0]
Делает то, что вы хотите? Он находит разницу между первым ключом над границей и первым ключом. В данном случае это 105 — 101 = 4. Если это так, имейте в виду, чтоnext()
возникнет исключение StopIteration, если никакие элементы не превышают границы.3. Спасибо! Кроме того, существует ли «стратегия» для решения таких проблем в Python, которая обычно может быть решена в C с использованием красно-черного дерева (std::map)? Ваш метод работает, но все это больше похоже на «взлом», а не на идиоматическое питоновское действие, отсюда и мой вопрос.
4. Я собираюсь рискнуть и сказать «вероятно». В экосистеме python много закоулков. Но мой путь увел меня от деревьев к более общим структурам, таким как ориентированные графики, поэтому я действительно не знаю, какой уголок или трещину порекомендовать для вас. Извините.
Ответ №4:
Все еще не уверен, что такое «нижняя граница»: последняя дата до / после даты запроса?
В любом случае, поскольку dict не налагает внутренний порядок на свои ключи, вам нужна другая структура. Храните свои ключи в некоторой структуре, которая сохраняет их отсортированными и позволяет выполнять быстрый поиск.
Самым простым решением было бы просто сохранить даты, отсортированные, в списке (дата, значение) и выполнить двоичный поиск, чтобы увеличить нужный регион. Если вам нужна лучшая производительность, я думаю, что b-дерево — это то, что вам нужно.