【问题标题】:finding elements in python association lists efficiently有效地在 python 关联列表中查找元素
【发布时间】:2011-03-03 16:35:16
【问题描述】:

我有一组如下所示的列表:

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

如何在 Python 中高效优雅地执行以下操作?

  1. 找出某个条件下的所有元素?

    例如获取条件2中的所有样本。现在我可以做到:

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

    但这很笨重。

  2. 找到条件列表的有序联合?例如。 ordered_union(["condition1", "condition2"], conditions) 应该返回:

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

如何在 Python 中有效地做到这一点?可能有聪明的一个班轮?

【问题讨论】:

  • 为什么这是一个列表列表?为什么这不是字典?
  • 这通常很小,只有 3 或 4 个条件,似乎不值得字典。此外,顺序对我来说很重要,因为我稍后会按条件顺序绘制这些值......所以我不确定如何使用 dicts 来完成它并对其进行排序。
  • “似乎不值得一本字典” 毫无意义。您的问题似乎是精确且仅类似于字典的键查找。阅读sorted 并重新提出您的问题。
  • 你能澄清一下#2中的“有序联合”是什么意思吗?例如,如果条件列表重叠会发生什么?假设 cond1 是 ['item1','item2'] 而 cond2 是 ['item2', 'item1'] - 结果应该是什么?
  • 还有OrderedDicts,它的作用就像普通的字典,但保留了插入项目的顺序,我认为是在 Python 3.1 和 2.7 中添加的。请参阅python.org/dev/peps/pep-0372 了解更多信息以及使用旧版 Python 执行此操作的方法的链接。

标签: python list list-comprehension


【解决方案1】:

这看起来更像是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']

【讨论】:

  • 这假设样本名称可以像这样按字母顺序排序,这在我的示例中恰好是正确的,但在我的代码中通常不是正确的——对于这种混淆感到抱歉。 condition1 可以称为 foo,condition2 可以是 bar,其中的样本可以以不易排序的方式任意命名。
  • 如果有重复的元素,连接列表将不是联合。
  • @user248237:好的,那它们应该如何排序呢?或者你是怎么到达["sample1", "sample2", "sample3", "sample4", "sample5", "sample6"]的?
  • @tzaman:你是对的。但我们不知道他所说的“有序联合”究竟是什么意思。让我们等待澄清。
  • @tim:我通过简单地连接两个列表来得出该排序。我假设没有样本属于一个以上的条件。所以排序是由列表给出的。这就是我使用列表而不是字典的原因。
【解决方案2】:

嗯,如果你不得不保留那个笨重的数据结构,你不能期望太多。您的第一个解决方案的单线等效项将类似于:

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]]

请注意,后一种方法更快的关键在于它使用了正确的数据结构(一个集合和一个字典)——不幸的是,它必须自己构建它们,因为传入的 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(条件序列)来处理其他事情(您提到的两个操作肯定不需要它!),并且该序列和示例中没有重复(无论您的实际规格是什么,它们都不会难以适应,但是当您没有提及它们时盲目地尝试猜测它们非常困难且毫无意义;-)。

【讨论】:

  • +1 但是阅读 cmets,操作人员可能会认为这个问题不值得整个类自己
  • 感谢您的所有 cmets 和回复,我很感激。我得到的印象是每个人都认为这应该用字典来完成——我对此非常开放,只要我能保持排序。上面的课程就是这样做的,但就像 gnibbler 说的那样,这是一个完整的课程。这是每个人都会推荐的吗?如果是这样,我对这个解决方案持开放态度,我只是想知道这是否不是矫枉过正。基本上,据我所知,这必须是一个带有额外参数的字典,该参数是一个提供顺序的列表。
  • in return next( <some generator> ) 以上你的意思是return (<some generator>).next() 吗?不是讽刺,只是不知道这样的 fn next()
  • @EnTerr,在 Python 2.6 和更好的版本中,有一个不错的 next 内置(仅当您坚持使用旧版本 2.5 或更早版本时才使用该方法)。 “一堂课”基本上不花任何钱,所以当然如果它使解决方案更好、更快等等,这是值得的。
  • @EnTerr,我喜欢函数式编程在适当的时候,但是你下面的代码真的很糟糕(reduce(list._add__, ...eep,更不用说无用地重复构建中间 dicts —— Sensible 明智地一劳永逸地做,这是关键的加速,在这种情况下无法通过函数式编程获得)。
【解决方案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.M. 的正当批评而贬值的示例 - 由于实现问题 sum() 随着列表大小的增加表现出二次行为。相反,我建议使用以下代码:

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

    【讨论】:

    • 你可以使用tmpdict.get和/或itertools.chain
    • @J.F.塞巴斯蒂安:感谢 dic.get(i) - 我使用了 _getitem_ 因为我正在寻找与dict[i] 等效的exact fn,我知道dict.get() 返回@ 987654330@ 未找到项目而[] 引发异常。除了没有理由我应该更喜欢一个而不是另一个 - 尝试将None 附加到列表也会导致异常,所以最简单 - 获取 - 获胜。 re itertools.chain - 我还不知道 itertools,会调查一下 - 看起来很方便!
    • reduce(operator.iadd,...) 很快。 stackoverflow.com/questions/406121/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    • 2010-10-10
    • 1970-01-01
    相关资源
    最近更新 更多