【问题标题】:Mapping a range of values to another将一系列值映射到另一个值
【发布时间】:2009-12-28 12:24:01
【问题描述】:

我正在寻找有关如何在 Python 中将一个范围值转换为另一个范围值的想法。我正在从事硬件项目,正在从可以返回一系列值的传感器读取数据,然后我使用该数据来驱动需要不同值范围的执行器。

例如,假设传感器返回 1 到 512 范围内的值,而执行器由 5 到 10 范围内的值驱动。我想要一个可以传递一个值和两个范围并得到的函数返回映射到第二个范围的值。如果这样的函数被命名为translate,它可以这样使用:

sensor_value = 256
actuator_value = translate(sensor_value, 1, 512, 5, 10)

在此示例中,我希望输出 actuator_value7.5,因为 sensor_value 位于可能输入范围的中间。

【问题讨论】:

  • 感谢大家的回答,我接受了 Adam Luchjenbroers 的回答,因为它与我的想法非常吻合,没有为相对简单的任务引入第三方库。

标签: python algorithm


【解决方案1】:

一种解决方案是:

def translate(value, leftMin, leftMax, rightMin, rightMax):
    # Figure out how 'wide' each range is
    leftSpan = leftMax - leftMin
    rightSpan = rightMax - rightMin

    # Convert the left range into a 0-1 range (float)
    valueScaled = float(value - leftMin) / float(leftSpan)

    # Convert the 0-1 range into a value in the right range.
    return rightMin + (valueScaled * rightSpan)

您可以使用代数来提高效率,但会牺牲可读性。

【讨论】:

  • @Adam 谢谢你的回答。我遇到了同样的问题,我花了一些时间才找到正确的解决方案。然后我想用我的解决方案在 sf 上发布新问题,并询问是否有可能以更有效的方式解决它。你能用代数来说明一下解决方案吗?
  • 很好的解决方案。一项建议是在代码中使用“to”和“from”而不是“left”和“right”,这可能会造成混淆。
  • @catalyst294 - 必须使用from_,因为from 是Python 中的保留字。
  • Python Zen:“显式优于隐式”。我怀疑你会看到速度的大幅提升,所以不妨保持漂亮!
【解决方案2】:

使用 scipy.interpolate.interp1d

您也可以使用scipy.interpolate 包进行此类转换(如果您不介意依赖 SciPy):

>>> from scipy.interpolate import interp1d
>>> m = interp1d([1,512],[5,10])
>>> m(256)
array(7.4951076320939336)

或将其从 0-rank scipy 数组转换回普通浮点数:

>>> float(m(256))
7.4951076320939336

您还可以在一个命令中轻松进行多次转换:

>>> m([100,200,300])
array([ 5.96868885,  6.94716243,  7.92563601])

作为奖励,您可以进行从一个范围到另一个范围的非均匀映射,例如,如果您想将 [1,128] 映射到 [1,10]、[128,256] 到 [10,90] 和 [256,512]到 [90,100] 你可以这样做:

>>> m = interp1d([1,128,256,512],[1,10,90,100])
>>> float(m(400))
95.625

interp1d 创建分段线性插值对象(可以像函数一样调用)。

使用 numpy.interp

正如 ~unutbu 所述,numpy.interp 也是一个选项(依赖较少):

>>> from numpy import interp
>>> interp(256,[1,512],[5,10])
7.4951076320939336

【讨论】:

  • 你也可以使用numpy.interp(256,[1,512],[5,10]),减少对numpy的依赖。
  • 要将array([ 5.96868885, 6.94716243, 7.92563601]) 转换为列表,请使用m([100,200,300]).tolist()
  • 另外,scipy.interpolate.interp1d 可能比numpy.interp 慢得多。 (请参阅this question。)如果您关心性能,请先对您的代码进行基准测试。
  • 对于非统一插值,我想将 0 和 2 之间的数字映射到 -15 和 1,并将高于 2 和 6 的数字映射到 15 和 1。这可能吗?我试过interp1d( [0,2,2.1,6],[-15,1,15,1]) 但我收到错误:x_new 中的值高于插值范围。
  • 此外,是否也可以进行非线性插值,例如指数或平方插值?
【解决方案3】:

这实际上是创建闭包的好案例,即编写一个返回函数的函数。由于您可能拥有许多这些值,因此为每个值计算和重新计算这些值跨度和因子几乎没有价值,而且始终超过这些最小/最大限制。

试试这个:

def make_interpolater(left_min, left_max, right_min, right_max): 
    # Figure out how 'wide' each range is  
    leftSpan = left_max - left_min  
    rightSpan = right_max - right_min  

    # Compute the scale factor between left and right values 
    scaleFactor = float(rightSpan) / float(leftSpan) 

    # create interpolation function using pre-calculated scaleFactor
    def interp_fn(value):
        return right_min + (value-left_min)*scaleFactor

    return interp_fn

现在你可以把你的处理器写成:

# create function for doing interpolation of the desired
# ranges
scaler = make_interpolater(1, 512, 5, 10)

# receive list of raw values from sensor, assign to data_list

# now convert to scaled values using map 
scaled_data = map(scaler, data_list)

# or a list comprehension, if you prefer
scaled_data = [scaler(x) for x in data_list]

【讨论】:

  • 我喜欢这个答案,因为它不会将转换后的值限制在指定的最小值/最大值,而是允许它们“越界”。
【解决方案4】:

我在 python 中寻找相同的东西来将角度 0-300deg 映射到原始 dynamixel 值 0-1023 或 1023-0,具体取决于执行器方向。

我最终变得非常简单。

变量:

x:input value; 
a,b:input range
c,d:output range
y:return value

功能:

def mapFromTo(x,a,b,c,d):
   y=(x-a)/(b-a)*(d-c)+c
   return y

用法:

dyn111.goal_position=mapFromTo(pos111,0,300,0,1024)

【讨论】:

    【解决方案5】:
    def translate(sensor_val, in_from, in_to, out_from, out_to):
        out_range = out_to - out_from
        in_range = in_to - in_from
        in_val = sensor_val - in_from
        val=(float(in_val)/in_range)*out_range
        out_val = out_from+val
        return out_val
    

    【讨论】:

      【解决方案6】:
      def maprange(a, b, s):
          (a1, a2), (b1, b2) = a, b
          return  b1 + ((s - a1) * (b2 - b1) / (a2 - a1))
      
      
      a = [from_lower, from_upper]
      b = [to_lower, to_upper]
      

      发现于https://rosettacode.org/wiki/Map_range#Python_

      • 不会将转换后的值限制在ab 的范围内(它会外推)
      • 也适用于from_lower > from_upperto_lower > to_upper

      【讨论】:

        【解决方案7】:

        简单的地图范围函数:

        def mapRange(value, inMin, inMax, outMin, outMax):
            return outMin + (((value - inMin) / (inMax - inMin)) * (outMax - outMin))
        

        【讨论】:

          猜你喜欢
          • 2015-08-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多