그래서 나는 숫자의 범위를 취하고 범위에 맞게 값을 축소하는 방법을 알아 내려고 노력하고 있습니다. 이 작업을 수행하려는 이유는 Java 스윙 jpanel에서 타원을 그리려고하기 때문입니다. 각 타원의 높이와 너비가 1-30 사이의 범위에 있기를 원합니다. 내 데이터 세트에서 최소값과 최대 값을 찾는 방법이 있지만 런타임까지 최소값과 최대 값이 없습니다. 이 작업을 수행하는 쉬운 방법이 있습니까?
그래서 나는 숫자의 범위를 취하고 범위에 맞게 값을 축소하는 방법을 알아 내려고 노력하고 있습니다. 이 작업을 수행하려는 이유는 Java 스윙 jpanel에서 타원을 그리려고하기 때문입니다. 각 타원의 높이와 너비가 1-30 사이의 범위에 있기를 원합니다. 내 데이터 세트에서 최소값과 최대 값을 찾는 방법이 있지만 런타임까지 최소값과 최대 값이 없습니다. 이 작업을 수행하는 쉬운 방법이 있습니까?
답변:
범위 [min,max]
를 로 조정하려는 경우를 가정 해 보겠습니다 [a,b]
. 만족하는 (연속) 기능을 찾고 있습니다.
f(min) = a
f(max) = b
귀하의 경우 a
1 b
이 30이 될 것입니다.하지만 더 간단한 것으로 시작 [min,max]
하여 range 에 매핑 하십시오 [0,1]
.
퍼팅 min
함수에 0을 얻는 것은 달성 할 수
f(x) = x - min ===> f(min) = min - min = 0
거의 우리가 원하는 것입니다. 그러나 실제로 1을 원할 때 우리 max
에게 줄 것이다 max - min
. 그래서 우리는 그것을 확장해야 할 것이다.
x - min max - min
f(x) = --------- ===> f(min) = 0; f(max) = --------- = 1
max - min max - min
우리가 원하는 것입니다. 번역과 스케일링이 필요합니다. 이제 대신 a
and의 임의의 값을 얻으려면 b
조금 더 복잡한 것이 필요합니다.
(b-a)(x - min)
f(x) = -------------- + a
max - min
당신은 퍼팅 확인할 수 있습니다 min
에 대한 x
지금하는 제공 a
하고, 퍼팅 max
수 있습니다 b
.
(b-a)/(max-min)
새 범위의 크기와 원래 범위의 크기 사이의 스케일링 요인 임을 알 수 있습니다 . 그래서 정말 우리가 처음 번역되어 x
에서 -min
올바른 요소에 확장, 다음이의 새로운 최소 값으로 백업 번역 a
.
도움이 되었기를 바랍니다.
max != min
그렇지 않으면 함수 결과가 결정되지 않습니다. :)
min
부정적이고 max
긍정적 이면 효과 가 있습니까 , 아니면 둘 다 긍정적이어야합니까?
복사-붙여 넣기 용이성을위한 JavaScript가 있습니다 (이것은 짜증나는 답변입니다) :
function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) {
return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed;
}
이와 같이 적용하여 10-50 범위를 0-100 범위로 조정합니다.
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));
}
0.00, 18.37, 48.98, 55.10, 85.71, 100.00
편집하다:
나는 오래전에 이것에 대답했다는 것을 알고 있지만, 지금 사용하는 더 깨끗한 기능이 있습니다.
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);
}
다음과 같이 적용됩니다 :
[-4, 0, 5, 6, 9].scaleBetween(0, 100);
[0, 30.76923076923077, 69.23076923076923, 76.92307692307692, 100]
[1, 1, 1]
, [100, 100, 100]
, 또는 [50.5, 50.5, 50.5]
. 당신은 사건을 넣을 수 있습니다 :if (max-min == 0) return this.map(num => (scaledMin+scaledMax)/2);
편의상 여기에 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
내가 이해하는 방법은 다음과 같습니다.
x
범위에 속하는 비율범위가 0
~ 이라고 가정합니다 100
. 해당 범위의 임의의 숫자가 주어지면 해당 범위의 "퍼센트"는 무엇입니까? 이것은 아주 간단해야 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%
의가 [0, 20]
있다. 그것은 것 10
맞죠? 그 대답에 어떻게 도착 했습니까? 글쎄, 우리는 할 수 있습니다 :
20 * 0.5 = 10
그러나 [10, 20]
어떻습니까? 지금은 모든 것을 바꿔야합니다 10
. 예 :
((20 - 10) * 0.5) + 10
보다 일반적인 공식은 다음과 같습니다.
((MAX - MIN) * PERCENT) + MIN
무엇 25%
의 원래 예를 들면 다음과 [10, 20]
같습니다.
((20 - 10) * 0.25) + 10 = 12.5
그래서, 400
범위 [200, 1000]
에 매핑 할 12.5
범위[10, 20]
x
이전 범위에서 새 범위로 매핑하려면
OLD PERCENT = (x - OLD MIN) / (OLD MAX - OLD MIN)
NEW X = ((NEW MAX - NEW MIN) * OLD PERCENT) + NEW MIN
나는이 솔루션을 보았지만 이것은 실제로 내 필요에 맞지 않습니다. 그래서 나는 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);
}
}
나는 Irritate의 대답을 취하고 그것을 최소한의 상수로 인수 화하여 후속 계산을위한 계산 단계를 최소화하기 위해 리팩토링했습니다. 동기 부여는 스케일러가 하나의 데이터 세트에서 학습 된 다음 새 데이터 (ML 알고의 경우)에서 실행되도록하는 것입니다. 실제로 SciKit의 사전 처리 MinMaxScaler for Python 사용법과 매우 비슷합니다.
따라서 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;
}
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; }