我正在寻找有关如何在Python中将一个范围值转换为另一个范围值的想法。我正在研究硬件项目,并且正在读取可以返回一系列值的传感器的数据,然后我使用该数据驱动需要不同范围值的执行器。
例如,假设传感器返回1到512范围内的值,并且执行器由5到10范围内的值驱动。我想要一个函数,我可以传递一个值和两个范围并得到返回映射到第二个范围的值。如果这样的函数名为translate
,则可以像这样使用:
sensor_value = 256
actuator_value = translate(sensor_value, 1, 512, 5, 10)
在此示例中,我希望输出actuator_value
为7.5
,因为sensor_value
位于可能的输入范围的中间。
答案 0 :(得分:81)
一种解决方案是:
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)
您可以使用代数来提高效率,但会牺牲可读性。
答案 1 :(得分:68)
您还可以使用scipy.interpolate
包进行此类转换(如果您不介意依赖SciPy):
>>> from scipy.interpolate import interp1d
>>> m = interp1d([1,512],[5,10])
>>> m(256)
array(7.4951076320939336)
或将其从0级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
创建分段线性插值对象(可以像函数一样调用)。
如 ~unutbu 所述,numpy.interp
也是一个选项(具有较少的依赖性):
>>> from numpy import interp
>>> interp(256,[1,512],[5,10])
7.4951076320939336
答案 2 :(得分:17)
这实际上是创建闭包的好例子,即写一个返回函数的函数。由于您可能拥有许多这些值,因此计算和重新计算每个值的这些值跨度和因子几乎没有什么价值,也没有任何价值,无论是在所有时间内传递这些最小/最大限制。
相反,试试这个:
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]
答案 3 :(得分: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
答案 4 :(得分:2)
我在python中寻找相同的东西,将角度0-300deg映射到原始动态像素值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 :(得分:1)
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_
a
或b
的范围内(而是推断)from_lower > from_upper
或to_lower > to_upper
时也可以使用答案 6 :(得分:0)
简单的地图范围函数:
def mapRange(value, inMin, inMax, outMin, outMax):
return outMin + (((value - inMin) / (inMax - inMin)) * (outMax - outMin))