【问题标题】:Rounding an array to values given in another array将数组舍入为另一个数组中给定的值
【发布时间】:2015-01-08 13:32:25
【问题描述】:

假设我有一个数组:

values = np.array([1.1,2.2,3.3,4.4,2.1,8.4])

我想将这些值四舍五入为任意数组的成员,例如:

rounds = np.array([1.,3.5,5.1,6.7,9.2])

理想情况下返回一个四舍五入的数组和一个残差数组:

rounded = np.array([1.,1.,3.5,5.1,1.,9.2])
residues = np.array([-0.1,-1.2,0.2,0.7,-1.1,0.6])

有没有很好的pythonic方式来做到这一点?

【问题讨论】:

  • 你能解释一下什么是rounds数组吗?
  • 一个数字数组,其中包含我希望将第一个数组的元素四舍五入到的值。
  • 我在这里看到的是两个数组之间的减法。从一个数组到另一个数组的舍入是什么意思?
  • 例如values中的2.1最接近rounds中的1.,因此rounded中的对应元素为1.。这不是减法,数组有不同的长度。

标签: python arrays numpy rounding


【解决方案1】:

一个选项是这样的:

>>> x = np.subtract.outer(values, rounds)
>>> y = np.argmin(abs(x), axis=1)

然后roundedresidues 分别是:

>>> rounds[y]
array([ 1. ,  1. ,  3.5,  5.1,  1. ,  9.2])

>>> rounds[y] - values
array([-0.1, -1.2,  0.2,  0.7, -1.1,  0.8])

基本上x 是一个二维数组,由values 中的每个值减去rounds 中的每个值组成。 yx 的每一行的最小绝对值的索引的一维数组。这个y 然后用于索引rounds

我应该提醒这个答案,如果len(values) * len(rounds) 很大(例如开始超过10e8),内存使用可能会开始成为问题。在这种情况下,您可以考虑迭代地构建y,以避免为x 分配大块内存。

【讨论】:

  • 对于更大的数组可能需要一些额外的内存,但非常简洁。
【解决方案2】:

由于rounds 数组中的项目已排序(或者如果不排序),我们可以这样做是O(n logn) 时间使用numpy.searchsorted

from functools import partial

def closest(rounds, x):
   ind = np.searchsorted(rounds, x, side='right')
   length = len(rounds)
   if ind in (0, length) :
      return rounds[ind]
   else:
      left, right = rounds[ind-1], rounds[ind]
      val = min((left, right), key=lambda y:abs(x-y))
      return val

f = partial(closest, rounds)
rounded = np.apply_along_axis(f, 1, values[:,None])[:,0]
residues = rounded - values
print repr(rounded)
print repr(residues)

输出:

array([ 1. ,  1. ,  3.5,  5.1,  1. ,  9.2])
array([-0.1, -1.2,  0.2,  0.7, -1.1,  0.8])

【讨论】:

    【解决方案3】:

    与 Ashwini Chaudhary 的答案相同的时间复杂度,但完全矢量化:

    def round_to(rounds, values):
        # The main speed is in this line
        I = np.searchsorted(rounds, values)
    
        # Pad so that we can index easier
        rounds_p = np.pad(rounds, 1, mode='edge')
    
        # We have to decide between I and I+1
        rounded = np.vstack([rounds_p[I], rounds_p[I+1]])
        residues = rounded - values
        J = np.argmin(np.abs(residues), axis=0)
    
        K = np.arange(len(values))
        return rounded[J,K], residues[J,K]
    

    【讨论】:

      【解决方案4】:

      找出最接近的 x 轮数:

      def findClosest(x,rounds):
          return rounds[np.argmin(np.absolute(rounds-x))]
      

      遍历所有值:

      rounded = [findClosest(x,rounds) for x in values]
      residues = values - rounded
      

      这是一种简单的方法,但是您可以更有效地使用您的 rounds 数组是有序的。

      def findClosest(x,rounds):
          for n in range(len(rounds)):
              if x > rounds[n]:
                  if n == 0:
                      return rounds[n]
                  elif rounds[n]-x > x-rounds[n-1]:
                      return rounds[n-1]
                  else:
                      return rounds[n]  
      
              return rounds[-1]
      

      这可能比 argmin 方法快,但不一定比 argmin 方法快,因为你会浪费时间使用 python for 循环,但你不必检查整个 rounds 数组。

      【讨论】:

        【解决方案5】:

        选择的答案已经很好了。对于那些不一定习惯于更复杂的列表理解的人来说,这似乎令人费解,但如果您熟悉它,它实际上非常清楚 (IMO)。

        够有趣,这恰好比选择的答案跑得快。为什么numPy版本会比这个慢?嗯...)

        values = np.array([1.1,2.2,3.3,4.4,2.1,8.4])
        rounds = np.array([1.,3.5,5.1,6.7,9.2])
        
        rounded, residues = zip(*[
            [
                (rounds[cIndex]),
                (dists[cIndex])
            ]
            for v in values
            for dists in [[r-v for r in rounds]]
            for absDists in [[abs(d) for d in dists]]
            for cIndex in [absDists.index(min(absDists))]
        ])
        
        print np.array(rounded)
        print np.array(residues)
        

        【讨论】:

          猜你喜欢
          • 2017-07-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-09
          • 1970-01-01
          • 2017-09-09
          • 1970-01-01
          • 2015-12-29
          相关资源
          最近更新 更多