Cómo interpolar datos en un rango en Hojas de cálculo de Google

9

Tengo una matriz con datos:

   X      Y
   3     50
   5     60
   9    120
  11    130
  18     90
  20    150

Los datos son completamente no lineales. X está garantizado para ser ordenado.

Ahora, para cualquier valor dado, me gustaría tener una interpolación lineal entre los números (por ejemplo, 3 => 50, 4 => 55, 5 => 60). Una interpolación bilineal sería aún mejor, pero mantengo mis expectativas bajas.

EboMike
fuente

Respuestas:

9

Este script hará lo mismo (y un poco más).

Código

function myInterpolation(x, y, value) {
  if(value > Math.max.apply(Math, x) || value < Math.min.apply(Math, x)) {
    throw "value can't be interpolated !!";
    return;
  }

  var check = 0, index;
  for(var i = 0, iLen = x.length; i < iLen; i++) {
    if(x[i][0] == value) {
      return y[i][0];
    } else {      
      if(x[i][0] < value && ((x[i][0] - check) < (value - check))) {
        check = x[i][0];
        index = i;
      }
    }
  }

  var xValue, yValue, xDiff, yDiff, xInt;
  yValue = y[index][0];
  xDiff = x[index+1][0] - check;
  yDiff = y[index+1][0] - yValue;
  xInt = value - check; 

  return (xInt * (yDiff / xDiff)) + yValue;
}

Explicado

Al comienzo del script, hay un pequeño manejo de errores. Después de eso, encontrará la primera entrada más baja en comparación con el valor de entrada. Una vez encontrado, hará algunas matemáticas y presentará el resultado.

Nota

Si el valor seleccionado es igual a 20, el script devuelve 150 como el rendimiento de la fórmula #DIV/0.

Captura de pantalla

ingrese la descripción de la imagen aquí

Fórmula

Use la siguiente fórmula para tener en cuenta todos los valores

=IF(
   ISNA(
     MATCH(C2,A2:A7,0)),
   FORECAST(
     $C$2,
     OFFSET(B$2,MATCH($C$2,A$2:A$7,1)-1,0,2,1),
     OFFSET(A$2,MATCH($C$2,A$2:A$7,1)-1,0,2,1)), 
   INDEX(
     B2:B7,
     MATCH(C2,A2:A7,0)
     ,0)
 )

 copy / paste
 =IF(ISNA(MATCH(C2, A2:A7, 0)), FORECAST($C$2,OFFSET(B$2,MATCH($C$2,A$2:A$7,1)-1,0,2,1),OFFSET(A$2,MATCH($C$2,A$2:A$7,1)-1,0,2,1)), INDEX(B2:B7, MATCH(C2, A2:A7, 0), 0))

Ejemplo

Agregue la secuencia de comandos en Herramientas> Editor de secuencias de comandos y presione el botón Guardar (no se necesita autenticación).

He creado un archivo de ejemplo para usted: Cómo interpolar datos en un rango en Hojas de cálculo de Google

Jacob Jan Tuinstra
fuente
2
Gracias Jacob! Para ser honesto, casi me gusta más mi versión porque también funciona en dispositivos móviles (la aplicación nativa de Android Sheets y la versión móvil de la aplicación web no tienen soporte para scripts), pero su función es definitivamente más limpia y elegante . Así que elijo tu respuesta.
EboMike
@EboMike Miré mi código y noté un error. Revisé el código y se me ocurrió una fórmula, para que pueda usarlo en su aplicación móvil.
Jacob Jan Tuinstra
2
Y es por eso que es desafortunado que no puedas votar una respuesta más de una vez :) Gracias Jacob.
EboMike
10

Encontré una manera de hacerlo; puede haber una mejor manera, pero esto es lo que se me ocurrió:

Suponiendo que los datos están en A1: B10 y $ C $ 1 contiene la clave para buscar:

=FORECAST($C$1,
    OFFSET(B$1,MATCH($C$1,A$1:A$10,1)-1,0,2,1),
    OFFSET(A$1,MATCH($C$1,A$1:A$10,1)-1,0,2,1))

En detalle:

PRONOSTICO hace una interpolación lineal, pero asume una línea recta. Por lo tanto, debemos encontrar los dos valores que encierran el valor que estamos buscando.

Entonces usamos MATCH para encontrar el primer número que sea igual o mayor a lo que estamos buscando.

PRONÓSTICO espera un rango de datos, por lo que usamos OFFSET para crear una referencia a un rango de datos. MATCH está indexado en uno, por lo que debemos restar uno primero. Creamos un rango que es uno ancho y dos altos. Se garantiza que este valor incluya $ C $ 1, nuestro valor de búsqueda.

EboMike
fuente
+1; buena formula !! Elegir x=20resultará en #DIV/0.
Jacob Jan Tuinstra
1

Esta es una pequeña modificación del script de Jacob Jan Tuinstra , que le permite tomar una matriz o un valor como tercer argumento, de modo que la función interpolada se pueda calcular en muchos lugares a la vez. La única diferencia son algunas líneas agregadas al principio; Esta es una forma rápida de convertir prácticamente cualquier función personalizada en una función personalizada que acepte una matriz.

function myInterpolation(x, y, value) {
  if (value.map) {
    return value.map(function(v) {
      return myInterpolation(x, y, v);
    });
  }
  //  the rest stays the same

  if (value > Math.max.apply(Math, x) || value < Math.min.apply(Math, x)) {
    throw "value can't be interpolated !!";
    return;
  }

  var check = 0, index;
  for(var i = 0, iLen = x.length; i < iLen; i++) {
    if(x[i][0] == value) {
      return y[i][0];
    } else {      
      if(x[i][0] < value && ((x[i][0] - check) < (value - check))) {
        check = x[i][0];
        index = i;
      }
    }
  }

  var xValue, yValue, xDiff, yDiff, xInt;
  yValue = y[index][0];
  xDiff = x[index+1][0] - check;
  yDiff = y[index+1][0] - yValue;
  xInt = value - check; 

  return (xInt * (yDiff / xDiff)) + yValue;
}
usuario135384
fuente