代码之家  ›  专栏  ›  技术社区  ›  SpliFF

将数字范围转换为其他范围,保持比率

  •  209
  • SpliFF  · 技术社区  · 15 年前

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

    我有一个图像文件,其中点的值可能在-16000.00到16000.00之间,尽管典型的范围可能要小得多。我要做的是将这些值压缩到整数范围0-100,其中0是最小点的值,100是最大点的值。在这两者之间的所有点都应该保持一个相对的比率,即使一些精度正在丢失,我希望在Python中这样做,但即使是一个通用的算法也足够了。我更喜欢一种可以调整最小/最大值或任意范围的算法(即,第二个范围可以是-50到800,而不是0到100)。

    13 回复  |  直到 7 年前
        1
  •  412
  •   jerryjvl    10 年前
    NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
    

    或者更易读一点:

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

    或者,如果要保护旧范围为0的情况( OLDMI= OLDMAX ):

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

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

        2
  •  53
  •   cletus    15 年前

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

    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
    
        3
  •  20
  •   PenguinTD    11 年前

    实际上,有些情况下,上面的答案会被打破。 如输入值错误、输入范围错误、输入/输出范围为负。

    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
    
        4
  •  9
  •   p2or bTab    8 年前

    当您检查的所有值都相同时,有一个条件,@jerryjvl的代码将返回nan。

    if (OldMin != OldMax && NewMin != NewMax):
        return (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
    else:
        return (NewMax + NewMin) / 2
    
        5
  •  3
  •   dragon788    7 年前

    我没有挖到 BNF 对于这个,但是Arduino文档有一个很好的函数和它的分解示例。我可以在python中使用它,只需添加一个def重命名为remap(因为map是内置的),并删除类型转换和花括号(即只删除所有的“long”)。

    原件

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

    蟒蛇

    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

        6
  •  2
  •   Rizier123 tarnfeld    10 年前

    在企鹅TD提供的列表中,我不明白为什么要反转范围,它不需要反转范围就可以工作。线性范围转换基于线性方程 Y=Xm+n 在哪里 m n 从给定的范围派生。而不是将范围称为 min max ,最好将它们称为1和2。所以公式是:

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

    在哪里? Y=y1 什么时候? X=x1 Y=y2 什么时候? X=x2 . x1 , x2 , y1 和; y2 可以给任何 positive negative 价值。在宏中定义表达式使其更有用,然后可以与任何参数名一起使用。

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

    这个 float 如果所有参数都是 integer 价值观。 根据应用情况,可能不需要检查范围 x1=x2 y1==y2 .

        7
  •  2
  •   Neil    8 年前

    PHP端口

    发现企鹅的解决方案很有用,所以我把它移植到了PHP。请随意!

    /**
    * =====================================
    *              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;
    }
    
        8
  •  1
  •   fonstok    10 年前

    我在JS中解决的一个问题中使用了这个解决方案,所以我想我会分享这个翻译。感谢您的解释和解决方案。

    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;
    }
    
        9
  •  1
  •   user1767754    10 年前

    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; }
    
        10
  •  1
  •   Charles Clayton    7 年前

    下面是一些简单的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]

        11
  •  0
  •   wayne    9 年前

    简略建议

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

    韦恩

        12
  •  0
  •   Grzegorz Krukowski    7 年前

    我个人使用支持泛型的helper类(与swift 3兼容)

    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) )
        }
    }
    
        13
  •  0
  •   David Graham    7 年前

    此示例将歌曲当前位置转换为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;
        }