Created
July 15, 2021 14:26
-
-
Save jimcrozier/a95cfb714391ea88ef814ea5ebea9569 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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