Як інтерполювати дані в діапазон у Google Таблицях


9

У мене масив даних:

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

Дані повністю нелінійні. X гарантовано сортується.

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

Відповіді:


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 Таблицях


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

Детально:

FORECAST робить лінійну інтерполяцію, але вона передбачає пряму лінію. Тому нам потрібно знайти два значення, які додають значення, яке ми шукаємо.

Тому ми використовуємо MATCH, щоб знайти перше число, яке дорівнює або вище того, що ми шукаємо.

FORECAST очікує діапазон даних, тому ми використовуємо OFFSET для створення посилання на діапазон даних. МАТЧ є одноіндексованим, тому нам потрібно відняти перше. Ми створюємо діапазон, який є одним широким і двома високими. Це значення гарантовано додасть нам $ C $ 1, наше значення пошуку.


+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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.