2015-08-18 123 views
1

我是一名初学者,使用Python并试图在字典中使用搜索功能来搜索具有点坐标(2)的numpy数组的键。所以,我想要的是:一个字典,其键是numpy数组,其值是整数。然后使用in运算符来比较使用某种容差度量(numpy.allclose函数)的键。据我所知,numpy数组并不是必须的,因此我必须重写getitemsetitem函数(基于我在How to properly subclass dict and override __getitem__ & __setitem__中找到的内容)。但是,如何让这些哈希值可以在字典中添加为键?如何覆盖这种情况下in运算符的行为?重写字典行为Python3

感谢您的帮助!

+0

由于您不是该类型的作者,因此无法使numpy数组变为可散列。你可以创建一个从numpy数组继承而来的子类型,尽管如此。 – poke

+0

你能告诉我们更多关于你想用这个数据结构实现的内容吗?我敢打赌,有一个更好的方式来做到这一点与numpy。 – swenzel

+0

我需要检查一下我试图添加的键(numpy数组)是否已经在所述数组中,并且只添加该词典条目,如果不是。 –

回答

0

而不是一个numpy数组,使用浮点数的2元组作为关键。元组是可变的,因为它们是不可变的。

Python字典在后台使用hash-table来快速进行密钥查找。

写一个closeto函数并不难;

def closeto(a, b, limit=0.1): 
    x, y = a 
    p, q = b 
    return (x-p)**2 + (y-q)**2 < limit**2 

这可以用来找到接近的点。但是,你必须迭代所有的密钥,因为密钥查找是确切的。但是如果你在理解中做了这样的迭代,它比它的循环快得多。

测试(在IPython中,使用Python 3):

In [1]: %cpaste 
Pasting code; enter '--' alone on the line to stop or use Ctrl-D. 
: def closeto(a, b, limit=0.1): 
:  x, y = a 
:  p, q = b 
:  return (x-p)**2 + (y-q)**2 < limit**2 
:-- 

In [2]: d = {(0.0, 0.0): 12, (1.02, 2.17): 32, (2.0, 4.2): 23} 

In [3]: {k: v for k, v in d.items() if closeto(k, (1.0, 2.0), limit=0.5)} 
Out[3]: {(1.02, 2.17): 32} 
+0

我需要使用numpy数组,所以我不能改变它。 –

+0

@GonçaloValente从一个numpy数组转换为一个元组很简单... –

+0

看来,为了找到一个对象是否在这个字典中,你必须经历所有这些对象,而不是使用字典来有快速访问?是不是有一个聪明的方法来散列一个numpy数组? –

0

转换的数组元组,其中可哈希:

In [18]: a1 = np.array([0.5, 0.5]) 

In [19]: a2 = np.array([1.0, 1.5]) 

In [20]: d = {} 

In [21]: d[tuple(a1)] = 14 

In [22]: d[tuple(a2)] = 15 

In [23]: d 
Out[23]: {(0.5, 0.5): 14, (1.0, 1.5): 15} 

In [24]: a3 = np.array([0.5, 0.5]) 

In [25]: a3 in d 
--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
<ipython-input-25-07c81d61b999> in <module>() 
----> 1 a3 in d 

TypeError: unhashable type: 'numpy.ndarray' 

In [26]: tuple(a3) in d 
Out[26]: True 

不幸的是,因为你想申请一个宽容比较,你没有太多的选择,只能迭代所有寻找“关闭”匹配的键,不管你是作为函数还是联机来实现。

+0

所以这将工作,除了我需要比较成员使用allcose numpy函数,该函数在容差值内测试数组。必须有一个更聪明的方法来做到这一点。重写__contains__函数可以避免在询问a3是否在d时显示TypeError? –

+0

事实上,无论你如何实现它,你将不得不将'allclose'应用于新密钥和所有现有密钥,直到找到足够近的密钥或运行完毕(如果找不到匹配结果)。 – holdenweb

+0

但使用字典的要点是能够快速找到元素,不是吗?我想在常量时间内使用散列函数而不是线性时间来找到它。 –

1

Numpy数组不可哈希,但元组是。所以你可以散列数组,如果你把它变成一个元组。从理论上讲,如果您事先将其舍入,则可以利用快速查找,因为您现在有离散点。但是在翻译过程中你会得到解决的问题,因为舍入是用十进制来完成的,但是数字是以二进制存储的。可以通过将其转换为缩放整数来规避这种情况,但会使所有内容都变慢。

最后,你只需要编写一个类,它可以在数组和元组之间来回转换,而且你很好。
实现可能是这样的:

import numpy as np 

class PointDict(dict): 

    def __init__(self, precision=5): 
     super(PointDict, self).__init__() 
     self._prec = 10**precision 

    def decode(self, tup): 
     """ 
     Turns a tuple that was used as index back into a numpy array. 
     """ 
     return np.array(tup, dtype=float)/self._prec 

    def encode(self, ndarray): 
     """ 
     Rounds a numpy array and turns it into a tuple so that it can be used 
     as index for this dict. 
     """ 
     return tuple(int(x) for x in ndarray*self._prec) 

    def __getitem__(self, item): 
     return self.decode(super(PointDict, self).__getitem__(self.encode(item))) 

    def __setitem__(self, item, value): 
     return super(PointDict, self).__setitem__(self.encode(item), value) 

    def __contains__(self, item): 
     return super(PointDict, self).__contains__(self.encode(item)) 

    def update(self, other): 
     for item, value in other.items(): 
      self[item] = value 

    def items(self): 
     for item in self: 
      yield (item, self[item]) 

    def __iter__(self): 
     for item in super(PointDict, self).__iter__(): 
      yield self.decode(item) 

当寻找了很多分,与量化批量写入/查找纯numpy的解决方案可能会更好。然而,这个解决方案很容易理解和实现。

+0

要小心任何包含短语“你只需要”的答案...... :) – holdenweb

+0

你能告诉我你的意思是“向量化批量写/查找”吗?我在哪里可以阅读关于此的内容?谢谢! –

+0

@GonçaloValente我的意思是你在同一时间查找或更新多个点。当你可以矢量化操作时,Numpy是最快的,这意味着对数组进行索引并一次赋值或更改多个条目。这里的问题将是设计一个聪明的方式来保存项目及其值,以便我们可以利用它。你不会得到O(1)查找,但O(log(n))可能是可能的... – swenzel