Как интерполировать данные в диапазоне в Google Sheets

9

У меня есть массив с данными:

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

Данные полностью нелинейны. Х гарантированно будет отсортирован.

Теперь для любого заданного значения я хотел бы иметь линейную интерполяцию между числами (например, 3 => 50, 4 => 55, 5 => 60). Билинейная интерполяция была бы еще лучше, но я держу свои ожидания на низком уровне.

EboMike
источник

Ответы:

9

Этот скрипт будет делать то же самое (плюс немного больше).

Код

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

Разъяснения

В начале скрипта есть небольшая обработка ошибок. После этого он найдет первую наименьшую запись по сравнению с входным значением. После того, как он найден, он немного подсчитает и представит результат.

Запись

Если выбранное значение равно 20, сценарий возвращает 150 в соответствии с формулой #DIV/0.

Скриншот

введите описание изображения здесь

формула

Используйте следующую формулу, чтобы учесть все значения

=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))

пример

Добавьте скрипт в Сервис> Редактор скриптов и нажмите кнопку сохранения (аутентификация не требуется).

Я создал для вас файл примера: как интерполировать данные в диапазоне в Google Sheets

Джейкоб Ян Туинстра
источник
2
Спасибо Джейкоб! Честно говоря, мне почти нравится моя версия, потому что она работает и на мобильных устройствах (нативное приложение Android Sheets и мобильная версия веб-приложения не имеют поддержки сценариев), но ваша функция определенно чище и элегантнее , Я выбираю твой ответ.
EboMike
@EboMike Я посмотрел на свой код и заметил ошибку. Я пересмотрел код и придумал формулу, чтобы вы могли использовать ее в своем мобильном приложении.
Джейкоб Ян Туинстра
2
И поэтому очень жаль, что вы не можете высказать ответ более одного раза :) Спасибо, Джейкоб.
EboMike
10

Я нашел способ сделать это - может быть, есть лучший способ, но вот что я придумал:

Предполагая, что данные находятся в A1: B10 и $ C $ 1 содержит ключ для поиска:

=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))

В деталях:

ПРОГНОЗ делает линейную интерполяцию, но предполагает прямую линию. Таким образом, нам нужно найти два значения, которые содержат значение, которое мы ищем.

Поэтому мы используем MATCH, чтобы найти первое число, которое равно или больше того, что мы ищем.

FORECAST ожидает диапазон данных, поэтому мы используем OFFSET для создания ссылки на диапазон данных. MATCH с одним индексом, поэтому сначала нужно вычесть один. Мы создаем диапазон, один широкий и два высоких. Это значение гарантированно вмещает $ C $ 1, значение нашего поиска.

EboMike
источник
+1; хорошая формула !! Выбор x=20приведет к #DIV/0.
Джейкоб Ян Туинстра
1

Это небольшая модификация сценария Джейкоба Яна Туинстры , позволяющая ему принимать массив или значение в качестве третьего аргумента, так что интерполированная функция может быть вычислена во многих местах одновременно. Единственная разница - несколько строк, добавленных в начале; это быстрый способ превратить практически любую пользовательскую функцию в пользовательскую функцию, которая принимает массив.

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;
}
user135384
источник