javascript - logaritmo - math.log java
Cálculo de la longitud de un segmento en una escala logarĂtmica (6)
Quiero calcular la longitud de una línea para una serie de eventos.
Estoy haciendo esto con el siguiente código.
var maxLineLength = 20;
var lineLen = function(x, max) {
return maxLineLength * (x / max);
}
var events = [0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
Esto funciona, pero estoy usando una escala lineal, mientras que me gustaría usar logaritmos, porque no quiero que los eventos pequeños se conviertan en números muy pequeños cuando están presentes los grandes.
Modifiqué la función lineLen como se puede ver a continuación, pero, obviamente, no funciona para eventos iguales a uno, porque el registro de uno es cero. Quiero mostrar eventos iguales a uno (escalado oportunamente) y no hacer que se vuelvan cero. También necesito números positivos para seguir siendo positivo (0.1 se convierte en un número negativo)
¿Cómo debo modificar lineLen para usar una escala logarítmica?
var maxLineLength = 20;
var lineLen = function(x, max) {
return maxLineLength * (Math.log(x) / Math.log(max));
}
var events = [0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
Más corto pero no demasiado corto; Más largo pero no demasiado largo.
De hecho, me encontré con el mismo problema hace años y me rendí por esto (¿quizás?). Acabo de leer tu pregunta aquí y ahora, y creo que acabo de encontrar la solución: cambiar .
const log = (base, value) => (Math.log(value) / Math.log(base));
const weights = [0, 0.1, 1, 5, 20, 50, 100];
const base = Math.E; // Setting
const shifted = weights.map(x => x + base);
const logged = shifted.map(x => log(base, x));
const unshifted = logged.map(x => x - 1);
const total = unshifted.reduce((a, b) => a + b, 0);
const ratio = unshifted.map(x => x / total);
const percents = ratio.map(x => x * 100);
console.log(percents);
// [
// 0,
// 0.35723375538333857,
// 3.097582209424984,
// 10.3192042142806,
// 20.994247877004888,
// 29.318026542735115,
// 35.91370540117108
// ]
Visualización
Cuanto más pequeña es la base logarítmica, más se ajustan ; y viceversa. En realidad no sé la razón. XD
<!DOCTYPE html>
<html>
<head>
<meta name="author" content="K.">
<title>Shorter but not too short; longer but not too long.</title>
<style>
canvas
{
background-color: whitesmoke;
}
</style>
</head>
<body>
<canvas id="canvas" height="5"></canvas>
<label>Weights: <input id="weights" type="text" value="[0, 0.1, 100, 1, 5, 20, 2.718, 50]">.</label>
<label>Base: <input id="base" type="number" value="2.718281828459045">.</label>
<button id="draw" type="button">Draw</button>
<script>
const input = new Proxy({}, {
get(_, thing)
{
return eval(document.getElementById(thing).value);
}
});
const color = ["tomato", "black"];
const canvas_element = document.getElementById("canvas");
const canvas_context = canvas_element.getContext("2d");
canvas_element.width = document.body.clientWidth;
document.getElementById("draw").addEventListener("click", _ => {
const log = (base, value) => (Math.log(value) / Math.log(base));
const weights = input.weights;
const base = input.base;
const orig_total = weights.reduce((a, b) => a + b, 0);
const orig_percents = weights.map(x => x / orig_total * 100);
const adjusted = weights.map(x => x + base);
const logged = adjusted.map(x => log(base, x));
const rebased = logged.map(x => x - 1);
const total = rebased.reduce((a, b) => a + b, 0);
const ratio = rebased.map(x => x / total);
const percents = ratio.map(x => x * 100);
const result = percents.map((percent, index) => `${weights[index]} | ${orig_percents[index]}% --> ${percent}% (${color[index & 1]})`);
console.info(result);
let position = 0;
ratio.forEach((rate, index) => {
canvas_context.beginPath();
canvas_context.moveTo(position, 0);
position += (canvas_element.width * rate);
canvas_context.lineTo(position, 0);
canvas_context.lineWidth = 10;
canvas_context.strokeStyle = color[index & 1];
canvas_context.stroke();
});
});
</script>
</body>
</html>
Estás escalando estos números. Tu conjunto inicial es el dominio, con lo que terminas es el rango. La forma de esa transformación, suena, será un registro o seguirá una potencia.
Resulta que este es un problema muy común en muchos campos, especialmente en la visualización de datos. Es por eso que D3.js, la herramienta de visualización de datos, tiene todo lo que necesita para hacer esto fácilmente.
const x = d3.scale.log().range([0, events]);
Es el derecho para el trabajo. ¡Y si necesitas hacer algunos gráficos, estás listo!
Podría disminuir maxLineLength
y agregar uno al final del cálculo.
Para valores más pequeños que uno, podría usar un factor que normalice todos los valores relativos al primer valor. El valor de inicio es siempre uno, o en términos de vista logarítmica, cero.
var maxLineLength = 20,
lineLen = function(max) {
return function (x) {
return (maxLineLength - 1) * Math.log(x) / Math.log(max) + 1;
};
},
events = [0.1, 1, 5, 20, 50],
normalized = events.map((v, _, a) => v / a[0]),
max = Math.max.apply(null, normalized),
result = normalized.map(lineLen(max));
console.log(result);
console.log(normalized);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Puede tomar log(x+1)
lugar de log(x)
, eso no cambia demasiado el valor y las proporciones se mantienen para números más pequeños.
var maxLineLength = 20;
var lineLen = (x, max) => maxLineLength * Math.log(x+1)/Math.log(max+1);
var events = [ 0.1, 1, 5, 20, 50];
var visualizer = function(events){
var max = Math.max.apply(null, events);
return events.reduce((y, x) => {
y.push(lineLen(x, max));
return y;
}, []);
};
console.log(visualizer(events));
Puedes usar una expresión como Math.pow(x, 0.35)
lugar de Math.log(x)
. Mantiene todos los valores positivos y proporciona el comportamiento que desea para proporciones pequeñas. Puede experimentar con diferentes valores exponenciales en el rango (0,1) para encontrar el que se ajuste a sus necesidades.
var maxLineLength = 20;
var exponent = 0.35;
var lineLen = function(x, max) {
return maxLineLength * Math.pow(x/max, exponent);
}
var events = [0, 0.01, 0.1, 1, 5, 20, 50];
var max = Math.max.apply(null, events);
events.map(function (x) {
console.log(lineLen(x, max));
});
Siempre que sus eventos sean> 0, puede evitar los cambios al escalar los eventos de manera que el valor mínimo sea superior a 1. El escalar se puede calcular basándose en una longitud de línea mínima además de la longitud de línea máxima que ya tiene.
// generates an array of line lengths between minLineLength and maxLineLength
// assumes events contains only values > 0 and 0 < minLineLength < maxLineLength
function generateLineLengths(events, minLineLength, maxLineLength) {
var min = Math.min.apply(null, events);
var max = Math.max.apply(null, events);
//calculate scalar that sets line length for the minimum value in events to minLineLength
var mmr = minLineLength / maxLineLength;
var scalar = Math.pow(Math.pow(max, mmr) / min, 1 / (1 - mmr));
function lineLength(x) {
return maxLineLength * (Math.log(x * scalar) / Math.log(max * scalar));
}
return events.map(lineLength)
}
var events = [0.1, 1, 5, 20, 50];
console.log(''Between 1 and 20'')
generateLineLengths(events, 1, 20).forEach(function(x) {
console.log(x)
})
// 1
// 8.039722549519123
// 12.960277450480875
// 17.19861274759562
// 20
console.log(''Between 5 and 25'')
generateLineLengths(events, 5, 25).forEach(function(x) {
console.log(x)
})
// 5
// 12.410234262651711
// 17.58976573734829
// 22.05117131325855
// 25