【问题标题】:In Python, find item in list of dicts, using bisect在 Python 中,使用 bisect 在字典列表中查找项目
【发布时间】:2010-11-23 13:52:59
【问题描述】:

我有一个字典列表,如下所示:

test_data = [
    { 'offset':0, 'data':1500 },
    { 'offset':1270, 'data':120 },
    { 'offset':2117, 'data':30 },
    { 'offset':4055, 'data':30000 },
]

dict 项在列表中根据'offset' 数据进行排序。真实数据可能更长。

我想要做的是在列表中查找给定特定偏移值的项目,该偏移值正是这些值之一,但在该范围内。所以,二分查找是我想做的。

我现在知道 Python bisect 模块,它是一个现成的二进制搜索 - 很棒,但不能直接用于这种情况。我只是想知道使bisect 适应我的需求的最简单方法是什么。这是我想出的:

import bisect

class dict_list_index_get_member(object):
    def __init__(self, dict_list, member):
        self.dict_list = dict_list
        self.member = member
    def __getitem__(self, index):
        return self.dict_list[index][self.member]
    def __len__(self):
        return self.dict_list.__len__()

test_data_index_get_offset = dict_list_index_get_member(test_data, 'offset')
print bisect.bisect(test_data_index_get_offset, 1900)

打印出来:

2

我的问题是,这是做我想做的最好的方式,还是有其他更简单、更好的方式?

【问题讨论】:

    标签: python dictionary binary-search


    【解决方案1】:

    您还可以使用 Python 的众多 SortedDict 实现之一来管理您的 test_data。排序的 dict 按键对元素进行排序并维护到值的映射。一些实现还支持对键进行二等分操作。例如,Python sortedcontainers module 有一个满足您要求的SortedDict

    在你的情况下,它看起来像:

    from sortedcontainers import SortedDict
    offset_map = SortedDict((item['offset'], item['data']) for item in test_data)
    index = offset_map.bisect(1275)
    key = offset_map.iloc[index]
    print offset_map[key]
    # 120
    

    SortedDict 类型有一个 bisect 函数,它返回所需键的二等分索引。使用该索引,您可以查找实际的键。使用该键,您可以获得值。

    所有这些操作在 sortedcontainers 中都非常快,这在纯 Python 中也很方便地实现。还有一个performance comparison 讨论其他选择并有基准数据。

    【讨论】:

      【解决方案2】:

      这里通常的模式类似于按属性排序,装饰、操作和取消装饰。所以在这种情况下,你只需要装饰然后打电话。但是,您希望避免这样做,因为 decorate 将是 O(n),而您希望它是 O(logn)。所以我会考虑你的方法最好。

      【讨论】:

        【解决方案3】:

        当您说实际数据可能更长时,这是否会妨碍您保留手头的偏移值列表?

        offset_values = [i['offset'] for i in test_data]
        bisect.bisect(offset_values, 1900)
        

        不过我觉得你的方法很好。

        【讨论】:

          【解决方案4】:

          你能做的就是这个

          class OffsetWithAttributes( object ):
              def __init__( self, offset, **kw ):
                  self.offset= offset
                  self.attributes= kw
              def __eq__( self, other ):
                  return self.offset == other.offset
              def __lt__( self, other ):
                  return self.offset < other.offset
              def __le__( self, other ):
                  return self.offset <= other.offset
              def __gt__( self, other ):
                  return self.offset > other.offset
              def __ge__( self, other ):
                  return self.offset >= other.offset
              def __ne__( self, other ):
                  return self.offset != other.offset
          

          这应该允许您创建一个简单的list OffsetWithAttributes 实例。 bisect 算法应该非常乐意使用定义的运算符。

          您可以使用您的someOWA.attributes['data']

          或者

              def __getattr__( self, key ):
                  return self.attributes[key]
          

          这应该使OffsetWithAttributes 更像dict

          【讨论】:

            【解决方案5】:

            如果您可以使用 bisect 来代替元组...

            import bisect
            
            offset = 0
            data = 1
            test_data = [
                (0, 1500),
                (1270, 120),
                (2117, 30),
                (4055, 30000),
            ]
            
            i = bisect.bisect(test_data, (1900,0))
            test_data.insert(i, (1900,0))
            print(test_data[i][data])
            

            虽然因为元组是按“字典顺序”(从左到右)进行比较,直到一个元素不等于另一个元素 - 您必须考虑这是否是所需的行为

            >>> bisect.insort(test_data, (2117,29))
            >>> print(test_data)
            [(0, 1500), (1270, 120), (2117, 29), (2117, 30), (4055, 30000)]
            

            【讨论】:

              猜你喜欢
              • 2021-11-17
              • 1970-01-01
              • 1970-01-01
              • 2021-12-14
              • 1970-01-01
              • 2019-01-03
              • 2016-12-27
              • 2021-12-24
              相关资源
              最近更新 更多