Skip to content

Instantly share code, notes, and snippets.

@jimcrozier
Created July 15, 2021 14:26
Show Gist options
  • Save jimcrozier/a95cfb714391ea88ef814ea5ebea9569 to your computer and use it in GitHub Desktop.
Save jimcrozier/a95cfb714391ea88ef814ea5ebea9569 to your computer and use it in GitHub Desktop.
CREATE TEMP FUNCTION
loess(x ARRAY<FLOAT64>,
y ARRAY<FLOAT64>)
RETURNS STRUCT<x ARRAY<FLOAT64>,
y ARRAY<FLOAT64>,
pred ARRAY<FLOAT64>>
LANGUAGE js AS """
//adapted from https://gist.github.com/avibryant/1151823
//adapted from the LoessInterpolator in org.apache.commons.math
//turtles on turles. Matches R pretty damn close:
// R > pred = loess(dist ~ speed, cars, spam = -.3)
loess_out = loess_fn(x,y,.3)
//return(1.6666)
//return(loess_out)
var out = {};
out["x"] = x;
out["y"] = y;
out["pred"] = loess_out;
return out;
function loess_pairs(pairs, bandwidth)
{
var xval = pairs.map(function(pair){return pair[0]});
var yval = pairs.map(function(pair){return pair[1]});
console.log(xval);
console.log(yval);
var res = loess(xval, yval, bandwidth);
console.log(res);
return xval.map(function(x,i){return [x, res[i]]});
}
function loess_fn(xval, yval, bandwidth=0.3)
{
function tricube(x) {
var tmp = 1 - x * x * x;
return tmp * tmp * tmp;
}
var res = [];
var left = 0;
var right = Math.floor(bandwidth * xval.length) - 1;
for(var i in xval)
{
var x = xval[i];
if (i > 0) {
if (right < xval.length - 1 &&
xval[right+1] - xval[i] < xval[i] - xval[left]) {
left++;
right++;
}
}
var edge;
if (xval[i] - xval[left] > xval[right] - xval[i])
edge = left;
else
edge = right;
var denom = Math.abs(1.0 / (xval[edge] - x));
var sumWeights = 0;
var sumX = 0, sumXSquared = 0, sumY = 0, sumXY = 0;
var k = left;
while(k <= right)
{
var xk = xval[k];
var yk = yval[k];
var dist;
if (k < i) {
dist = (x - xk);
} else {
dist = (xk - x);
}
var w = tricube(dist * denom);
var xkw = xk * w;
sumWeights += w;
sumX += xkw;
sumXSquared += xk * xkw;
sumY += yk * w;
sumXY += yk * xkw;
k++;
}
var meanX = sumX / sumWeights;
var meanY = sumY / sumWeights;
var meanXY = sumXY / sumWeights;
var meanXSquared = sumXSquared / sumWeights;
var beta;
if (meanXSquared == meanX * meanX)
beta = 0;
else
beta = (meanXY - meanX * meanY) / (meanXSquared - meanX * meanX);
var alpha = meanY - beta * meanX;
res[i] = beta * x + alpha;
}
return res;
}
""" ;
with
dat as (
select * from unnest(ARRAY<STRUCT<y INT64, x float64>>
[(2,4),(10,4),(4,7),(22,7),(16,8),(10,9),(18,10),(26,10),(34,10),(17,11),(28,11),(14,12),(20,12),(24,12),(28,12),(26,13),(34,13),(34,13),(46,13),(26,14),(36,14),(60,14),(80,14),(20,15),(26,15),(54,15),(32,16),(40,16),(32,17),(40,17),(50,17),(42,18),(56,18),(76,18),(84,18),(36,19),(46,19),(68,19),(32,20),(48,20),(52,20),(56,20),(64,20),(66,22),(54,23),(70,24),(92,24),(93,24),(120,24),(85,25)]))
, preds as (
SELECT
loess(ARRAY_AGG(cast(x as float64)),
ARRAY_AGG(cast(y as float64)))lr
FROM
dat)
select y, x, pred from preds t CROSS JOIN unnest(lr.y) y WITH OFFSET nd
LEFT JOIN unnest(lr.x) x WITH OFFSET nv ON nd = nv
LEFT JOIN unnest(lr.pred) pred WITH OFFSET pv ON nd = pv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment