所以我试图弄清楚如何获取一系列数字并将值缩小到适合范围。想要这样做的原因是我试图在java swing jpanel中绘制省略号。我希望每个椭圆的高度和宽度在1-30的范围内。我有从我的数据集中找到最小值和最大值的方法,但是直到运行时才会有min和max。有一个简单的方法吗?
答案 0 :(得分:471)
假设您要将范围[min,max]
缩放为[a,b]
。您正在寻找满足
f(min) = a
f(max) = b
在您的情况下,a
为1,b
为30,但让我们从更简单的事情开始,尝试将[min,max]
映射到范围[0,1]
。< / p>
将min
置于一个函数中并且可以使用
f(x) = x - min ===> f(min) = min - min = 0
这几乎就是我们想要的。但是当我们真正想要时,输入max
会给我们max - min
所以我们必须扩展它:
x - min max - min
f(x) = --------- ===> f(min) = 0; f(max) = --------- = 1
max - min max - min
这就是我们想要的。所以我们需要进行翻译和缩放。现在,如果我们想获得a
和b
的任意值,我们需要更复杂的东西:
(b-a)(x - min)
f(x) = -------------- + a
max - min
您可以验证现在min
x
为a
提供了max
,而b
提供了(b-a)/(max-min)
。
您可能还注意到x
是新范围大小与原始范围大小之间的缩放系数。实际上,我们首先将-min
翻译为a
,将其缩放到正确的因子,然后将其翻译回新的最小值{{1}}。
希望这有帮助。
答案 1 :(得分:38)
这里有一些用于复制粘贴的JavaScript(这是烦人的答案):
r = csv.DictReader(sst_data, delimiter = " ", skipinitialspace = True)
如此应用,将范围10-50缩放到0-100之间的范围。
function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) {
return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed;
}
0.00,18.37,48.98,55.10,85.71,100.00
修改:
我知道我很久以前就回答了这个问题,但是我现在使用的是一个更清洁的功能:
var unscaledNums = [10, 13, 25, 28, 43, 50];
var maxRange = Math.max.apply(Math, unscaledNums);
var minRange = Math.min.apply(Math, unscaledNums);
for (var i = 0; i < unscaledNums.length; i++) {
var unscaled = unscaledNums[i];
var scaled = scaleBetween(unscaled, 0, 100, minRange, maxRange);
console.log(scaled.toFixed(2));
}
如此应用:
Array.prototype.scaleBetween = function(scaledMin, scaledMax) {
var max = Math.max.apply(Math, this);
var min = Math.min.apply(Math, this);
return this.map(num => (scaledMax-scaledMin)*(num-min)/(max-min)+scaledMin);
}
[0,30.76923076923077,66.23076923076923,76.92307692307692,100]
答案 2 :(得分:26)
为方便起见,这里是Java形式的Irritate算法。根据需要添加错误检查,异常处理和调整。
public class Algorithms {
public static double scale(final double valueIn, final double baseMin, final double baseMax, final double limitMin, final double limitMax) {
return ((limitMax - limitMin) * (valueIn - baseMin) / (baseMax - baseMin)) + limitMin;
}
}
测试仪:
final double baseMin = 0.0;
final double baseMax = 360.0;
final double limitMin = 90.0;
final double limitMax = 270.0;
double valueIn = 0;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));
valueIn = 360;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));
valueIn = 180;
System.out.println(Algorithms.scale(valueIn, baseMin, baseMax, limitMin, limitMax));
90.0
270.0
180.0
答案 3 :(得分:16)
以下是我的理解:
x
在一个范围内的百分比假设您的范围从0
到100
。给定该范围内的任意数字,&#34;%&#34;从那个范围它是否在于?这应该非常简单,0
为0%
,50
为50%
,100
为100%
。
现在,如果您的范围是20
到100
怎么办?我们不能应用与上面相同的逻辑(除以100),因为:
20 / 100
没有给我们0
(20
现在应该是0%
。这应该很容易修复,我们只需要为0
的情况制作分子20
。我们可以通过减去:
(20 - 20) / 100
但是,这对100
不起作用了,因为:
(100 - 20) / 100
没有给我们100%
。同样,我们也可以通过从分母中减去来解决这个问题:
(100 - 20) / (100 - 20)
找出%x
在一个范围内的更通用的等式是:
(x - MIN) / (MAX - MIN)
现在我们知道数字在一个范围内的百分比,我们可以应用它来将数字映射到另一个范围。让我们来看一个例子。
old range = [200, 1000]
new range = [10, 20]
如果我们在旧范围内有一个数字,那么这个数字在新范围内会是多少?我们说这个数字是400
。首先,弄清楚旧范围内的400
百分比。我们可以应用上面的等式。
(400 - 200) / (1000 - 200) = 0.25
所以,400
位于旧范围的25%
。我们只需要弄清楚新范围的25%
是多少。想想50%
的{{1}}是什么。它会[0, 20]
对吗?你是怎么得到答案的?好吧,我们可以做到:
10
但是,20 * 0.5 = 10
呢?我们现在需要将所有内容都移到[10, 20]
。例如:
10
更通用的公式是:
((20 - 10) * 0.5) + 10
到((MAX - MIN) * PERCENT) + MIN
的{{1}}的原始示例:
25%
因此,[10, 20]
范围内的((20 - 10) * 0.25) + 10 = 12.5
将映射到400
[200, 1000]
将12.5
从旧范围映射到新范围:
[10, 20]
答案 4 :(得分:10)
我遇到过这个解决方案,但这并不适合我的需要。所以我在d3源代码中挖掘了一下。我个人会建议像d3.scale那样做。
所以在这里你将域缩放到范围。优点是您可以将标志翻转到目标范围。这很有用,因为计算机屏幕上的y轴自上而下,因此较大的值具有较小的y。
public class Rescale {
private final double range0,range1,domain0,domain1;
public Rescale(double domain0, double domain1, double range0, double range1) {
this.range0 = range0;
this.range1 = range1;
this.domain0 = domain0;
this.domain1 = domain1;
}
private double interpolate(double x) {
return range0 * (1 - x) + range1 * x;
}
private double uninterpolate(double x) {
double b = (domain1 - domain0) != 0 ? domain1 - domain0 : 1 / domain1;
return (x - domain0) / b;
}
public double rescale(double x) {
return interpolate(uninterpolate(x));
}
}
以下是您可以看到我的意思的测试
public class RescaleTest {
@Test
public void testRescale() {
Rescale r;
r = new Rescale(5,7,0,1);
Assert.assertTrue(r.rescale(5) == 0);
Assert.assertTrue(r.rescale(6) == 0.5);
Assert.assertTrue(r.rescale(7) == 1);
r = new Rescale(5,7,1,0);
Assert.assertTrue(r.rescale(5) == 1);
Assert.assertTrue(r.rescale(6) == 0.5);
Assert.assertTrue(r.rescale(7) == 0);
r = new Rescale(-3,3,0,1);
Assert.assertTrue(r.rescale(-3) == 0);
Assert.assertTrue(r.rescale(0) == 0.5);
Assert.assertTrue(r.rescale(3) == 1);
r = new Rescale(-3,3,-1,1);
Assert.assertTrue(r.rescale(-3) == -1);
Assert.assertTrue(r.rescale(0) == 0);
Assert.assertTrue(r.rescale(3) == 1);
}
}
答案 5 :(得分:2)
基于Charles Clayton的回复,我包括了一些JSDoc,ES6调整,并在原始回复中纳入了评论中的建议。
/**
* Returns a scaled number within its source bounds to the desired target bounds.
* @param {number} n - Unscaled number
* @param {number} tMin - Minimum (target) bound to scale to
* @param {number} tMax - Maximum (target) bound to scale to
* @param {number} sMin - Minimum (source) bound to scale from
* @param {number} sMax - Maximum (source) bound to scale from
* @returns {number} The scaled number within the target bounds.
*/
const scaleBetween = (n, tMin, tMax, sMin, sMax) => {
return (tMax - tMin) * (n - sMin) / (sMax - sMin) + tMin;
}
if (Array.prototype.scaleBetween === undefined) {
/**
* Returns a scaled array of numbers fit to the desired target bounds.
* @param {number} tMin - Minimum (target) bound to scale to
* @param {number} tMax - Maximum (target) bound to scale to
* @returns {number} The scaled array.
*/
Array.prototype.scaleBetween = function(tMin, tMax) {
if (arguments.length === 1 || tMax === undefined) {
tMax = tMin; tMin = 0;
}
let sMax = Math.max(...this), sMin = Math.min(...this);
if (sMax - sMin == 0) return this.map(num => (tMin + tMax) / 2);
return this.map(num => (tMax - tMin) * (num - sMin) / (sMax - sMin) + tMin);
}
}
// ================================================================
// Usage
// ================================================================
let nums = [10, 13, 25, 28, 43, 50], tMin = 0, tMax = 100,
sMin = Math.min(...nums), sMax = Math.max(...nums);
// Result: [ 0.0, 7.50, 37.50, 45.00, 82.50, 100.00 ]
console.log(nums.map(n => scaleBetween(n, tMin, tMax, sMin, sMax).toFixed(2)).join(', '));
// Result: [ 0, 30.769, 69.231, 76.923, 100 ]
console.log([-4, 0, 5, 6, 9].scaleBetween(0, 100).join(', '));
// Result: [ 50, 50, 50 ]
console.log([1, 1, 1].scaleBetween(0, 100).join(', '));
.as-console-wrapper { top: 0; max-height: 100% !important; }
答案 6 :(得分:1)
我采用了Irritate的答案并重构了它,以便通过将其分解为最少的常数来最小化后续计算的计算步骤。动机是允许在一组数据上训练定标器,然后在新数据上运行(对于ML算法)。实际上,它与SciKit的Python预处理MinMaxScaler非常相似。
因此,x' = (b-a)(x-min)/(max-min) + a
(其中b!= a)变为x' = x(b-a)/(max-min) + min(-b+a)/(max-min) + a
,可以将x' = x*Part1 + Part2
形式缩减为两个常量。
这是一个带有两个构造函数的C#实现:一个用于训练,一个用于重新加载训练过的实例(例如,支持持久性)。
public class MinMaxColumnSpec
{
/// <summary>
/// To reduce repetitive computations, the min-max formula has been refactored so that the portions that remain constant are just computed once.
/// This transforms the forumula from
/// x' = (b-a)(x-min)/(max-min) + a
/// into x' = x(b-a)/(max-min) + min(-b+a)/(max-min) + a
/// which can be further factored into
/// x' = x*Part1 + Part2
/// </summary>
public readonly double Part1, Part2;
/// <summary>
/// Use this ctor to train a new scaler.
/// </summary>
public MinMaxColumnSpec(double[] columnValues, int newMin = 0, int newMax = 1)
{
if (newMax <= newMin)
throw new ArgumentOutOfRangeException("newMax", "newMax must be greater than newMin");
var oldMax = columnValues.Max();
var oldMin = columnValues.Min();
Part1 = (newMax - newMin) / (oldMax - oldMin);
Part2 = newMin + (oldMin * (newMin - newMax) / (oldMax - oldMin));
}
/// <summary>
/// Use this ctor for previously-trained scalers with known constants.
/// </summary>
public MinMaxColumnSpec(double part1, double part2)
{
Part1 = part1;
Part2 = part2;
}
public double Scale(double x) => (x * Part1) + Part2;
}
答案 7 :(得分:1)
有时我会发现它有用。
JavaScript示例:
class Scaler {
constructor(inMin, inMax, outMin, outMax) {
this.inMin = inMin;
this.inMax = inMax;
this.outMin = outMin;
this.outMax = outMax;
}
scale(value) {
const result = (value - this.inMin) * (this.outMax - this.outMin) / (this.inMax - this.inMin) + this.outMin;
if (result < this.outMin) {
return this.outMin;
} else if (result > this.outMax) {
return this.outMax;
}
return result;
}
}
此示例以及基于功能的版本来自页面https://writingjavascript.com/scaling-values-between-two-ranges