Konvertieren eines Nummernbereich in einen anderen Bereich, die Aufrechterhaltung Verhältnis

StackOverflow https://stackoverflow.com/questions/929103

  •  06-09-2019
  •  | 
  •  

Frage

Ich versuche, eine Reihe von Zahlen in ein anderes zu konvertieren, die Aufrechterhaltung Verhältnis. Mathe ist nicht meine Stärke.

Ich habe eine Bilddatei in den Punktwert -16.000,00-16000,00 reichen kann, obwohl der typische Bereich viel weniger sein kann. Was ich tun möchte, ist diese Werte in die Integerbereich komprimieren 0-100, wobei 0 der Wert des kleinsten Punkt ist, und 100 ist der Wert der größten. Alle Punkte dazwischen ein relatives Verhältnis halten sollte, auch wenn einige Genauigkeit verloren geht möchte ich dies in Python tun, sondern auch ein allgemeiner Algorithmus sollte ausreichen. Ich würde es vorziehen, einen Algorithmus, wo die min / max oder entweder Bereich kann eingestellt werden (dh könnte der zweite Bereich von -50 bis 800 anstelle von 0 bis 100 sein).

War es hilfreich?

Lösung

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

oder ein wenig mehr lesbar:

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

Oder wenn Sie wollen für den Fall zu schützen, wo der alte Bereich ist 0 ( OldMin = OldMax ):

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

Beachten Sie, dass wir in diesem Fall gezwungen sind, eine der möglichen neuen Bereichswerte willkürlich auszuwählen. Je nach Kontext kann vernünftige Entscheidungen sein: NewMin ( siehe Beispiel ), NewMax oder (NewMin + NewMax) / 2

Andere Tipps

Das ist eine einfache lineare Konvertierung.

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

So 10000 auf der Skala von -16.000-16.000 auf eine neue Skala von 0 bis 100 Ausbeuten Umwandlung:

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

Eigentlich gibt es einige Fälle, dass oben genannte Antworten brechen. Wie falsch Eingabewert, falsch Eingangsbereich, negativ Ein- / Ausgangsbereiche.

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

Es ist eine Bedingung, wenn alle Werte, die Sie sind gleich einchecken, wo Code des @ jerryjvl zurückkehren würde NaN.

if (OldMin != OldMax && NewMin != NewMax):
    return (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
else:
    return (NewMax + NewMin) / 2

Ich habe nicht graben die BNF für diese, aber die Arduino-Dokumentation hatte ein großartiges Beispiel für die Funktion und es ist Zusammenbruch. Ich konnte dies durch einfaches Hinzufügen einer def Umbenennung in Python verwenden, um neu zuordnen (Ursache Karte ist eine integrierte) und die Art Entfernen wirft und geschweiften Klammern (dh nur entfernen Sie alle ‚Longs).

Original

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

In der Auflistung von PenguinTD zur Verfügung gestellt, die ich nicht verstehe, warum die Bereiche umgekehrt sind, funktioniert es ohne die Bereiche umkehren zu müssen. Linearbereichsumwandlung wird auf die lineare Gleichung Y=Xm+n basiert, wobei m n und werden aus den angegebenen Bereichen abgeleitet. Anstatt auf die Bereiche wie min und max zu beziehen, wäre es besser, sie zu beziehen, wie 1 und 2. So ist die Formel wäre:

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

Wo Y=y1 wenn X=x1 und Y=y2 wenn X=x2. x1, x2, y1 & y2 kann jeden positive oder negative Wert angegeben werden. Definieren Sie den Ausdruck in einem Makro macht es nützlich, es kann dann mit irgendwelchen Argumenten Namen verwendet werden.

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

Der float Guss würde Gleitkomma-Division in dem Fall sicherzustellen, in dem alle Argumente integer Werte. Je nach Anwendung kann es nicht notwendig sein, die Bereiche x1=x2 und y1==y2 zu überprüfen.

PHP-Port

Gefunden PenguinTD Lösung hilfreich, damit ich es zu PHP portiert. Helfen Sie sich selbst!

/**
* =====================================
*              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;
}

habe ich diese Lösung in einem Problem, das ich in js wurde die Lösung, so dass ich dachte, ich würde die Übersetzung teilen. Danke für die Erklärung und Lösung.

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;
}

C ++ Variante

fand ich PenguinTD Solution nützlich, so dass ich portierte es zu C ++, wenn jemand sie braucht:

  

float remap (float x, float omin, schweben OMAX, schweben 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; }

Hier einige kurze Python-Funktionen für Ihre Kopieren und Einfügen von Leichtigkeit, einschließlich einer Funktion eine ganze Liste zu skalieren.

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]

, die verwendet werden können, etwa so:

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

[0,0, 50,0, 75,0, 100,0]

In meinem Fall wollte ich eine logarithmische Kurve skalieren, etwa so:

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

[0.0, 21,533827903669653, 34,130309724299266, 43,06765580733931, 50,0]

Short-cut / vereinfachte Vorschlag

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

wayne

ich persönlich die Hilfsklasse verwenden, die Generika (Swift 3 kompatibel)

unterstützt
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) )
    }
}

Dieses Beispiel wandelt einen Song aktuelle Position in einen Winkelbereich von 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;
    }

Hier ist eine Javascript-Version, die eine Funktion gibt, die die Neuskalierung für vorbestimmte Quell- und Zielbereiche der Fall ist, die Berechnungsmenge zu minimieren, die jedes Mal durchgeführt werden muss.

// This function returns a function bound to the 
// min/max source & target ranges given.
// oMin, oMax = source
// nMin, nMax = dest.
function makeRangeMapper(oMin, oMax, nMin, nMax ){
    //range check
    if (oMin == oMax){
        console.log("Warning: Zero input range");
        return undefined;
    };

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

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

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

    // Hot-rod the most common case.
    if (!reverseInput && !reverseOutput) {
        let dNew = newMax-newMin;
        let dOld = oldMax-oldMin;
        return (x)=>{
            return ((x-oldMin)* dNew / dOld) + newMin;
        }
    }

    return (x)=>{
        let portion;
        if (reverseInput){
            portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin);
        } else {
            portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin)
        }
        let result;
        if (reverseOutput){
            result = newMax - portion;
        } else {
            result = portion + newMin;
        }

        return result;
    }   
}

Hier ist ein Beispiel dieser Funktion mit 0-1 in -0x80000000, 0x7FFFFFFF

maßstab
let normTo32Fn = makeRangeMapper(0, 1, -0x80000000, 0x7FFFFFFF);
let fs = normTo32Fn(0.5);
let fs2 = normTo32Fn(0);

Liste Verständnis einzeiler Lösung

color_array_new = [int((((x - min(node_sizes)) * 99) / (max(node_sizes) - min(node_sizes))) + 1) for x in node_sizes]

Längere Version

def colour_specter(waste_amount):
color_array = []
OldRange = max(waste_amount) - min(waste_amount)
NewRange = 99
for number_value in waste_amount:
    NewValue = int((((number_value - min(waste_amount)) * NewRange) / OldRange) + 1)
    color_array.append(NewValue)
print(color_array)
return color_array
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top