384 Stimmen

Umwandlung eines Zahlenbereichs in einen anderen Bereich unter Beibehaltung des Verhältnisses

Ich versuche, einen Zahlenbereich in einen anderen umzurechnen und dabei das Verhältnis beizubehalten. Mathe ist nicht meine Stärke.

Ich habe eine Bilddatei, in der die Punktwerte von -16000.00 bis 16000.00 reichen können, obwohl der typische Bereich viel kleiner sein kann. Ich möchte diese Werte in den ganzzahligen Bereich 0-100 komprimieren, wobei 0 der Wert des kleinsten Punktes und 100 der Wert des größten Punktes ist. Alle Punkte dazwischen sollten ein relatives Verhältnis behalten, auch wenn etwas Präzision verloren geht. Ich möchte dies in Python tun, aber auch ein allgemeiner Algorithmus sollte ausreichen. Ich würde einen Algorithmus bevorzugen, bei dem der Min-/Max-Wert oder einer der beiden Bereiche angepasst werden kann (d.h. der zweite Bereich könnte -50 bis 800 statt 0 bis 100 sein).

746voto

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

Oder ein wenig lesbarer:

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

Oder wenn Sie sich für den Fall schützen wollen, dass der alte Bereich 0 ist ( AltMin = AltMax ) :

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, einen der möglichen neuen Bereichswerte willkürlich auszuwählen. Je nach Kontext könnte eine sinnvolle Auswahl sein: NewMin ( siehe Muster ), NewMax o (NewMin + NewMax) / 2

1 Stimmen

Muss oldMax 16000 sein oder kann es auch der höchste Wert im alten Punktesatz sein (z. B. 15034,00) - ist diese Unterscheidung wichtig?

5 Stimmen

Sie können es machen, was Sie wollen ... beachten Sie, dass Sie möglicherweise seltsame Ergebnisse, wenn einer der Bereiche ist sehr klein im Verhältnis zu den anderen (nicht genau sicher, aber wenn es mehr als ein 1000000 Faktor Unterschied zwischen der Größe der Bereiche, stellen Sie sicher, dass es tatsächlich verhält sich wie Sie erwarten ... oder lernen Sie über Fließkomma-Ungenauigkeit)

3 Stimmen

In Anbetracht der Beliebtheit dieser Antwort sollten Sie für einen allgemeineren Fall die Möglichkeit OldMax == OldMin in Betracht ziehen, die zu einer Division durch Null führen könnte.

88voto

cletus Punkte 596503

Das ist eine einfache lineare Umrechnung.

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

Die Umrechnung von 10000 auf der Skala von -16000 bis 16000 in eine neue Skala von 0 bis 100 ergibt also:

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 Stimmen

Das ist falsch. Sie müssen Old Min von Old Value abziehen, bevor Sie dividieren.

0 Stimmen

Ist es nicht genau das, was er getan hat? Für mich sieht das zu 100 % korrekt aus. Es ist im Grunde die gleiche Antwort wie die akzeptierte, nur in einer logischeren Reihenfolge (subjektiv), 2 Minuten früher gepostet und mit einem Satz überflüssiger Klammern weniger!

1 Stimmen

Meiner bescheidenen Meinung nach ist das auch leichter zu verstehen als die akzeptierte Antwort.

29voto

PenguinTD Punkte 279

Tatsächlich gibt es einige Fälle, in denen die oben genannten Antworten nicht funktionieren würden. Zum Beispiel falsch eingegebener Wert, falscher Eingabebereich, negative Eingabe-/Ausgabebereiche.

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 Stimmen

Wann ist es eine gute Praxis, dem Benutzer ein "gültiges" Ergebnis zurückzuliefern, wenn er eine Funktion unsachgemäß verwendet?

11voto

dragon788 Punkte 3063

Ich habe nicht die BNF aber in der Arduino-Dokumentation gibt es ein großartiges Beispiel für die Funktion und ihre Aufschlüsselung. Ich war in der Lage, dies in Python zu verwenden, indem ich einfach eine def Umbenennung zu remap (weil map ist eine eingebaute) und das Entfernen der Typ Casts und geschweiften Klammern (dh entfernen Sie einfach alle 'long's).

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

11voto

Charles Clayton Punkte 15150

Hier finden Sie einige kurze Python-Funktionen, die das Kopieren und Einfügen erleichtern, darunter eine Funktion zum Skalieren einer ganzen Liste.

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]

Das kann folgendermaßen verwendet werden:

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]

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X