Отображение диапазона значений для другого
Я ищу идеи о том, как перевести значения одного диапазона в другой в Python. Я работаю над аппаратным проектом и считываю данные с датчика, который может возвращать диапазон значений. Затем я использую эти данные для управления приводом, для которого требуется другой диапазон значений.
Например, скажем, что датчик возвращает значения в диапазоне от 1 до 512, а привод управляется значениями в диапазоне от 5 до 10. Я хотел бы, чтобы функция могла передавать значение и два диапазона и получать верните значение, отображаемое во второй диапазон. Если такая функция была названа translate
, ее можно было бы использовать следующим образом:
sensor_value = 256
actuator_value = translate(sensor_value, 1, 512, 5, 10)
В этом примере я ожидал бы, что вывод actuator_value
будет 7.5
, так как sensor_value
находится в середине возможного диапазона ввода.
Ответы
Ответ 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)
Вы могли бы использовать алгебру, чтобы сделать ее более эффективной, за счет удобочитаемости.
Ответ 2
Использование scipy.interpolate.interp1d
Вы также можете использовать пакет scipy.interpolate
для выполнения таких преобразований (если вы не против зависимости от SciPy):
>>> from scipy.interpolate import interp1d
>>> m = interp1d([1,512],[5,10])
>>> m(256)
array(7.4951076320939336)
или преобразовать его обратно в обычный float из 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
создает кусочно-линейные объекты интерполяции (которые вызываются как функции).
Использование numpy.interp
Как отмечено ~ unutbu, numpy.interp
также является опцией (с меньшими зависимостями):
>>> from numpy import interp
>>> interp(256,[1,512],[5,10])
7.4951076320939336
Ответ 3
Это действительно хороший пример для создания замыкания, то есть запись функции, возвращающей функцию. Поскольку у вас, вероятно, есть многие из этих значений, мало что нужно для вычисления и пересчета этих значений и коэффициентов значения для каждого значения и, при этом, для ограничения этих пределов min/max все время.
Вместо этого попробуйте следующее:
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
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
Ответ 5
Я искал одно и то же в 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)
Ответ 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_
- не привязывает преобразованные значения к указанному диапазону (экстраполирует)
- также работает, когда
from_lower > from_upper
или to_lower > to_upper