Skip to content

Instantly share code, notes, and snippets.

@nascif
Last active February 20, 2017 21:15
Show Gist options
  • Save nascif/f7a447fdf1e854e194095c8caa9314c6 to your computer and use it in GitHub Desktop.
Save nascif/f7a447fdf1e854e194095c8caa9314c6 to your computer and use it in GitHub Desktop.
Valentine
license: mit

Built with blockbuilder.org

Adapted from a D3.js scatter plot example.

At every cycle, a thousand points are moved to new randomly generated positions. They are then scored by a Neural Network model created in JMP and exported to JavaScript.

Scoring results are used to determine if the points fell inside a heart curve and colored accordingly.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<div id="heart"></div> <!-- svg goes here -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var model = require('./jmp_heart.js');
function scoreHeartSizeAndColor(d) {
var sizeAndColor = [1, "grey"];
input = {"X": d[0], "Y": d[1]};
var location = model.score(input, {});
switch (location) {
case "Inside": sizeAndColor = [6, "red"]; break;
case "Outside": sizeAndColor = [3, "blue"]; break;
//default: return "grey";
};
return sizeAndColor;
}
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
// Ranges come from original data used to create jmp_score model
var xRange = [-1.20, 1.20];
var yRange = [-17, 17];
function newX() {
return getRandomArbitrary(xRange[0], xRange[1]);
}
function newY() {
return getRandomArbitrary(yRange[0], yRange[1]);
}
function newDataSet(numDataPoints) {
var dataset = []; // Initialize empty array
for (var i = 0; i < numDataPoints; i++) {
var newNumber1 = newX();
var newNumber2 = newY();
dataset.push([newNumber1, newNumber2]); // Add new number to array
}
return dataset;
}
// Setup data
var numDataPoints = 1000; // Number of dummy data points
var dataset = newDataSet(numDataPoints);
// Setup settings for graphic
var canvas_width = 500;
var canvas_height = 500;
var padding = 30; // for chart edges
// Create scale functions
var xScale = d3.scale.linear() // xScale is width of graphic
.domain(xRange)
.range([padding, canvas_width - padding * 2]); // output range
var yScale = d3.scale.linear() // yScale is height of graphic
.domain(yRange)
.range([canvas_height - padding, padding]); // remember y starts on top going down so we flip
// Create SVG element
var svg = d3.select("#heart") // This is where we put our vis
.append("svg")
.attr("width", canvas_width)
.attr("height", canvas_height)
// Create Circles
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle") // Add circle svg
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) { // Circle's Y
return yScale(d[1]);
})
.attr("r", 2); // radius
function updateCircles() {
svg.selectAll("circle")
.data(dataset) // Update with new data
.transition() // Transition from old to new
.duration(1000) // Length of animation
.each("start", function(d) { // Start animation
d3.select(this) // 'this' means the current element
.attr("fill", "grey") // Change color
.attr("r", 2); // Change size
})
.delay(function(d, i) {
return i / dataset.length * 500; // Dynamic delay (i.e. each item delays a little longer)
})
//.ease("linear") // Transition easing - default 'variable' (i.e. has acceleration), also: 'circle', 'elastic', 'bounce', 'linear'
.attr("cx", function(d) {
return xScale(d[0]); // Circle's X
})
.attr("cy", function(d) {
return yScale(d[1]); // Circle's Y
})
.each("end", function(d) { // End animation
var sizeAndColor = scoreHeartSizeAndColor(d)
d3.select(this) // 'this' means the current element
.transition()
.duration(500)
.attr("r", sizeAndColor[0]) // Change radius
.attr("fill", sizeAndColor[1]); // Change color
});
}
var updateHeart = function() {
dataset = newDataSet(dataset.length);
updateCircles();
};
window.setInterval(updateHeart, 2500);
// first pass
updateCircles();
},{"./jmp_heart.js":2}],2:[function(require,module,exports){
"use strict";
var jmp = require('./jmp_score.js');
/* ==================================================================
Copyright(C) 2018 SAS Institute Inc.All rights reserved.
Notice:
The following permissions are granted provided that the
above copyright and this notice appear in the score code and
any related documentation.Permission to copy, modify
and distribute the score code generated using
JMP(R) software is limited to customers of SAS Institute Inc. ("SAS")
and successive third parties, all without any warranty, express or
implied, or any other obligation by SAS.SAS and all other SAS
Institute.Inc.product and service names are registered
trademarks or trademarks of SAS Institute Inc.in the USA
and other countries.Except as contained in this notice,
the name of the SAS Institute Inc. and JMP shall not be used in
the advertising or promotion of products or services without
prior written authorization from SAS Institute Inc.
================================================================== */
/* JavaScript code generated by JMP v14.0.0 */
var getModelMetadata = function() {
return {"creator": "Neural", "modelName": "", "predicted": "Location", "table": "Heart", "version": "14.0.0", "timestamp": "2017-02-14T18:49:48Z"};
};
var getInputMetadata = function() {
return {
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "/",
"type": "object",
"properties": {
"X": {
"id": "X",
"type": "number"
},
"Y": {
"id": "Y",
"type": "number"
} },
"additionalProperties": false,
};
};
var getOutputMetadata = function() {
return {
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "/",
"type": "object",
"properties": {
"Probability( Location=Inside )": {
"id": "Probability( Location=Inside )",
"type": "number"
},
"Probability( Location=Outside )": {
"id": "Probability( Location=Outside )",
"type": "number"
},
"Most Likely Location": {
"id": "Most Likely Location",
"type": "string"
} },
"additionalProperties": false,
};
};
var score = function(indata, outdata) {
var H2_1;
var H2_2;
var H2_3;
var H2_4;
var H2_5;
var H2_6;
var H2_7;
var H2_8;
var H2_9;
var H2_10;
var H1_1;
var _temp0 = -0.0025889527863438;
var H1_2;
var _temp1 = -0.561104818648586;
var H1_3;
var _temp2 = -1.07587651435724;
var H1_4;
var _temp3 = 0.261864321167137;
var H1_5;
var _temp4 = 0.666162571175294;
var _temp5 = -14.5165870963556;
var _temp6 = -14.5165870963556;
var _temp7 = -14.5165870963556;
var _temp8;
var _temp9 = [];
var _temp10 = "";
H2_1 = Math.tanh((0.906862452221691 + 0.28589541239972 * indata['X'] + 0.123476732643996 * indata['Y']));
H2_2 = Math.tanh((0.389272400663128 + -4.49035082943397 * indata['X'] + 0.029994437264834 * indata['Y']));
H2_3 = Math.tanh((0.135670707094981 + 0.517955296902832 * indata['X'] + -0.00491296263388025 * indata['Y']));
H2_4 = Math.tanh((1.24990105123269 + -1.82515191811896 * indata['X'] + 0.0666137664602011 * indata['Y']));
H2_5 = Math.tanh((0.359229490439728 + 0.0258170532501211 * indata['X'] + 0.0208812618846582 * indata['Y']));
H2_6 = Math.tanh((0.374851968137568 + 4.26737072750981 * indata['X'] + 0.028198711283364 * indata['Y']));
H2_7 = Math.tanh((1.38109738462623 + 0.225812298040086 * indata['X'] + -0.141652342202742 * indata['Y']));
H2_8 = Math.tanh((-0.122731904982036 + -0.452476198958453 * indata['X'] + 0.00391439763422938 * indata['Y']));
H2_9 = Math.tanh((-1.23636971826989 + -1.77282769376887 * indata['X'] + -0.100681293644277 * indata['Y']));
H2_10 = Math.tanh((0.222879336557181 + 0.139106195899012 * indata['X'] + 0.0100462854321572 * indata['Y']));
_temp0 += 1.01721439823662 * H2_1;
_temp0 += -0.0990308956267049 * H2_10;
_temp0 += 1.12716455100972 * H2_2;
_temp0 += -0.236721434972115 * H2_3;
_temp0 += 1.02236279743244 * H2_4;
_temp0 += -0.0219020460106092 * H2_5;
_temp0 += -0.630461324126668 * H2_6;
_temp0 += 1.5970866200482 * H2_7;
_temp0 += 0.267748304254134 * H2_8;
_temp0 += -0.197993252816764 * H2_9;
H1_1 = Math.tanh(_temp0);
_temp1 += 0.0682915694354052 * H2_1;
_temp1 += -0.109291835705242 * H2_10;
_temp1 += -0.635229268164431 * H2_2;
_temp1 += -0.0396494694094181 * H2_3;
_temp1 += 0.388047613790836 * H2_4;
_temp1 += -0.212086684680784 * H2_5;
_temp1 += -0.675181583160569 * H2_6;
_temp1 += 0.432940053549962 * H2_7;
_temp1 += 0.0306297055684985 * H2_8;
_temp1 += -0.337968618584483 * H2_9;
H1_2 = Math.tanh(_temp1);
_temp2 += 0.107124211798651 * H2_1;
_temp2 += -0.225756898877663 * H2_10;
_temp2 += -1.20684928223233 * H2_2;
_temp2 += -0.0598737048818925 * H2_3;
_temp2 += 0.988838152231585 * H2_4;
_temp2 += -0.339143237903077 * H2_5;
_temp2 += -1.19274318844864 * H2_6;
_temp2 += 0.797801775665556 * H2_7;
_temp2 += -0.000402920388415558 * H2_8;
_temp2 += -0.809904071880251 * H2_9;
H1_3 = Math.tanh(_temp2);
_temp3 += 0.65211608156898 * H2_1;
_temp3 += 0.153358826229231 * H2_10;
_temp3 += -0.577284797592672 * H2_2;
_temp3 += 0.429987946104019 * H2_3;
_temp3 += -0.0214944881562618 * H2_4;
_temp3 += -0.0197578404119275 * H2_5;
_temp3 += 0.81602931772807 * H2_6;
_temp3 += 1.55441041269438 * H2_7;
_temp3 += -0.354654919286015 * H2_8;
_temp3 += -1.35246623279643 * H2_9;
H1_4 = Math.tanh(_temp3);
_temp4 += -0.0205229428050754 * H2_1;
_temp4 += 0.10826081444459 * H2_10;
_temp4 += 0.689829328369476 * H2_2;
_temp4 += 0.0180524122986651 * H2_3;
_temp4 += -0.487032459020667 * H2_4;
_temp4 += 0.235838175047739 * H2_5;
_temp4 += 0.725612954061537 * H2_6;
_temp4 += -0.529678615104436 * H2_7;
_temp4 += -0.0626682444697669 * H2_8;
_temp4 += 0.405148933483215 * H2_9;
H1_5 = Math.tanh(_temp4);
_temp5 += 15.3285630835005 * H1_1;
_temp5 += 10.5633968636429 * H1_2;
_temp5 += 19.5732699419147 * H1_3;
_temp5 += 19.2877635933711 * H1_4;
_temp5 += -12.149757986894 * H1_5;
_temp6 += 15.3285630835005 * H1_1;
_temp6 += 10.5633968636429 * H1_2;
_temp6 += 19.5732699419147 * H1_3;
_temp6 += 19.2877635933711 * H1_4;
_temp6 += -12.149757986894 * H1_5;
outdata['Probability( Location=Inside )'] = Math.exp(_temp5) / (1 + Math.exp(_temp6));
_temp7 += 15.3285630835005 * H1_1;
_temp7 += 10.5633968636429 * H1_2;
_temp7 += 19.5732699419147 * H1_3;
_temp7 += 19.2877635933711 * H1_4;
_temp7 += -12.149757986894 * H1_5;
outdata['Probability( Location=Outside )'] = 1 / (1 + Math.exp(_temp7));
_temp9[0] = outdata['Probability( Location=Inside )'];
_temp9[1] = outdata['Probability( Location=Outside )'];
_temp8 = jmp.max_array(2, _temp9);
switch (_temp8) {
case 0: {
_temp10 = "Inside";
}
break;
case 1: {
_temp10 = "Outside";
}
break;
default: {
_temp10 = "";
}
}
outdata['Most Likely Location'] = _temp10;
return outdata['Most Likely Location'];
};
module.exports = {
getInputMetadata: getInputMetadata,
getOutputMetadata : getOutputMetadata,
getModelMetadata : getModelMetadata,
score : score
};
},{"./jmp_score.js":3}],3:[function(require,module,exports){
"use strict";
/*-----------------------------------------------------------------
* jmp_score.js
* Helper module with defines and functions supporting
* the JavaScript language scoring code generated by JMP
*
* Required by customer code: Yes
* Follows the require.js module convention
* Use with a compatible environment like node.js or browserify
*-----------------------------------------------------------------*/
var squish = function(x) {
return 1.0 / (1.0 + Math.exp(-x));
};
var squash = function(x) {
return 1.0 / (1.0 + Math.exp(x));
};
var is_missing = function (x) {
return isNaN(x) || !isFinite(x) || x === undefined;
};
/*-----------------------------------------------------------------
* return the index of the max value found in an array
* or -1 if all are missing
*-----------------------------------------------------------------*/
var max_array = function(n, lst) {
var maxval = -Infinity;
var maxidx = 0;
var count_miss = 0;
for (var i = 0; i < n; i++) {
if (is_missing(lst[i])) {
count_miss++;
}
else if (maxval < lst[i]) {
maxval = lst[i];
maxidx = i;
}
}
return (count_miss == n) ? -1 : maxidx;
};
/*-----------------------------------------------------------------
* return the index of the min value found in an array
* or -1 if all are missing
*-----------------------------------------------------------------*/
var min_array = function(n, lst) {
var minval = Infinity;
var minidx = 0;
var count_miss = 0;
for (var i = 0; i < n; i++) {
if (is_missing(lst[i])) {
count_miss++;
}
else if (minval > lst[i]) {
minval = lst[i];
minidx = i;
}
}
return (count_miss == n) ? -1 : minidx;
};
/*-----------------------------------------------------------------
* Returns true if the numbers are identical using straight comparison.
* If necessary, replace with a suitable comparison using a value of EPSILON
* appropriate for your domain.
*-----------------------------------------------------------------*/
var numeq = function(x, y) {
return x == y;
// return Math.abs(a - b) < EPSILON;
}
module.exports = {
squish: squish,
squash: squash,
is_missing: is_missing,
max_array: max_array,
min_array: min_array,
numeq: numeq
};
},{}]},{},[1]);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment