我想为稀疏数据做一种字典类。这个想法是,如果键不在字典中,找到具有较低值的最接近的键(我的字典的键总是正整数,最低值始终为零)
这是我的原型
class specialdict(dict):
def __getitem__(self, beg):
try:
return self[beg - 1]
except:
return dict.__getitem__(self, beg)
a = specialdict([(10, True)])
print a[137]
print a[500]
这样可行,但最多只能达到338
我想它与递归有关,但我虽然在python中的递归数量更高。我还需要查找速度非常快......
我做错了什么,有更好的方法吗?
感谢
编辑:
一个例子:
如果我只有钥匙" 0"," 10"和" 15",并搜索关键" 13",我希望 getitem 函数给我对应 getitem (10)
如果我想要钥匙" 100"应该得到 getitem (15))的值。
编辑2
我没有特别需要将其作为字典,或 getitem 函数进行递归。但我觉得这将是最快的方式。
编辑3
我尝试了@gilland @ thomas-lotze提出的所有3种解决方案:
import bisect
class SparseData(object):
def __init__(self, pairs=()):
if pairs:
indexes, values = zip(*pairs)
self.indexes = list(indexes)
self.values = list(values)
else:
self.indexes = []
self.values = []
def __setitem__(self, index, value):
i = bisect.bisect(self.indexes, index)
self.indexes.insert(i, index)
self.values.insert(i, value)
def __getitem__(self, index):
i = bisect.bisect(self.indexes, index)
if not i:
raise IndexError(i)
return self.values[i-1]
class specialdict_rec(dict):
def __getitem__(self, beg):
try:
return dict.__getitem__(self, beg)
except KeyError:
return self[beg - 1]
class specialdict_non_rec(dict):
def __getitem__(self, beg):
while beg >= 0:
try:
return dict.__getitem__(self, beg)
except KeyError:
beg -= 1
这里是基准的结果:
In [1]: a = [(1, '1'), (7, '7'), (100, '100')]
In [2]: a1 = SparseData(a)
In [3]: a2 = specialdict_rec(a)
In [4]: a3 = specialdict_non_rec(a)
In [5]: %timeit -n10000 a1[200]
10000 loops, best of 3: 1.12 µs per loop
In [6]: %timeit -n10000 a2[200]
10000 loops, best of 3: 96 µs per loop
In [7]: %timeit -n10000 a3[200]
10000 loops, best of 3: 58.6 µs per loop
因此,递归并没有改善任何事情,正如@gill所说,这很危险。
但最后我要使用的解决方案是来自@ thomas-lotze的解决方案。 非常感谢你的答案!
答案 0 :(得分:3)
这将实现您的狭隘目标(假设密钥为int
并且不会小于0
),但就像@JustinR在评论中所说的那样,可能会有更好的解决方案问题
class specialdict(dict):
def __getitem__(self, beg):
while beg >= 0:
try:
return dict.__getitem__(self, beg)
except KeyError:
beg -= 1
修改
只是为了展示如何递归地执行相同的操作(因为OP问),但这是非常不推荐的。有递归限制。正如其他人所说,二元搜索更有效率。
class specialdict(dict):
def __getitem__(self, beg):
try:
return dict.__getitem__(self, beg)
except KeyError:
return self[beg - 1]
编辑2
改进@ ThomasLotze的答案,以下是在保持bisect
界面时如何包装dict
:
import bisect
class SpecialDict(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self._keys = sorted(self.keys()) # maintain a sorted list of keys
def __setitem__(self, key, value):
if key not in self:
bisect.insort(self._keys, key)
dict.__setitem__(self, key, value)
def __getitem__(self, key):
if key not in self:
try:
key = self._keys[bisect.bisect(self._keys, key) - 1]
except IndexError:
raise KeyError(key)
return dict.__getitem__(self, key)
答案 1 :(得分:1)
对于速度,使用the bisect
module处理(键,值)对列表而不是字典:
import bisect
class SparseData(object):
def __init__(self, pairs=()):
if pairs:
indexes, values = zip(*pairs)
self.indexes = list(indexes)
self.values = list(values)
else:
self.indexes = []
self.values = []
def __setitem__(self, index, value):
i = bisect.bisect(self.indexes, index)
if self.indexes[i-1] == index:
self.values[i-1] = value
else:
self.indexes.insert(i, index)
self.values.insert(i, value)
def __getitem__(self, index):
i = bisect.bisect(self.indexes, index)
if not i:
raise IndexError(i)
return self.values[i-1]
>>> x = SparseData([(1, '1'), (2, '2'), (4, '4')])
>>> x[0]
Traceback (most recent call last):
...
IndexError: 0
>>> x[3]
'2'
>>> x[4]
'4'
>>> x[27] = '27'
>>> x[25]
'4'
>>> x[27]
'27'
>>> x[29]
'27'