【问题标题】:Populate a dict with missing values用缺失值填充字典
【发布时间】:2015-03-20 05:21:54
【问题描述】:

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? (它将在字典中提供 16384 个键,所以这对我的应用程序来说没问题)。

例如,我想要b[73, 40] = b[70, 45] = 41,即二维平面中的最近邻。

这是我尝试过的:

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 循环,我们在其中再次循环所有元素以计算距离!

我们如何以更有效的方式填充缺失值和最近邻的字典?

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

    您正在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(n^4)下降到O(k * n^2),其中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]]
    

    【讨论】:

    • 如果你只有几个初始值,这是一个 O(n^2) 解决方案。
    • 我有类似 k=2000 的初始值,最后会有 16384 个值 b[i,j]...我在我的 RaspPi 上做了一些测试,O(k*n²) 是还是太大了(运行4秒,应该
    【解决方案2】:

    字典绝对不适合这种用途 - 除非您对 O(n) 的复杂性感到满意(然后,使用列表会更清楚)。可以想象,a class of hashing functions 可以用来实现适当的“字典”——但 python 的 dict 绝对不能胜任这项任务。

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

    您可能想查看专门针对nearest neighbor search 的维基百科文章


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

    【讨论】:

    • 一旦字典将填充b[i,j] for all1 <= i <= 1271 <= j <= 127,那么就不会再有问题了。所以唯一的问题是,一次性填充字典
    • @Basj 确实 - 正如你所发现的,问题在于用 all 值填充 dict 并不实用 - 因为维数呈指数增长。您正在尝试通过执行额外的O(n^2) 步骤来解决O(1) 中的O(n) 问题:)
    • 也许你是对的@goncalopp,但实际上,我更喜欢 1) 在我的程序开始时使用O(n^2) 一次,然后每次我都使用O(1)必须访问数据(每秒数百次),然后 2) 没有加载,然后每次我必须访问数据时 O(n)
    • @Basj 听起来很合适。在这种情况下,您在问题中提出的解决方案与您将获得的一样好。请注意,如果您实现缓存,则无需使用所有值填充dict,如果您得到重复查询,您仍然会获得O(1) 性能(如果您没有得到重复查询,首先构建字典根本没有意义!除非您有严格的时间要求,例如在实时系统中)
    【解决方案3】:

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

    完整示例:

    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.
    

    【讨论】:

      猜你喜欢
      • 2012-10-25
      • 1970-01-01
      • 1970-01-01
      • 2022-01-12
      • 1970-01-01
      • 2013-06-22
      • 1970-01-01
      相关资源
      最近更新 更多