我试图将一个范围的数字转换为另一个,保持比率。数学不是我的强项。

I have an image file where point values may range from -16000.00 to 16000.00 though the typical range may be much less. What I want to do is compress these values into the integer range 0-100, where 0 is the value of the smallest point, and 100 is the value of the largest. All points in between should keep a relative ratio even though some precision is being lost I'd like to do this in python but even a general algorithm should suffice. I'd prefer an algorithm where the min/max or either range can be adjusted (ie, the second range could be -50 to 800 instead of 0 to 100).


当前回答

下面是一些简单的Python函数,便于复制和粘贴,包括一个扩展整个列表的函数。

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]

可以这样使用:

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

[0.0, 50.0, 75.0, 100.0]

在我的例子中,我想缩放一条对数曲线,像这样:

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

[0.0, 21.533827903669653, 34.130309724299266, 43.06765580733931, 50.0]

其他回答

C++变体

我发现PenguinTD的解决方案很有用,所以我把它移植到c++,如果有人需要它:

float remap(float x, float oMin, float 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; }

下面是一些简单的Python函数,便于复制和粘贴,包括一个扩展整个列表的函数。

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]

可以这样使用:

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

[0.0, 50.0, 75.0, 100.0]

在我的例子中,我想缩放一条对数曲线,像这样:

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

[0.0, 21.533827903669653, 34.130309724299266, 43.06765580733931, 50.0]

这是一个简单的线性变换。

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

因此,将10000在-16000到16000的范围内转换为0到100的新范围会得到:

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
NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin

或者更容易读懂:

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

或者如果你想保护旧范围为0的情况(OldMin = OldMax):

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

注意,在这种情况下,我们被迫任意选择一个可能的新范围值。根据上下文,明智的选择可能是:NewMin(见示例),NewMax或(NewMin + NewMax) / 2

使用Numpy和interp函数,你可以将你的值从旧范围转换为新范围:

>>> import numpy as np
>>> np.interp(0, [-16000,16000], [0,100])
50.0

你也可以尝试映射一个值列表:

>>> np.interp([-16000,0,12000] ,[-16000,16000], [0,100])
array([ 0. , 50. , 87.5])