web-dev-qa-db-fra.com

Mappage d'une plage de valeurs à une autre

Je cherche des idées sur la façon de traduire des valeurs d'une plage en une autre en Python. Je travaille sur un projet matériel et je lis les données d'un capteur qui peut renvoyer une plage de valeurs, j'utilise ensuite ces données pour piloter un actionneur qui nécessite une plage de valeurs différente.

Par exemple, disons que le capteur renvoie des valeurs comprises entre 1 et 512, et que l'actionneur est piloté par des valeurs comprises entre 5 et 10. Je voudrais une fonction permettant de transmettre une valeur et les deux plages et de récupérer la valeur mappé à la deuxième plage. Si une telle fonction était nommée translate, elle pourrait être utilisée comme ceci:

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

Dans cet exemple, je m'attendrais à ce que la sortie actuator_value être 7.5 depuis le sensor_value est au milieu de la plage d'entrée possible.

53
Tendayi Mawushe

Une solution serait:

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)

Vous pourriez éventuellement utiliser l'algèbre pour la rendre plus efficace, au détriment de la lisibilité.

81
Adam Luchjenbroers

Utilisation de scipy.interpolate.interp1d

Vous pouvez aussi utiliser scipy.interpolate package pour effectuer de telles conversions (si cela ne vous dérange pas la dépendance à SciPy):

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

ou pour le reconvertir en flottant normal à partir d'un tableau scipy de rang 0:

>>> float(m(256))
7.4951076320939336

Vous pouvez également effectuer plusieurs conversions en une seule commande facilement:

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

En prime, vous pouvez effectuer des mappages non uniformes d'une plage à une autre, par exemple si vous souhaitez mapper [1,128] à [1,10], [128,256] à [10,90] et [256,512] à [90,100 ] vous pouvez le faire comme ceci:

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

interp1d crée des objets d'interpolation linéaire par morceaux (qui peuvent être appelés comme des fonctions).

Utilisation de numpy.interp

Comme indiqué par ~ unutb, numpy.interp est également une option (avec moins de dépendances):

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

Ce serait en fait un bon cas pour créer une fermeture, c'est-à-dire écrire une fonction qui renvoie une fonction. Étant donné que vous avez probablement un grand nombre de ces valeurs, il y a peu de valeur à calculer et recalculer ces plages de valeurs et facteurs pour chaque valeur, ni d'ailleurs, à passer ces limites min/max tout le temps.

Essayez plutôt ceci:

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

Vous pouvez maintenant écrire votre processeur comme:

# 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]
17
PaulMcG
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
inspectorG4dget

Je cherchais la même chose dans python pour mapper les angles 0-300deg aux valeurs de dynamixel brutes 0-1023 ou 1023-0 selon les orientations de l'actionneur.

J'ai fini par être très simple.

Variables:

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

Une fonction:

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)
2
James Cann
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]

trouvé à https://rosettacode.org/wiki/Map_range#Python_

  • ne fixe pas les valeurs transformées aux plages a ou b (elle extrapole)
  • fonctionne également lorsque from_lower > from_upper ou to_lower > to_upper
1
Joe