2009-05-30 29 views
177

Sto provando a convertire un intervallo di numeri in un altro, mantenendo il rapporto. La matematica non è il mio punto forte.Convertire un intervallo di numeri in un altro intervallo, mantenendo il rapporto

Possiedo un file di immagine in cui i valori dei punti possono variare da -16000,00 a 16000,00 sebbene l'intervallo tipico possa essere molto inferiore. Quello che voglio fare è comprimere questi valori nell'intervallo intero 0-100, dove 0 è il valore del punto più piccolo e 100 è il valore del più grande. Tutti i punti in mezzo dovrebbero mantenere un rapporto relativo anche se si sta perdendo un po 'di precisione mi piacerebbe farlo in python ma anche un algoritmo generale dovrebbe essere sufficiente. Preferirei un algoritmo in cui è possibile regolare il minimo/massimo o l'intervallo (ad esempio, il secondo intervallo potrebbe essere compreso tra -50 e 800 anziché tra 0 e 100).

+0

Vi ringrazio entrambi , Sto dando g la risposta al cletus perché è entrato per primo e un +1 in jerry per aver risposto al mio follow-up. – SpliFF

+1

attuale spiacente cleto, lo sto dando a jerry perché è nuovo e ha bisogno dei punti. – SpliFF

+1

Ehi, questo è l'ageismo! Heheh, j/k, non preoccuparti. :) – cletus

risposta

353
NewValue = (((OldValue - OldMin) * (NewMax - NewMin))/(OldMax - OldMin)) + NewMin 

o un po 'più leggibile:

OldRange = (OldMax - OldMin) 
NewRange = (NewMax - NewMin) 
NewValue = (((OldValue - OldMin) * NewRange)/OldRange) + NewMin 

O se si desidera proteggere per il caso in cui il vecchio intervallo è 0 (OldMin = OldMax):

OldRange = (OldMax - OldMin) 
if (OldRange == 0) 
    NewValue = NewMin 
else 
{ 
    NewRange = (NewMax - NewMin) 
    NewValue = (((OldValue - OldMin) * NewRange)/OldRange) + NewMin 
} 

Si noti che in questo caso siamo costretti a scegliere arbitrariamente uno dei possibili nuovi valori di intervallo. A seconda del contesto, scelte ragionevoli potrebbero essere: NewMin (vedi esempio), NewMax o (NewMin + NewMax)/2

+0

oldMax deve essere 16000 o può essere il valore più alto nel vecchio set di punti (ad esempio, 15034.00, ad esempio) la distinzione è importante? – SpliFF

+5

Puoi renderlo quello che vuoi ... tieni presente che potresti ottenere strani risultati se uno degli intervalli è molto piccolo rispetto all'altro (non esattamente sicuro, ma se c'è più di una differenza di fattore di 1000000 tra la dimensione degli intervalli, assicurati che si comporti effettivamente come ti aspetti ... o scopri l'inesattezza in virgola mobile) – jerryjvl

+1

Considerando la popolarità di questa risposta, per un caso più generale dovresti considerare OldMax == OldMin possibilità, che potrebbe risultare in un divisione per zero. – Medorator

49

Questa è una semplice conversione lineare.

new_value = ((old_value - old_min)/(old_max - old_min)) * (new_max - new_min) + new_min 

Quindi, la conversione 10000 sulla scala di -16.000-16.000 ad una nuova scala da 0 a 100 rendimenti:

old_value = 10000 
old_min = -16000 
old_max = 16000 
new_min = 0 
new_max = 100 

new_value = ((10000 - -16000)/(16000 - -16000)) * (100 - 0) + 0 
      = 81.25 
+2

Questo è sbagliato. Devi sottrarre Old Min dal vecchio valore prima della divisione. – SPWorley

+21

Ehm, io sono ...... – cletus

10

c'è una condizione, quando tutti i valori che si sta verificando sono gli stessi, in cui il codice di @ jerryjvl sarebbe tornato NaN.

19

In realtà ci sono alcuni casi in cui le risposte di cui sopra si rompono. Ad esempio un valore di ingresso errato, un intervallo di input errato, intervalli di input/output negativi.

def remap(x, oMin, oMax, nMin, nMax): 

    #range check 
    if oMin == oMax: 
     print "Warning: Zero input range" 
     return None 

    if nMin == nMax: 
     print "Warning: Zero output range" 
     return None 

    #check reversed input range 
    reverseInput = False 
    oldMin = min(oMin, oMax) 
    oldMax = max(oMin, oMax) 
    if not oldMin == oMin: 
     reverseInput = True 

    #check reversed output range 
    reverseOutput = False 
    newMin = min(nMin, nMax) 
    newMax = max(nMin, nMax) 
    if not newMin == nMin : 
     reverseOutput = True 

    portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin) 
    if reverseInput: 
     portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin) 

    result = portion + newMin 
    if reverseOutput: 
     result = newMax - portion 

    return result 

#test cases 
print remap(25.0, 0.0, 100.0, 1.0, -1.0), "==", 0.5 
print remap(25.0, 100.0, -100.0, -1.0, 1.0), "==", -0.25 
print remap(-125.0, -100.0, -200.0, 1.0, -1.0), "==", 0.5 
print remap(-125.0, -200.0, -100.0, -1.0, 1.0), "==", 0.5 
#even when value is out of bound 
print remap(-20.0, 0.0, 100.0, 0.0, 1.0), "==", -0.2 
1

Ho usato questa soluzione in un problema che stavo risolvendo in js, quindi ho pensato di condividere la traduzione. Grazie per la spiegazione e la soluzione.

function remap(x, oMin, oMax, nMin, nMax){ 
//range check 
if (oMin == oMax){ 
    console.log("Warning: Zero input range"); 
    return None; 
}; 

if (nMin == nMax){ 
    console.log("Warning: Zero output range"); 
    return None 
} 

//check reversed input range 
var reverseInput = false; 
oldMin = Math.min(oMin, oMax); 
oldMax = Math.max(oMin, oMax); 
if (oldMin != oMin){ 
    reverseInput = true; 
} 

//check reversed output range 
var reverseOutput = false; 
newMin = Math.min(nMin, nMax) 
newMax = Math.max(nMin, nMax) 
if (newMin != nMin){ 
    reverseOutput = true; 
}; 

var portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin) 
if (reverseInput){ 
    portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin); 
}; 

var result = portion + newMin 
if (reverseOutput){ 
    result = newMax - portion; 
} 

return result; 
} 
+0

grazie! soluzione fantastica e impostata come una funzione pronta all'uso! –

1

C++ Variante

ho trovato soluzione utile di PenguinTD, così ho portato in C++, se qualcuno ha bisogno:

galleggiante rimappare (float x, float omin, galleggiare OMAX, float nMin , float nMax) {

//range check 
if(oMin == oMax) { 
    //std::cout<< "Warning: Zero input range"; 
    return -1; } 

if(nMin == nMax){ 
    //std::cout<<"Warning: Zero output range"; 
    return -1;  } 

//check reversed input range 
bool reverseInput = false; 
float oldMin = min(oMin, oMax); 
float oldMax = max(oMin, oMax); 
if (oldMin == oMin) 
    reverseInput = true; 

//check reversed output range 
bool reverseOutput = false; 
float newMin = min(nMin, nMax); 
float newMax = max(nMin, nMax); 
if (newMin == nMin) 
    reverseOutput = true; 

float portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin); 
if (reverseInput) 
    portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin); 

float result = portion + newMin; 
if (reverseOutput) 
    result = newMax - portion; 

return result; } 
2

Nella lista fornita da PenguinTD, non lo faccio un ignorare perché gli intervalli sono invertiti, funziona senza dover invertire gli intervalli. La conversione dell'intervallo lineare si basa sull'equazione lineare Y=Xm+n, dove m e n derivano dagli intervalli dati. Piuttosto che fare riferimento agli intervalli come min e max, sarebbe meglio fare riferimento a questi come 1 e 2.Così la formula sarà:

Y = (((X - x1) * (y2 - y1))/(x2 - x1)) + y1 

Dove Y=y1 quando X=x1, e Y=y2 quando X=x2. x1, x2, y1 & y2 può essere assegnato a qualsiasi valore positive o negative. Definire l'espressione in una macro lo rende più utile, quindi può essere utilizzato con qualsiasi nome di argomento.

#define RangeConv(X, x1, x2, y1, y2) (((float)((X - x1) * (y2 - y1))/(x2 - x1)) + y1) 

Il float Cast garantirebbe floating point divisione nel caso in cui tutti gli argomenti sono integer valori. A seconda dell'applicazione potrebbe non essere necessario controllare gli intervalli x1=x2 e y1==y2.

+0

Grazie! _here è C# di conversione: _ 'galleggiare RangeConv (float di ingresso, galleggiante x1, galleggiante x2, galleggiante y1, galleggiante y2) { ritorno (((ingresso - x1) * (y2 - y1))/(x2 - x1)) + y1; } ' – Zunair

0

Short-cut/proposta semplificata

NewRange/OldRange = Handy multiplicand or HM 
Convert OldValue in OldRange to NewValue in NewRange = 
(OldValue - OldMin x HM) + NewMin 

Wayne

+0

Cosa significa' NewRange/OldRange' qui? – Zunair

2

PHP Port

Trovato soluzione PenguinTD utile in modo ho portato a PHP. Aiuta te stesso!

/** 
* ===================================== 
*    Remap Range    
* ===================================== 
* - Convert one range to another. (including value) 
* 
* @param int $intValue The value in the old range you wish to convert 
* @param int $oMin  The minimum of the old range 
* @param int $oMax  The maximum of the old range 
* @param int $nMin  The minimum of the new range 
* @param int $nMax  The maximum of the new range 
* 
* @return float $fResult The old value converted to the new range 
*/ 
function remapRange($intValue, $oMin, $oMax, $nMin, $nMax) { 
    // Range check 
    if ($oMin == $oMax) { 
     echo 'Warning: Zero input range'; 
     return false; 
    } 

    if ($nMin == $nMax) { 
     echo 'Warning: Zero output range'; 
     return false; 
    } 

    // Check reversed input range 
    $bReverseInput = false; 
    $intOldMin = min($oMin, $oMax); 
    $intOldMax = max($oMin, $oMax); 
    if ($intOldMin != $oMin) { 
     $bReverseInput = true; 
    } 

    // Check reversed output range 
    $bReverseOutput = false; 
    $intNewMin = min($nMin, $nMax); 
    $intNewMax = max($nMin, $nMax); 
    if ($intNewMin != $nMin) { 
     $bReverseOutput = true; 
    } 

    $fRatio = ($intValue - $intOldMin) * ($intNewMax - $intNewMin)/($intOldMax - $intOldMin); 
    if ($bReverseInput) { 
     $fRatio = ($intOldMax - $intValue) * ($intNewMax - $intNewMin)/($intOldMax - $intOldMin); 
    } 

    $fResult = $fRatio + $intNewMin; 
    if ($bReverseOutput) { 
     $fResult = $intNewMax - $fRatio; 
    } 

    return $fResult; 
} 
+0

Grazie, è stato molto utile. – dearsina

1

Ecco alcune funzioni Python brevi per la vostra copia e incolla facilità, tra cui una funzione per ridimensionare l'intero elenco.

def scale_number(unscaled, to_min, to_max, from_min, from_max): 
    return (to_max-to_min)*(unscaled-from_min)/(from_max-from_min)+to_min 

def scale_list(l, to_min, to_max): 
    return [scale_number(i, to_min, to_max, min(l), max(l)) for i in l] 

che può essere utilizzato in questo modo:

scale_list([1,3,4,5], 0, 100) 

[0.0, 50.0, 75.0, 100.0]

Nel mio caso ho voluto scalare una curva logaritmica, come so:

scale_list([math.log(i+1) for i in range(5)], 0, 50) 

[0.0, 21,533827903669653, 34,130309724299266, 43,06765580733931, 50.0]

0

Io personalmente uso la classe di supporto che sostiene generici (Swift 3 compatibile)

struct Rescale<Type : BinaryFloatingPoint> { 
    typealias RescaleDomain = (lowerBound: Type, upperBound: Type) 

    var fromDomain: RescaleDomain 
    var toDomain: RescaleDomain 

    init(from: RescaleDomain, to: RescaleDomain) { 
     self.fromDomain = from 
     self.toDomain = to 
    } 

    func interpolate(_ x: Type) -> Type { 
     return self.toDomain.lowerBound * (1 - x) + self.toDomain.upperBound * x; 
    } 

    func uninterpolate(_ x: Type) -> Type { 
     let b = (self.fromDomain.upperBound - self.fromDomain.lowerBound) != 0 ? self.fromDomain.upperBound - self.fromDomain.lowerBound : 1/self.fromDomain.upperBound; 
     return (x - self.fromDomain.lowerBound)/b 
    } 

    func rescale(_ x: Type) -> Type { 
     return interpolate(uninterpolate(x)) 
    } 
} 
2

non ho scavare il BNF per questo , ma la documentazione di Arduino aveva un ottimo esempio della funzione e della sua rottura. Sono stato in grado di usarlo in Python semplicemente aggiungendo una def come ridenominazione per rimappare (perché la mappa è un built-in) e rimuovendo i cast di tipo e le parentesi graffe (cioè rimuovendo tutti i 'long').

originale

long map(long x, long in_min, long in_max, long out_min, long out_max) 
{ 
    return (x - in_min) * (out_max - out_min)/(in_max - in_min) + out_min; 
} 

Python

def remap(x, in_min, in_max, out_min, out_max): 
    return (x - in_min) * (out_max - out_min)/(in_max - in_min) + out_min 

https://www.arduino.cc/en/reference/map

0

Questo esempio converte un posizione corrente canzoni in una gamma angolo di 20 - 40.

/// <summary> 
    /// This test converts Current songtime to an angle in a range. 
    /// </summary> 
    [Fact] 
    public void ConvertRangeTests() 
    {    
     //Convert a songs time to an angle of a range 20 - 40 
     var result = ConvertAndGetCurrentValueOfRange(
      TimeSpan.Zero, TimeSpan.FromMinutes(5.4), 
      20, 40, 
      2.7 
      ); 

     Assert.True(result == 30); 
    } 

    /// <summary> 
    /// Gets the current value from the mixValue maxValue range.   
    /// </summary> 
    /// <param name="startTime">Start of the song</param> 
    /// <param name="duration"></param> 
    /// <param name="minValue"></param> 
    /// <param name="maxValue"></param> 
    /// <param name="value">Current time</param> 
    /// <returns></returns> 
    public double ConvertAndGetCurrentValueOfRange(
       TimeSpan startTime, 
       TimeSpan duration, 
       double minValue, 
       double maxValue, 
       double value) 
    { 
     var timeRange = duration - startTime; 
     var newRange = maxValue - minValue; 
     var ratio = newRange/timeRange.TotalMinutes; 
     var newValue = value * ratio; 
     var currentValue= newValue + minValue; 
     return currentValue; 
    } 
Problemi correlati