Skip to content

Latest commit

 

History

History
497 lines (370 loc) · 12.7 KB

Stdlib.md

File metadata and controls

497 lines (370 loc) · 12.7 KB

stdlib Experiments

stdlib is a Javascript library supporting both NodeJS and web browsers. Quoting the authors:

Stdlib is a standard library for JavaScript and Node.js, with an emphasis on numeric computing. The library provides a collection of robust, high performance libraries for mathematics, statistics, streams, utilities, and more.

The examples below are adapted from various stdlib documentation examples, although many uses of console.log() have been replaced with writing to Smartdown variables and having these variables rendered via Smartdown cells. The other minor change needed to use stdlib within Smartdown is to replace usage of require('@stdlib/foo/bar') with a reference to Smartdown's namesspaced version stdlib.foo.bar. This is currently necessary because stdlib is being bundled (via Webpack) with Smartdown. Eventually, this will be replaced with a more dynamic mechanism.

The source for this example can be viewed and transiently edited at Smartdown Stdlib Example

CDF Experiments

Trying to plot the CDF for various $\sigma$ and $\mu$ versions of the normal distribution.

Based upon the Stdlib examples:


const thisDiv = this.div;

var toHTML = stdlib.vdomToHtml;
var randn = stdlib.random.base.boxMuller;
var Plot = stdlib.plot;
var cdf = stdlib.stats.base.dists.normal.cdf;

var x = new Float64Array( 100 );
var y1 = new Float64Array( x.length );
var y2 = new Float64Array( x.length );
var y3 = new Float64Array( x.length );
var y4 = new Float64Array( x.length );
for (var i = 0; i < x.length; i++) {
  x[ i ] = (((i + 1) * 1.0) - x.length / 2) / 25.0;
  y1[ i ] = cdf(x[i], 0, 1);
  y2[ i ] = cdf(x[i], 0, 0.5);
  y3[ i ] = cdf(x[i], 0, 0.2);
  y4[ i ] = cdf(x[i], 0, 0.1);
}

var h = new Plot(
    [x, x, x, x],
    [y1, y2, y3, y4],
    {
      yMin: -0.1,
      yMax: 1.1,
      'description': 'Plotting the CDF of the Normal Distribution',
      'title': 'CDF of the Normal Distribution for various σ',
      'labels': [
        'σ = 1',
        'σ = 0.5',
        'σ = 0.2',
        'σ = 0.1'
      ],
      lineWidth: 5,
      width: 450,
      paddingLeft: 30,
      paddingRight: 50,
    });

thisDiv.innerHTML = stdlib.vdomToHtml( h.render() );

Machine Learning

From this example: https://stdlib.io/develop/docs/api/@stdlib/ml/online-binary-classification

I honestly don't understand this example (yet), I've just transliterated it to Smartdown.

//smartdown.import=stdlib

var binomial = stdlib.random.base.binomial;
var normal = stdlib.random.base.normal;
var exp = stdlib.math.base.special.exp;
var onlineBinaryClassification = stdlib.ml.onlineBinaryClassification;

var phat;
var lp;
var x1;
var x2;
var y;
var i;

// Create model:
var model = onlineBinaryClassification({
  'lambda': 1e-3,
  'loss': 'log',
  'intercept': true
});

// Update model as data comes in...
for ( i = 0; i < 10000; i++ ) {
  x1 = normal( 0.0, 1.0 );
  x2 = normal( 0.0, 1.0 );
  lp = (3.0 * x1) - (2.0 * x2) + 1.0;
  phat = 1.0 / ( 1.0 + exp( -lp ) );
  y = binomial( 1, phat ) ? 1.0 : -1.0;
  model.update( [ x1, x2 ], y );
}

// Extract model coefficients:
var markdownCoefficients =
`
### Model Coefficients

$$
${model.coefs}
$$
`;

// Predict new observations:
// console.log( 'Pr(Y=1)_hat = %d; x1 = %d; x2 = %d', model.predict( [0.9, 0.1], 'probability' ), 0.9, 0.1 );
// console.log( 'y_hat = %d; x1 = %d; x2 = %d', model.predict( [0.1, 0.9], 'link' ), 0.1, 0.9 );
// console.log( 'y_hat = %d; x1 = %d; x2 = %d', model.predict( [0.9, 0.9], 'link' ), 0.9, 0.9 );

const p1 = 0.9;
const p2 = 0.1;
const predictionProbability = model.predict( [p1, p2], 'probability' );
const predictionLink12 = model.predict( [p1, p2], 'link' );
const predictionLink22 = model.predict( [p1, p2], 'link' );

var markdownOutput =
`
### Output

|Expression|Value|$x_1$|$x_2$|
|:---:|---:|---:|---:|
|$\\hat{P_r(Y=1)}$|${predictionProbability}|${x1}|${x2}|
|$\\hat{y}$|${predictionLink12}|${x1}|${x2}|
|$\\hat{y}$|${predictionLink22}|${x2}|${x2}|
`;

smartdown.setVariable('MarkdownCoefficients', markdownCoefficients);
smartdown.setVariable('MarkdownOutput', markdownOutput);


Unicode Sparklines

From https://stdlib.io/develop/docs/api/@stdlib/plot/sparklines/unicode/column

For this example, we modify the use of console.log() to instead place the Unicode sparkline into a Smartdown variable, where it will be rendered automatically as it is updated.

The Generated Plot

The stdlib.plot.sparklines.unicode.column script
//smartdown.import=stdlib

var randu = stdlib.random.base.randu;
var columnChart = stdlib.plot.sparklines.unicode.UnicodeColumnChartSparkline;

var chart;
var data;
var id;
var i;

// Generate some random data...
data = new Float64Array( 30 );
for ( i = 0; i < data.length; i++ ) {
  data[ i ] = randu() * 100.0;
}

// Create a new column chart:
chart = columnChart();

// Set the chart data:
chart.data = data;

// Configure the chart to support streaming data:
chart.window = data.length;
chart.yMin = 0.0;
chart.yMax = 100.0;

// Update the terminal chart with new data every second:
id = setInterval( update, 1000 );

// After some time, stop updating and close:
setTimeout( stop, 20000 );

function update() {
  // Update the chart with new data:
  chart.push( randu() * 100.0 );

  var rendered = chart.render();
  smartdown.setVariable('SparklinePlot', rendered);
}

function stop() {
  clearInterval( id );
}

Plots

Adapted from https://stdlib.io/develop/docs/api/@stdlib/plot/ctor

This example generates a plot as virtual DOM, which is then converted to HTML prior to rendering within the Smartdown-created playable's div (this.div).

//smartdown.import=stdlib

const thisDiv = this.div;

var toHTML = stdlib.vdomToHtml;
var randn = stdlib.random.base.boxMuller;
var Plot = stdlib.plot;

var now;
var x;
var y;
var i;

// Create some data...
now = ( new Date() ).getTime();
x = new Float64Array( 100 );
y = new Float64Array( x.length );
for ( i = 0; i < x.length; i++ ) {
  x[ i ] = now + (i * 360000);
  y[ i ] = 50.0 + (10.0 * randn());
}

// Create a new plot:
var h = new Plot( [x], [y], {
    'width': 500,
    'height': 500,
    'xScale': 'time',
    'xTickFormat': '%H:%M'
});

// Render as a virtual DOM tree:
var vtree = h.render();
// console.log( JSON.stringify( vtree ) );
smartdown.setVariable('treeJSON', vtree);

// Transform the virtual DOM tree to HTML:
var html = toHTML( vtree );
// console.log( html );
thisDiv.innerHTML = html;

The generated virtual DOM Tree


NLP (Natural Language Processing)

Derived from this example https://stdlib.io/docs/api/v0.0.90/@stdlib/nlp/lda, the following playable will utilize the SOTU State of the Union Dataset to perform a simple clustering of the speech texts into three main themes.

This example also uses the English Stop Words Dataset.

Smartdown outputs

The following smartdown cells will be populated with values generated by the stdlib script below. They are initially empty or undefined.

Stopwords

State of the Union Texts (first 100 characters)

Average $\theta$ per topic

Top words associated with each topic

The playable stdlib example.
//smartdown.import=stdlib

var that = this;

// Hack to keep the progress bar up while everything happens in
// this long-running script. Better would be a smartdown.setProgress()
// method that would not leak the implementation details.
//
var saveProgress = this.progress; // Oh, the hack
this.progress = null;

stdlib.loadSOTU(function() {
  var roundn = stdlib.math.base.special.roundn;
  var stopwords = stdlib.datasets['stopwords_en'];
  var lda = stdlib.nlp.lda;

  var STOPWORDS = stopwords();
  var terms;
  var model;
  var str;
  var i;

  smartdown.setVariable('stopwords', STOPWORDS);

  function getText(e) {
    function remove(word) {
      var RE = new RegExp('\\b' + word + '\\b', 'gi');
      str = str.replace(RE, '');
    }

    str = e.text.toLowerCase();
    STOPWORDS.forEach(remove);
    return str;
  }

  var startYear = 1930;
  var endYear = 2020;

  var speechTexts = null;
  var speeches = stdlib.datasets['sotu-data']();

  speechTexts = speeches.reduce(
                      function (
                        accumulator,
                        speech,
                        currentIndex,
                        array) {
                        if (speech.year >= startYear && speech.year <= endYear) {
                          accumulator.push(getText(speech));
                        }
                        return accumulator;
                      },
                      []);

  var trimmedTexts = speeches.map(function(speech, index, array) {
    return speech.year + ': ' + speech.text.slice(0, 100);
  });
  smartdown.setVariable('speechTexts', trimmedTexts);

  model = lda(speechTexts, 3);

  // model.fit(1000, 100, 10);
  model.fit(100, 50, 10);

  // Safari (at least on my machine) will timeout and Chrome will give a warning
  // if we try to execute too many iterations, so I'm using smaller parameters than would
  // be used in a non-browser context. Perhaps Service Workers would alleviate this?
  //

  var yearsMD = '|Year|Topic 1 Average $\\theta$|Topic 2 Average $\\theta$|Topic 3 Average $\\theta$|\n|:---|---:|---:|---:|\n';
  for (i = 0; i < speechTexts.length; i++) {
    var year = (startYear + i);
    var theta0 = roundn(model.avgTheta.get(i, 0), -3);
    var theta1 = roundn(model.avgTheta.get(i, 1), -3);
    var theta2 = roundn(model.avgTheta.get(i, 2), -3);

    str = 'Year: ' + year + '\t';
    str += 'Topic 1: ' + theta0 + '\t';
    str += 'Topic 2: ' + theta1 + '\t';
    str += 'Topic 3: ' + theta2;
    yearsMD += `|${year}|${theta0}|${theta1}|${theta2}|\n`;
  }
  yearsMD += '\n';

  smartdown.setVariable('yearsMD', yearsMD);

  var topicMD = '|Topic|Words Most Associated with Topic|\n|:---|:---|\n';
  var trim = stdlib.string.trim;
  var removePunctuation = stdlib.string.removePunctuation;

  for (var whichTopic = 0; whichTopic < 3; ++whichTopic) {
    terms = model.getTerms(whichTopic, 20);
    var topicString = `|Topic ${whichTopic}||\n`;

    for (i = 0; i < terms.length; i++) {
      terms[i] = terms[i].word;
      const stripped = trim(removePunctuation(terms[i]));
      if (stripped !== '' && stripped !== '-') {
        topicString += `||${terms[i]}|\n`;
      }
    }

    var termsString = terms.join(', ');

    topicMD += topicString;
  }

  smartdown.setVariable('topicMD', topicMD);

  saveProgress.style.display = 'none';
});

Use Graphviz to display stdlib functions

This is a very quick hack to demonstrate the synergy between Smartdown's playables, variables, and cells. It uses Graphviz to display the heirarchical structure of the stdlib namespace. For this example, we are only displaying the heirarchy under stdlib.math.

More Graphviz examples

//smartdown.import=stdlib

var index = 1;

function generateTree(rootIndex, rootfIndex, rootName, root) {
  var source =
`
"node${rootIndex}" [
    shape = "record"
    label = "`;

  if (!root) {
    return '';
  }

  var keys = Object.keys(root);
  var fIndex = 0;
  var subroot = [];

  keys.forEach(function(k) {
    ++fIndex;
    ++index;
    var v = root[k];
    var line = `  "${rootName}" -> "${k}";\n`;
    line = `<f${fIndex}> ${k}|`;
    source += line;

    if (typeof v === 'object') {
      subroot.push({
        v: v,
        k: k,
        index: index,
        fIndex: fIndex
      });
    }
  });
  source = source.slice(0, source.length - 1);
  source += '"\n];';

  subroot.forEach(function(subroot) {
    if (subroot.v) {
      source += generateTree(subroot.index, subroot.fIndex, subroot.k, subroot.v);
      source += `\n"node${rootIndex}":f${subroot.fIndex} -> "node${subroot.index}":f0;\n`;
    }
  });

  return source;
}

var tree = generateTree(1, 0, 'stdlib', stdlib.math);
var plot =
`
digraph G {
  rankdir = "LR"
  ranksep = 1.5
  ratio="compact"
  node [
    fontsize = "10"
    margin = "0"
    shape = "rectangle"
  ];
  edge [
  ];
  "node0" [
    label = "<f0> math"
    shape = "record"
  ];
  ${tree}
  "node0":f0 -> "node1":f1
}
`;
smartdown.setVariable('plot', plot);

Back to Home