1 |
|
2 | ## `stdlib` Experiments
|
3 |
|
4 | [stdlib](https://stdlib.io) is a Javascript library supporting both NodeJS and web browsers. Quoting the authors:
|
5 |
|
6 | > 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.
|
7 |
|
8 | 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.
|
9 |
|
10 | The source for this example can be viewed and transiently edited at [Smartdown Stdlib Example](https://smartdown.site/?url=lib/gallery/Stdlib.md)
|
11 |
|
12 |
|
13 | ### CDF Experiments
|
14 |
|
15 | Trying to plot the CDF for various $\sigma$ and $\mu$ versions of the normal distribution.
|
16 |
|
17 | Based upon the Stdlib examples:
|
18 |
|
19 | - https://stdlib.io/develop/docs/api/@stdlib/math/base/dists/normal/cdf
|
20 | - https://stdlib.io/develop/docs/api/@stdlib/plot/ctor/
|
21 |
|
22 |
|
23 | ```javascript/playable
|
24 | const thisDiv = this.div;
|
25 |
|
26 | var toHTML = Stdlib.vdomToHtml;
|
27 | var randn = Stdlib.random.base.boxMuller;
|
28 | var plot = Stdlib.plot.ctor;
|
29 | var cdf = Stdlib.math.base.dists.normal.cdf;
|
30 |
|
31 |
|
32 | var x = new Float64Array( 100 );
|
33 | var y1 = new Float64Array( x.length );
|
34 | var y2 = new Float64Array( x.length );
|
35 | var y3 = new Float64Array( x.length );
|
36 | var y4 = new Float64Array( x.length );
|
37 | for (var i = 0; i < x.length; i++) {
|
38 | x[ i ] = (((i + 1) * 1.0) - x.length / 2) / 25.0;
|
39 | y1[ i ] = cdf(x[i], 0, 1);
|
40 | y2[ i ] = cdf(x[i], 0, 0.5);
|
41 | y3[ i ] = cdf(x[i], 0, 0.2);
|
42 | y4[ i ] = cdf(x[i], 0, 0.1);
|
43 | }
|
44 |
|
45 | var h = plot( [x, x, x, x], [y1, y2, y3, y4], {
|
46 | yMin: -0.1,
|
47 | yMax: 1.1,
|
48 | 'description': 'Plotting the CDF of the Normal Distribution',
|
49 | 'title': 'CDF of the Normal Distribution for various σ',
|
50 | 'labels': [
|
51 | 'σ = 1',
|
52 | 'σ = 0.5',
|
53 | 'σ = 0.2',
|
54 | 'σ = 0.1'
|
55 | ],
|
56 | lineWidth: 5
|
57 | });
|
58 |
|
59 | thisDiv.innerHTML = Stdlib.vdomToHtml( h.render() );
|
60 |
|
61 | ```
|
62 |
|
63 | ---
|
64 |
|
65 | ### Machine Learning
|
66 |
|
67 | From this example: https://stdlib.io/develop/docs/api/@stdlib/ml/online-binary-classification
|
68 |
|
69 | I honestly don't understand this example (yet), I've just transliterated it to Smartdown.
|
70 |
|
71 | ```javascript/playable
|
72 | var binomial = Stdlib.random.base.binomial;
|
73 | var normal = Stdlib.random.base.normal;
|
74 | var exp = Stdlib.math.base.special.exp;
|
75 | var onlineBinaryClassification = Stdlib.ml.onlineBinaryClassification;
|
76 |
|
77 | var phat;
|
78 | var lp;
|
79 | var x1;
|
80 | var x2;
|
81 | var y;
|
82 | var i;
|
83 |
|
84 | // Create model:
|
85 | var model = onlineBinaryClassification({
|
86 | 'lambda': 1e-3,
|
87 | 'loss': 'log',
|
88 | 'intercept': true
|
89 | });
|
90 |
|
91 | // Update model as data comes in...
|
92 | for ( i = 0; i < 10000; i++ ) {
|
93 | x1 = normal( 0.0, 1.0 );
|
94 | x2 = normal( 0.0, 1.0 );
|
95 | lp = (3.0 * x1) - (2.0 * x2) + 1.0;
|
96 | phat = 1.0 / ( 1.0 + exp( -lp ) );
|
97 | y = binomial( 1, phat ) ? 1.0 : -1.0;
|
98 | model.update( [ x1, x2 ], y );
|
99 | }
|
100 |
|
101 | // Extract model coefficients:
|
102 | var markdownCoefficients =
|
103 | `
|
104 | ### Model Coefficients
|
105 |
|
106 | $$
|
107 | ${model.coefs}
|
108 | $$
|
109 | `;
|
110 |
|
111 | // Predict new observations:
|
112 | // console.log( 'Pr(Y=1)_hat = %d; x1 = %d; x2 = %d', model.predict( [0.9, 0.1], 'probability' ), 0.9, 0.1 );
|
113 | // console.log( 'y_hat = %d; x1 = %d; x2 = %d', model.predict( [0.1, 0.9], 'link' ), 0.1, 0.9 );
|
114 | // console.log( 'y_hat = %d; x1 = %d; x2 = %d', model.predict( [0.9, 0.9], 'link' ), 0.9, 0.9 );
|
115 |
|
116 | const p1 = 0.9;
|
117 | const p2 = 0.1;
|
118 | const predictionProbability = model.predict( [p1, p2], 'probability' );
|
119 | const predictionLink12 = model.predict( [p1, p2], 'link' );
|
120 | const predictionLink22 = model.predict( [p1, p2], 'link' );
|
121 |
|
122 | var markdownOutput =
|
123 | `
|
124 | ### Output
|
125 |
|
126 | |Expression|Value|$x_1$|$x_2$|
|
127 | |:---:|---:|---:|---:|
|
128 | |$\\hat{P_r(Y=1)}$|${predictionProbability}|${x1}|${x2}|
|
129 | |$\\hat{y}$|${predictionLink12}|${x1}|${x2}|
|
130 | |$\\hat{y}$|${predictionLink22}|${x2}|${x2}|
|
131 | `;
|
132 |
|
133 | smartdown.setVariable('MarkdownCoefficients', markdownCoefficients);
|
134 | smartdown.setVariable('MarkdownOutput', markdownOutput);
|
135 | ```
|
136 |
|
137 | [](:!MarkdownCoefficients|markdown)
|
138 | [](:!MarkdownOutput|markdown)
|
139 |
|
140 | ---
|
141 |
|
142 | ### Unicode Sparklines
|
143 |
|
144 | From https://stdlib.io/develop/docs/api/@stdlib/plot/sparklines/unicode/column
|
145 |
|
146 | 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.
|
147 |
|
148 | #### The Generated Plot
|
149 | [](:!SparklinePlot)
|
150 |
|
151 |
|
152 | #### The `Stdlib.plot.sparklines.unicode.column` script
|
153 |
|
154 | ```javascript/playable
|
155 | var randu = Stdlib.random.base.randu;
|
156 | var columnChart = Stdlib.plot.sparklines.unicode.column;
|
157 |
|
158 | var chart;
|
159 | var data;
|
160 | var id;
|
161 | var i;
|
162 |
|
163 | // Generate some random data...
|
164 | data = new Float64Array( 30 );
|
165 | for ( i = 0; i < data.length; i++ ) {
|
166 | data[ i ] = randu() * 100.0;
|
167 | }
|
168 |
|
169 | // Create a new column chart:
|
170 | chart = columnChart();
|
171 |
|
172 | // Set the chart data:
|
173 | chart.data = data;
|
174 |
|
175 | // Configure the chart to support streaming data:
|
176 | chart.window = data.length;
|
177 | chart.yMin = 0.0;
|
178 | chart.yMax = 100.0;
|
179 |
|
180 | // Update the terminal chart with new data every second:
|
181 | id = setInterval( update, 1000 );
|
182 |
|
183 | // After some time, stop updating and close:
|
184 | setTimeout( stop, 20000 );
|
185 |
|
186 | function update() {
|
187 | // Update the chart with new data:
|
188 | chart.push( randu() * 100.0 );
|
189 |
|
190 | var rendered = chart.render();
|
191 | smartdown.setVariable('SparklinePlot', rendered);
|
192 | }
|
193 |
|
194 | function stop() {
|
195 | clearInterval( id );
|
196 | }
|
197 | ```
|
198 |
|
199 | ---
|
200 |
|
201 | ### Plots
|
202 |
|
203 | Adapted from https://stdlib.io/develop/docs/api/@stdlib/plot/ctor
|
204 |
|
205 | 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`).
|
206 |
|
207 |
|
208 | ```javascript/playable
|
209 | const thisDiv = this.div;
|
210 |
|
211 | var toHTML = Stdlib.vdomToHtml;
|
212 | var randn = Stdlib.random.base.boxMuller;
|
213 | var plot = Stdlib.plot.ctor;
|
214 |
|
215 | var now;
|
216 | var x;
|
217 | var y;
|
218 | var i;
|
219 |
|
220 | // Create some data...
|
221 | now = ( new Date() ).getTime();
|
222 | x = new Float64Array( 100 );
|
223 | y = new Float64Array( x.length );
|
224 | for ( i = 0; i < x.length; i++ ) {
|
225 | x[ i ] = now + (i * 360000);
|
226 | y[ i ] = 50.0 + (10.0 * randn());
|
227 | }
|
228 |
|
229 | // Create a new plot:
|
230 | var h = plot( [x], [y], {
|
231 | 'width': 500,
|
232 | 'height': 500,
|
233 | 'xScale': 'time',
|
234 | 'xTickFormat': '%H:%M'
|
235 | });
|
236 |
|
237 | // Render as a virtual DOM tree:
|
238 | var vtree = h.render();
|
239 | // console.log( JSON.stringify( vtree ) );
|
240 | smartdown.setVariable('treeJSON', vtree);
|
241 |
|
242 | // Transform the virtual DOM tree to HTML:
|
243 | var html = toHTML( vtree );
|
244 | // console.log( html );
|
245 | thisDiv.innerHTML = html;
|
246 |
|
247 | ```
|
248 |
|
249 | ##### The generated virtual DOM Tree
|
250 |
|
251 | [](:!treeJSON|json)
|
252 |
|
253 |
|
254 | ---
|
255 |
|
256 | ### NLP (Natural Language Processing)
|
257 |
|
258 | Derived from this example https://stdlib.io/develop/docs/api/@stdlib/nlp/lda
|
259 |
|
260 | 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.
|
261 |
|
262 | This example also uses the [English Stop Words Dataset](https://stdlib.io/develop/docs/api/@stdlib/datasets/stopwords-en).
|
263 |
|
264 |
|
265 | #### Smartdown outputs
|
266 |
|
267 | The following smartdown cells will be populated with values generated by the `stdlib` script below. They are initially empty or undefined.
|
268 |
|
269 | ##### Stopwords
|
270 | [](:!stopwords)
|
271 |
|
272 | ##### State of the Union Texts (first 100 characters)
|
273 | [](:!speechTexts)
|
274 |
|
275 | ##### Average $\theta$ per topic
|
276 | [](:!yearsMD|markdown)
|
277 |
|
278 | ##### Top words associated with each topic
|
279 | [](:!topicMD|markdown)
|
280 |
|
281 |
|
282 | ##### The playable `stdlib` example.
|
283 |
|
284 | ```javascript/playable
|
285 | var that = this;
|
286 |
|
287 | // Hack to keep the progress bar up while everything happens in
|
288 | // this long-running script. Better would be a smartdown.setProgress()
|
289 | // method that would not leak the implementation details.
|
290 | //
|
291 | var saveProgress = this.progress; // Oh, the hack
|
292 | this.progress = null;
|
293 |
|
294 | Stdlib.loadSOTU(function() {
|
295 | var roundn = Stdlib.math.base.special.roundn;
|
296 | var stopwords = Stdlib.datasets['stopwords-en'];
|
297 | var lda = Stdlib.nlp.lda;
|
298 |
|
299 | var STOPWORDS = stopwords();
|
300 | var terms;
|
301 | var model;
|
302 | var str;
|
303 | var i;
|
304 |
|
305 | smartdown.setVariable('stopwords', STOPWORDS);
|
306 |
|
307 | function getText(e) {
|
308 | function remove(word) {
|
309 | var RE = new RegExp('\\b' + word + '\\b', 'gi');
|
310 | str = str.replace(RE, '');
|
311 | }
|
312 |
|
313 | str = e.text.toLowerCase();
|
314 | STOPWORDS.forEach(remove);
|
315 | return str;
|
316 | }
|
317 |
|
318 | var startYear = 1930;
|
319 | var endYear = 2010;
|
320 |
|
321 | var speechTexts = null;
|
322 | var speeches = Stdlib.datasets['sotu-data'];
|
323 |
|
324 | speechTexts = speeches.reduce(
|
325 | function (
|
326 | accumulator,
|
327 | speech,
|
328 | currentIndex,
|
329 | array) {
|
330 | if (speech.year >= startYear && speech.year <= endYear) {
|
331 | accumulator.push(getText(speech));
|
332 | }
|
333 | return accumulator;
|
334 | },
|
335 | []);
|
336 |
|
337 | var trimmedTexts = speeches.map(function(speech, index, array) {
|
338 | return speech.text.slice(0, 100);
|
339 | });
|
340 | smartdown.setVariable('speechTexts', trimmedTexts);
|
341 |
|
342 | model = lda(speechTexts, 3);
|
343 |
|
344 | // model.fit(1000, 100, 10);
|
345 | model.fit(100, 50, 10);
|
346 |
|
347 | // Safari (at least on my machine) will timeout and Chrome will give a warning
|
348 | // if we try to execute too many iterations, so I'm using smaller parameters than would
|
349 | // be used in a non-browser context. Perhaps Service Workers would alleviate this?
|
350 | //
|
351 |
|
352 | var yearsMD = '|Year|Topic 1 Average $\\theta$|Topic 2 Average $\\theta$|Topic 3 Average $\\theta$|\n|:---|---:|---:|---:|\n';
|
353 | for (i = 0; i <= 80; i++) {
|
354 | var year = (startYear + i);
|
355 | var theta0 = roundn(model.avgTheta.get(i, 0), -3);
|
356 | var theta1 = roundn(model.avgTheta.get(i, 1), -3);
|
357 | var theta2 = roundn(model.avgTheta.get(i, 2), -3);
|
358 |
|
359 | str = 'Year: ' + year + '\t';
|
360 | str += 'Topic 1: ' + theta0 + '\t';
|
361 | str += 'Topic 2: ' + theta1 + '\t';
|
362 | str += 'Topic 3: ' + theta2;
|
363 | yearsMD += `|${year}|${theta0}|${theta1}|${theta2}|\n`;
|
364 | }
|
365 | yearsMD += '\n';
|
366 |
|
367 | smartdown.setVariable('yearsMD', yearsMD);
|
368 |
|
369 | var topicMD = '|Topic|Words Most Associated with Topic|\n|:---|:---|\n';
|
370 | var trim = Stdlib.string.trim;
|
371 | var removePunctuation = Stdlib.string.removePunctuation;
|
372 |
|
373 | for (var whichTopic = 0; whichTopic < 3; ++whichTopic) {
|
374 | terms = model.getTerms(whichTopic, 20);
|
375 | var topicString = `|Topic ${whichTopic}||\n`;
|
376 |
|
377 | for (i = 0; i < terms.length; i++) {
|
378 | terms[i] = terms[i].word;
|
379 | const stripped = trim(removePunctuation(terms[i]));
|
380 | if (stripped !== '' && stripped !== '-') {
|
381 | topicString += `||${terms[i]}|\n`;
|
382 | }
|
383 | }
|
384 |
|
385 | var termsString = terms.join(', ');
|
386 |
|
387 | topicMD += topicString;
|
388 | }
|
389 |
|
390 | smartdown.setVariable('topicMD', topicMD);
|
391 |
|
392 | saveProgress.style.display = 'none';
|
393 | });
|
394 | ```
|
395 |
|
396 | ---
|
397 |
|
398 | ### Use Graphviz to display `stdlib` functions
|
399 |
|
400 | 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`.
|
401 |
|
402 | [More Graphviz examples](:@Graphviz)
|
403 |
|
404 | [](:!plot|code)
|
405 | [](:!plot|graphviz)
|
406 |
|
407 | ```javascript/playable
|
408 |
|
409 | var index = 1;
|
410 |
|
411 | function generateTree(rootIndex, rootfIndex, rootName, root) {
|
412 | var source =
|
413 | `
|
414 | "node${rootIndex}" [
|
415 | shape = "record"
|
416 | label = "`;
|
417 | var keys = Object.keys(root);
|
418 | var fIndex = 0;
|
419 | var subroot = [];
|
420 |
|
421 | keys.forEach(function(k) {
|
422 | ++fIndex;
|
423 | ++index;
|
424 | var v = root[k];
|
425 | var line = ` "${rootName}" -> "${k}";\n`;
|
426 | line = `<f${fIndex}> ${k}|`;
|
427 | source += line;
|
428 |
|
429 | if (typeof v === 'object') {
|
430 | subroot.push({
|
431 | v: v,
|
432 | k: k,
|
433 | index: index,
|
434 | fIndex: fIndex
|
435 | });
|
436 | }
|
437 | });
|
438 | source = source.slice(0, source.length - 1);
|
439 | source += '"\n];';
|
440 |
|
441 | subroot.forEach(function(subroot) {
|
442 | source += generateTree(subroot.index, subroot.fIndex, subroot.k, subroot.v);
|
443 | source += `\n"node${rootIndex}":f${subroot.fIndex} -> "node${subroot.index}":f0;\n`;
|
444 | });
|
445 |
|
446 | return source;
|
447 | }
|
448 |
|
449 | var tree = generateTree(1, 0, 'stdlib', Stdlib.math);
|
450 | var plot =
|
451 | `
|
452 | digraph G {
|
453 | rankdir = "LR"
|
454 | ranksep = 1.5
|
455 | ratio="compact"
|
456 | node [
|
457 | fontsize = "10"
|
458 | margin = "0"
|
459 | shape = "rectangle"
|
460 | ];
|
461 | edge [
|
462 | ];
|
463 | "node0" [
|
464 | label = "<f0> math"
|
465 | shape = "record"
|
466 | ];
|
467 | ${tree}
|
468 | "node0":f0 -> "node1":f1
|
469 | }
|
470 | `;
|
471 | smartdown.setVariable('plot', plot);
|
472 | ```
|
473 |
|
474 | ---
|
475 |
|
476 |
|
477 | [Back to Home](:@Home)
|
478 |
|