填充缺少值的字典

时间:2015-03-17 11:48:46

标签: python dictionary

b成为具有某些值的词典:

 b = {}  
 b[90, 1] = 100, b[90, 55] = 101, b[90, 127] = 102
 b[70, 1] = 40, b[70, 45] = 41, b[70, 107] = 42

如何一次性填充dict,其缺失值为最近邻居,例如0 <= i <= 1270 <= j <= 127? (它将在dict中提供16384个键,这样我的应用程序就可以了。)

作为一个例子,我想b[73, 40] = b[70, 45] = 41,即2D平面中最近的邻居。

以下是我的尝试:

for i in range(127):
  for j in range(127):
    closest_key = min(b.keys(), key=lambda c: (c[0] - i) ** 2 + (c[1] - j) ** 2)
    b[i, j] = b[closest_key]

但它很慢可能因为有127 * 127个循环,我们在所有元素上再循环一次以计算距离!

如何以更有效的方式使用最近邻居填充缺少值的字典?

3 个答案:

答案 0 :(得分:2)

您正在b内搜索最近的密钥。但b不仅包含原始键,还包含每次迭代时输入的新键。只需检查初始键,就会更快,更正确:

initial_keys = set(b.keys())
for i in xrange(127):
    for j in xrange(127):
        if (i, j) not in initial_keys:
            closest_key = min(
                initial_keys, key=lambda c: (c[0] - i) ** 2 + (c[1] - j) ** 2
            )
            b[i, j] = b[closest_key]

这样算法的运行时间从O(k * n^2)降至O(n^4),其中n是维度大小,k是初始键的数量。

编辑:

您可以使用numpy来提高速度:

import numpy as np

s = set(b.keys())
x = np.array([k[0] for k in s])
y = np.array([k[1] for k in s])
for i in xrange(128):
    for j in xrange(128):
        if (i, j) not in s:
            argmin = np.argmin((x - i) ** 2 + (y - j) ** 2)
            b[i, j] = b[x[argmin], y[argmin]]

答案 1 :(得分:1)

字典绝对不适合此类用途 - 除非您对O(n)复杂性感到满意(然后,使用列表会更清楚)。可以想象,a class of hashing functions可用于实施适当的&#34;字典&#34; - 但是python&#39; dict绝对不能胜任这项任务。

如果您需要适当的性能,您需要使用其他数据结构。最简单的是K-d tree 。有一个实现inside scipy

您可能需要查看专门针对nearest neighbor search

的维基百科文章

当然,如果你反复查询相同的值,你可以使用字典作为缓存(如Raydel Miranda的回答)。但是将它用作缓存 - 而不是用于存储/查询实际数据!

答案 2 :(得分:0)

您可以尝试按需计算,并使用结果构建缓存。这种方法的优点是,如果你不需要使用某些点,它将永远无法计算。

完整示例:

b = {}  
b[90, 1] = 100
b[90, 55] = 101
b[90, 127] = 102
b[70, 1] = 40
b[70, 45] = 41
b[70, 107] = 42

class NeighbourDist:

  def __init__(self, source_dict):
    # Original dict.
    self.__source_dict = source_dict
    # Dict used for cache.
    self.__cache_dict = {}


  def __calculate_distance(self, x0, x1, y0, y1):
    """ Calculate distance beetwen two points. """
    dx = x1 - x0
    dy = y1 - y0
    d = (dx**2 + dy**2)**0.5
    return d

  def __getitem__(self, key):
    """
    Look for the key in the cached dict, if not has been calculated yet
    then proceed to calculate it.
    Return the result and store in __cache_dict.
    """
    cached = self.__cache_dict.get(key)
    if cached is not None:
      return cached
    else:
      x0, y0 = key
      min_n = 0
      min_  = 1e100
      for (x1, y1) in self.__source_dict.keys():
        dist = self.__calculate_distance(x0, x1, y0, y1)
        if min_ > dist:
          min_ = dist
          min_n = self.__source_dict[x1, y1]
      self.__cache_dict[key] = min_n
      return min_n

if '__main__' == __name__:
  d = NeighbourDist(b)
  print(d[73, 40]) # >>> 41
  print(d[73, 40]) # >>> 41, Second time the result is obtained from the cached dict.