Поиск элементов в списках ассоциаций python эффективно

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

conditions = [
["condition1", ["sample1", "sample2", "sample3"]],
["condition2", ["sample4", "sample5", "sample6"],
...]

как я могу сделать следующие вещи эффективно и элегантно в Python?

  • Найти все элементы в определенном условии?

    например. получить все образцы в условии2. Сейчас я могу сделать:

    for cond in conditions:
      cond_name, samples = cond
      if cond_name == requested_cond:
        return samples
    

    но это неудобно.

  • Найдите упорядоченный союз списка условий? Например. ordered_union(["condition1", "condition2"], conditions) должен вернуться:

    ["sample1", "sample2", "sample3", "sample4", "sample5", "sample6"]
    

Как я могу сделать это эффективно на Python? Есть, наверное, умные лайнеры?

Ответ 1

Хорошо, если вы вынуждены держать эту неуклюжую структуру данных, вы не можете ожидать многого. Однострочный эквивалент вашего первого решения будет примерно таким:

def samplesof(requested_cond, conditions):
    return next(s for c, s in conditions if c==requested_cond)

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

def ordered_union(the_conds, conditions):
    return [s for c in the_conds for s in samplesof(c, conditions)]

Есть более быстрые способы решения второй проблемы, но все они многострочные, например:

aux_set = set(the_conds)
samples_by_cond = dict((c, s) for c, s in conditions if c in aux_set)
return [s for c in the_conds for s in samples_by_cond[c]]

Обратите внимание, что ключ к причине, по которой этот последний подход выполняется быстрее, заключается в том, что он использует правильные структуры данных (набор и dict) - к сожалению, он должен сам их создавать, потому что входящий conditions вложенный список действительно неправильная структура данных.

Не удалось ли инкапсулировать conditions как переменную-член класса, которая строит критические (правые, быстрые) вспомогательные структуры данных только один раз? Например:.

class Sensible(object):
  def __init__(self, conditions):
    self.seq = []
    self.dic = {}
    for c, s in conditions:
      self.seq.append(c)
      self.dic[c] = s
  def samplesof(self, requested_condition):
    return self.dic[requested_condition]
  def ordered_union(self, the_conds):
    return [s for c in the_conds for s in self.dic[c]]

Теперь это быстро и элегантно!

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

Ответ 2

Это больше похоже на работу для dict:

conditions = {
"condition1": ["sample1", "sample2", "sample3"],
"condition2": ["sample4", "sample5", "sample6"],
...}

Затем вы можете получить "упорядоченный союз", используя

>>> conditions["condition1"]+conditions["condition2"]
['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6']

В Python 3.1 или 2.7 вы можете сохранить порядок, используя OrderedDict вместо:

from collections import OrderedDict
conditions = OrderedDict([
["condition1", ["sample1", "sample2", "sample3"]],
["condition2", ["sample4", "sample5", "sample6"]]
])

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

>>> import itertools
>>> [item for item in itertools.chain(*conditions.values())]
['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6']

Ответ 3

Вам нужно использовать dict (словарь) вместо list. Кроме того, вы можете сохранить образцы в set, если хотите эффективные операции на основе набора.

conditions = { "condition1" : set(["sample1", "sample2", "sample3"]),
               "condition2" : set(["sample4", "sample5", "sample6"]) }

print conditions["condition2"]
# set(['sample5', 'sample4', 'sample6'])
union = conditions["condition1"].union(conditions["condition2"])
print sorted(union)
# ['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6']

Ответ 4

По первому вопросу:

>>> dict(conditions)['condition1']
['sample1', 'sample2', 'sample3']

На # 2 (не совсем понятно, что вы подразумеваете под "упорядоченным союзом", поэтому я делаю предположение "упорядоченные списки, объединенные по порядку" ):

>>> tmpdict = dict(conditions)
>>> sum( map(tmpdict.get, ["condition1", "condition2"]), [] )
['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6']

Забастовкa > пс. пример амортизируется по адресу A.M. законная критика - из-за проблем с реализацией sum() проявляется квадратичное поведение с увеличением размера списка. Вместо этого я предлагаю код ниже:

>>> import operator
>>> tmpdict = dict(conditions)
>>> reduce(operator.iadd, map(tmpdict.get, ["condition1", "condition2"]), [] )
['sample1', 'sample2', 'sample3', 'sample4', 'sample5', 'sample6']