将一系列值映射到另一个值

我正在寻找关于如何在 Python 中将一个范围值转换为另一个范围值的想法。我的工作在硬件项目和读取数据从传感器,可以返回一个范围的价值,然后我使用这些数据驱动器,需要一个不同的价值范围。

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

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

在这个例子中,我希望输出 actuator_value7.5,因为 sensor_value在可能的输入范围的中间。

131509 次浏览

One solution would be:

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)

You could possibly use algebra to make it more efficient, at the expense of readability.

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

Using scipy.interpolate.interp1d

You can also use scipy.interpolate package to do such conversions (if you don't mind dependency on SciPy):

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

or to convert it back to normal float from 0-rank scipy array:

>>> float(m(256))
7.4951076320939336

You can do also multiple conversions in one command easily:

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

As a bonus, you can do non-uniform mappings from one range to another, for intance if you want to map [1,128] to [1,10], [128,256] to [10,90] and [256,512] to [90,100] you can do it like this:

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

interp1d creates piecewise linear interpolation objects (which are callable just like functions).

Using numpy.interp

As noted by ~unutbu, numpy.interp is also an option (with less dependencies):

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

This would actually be a good case for creating a closure, that is write a function that returns a function. Since you probably have many of these values, there is little value in calculating and recalculating these value spans and factors for every value, nor for that matter, in passing those min/max limits around all the time.

Instead, try this:

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

Now you can write your processor as:

# 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]

I was looking for the same thing in python to map angles 0-300deg to raw dynamixel values 0-1023, or 1023-0 depending on the actuator orientations.

I ended up going very simple.

Variables:

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

Function:

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

Usage:

dyn111.goal_position=mapFromTo(pos111,0,300,0,1024)
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]

found at https://rosettacode.org/wiki/Map_range#Python_

  • does not clamp the transformed values to the ranges a or b (it extrapolates)
  • also works when from_lower > from_upper or to_lower > to_upper

Simple map range function:

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