Conversione di un numero di gamma di un'altra gamma, mantenendo il rapporto di
Domanda
Sto cercando di convertire una serie di numeri per un altro, il mantenimento di un rapporto.La matematica non è il mio forte.
Ho un file di immagine in cui i valori di punto può variare da -16000.00 per 16000.00 anche se la gamma tipica potrebbe essere molto meno.Quello che voglio fare è comprimere questi valori nell'intero intervallo da 0 a 100, dove 0 è il valore del più piccolo punto, e 100 è il valore più grande.In tutti i punti tra dovrebbe mantenere un rapporto anche se la precisione è perso e mi piacerebbe farlo in python, ma anche un algoritmo generale dovrebbe essere sufficiente.Io preferirei un algoritmo in cui il min/max o entrambi i range può essere regolata (vale a dire, il secondo potrebbe essere -50 a 800 invece che da 0 a 100).
Soluzione
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
Se si desidera proteggere per il caso in cui la vecchia gamma è 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 uno dei possibili nuovi valori di intervallo arbitrariamente. A seconda del contesto, scelte ragionevoli potrebbero essere: NewMin
( vedi esempio ), NewMax
o (NewMin + NewMax) / 2
Altri suggerimenti
Questa è una semplice conversione lineare.
new_value = ( (old_value - old_min) / (old_max - old_min) ) * (new_max - new_min) + new_min
Quindi, la conversione di 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
In realtà ci sono alcuni casi che le risposte di cui sopra si rompono. Come valore erroneamente ingresso, erroneamente campo di ingresso, ingresso negativo / campi di uscita.
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
C'è una condizione, quando tutti i valori che si sta verificando sono gli stessi, in cui il codice di @ jerryjvl sarebbe tornato NaN.
if (OldMin != OldMax && NewMin != NewMax):
return (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
else:
return (NewMax + NewMin) / 2
Non ho scavare il BNF per questo, ma la documentazione Arduino ha avuto un grande esempio della funzione ed è guasto. Sono stato in grado di utilizzare questo in Python con la semplice aggiunta di una ridenominazione def per rimappare (causa mappa è un built-in) tra parentesi graffe e rimuovendo il tipo di calchi e graffe (vale a dire basta rimuovere tutte le 'lunga del).
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
Nella lista fornita da PenguinTD, non capisco il motivo per cui gli intervalli sono invertiti, funziona senza dover invertire le gamme. conversione gamma lineare si basa sul Y=Xm+n
equazione lineare, dove m
e n
derivano dagli intervalli indicati. Piuttosto che fare riferimento alle gamme come min
e max
, sarebbe meglio fare riferimento a loro come 1 e 2. Quindi, la formula potrebbe essere:
Y = (((X - x1) * (y2 - y1)) / (x2 - x1)) + y1
Dove Y=y1
quando X=x1
, e Y=y2
quando X=x2
. x1
, x2
, y1
& y2
possono avere qualsiasi valore positive
o negative
. Definire l'espressione in una macro rende più utile, può quindi essere utilizzato con qualsiasi nomi degli argomenti.
#define RangeConv(X, x1, x2, y1, y2) (((float)((X - x1) * (y2 - y1)) / (x2 - x1)) + y1)
Il cast float
garantirebbe floating point divisione nel caso in cui tutti gli argomenti sono valori integer
.
A seconda dell'applicazione può non essere necessario controllare la x1=x2
gamme e y1==y2
.
PHP Port
Trovato soluzione PenguinTD utile in modo ho portato a PHP. Aiutati!
/**
* =====================================
* 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;
}
Ho usato questa soluzione in un problema che stavo risolvendo in js, così 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;
}
C ++ Variante
Ho trovato soluzione utile di PenguinTD, così ho portato in C ++, se qualcuno ha bisogno:
float rimappare (float x, float omin, galleggiare OMAX, galleggiare nMin, galleggiare 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; }
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, in questo modo:
scale_list([math.log(i+1) for i in range(5)], 0, 50)
[0.0, 21,533827903669653, 34,130309724299266, 43,06765580733931, 50.0]
Taglio corto/semplificata proposta
NewRange/OldRange = Handy multiplicand or HM
Convert OldValue in OldRange to NewValue in NewRange =
(OldValue - OldMin x HM) + NewMin
wayne
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) )
}
}
Questo esempio converte attuale posizione canzoni in un intervallo angolare 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;
}
Ecco una versione JavaScript che restituisce una funzione che esegue il ricalcolo per gli intervalli di origine e destinazione predeterminati, minimizzando la quantità di calcolo, che deve essere fatto ogni volta.
// 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;
}
}
Ecco un esempio di utilizzo di questa funzione per ridimensionare 0-1 in -0x80000000, 0x7FFFFFFF
let normTo32Fn = makeRangeMapper(0, 1, -0x80000000, 0x7FFFFFFF);
let fs = normTo32Fn(0.5);
let fs2 = normTo32Fn(0);
Lista comprensione un'unica soluzione di linea
color_array_new = [int((((x - min(node_sizes)) * 99) / (max(node_sizes) - min(node_sizes))) + 1) for x in node_sizes]
versione più lunga
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