UNPKG

smartdown

Version:

A library for translating, rendering and interacting with Smartdown documents. Smartdown is an extension of Markdown that provides richer media support and reactive programming capability.

479 lines (355 loc) 12.8 kB
## `stdlib` Experiments [stdlib](https://stdlib.io) 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 bundled 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](https://smartdown.site/?url=lib/gallery/Stdlib.md) ### CDF Experiments Trying to plot the CDF for various $\sigma$ and $\mu$ versions of the normal distribution. Based upon the Stdlib examples: - https://stdlib.io/develop/docs/api/@stdlib/math/base/dists/normal/cdf - https://stdlib.io/develop/docs/api/@stdlib/plot/ctor/ ```javascript/playable const thisDiv = this.div; var toHTML = Stdlib.vdomToHtml; var randn = Stdlib.random.base.boxMuller; var plot = Stdlib.plot.ctor; var cdf = Stdlib.math.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 = 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 }); 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. ```javascript/playable 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); ``` [](:!MarkdownCoefficients|markdown) [](:!MarkdownOutput|markdown) --- ### 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 [](:!SparklinePlot) #### The `Stdlib.plot.sparklines.unicode.column` script ```javascript/playable var randu = Stdlib.random.base.randu; var columnChart = Stdlib.plot.sparklines.unicode.column; 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`). ```javascript/playable const thisDiv = this.div; var toHTML = Stdlib.vdomToHtml; var randn = Stdlib.random.base.boxMuller; var plot = Stdlib.plot.ctor; 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 = 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 [](:!treeJSON|json) --- ### NLP (Natural Language Processing) Derived from this example https://stdlib.io/develop/docs/api/@stdlib/nlp/lda This version of Smartdown includes a subset (1930-2010) of the full 1790-2016 [SOTU State of the Union Dataset](https://stdlib.io/develop/docs/api/@stdlib/datasets/sotu), because the data is currently being bundled with the rest of Smartdown, and is quite large. In the future, the `stdlib` datasets will be optionally and dynamically loadable, which will reduce the default Smartdown bundle size and enable a much richer set of data to be used. This example also uses the [English Stop Words Dataset](https://stdlib.io/develop/docs/api/@stdlib/datasets/stopwords-en). #### Smartdown outputs The following smartdown cells will be populated with values generated by the `stdlib` script below. They are initially empty or undefined. ##### Stopwords [](:!stopwords) ##### State of the Union Texts (first 100 characters) [](:!speechTexts) ##### Average $\theta$ per topic [](:!yearsMD|markdown) ##### Top words associated with each topic [](:!topicMD|markdown) ##### The playable `stdlib` example. ```javascript/playable 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 = 2010; 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.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 <= 80; 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](:@Graphviz) [](:!plot|code) [](:!plot|graphviz) ```javascript/playable var index = 1; function generateTree(rootIndex, rootfIndex, rootName, root) { var source = ` "node${rootIndex}" [ shape = "record" label = "`; 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) { 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](:@Home)