Google E-Tablolar'daki bir aralıktaki verileri enterpolasyonlama


9

Ben verilerle bir dizi var:

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

Veriler tamamen doğrusal değildir. X'in sıralanması garanti edilir.

Şimdi herhangi bir değer için, sayılar arasında doğrusal enterpolasyon olmasını istiyorum (örneğin, 3 => 50, 4 => 55, 5 => 60). Bir bilinear enterpolasyon daha da güzel olurdu, ama beklentilerimi düşük tutuyorum.

Yanıtlar:


9

Bu komut dosyası da aynı şeyi yapacaktır (artı biraz daha fazla).

kod

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

Açıklaması

Komut dosyasının başında küçük bir hata işleme vardır. Bundan sonra, giriş değerine kıyasla ilk en düşük girişi bulur. Bulduktan sonra, biraz matematik yapar ve sonucu sunar.

Not

Seçilen değer 20'ye eşitse, komut dosyası formülün verildiği yerdeki 150 değerini döndürür #DIV/0.

Ekran görüntüsü

resim açıklamasını buraya girin

formül

Tüm değerleri hesaba katmak için aşağıdaki formülü kullanın

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

Misal

Komut dosyasını Araçlar> Komut Dosyası düzenleyicisi altına ekleyin ve kaydet düğmesine basın (kimlik doğrulaması gerekmez).

Sizin için bir örnek dosya oluşturdum: Google E-Tablolar'daki bir aralıktaki verileri enterpolasyona alma


2
Teşekkürler Jacob! Dürüst olmak gerekirse, sürümümü neredeyse daha çok seviyorum çünkü mobil cihazlarda da çalışıyor (yerel Android E-Tablolar uygulaması ve web uygulamasının mobil sürümünde komut dosyası desteği afaict'i yok), ancak işleviniz kesinlikle daha temiz ve daha zarif . Bu yüzden cevabınızı seçiyorum.
EboMike

@EboMike Koduma baktım ve bir hata fark ettim. Kodu revize ettim ve bir formül buldum, böylece mobil uygulamanızda kullanabilirsiniz.
Jacob Jan Tuinstra

2
Ve bu yüzden bir cevabı birden fazla kez değerlendirememeniz talihsiz bir durumdur :) Teşekkürler Jacob.
EboMike

10

Bunu yapmanın bir yolunu buldum - daha iyi bir yol olabilir, ama ben bu kadar geldim:

Verilerin A1: B10 ve $ C $ 1 olduğu varsayıldığında, aranacak anahtarı içerir:

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

Detayda:

TAHMİN doğrusal bir enterpolasyon yapar, ancak düz bir çizgi varsayar. Bu yüzden aradığımız değeri kapsayan iki değeri bulmamız gerekiyor.

Bu nedenle aradığımız şeye eşit veya daha yüksek olan ilk sayıyı bulmak için MATCH kullanırız.

TAHMİN bir veri aralığı beklediğinden, bir veri aralığına referans oluşturmak için OFFSET kullanırız. MATCH bir endekslidir, bu yüzden önce bir tane çıkarmamız gerekir. Bir geniş ve iki yüksek bir aralık oluşturuyoruz. Bu değerin, arama değerimiz olan $ C $ 1 değerini içermesi garanti edilir.


+ 1; güzel formül !! Seçim x=20sonuçlanır #DIV/0.
Jacob Jan Tuinstra

1

Bu, Jacob Jan Tuinstra'nın komut dosyasının küçük bir modifikasyonudur ve üçüncü argüman olarak bir dizi veya değer almasını sağlar, böylece enterpolasyonlu fonksiyon aynı anda birçok yerde hesaplanabilir. Tek fark, başlangıçta eklenen birkaç satırdır; bu, hemen hemen tüm özel işlevleri bir diziyi kabul eden özel bir işleve dönüştürmenin hızlı bir yoludur.

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.