jupyter-ijavascript-utils 1.22.4 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DOCS.md CHANGED
@@ -63,8 +63,19 @@ This is not intended to be the only way to accomplish many of these tasks, and a
63
63
 
64
64
  ![Screenshot of example notebook](img/mainExampleNotebook.png)
65
65
 
66
+ ## Running on Binder
67
+
68
+ [mybinder.org](https://mybinder.org/) is a great place to run a Jupyter Notebook online.
69
+
70
+ It means you can run Jupyter Notebooks with additional kernels without having to install anything,
71
+ and can try right in your browser.
72
+
73
+ Give it a try here:
74
+ [![Binder:what can I do with this](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/paulroth3d/jupyter-ijavascript-utils/main?labpath=example.ipynb)
75
+
66
76
  ## What's New
67
77
 
78
+ * 1.23 - add format.parseNumber and TableGenerator.styleColumn, align group.separateByFields to vega-lite fold transform
68
79
  * 1.22 - make chain iJavaScript aware, but still able to work outside of Jupyter
69
80
  * 1.21 - include {@link module:chain|chain} - simple monoid
70
81
  * 1.20 - fix vega dependency
package/Dockerfile CHANGED
@@ -1,3 +1,3 @@
1
1
  # syntax=docker/dockerfile:1
2
2
 
3
- FROM darkbluestudios/jupyter-ijavascript-utils:binder_1.22.4
3
+ FROM darkbluestudios/jupyter-ijavascript-utils:binder_1.23.0
package/README.md CHANGED
@@ -55,6 +55,7 @@ This is not intended to be the only way to accomplish many of these tasks, and a
55
55
 
56
56
  # What's New
57
57
 
58
+ * 1.23 - add format.parseNumber and TableGenerator.styleColumn, align group.separateByFields to vega-lite fold transform
58
59
  * 1.22 - make chain iJavaScript aware, but still able to work outside of Jupyter
59
60
  * 1.21 - include chain - simple monoid
60
61
  * 1.20 - fix vega dependency
@@ -104,14 +105,13 @@ Steps Overview:
104
105
 
105
106
  ## Running on Binder
106
107
 
107
- !TODO
108
-
109
108
  [mybinder.org](https://mybinder.org/) is a great place to run a Jupyter Notebook online.
110
109
 
111
- We are still attempting to sort out a suitable link that will work for everyone
112
- (without overloading their service)
110
+ It means you can run Jupyter Notebooks with additional kernels without having to install anything,
111
+ and can try right in your browser.
113
112
 
114
- (Please see [Issue #4](https://github.com/paulroth3d/jupyter-ijavascript-utils/issues/4))
113
+ Give it a try here:
114
+ [![Binder:what can I do with this](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/paulroth3d/jupyter-ijavascript-utils/main?labpath=example.ipynb)
115
115
 
116
116
  # For Example
117
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyter-ijavascript-utils",
3
- "version": "1.22.4",
3
+ "version": "1.23.0",
4
4
  "description": "Utilities for working with iJavaScript - a Jupyter Kernel",
5
5
  "homepage": "https://jupyter-ijavascript-utils.onrender.com/",
6
6
  "license": "MIT",
@@ -112,6 +112,7 @@ const { createSort } = require('./array');
112
112
  * * style the table
113
113
  * * {@link TableGenerator#styleTable|styleTable(string)} - css style for the table
114
114
  * * {@link TableGenerator#styleHeader|styleHeader(string)} - css styles for the header row
115
+ * * {@link TableGenerator#styleColumn|styleColumn(object)} - Function to style cells based on the column
115
116
  * * {@link TableGenerator#styleRow|styleRow(fn)} - Function to style rows
116
117
  * * {@link TableGenerator#styleCell|styleCell(fn)} - Function to style cells
117
118
  * * {@link TableGenerator#border|border(string)} - Apply a border to the table data cells
@@ -229,6 +230,12 @@ class TableGenerator {
229
230
  */
230
231
  #styleRow = null;
231
232
 
233
+ /**
234
+ * Style to apply at the column level
235
+ * @type {Function}
236
+ */
237
+ #styleColumn = null;
238
+
232
239
  /**
233
240
  * Style to apply at the cell
234
241
  * @type {Function}
@@ -287,6 +294,7 @@ class TableGenerator {
287
294
  this.#styleTable = '';
288
295
  this.#styleHeader = '';
289
296
  this.#styleRow = null;
297
+ this.#styleColumn = null;
290
298
  this.#styleCell = null;
291
299
  this.#isTransposed = false;
292
300
  }
@@ -386,6 +394,7 @@ class TableGenerator {
386
394
  * As this adds additional CSS, the styling applied:
387
395
  * * {@link TableGenerator#styleTable|to the whole table}
388
396
  * * or {@link TableGenerator#styleRow|to the rows}
397
+ * * or {@link TableGenerator#styleColumn|to the column}
389
398
  * * or {@link TableGenerator#styleCell|to the data cells} will be affected
390
399
  *
391
400
  * For example:
@@ -882,11 +891,99 @@ class TableGenerator {
882
891
  return this;
883
892
  }
884
893
 
894
+ /**
895
+ * Function that can apply a style to a given column
896
+ *
897
+ * (rowIndex, ({ columnHeader, columnIndex, record, row, rowIndex, value })) => string
898
+ *
899
+ * note: see {@link TableGenerator#styleCell} for another way to do style a cell.
900
+ *
901
+ * ```
902
+ * dataSet = [
903
+ * {reg: 'z', source: 'A', temp: 10},
904
+ * {reg: 'z', source: 'B', temp: 98},
905
+ * {reg: 'z', source: 'A', temp: 100}
906
+ * ];
907
+ *
908
+ * utils.table(dataSet)
909
+ * .styleColumn({
910
+ * //-- we want to make the background color of the color red, if the temp > 50
911
+ * temp: (temp) => temp > 50 ? 'background-color:pink' : '',
912
+ *
913
+ * //-- we want to make the source bold if the source is B
914
+ * source: (source) => source === 'B' ? 'font-weight:bold' : ''
915
+ * })
916
+ * .render();
917
+ * ```
918
+ * <table cellspacing="0px" >
919
+ * <tr ><th>reg</th><th>source</th><th>temp</th></tr>
920
+ * <tr ><td >z</td><td >A</td><td >10</td></tr>
921
+ * <tr ><td >z</td><td style="font-weight:bold;">B</td><td style="background-color:pink;">98</td></tr>
922
+ * <tr ><td >z</td><td >A</td><td style="background-color:pink;">100</td></tr>
923
+ * </table>
924
+ *
925
+ * Or you could style the cell based on information in other columns.
926
+ *
927
+ * ```
928
+ * dataSet = [
929
+ * {reg: 'z', source: 'A', tempFormat: 'c', temp: 42},
930
+ * {reg: 'z', source: 'B', tempFormat: 'f', temp: 98},
931
+ * {reg: 'z', source: 'A', tempFormat: 'f', temp: 100}
932
+ * ];
933
+ *
934
+ * utils.table(dataSet)
935
+ * .styleColumn({
936
+ * //-- we want to make the background color of the color red, if the temp > 50
937
+ * temp: (temp, { record }) => convertToKelvin(temp, record.tempFormat) > 283
938
+ * ? 'background-color:pink'
939
+ * : ''
940
+ * })
941
+ * .render();
942
+ * ```
943
+ *
944
+ * <table cellspacing="0px" >
945
+ * <tr ><th>reg</th><th>source</th><th>tempFormat</th><th>temp</th></tr>
946
+ * <tr ><td >z</td><td >A</td><td >c</td><td style="background-color:pink;">10</td></tr>
947
+ * <tr ><td >z</td><td >B</td><td >f</td><td style="background-color:pink;">98</td></tr>
948
+ * <tr ><td >z</td><td >A</td><td >f</td><td style="background-color:pink;">100</td></tr>
949
+ * </table>
950
+ *
951
+ * @param {object} styleObj - object with properties matching the column header label
952
+ * @param {function(value, contextObj)} styleObj.property - Function to evaluate for each row returning the inline css styles to apply.
953
+ *
954
+ * When it runs it will get passed the value and context,
955
+ * and should return the css inline styles to apply
956
+ *
957
+ * @param {any} styleObj.property.value - the value for a given row for that column
958
+ * @param {any} styleObj.property.context.value - destructured value of the cell
959
+ * @param {Number} styleObj.property.context.columnIndex - destructured 0 index column of the cell
960
+ * @param {Number} styleObj.property.context.rowIndex - destructured 0 index row of the cell
961
+ * @param {Array} styleObj.property.context.row - destructured full row provided
962
+ * @param {Array} styleObj.property.context.record - destructured original record
963
+ * @returns {TableGenerator} - chainable instance
964
+ */
965
+ styleColumn(styleObj) {
966
+ if (!styleObj) {
967
+ this.#styleColumn = null;
968
+ return this;
969
+ }
970
+
971
+ if (typeof styleObj !== 'object') {
972
+ throw Error('styleColumn(styleObj): expects an object with properties matching the column LABELs');
973
+ }
974
+
975
+ this.#styleColumn = styleObj;
976
+
977
+ return this;
978
+ }
979
+
885
980
  /**
886
981
  * Function that can apply a style to a given cell
887
982
  *
888
983
  * (value, columnIndex, rowIndex, row, record) => string
889
984
  *
985
+ * Note: see {@link TableGenerator#styleColumn} for another way to do style a cell.
986
+ *
890
987
  * ```
891
988
  * dataSet = [
892
989
  * { title:'row 0', a: 0.608, b: 0.351, c: 0.823, d: 0.206, e: 0.539 },
@@ -914,13 +1011,14 @@ class TableGenerator {
914
1011
  * ```
915
1012
  * ![Screenshot of styling the cell](img/Table_StyleCell.png)
916
1013
  *
917
- * @param {function(*):any} formatterFn - Translation function to apply to all cells.
918
1014
  *
919
1015
  * When it runs, you will receive a single parameter representing the current cell and row.
920
1016
  *
921
1017
  * Return what the new value should be.
1018
+ * @param {function(*):any} formatterFn - Translation function to apply to all cells.
922
1019
  * @param {any} formatterFn.value - destructured value of the cell
923
1020
  * @param {Number} formatterFn.columnIndex - destructured 0 index column of the cell
1021
+ * @param {Number} formatterFn.columnHeader - destructured header of the column
924
1022
  * @param {Number} formatterFn.rowIndex - destructured 0 index row of the cell
925
1023
  * @param {Array} formatterFn.row - destructured full row provided
926
1024
  * @param {Array} formatterFn.record - destructured original record
@@ -1061,6 +1159,7 @@ class TableGenerator {
1061
1159
  const styleTable = this.#styleTable;
1062
1160
  const styleHeader = this.#styleHeader;
1063
1161
  const styleRowFn = this.#styleRow;
1162
+ const styleColumnObj = this.#styleColumn;
1064
1163
  const styleCellFn = this.#styleCell;
1065
1164
  const printOptions = this.#printOptions;
1066
1165
  const borderCSS = this.#borderCSS;
@@ -1099,14 +1198,20 @@ class TableGenerator {
1099
1198
 
1100
1199
  return `<tr ${printInlineCSS(rowStyle)}>\n\t`
1101
1200
  + dataRow.map((value, columnIndex) => {
1201
+ const columnHeader = results.headers[columnIndex];
1102
1202
  //-- style for the cell
1103
- const cellStyle = !styleCellFn ? '' : styleCellFn({ value, columnIndex, rowIndex, row: dataRow, record });
1203
+ const cellData = { value, columnIndex, columnHeader, rowIndex, row: dataRow, record };
1204
+ const cellStyle = !styleCellFn ? '' : styleCellFn(cellData);
1205
+ const columnStyle = !styleColumnObj || !styleColumnObj[columnHeader] || !typeof styleColumnObj[columnHeader] === 'function'
1206
+ ? ''
1207
+ : styleColumnObj[columnHeader](value, cellData);
1104
1208
 
1105
1209
  return `<td ${
1106
1210
  printInlineCSS(
1107
1211
  borderCSS,
1108
1212
  //-- could be inline, but not as clear
1109
- cellStyle
1213
+ cellStyle,
1214
+ columnStyle
1110
1215
  )
1111
1216
  }>${
1112
1217
  cleanFn(value, printOptions)
package/src/format.js CHANGED
@@ -29,6 +29,9 @@
29
29
  * * {@link module:format.safeConvertFloat|format.safeConvertFloat} - converts a value to a Number (123.4), or uses a default for any error or NaN
30
30
  * * {@link module:format.safeConvertInteger|format.safeConvertInteger} - converts a value to a Number (123), or uses a default for any error or NaN
31
31
  * * {@link module:format.safeConvertBoolean|format.safeConvertBoolean} - converts a value to a boolean
32
+ * * Parsing values
33
+ * * {@link module:format.parseNumber|format.parseBoolean(val)} - converts a value to a boolean value
34
+ * * {@link module:format.parseNumber|format.parseNumber(val, locale)} - converts a value to a number
32
35
  * * Identifying values
33
36
  * * {@link module:format.isEmptyValue|format.isEmptyValue} - determine if a value is not 'empty'
34
37
  *
@@ -877,6 +880,51 @@ module.exports.isBoolean = function isBoolean(val) {
877
880
  || val === 'true' || val === 'false';
878
881
  };
879
882
 
883
+ FormatUtils.parseLocaleCache = new Map();
884
+
885
+ /**
886
+ * Parses a given number, based on a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat|Intl.NumberFormat}
887
+ *
888
+ * If the locale is not passed (ex: 'fr-FR'), then 'en-US' is assumed
889
+ *
890
+ * @param {any} val - value to parse
891
+ * @returns {Number} - parsed number
892
+ * @example
893
+ *
894
+ * utils.format.parseNumber(10); // 10
895
+ * utils.format.parseNumber('10'); // 10
896
+ * utils.format.parseNumber('1,000'); // 1000
897
+ * utils.format.parseNumber('1,000.5'); // 1000.5
898
+ * utils.format.parseNumber('1,000', 'en-US'); // 1000
899
+ * utils.format.parseNumber('1,000.5', 'en-US'); // 1000.5
900
+ * utils.format.parseNumber('1 000', 'fr-FR'); // 1000
901
+ * utils.format.parseNumber('1 000,5', 'fr-FR'); // 1000.5
902
+ */
903
+ module.exports.parseNumber = function parseNumber(val, locale = 'en-US') {
904
+ const valType = typeof val;
905
+ if (valType === 'number') {
906
+ return val;
907
+ } else if (valType === 'string') {
908
+ let separator;
909
+ if (FormatUtils.parseLocaleCache.has(locale)) {
910
+ separator = FormatUtils.parseLocaleCache.get(locale);
911
+ } else {
912
+ const example = Intl.NumberFormat(locale).format('1.1');
913
+ separator = example.charAt(1);
914
+ FormatUtils.parseLocaleCache.set(locale, separator);
915
+ }
916
+
917
+ const cleanPattern = new RegExp(`[^-+0-9${separator}]`, 'g');
918
+ const cleaned = val.replace(cleanPattern, '');
919
+ const normalized = cleaned.replace(separator, '.');
920
+
921
+ return parseFloat(normalized);
922
+ } else if (!val) {
923
+ return val;
924
+ }
925
+ return parseFloat(val);
926
+ };
927
+
880
928
  /**
881
929
  * Narrows to only fromLine - toLine (inclusive) within a string.
882
930
  *
package/src/group.js CHANGED
@@ -215,6 +215,8 @@ module.exports.rollup = function rollup(collection, reducer, prop, ...fields) {
215
215
  *
216
216
  * The object generated by the function is then merged.
217
217
  *
218
+ * See [vega-lite fold transform](https://vega.github.io/vega-lite/docs/fold.html)
219
+ *
218
220
  * @example
219
221
  * aggregateWeather = utils.group.by(weather, 'city')
220
222
  * .reduce((group) => ({
@@ -235,12 +237,12 @@ module.exports.rollup = function rollup(collection, reducer, prop, ...fields) {
235
237
  *
236
238
  * //-- gives
237
239
  * [
238
- * { city: 'Seattle', min: 0.87, max: 5.31, avg: 2.953, _field: 'min', _value: 0.87 },
239
- * { city: 'New York', min: 3.58, max: 4.13, avg: 3.883, _field: 'min', _value: 3.58 },
240
- * { city: 'Chicago', min: 2.56, max: 3.98, avg: 3.387, _field: 'min', _value: 2.56 },
241
- * { city: 'Seattle', min: 0.87, max: 5.31, avg: 2.953, _field: 'max', _value: 5.31 },
242
- * { city: 'New York', min: 3.58, max: 4.13, avg: 3.883, _field: 'max', _value: 4.13 },
243
- * { city: 'Chicago', min: 2.56, max: 3.98, avg: 3.387, _field: 'max', _value: 3.98},
240
+ * { city: 'Seattle', min: 0.87, max: 5.31, avg: 2.953, key: 'min', value: 0.87 },
241
+ * { city: 'New York', min: 3.58, max: 4.13, avg: 3.883, key: 'min', value: 3.58 },
242
+ * { city: 'Chicago', min: 2.56, max: 3.98, avg: 3.387, key: 'min', value: 2.56 },
243
+ * { city: 'Seattle', min: 0.87, max: 5.31, avg: 2.953, key: 'max', value: 5.31 },
244
+ * { city: 'New York', min: 3.58, max: 4.13, avg: 3.883, key: 'max', value: 4.13 },
245
+ * { city: 'Chicago', min: 2.56, max: 3.98, avg: 3.387, key: 'max', value: 3.98},
244
246
  * ...
245
247
  * ]
246
248
  *
@@ -256,7 +258,7 @@ module.exports.separateByFields = function separateByFields(collection, ...field
256
258
  if (!fields || !Array.isArray(fields) || fields.length < 1) {
257
259
  throw (Error('separateByFields: fields are expected'));
258
260
  }
259
- return fields.flatMap((field) => collection.map((obj) => ({ ...obj, _field: field, _value: obj[field] })));
261
+ return fields.flatMap((field) => collection.map((obj) => ({ ...obj, key: field, value: obj[field] })));
260
262
  };
261
263
 
262
264
  /**
package/src/vega.js CHANGED
@@ -8,6 +8,9 @@ const IJSUtils = require('./ijs');
8
8
  /**
9
9
  * Helper for working with [Vega-Lite](https://vega.github.io/vega-lite/) (and [Vega](https://vega.github.io/vega/)) within iJavaScript notebooks.
10
10
  *
11
+ * * [Vega-Lite Example Gallery](https://vega.github.io/vega-lite/examples/)
12
+ * * [Vega Example Gallery](https://vega.github.io/vega/examples/)
13
+ *
11
14
  * ([Vega-Lite-Api](https://vega.github.io/vega-lite-api/):
12
15
  * creates -> [Vega-Lite JSON specifications](https://vega.github.io/vega-lite/tutorials/getting_started.html):
13
16
  * creates -> [Vega charting specifications](https://vega.github.io/):
@@ -25,6 +28,39 @@ const IJSUtils = require('./ijs');
25
28
  * * Rendering specifications through the Jupyter Lab mime-type
26
29
  * * {@link module:vega.vegaMimeType|vega.vegaMimeType(Object | String)} - render the chart using the Vega mime-type (as png)
27
30
  * * {@link module:vega.vegaLiteMimeType|vega.vegaLiteMimeType(Object | String)} - render the chart using the Vega-Lite mime-type (as png)
31
+ *
32
+ * For example, this is a very simple demonstration for writing a vega-lite chart (the simplest way to get started)
33
+ *
34
+ * ```
35
+ * simpleData = [{fruit:'Apples',yield:20,year:'2020'},{fruit:'Apples',yield:22,year:'2021'},
36
+ * {fruit:'Bananas',yield:15,year:'2020'},{fruit:'Bananas',yield:12,year:'2021'},
37
+ * {fruit:'Pears',yield:18,year:'2020'},{fruit:'Pears',yield:19,year:'2021'}];
38
+ *
39
+ * utils.vega.svg(
40
+ * // accept the reference to the vega-lite instance passed
41
+ * (vl) => vl
42
+ * // render as points
43
+ * .markPoint()
44
+ * // use simpleData as the data source
45
+ * .data(simpleData)
46
+ * // title
47
+ * .title('Fruit by Yield')
48
+ * .width(100)
49
+ * .encode(
50
+ * // define the x axis as the Qualitative / Numerical 'yield' property
51
+ * vl.y().fieldQ('yield'),
52
+ * // define the y axis as the Nominative / TextBased 'fruit' property
53
+ * vl.x().fieldN('fruit'),
54
+ * // define the color series based on the Qualitative / Numerical 'year' property
55
+ * vl.color().fieldN('year')
56
+ * )
57
+ * );
58
+ * ```
59
+ * ![Screenshot](img/vegaSimpleChart.jpg)
60
+ *
61
+ * and with simple changes, convert it to a bar graph
62
+ *
63
+ * ![Screenshot](img/fruitYieldByYearBar.png)
28
64
  * -----
29
65
  *
30
66
  * * Check out the {@tutorial vegaLite1} tutorials
@@ -155,6 +191,7 @@ const IJSUtils = require('./ijs');
155
191
  * "x": {"field": "Horsepower", "type": "quantitative"},
156
192
  * "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
157
193
  * "color": {"field": "Origin", "type": "nominal"},
194
+ * //-- simply by adding the tooltip encoding here
158
195
  * "tooltip": {"field": "Name", "type": "nominal"},
159
196
  * "href": {"field": "url", "type": "nominal"}
160
197
  * }
@@ -163,6 +200,129 @@ const IJSUtils = require('./ijs');
163
200
  *
164
201
  * ![Screenshot for tooltips](img/vegaScript_tooltips.png)
165
202
  *
203
+ * or through the vega lite
204
+ *
205
+ * ---
206
+ *
207
+ * # FAQ
208
+ *
209
+ * The following are a series of common questions / issues, put here for visibility.
210
+ *
211
+ * ## Passing Objects to vega-lite-api
212
+ *
213
+ * Note that there are some things that are not supported by the vega-lite-api <br />
214
+ * ([such as grouping](https://github.com/vega/vega-lite/issues/4703))
215
+ *
216
+ * This is only a problem with the vega-lite-api, as it writes the spec used for vega-lite
217
+ *
218
+ * In these cases, you can send an object within many of the methods,
219
+ * as it no longer needs to translate how that object should look.
220
+ *
221
+ * (like sending `mark( type:'bar')` instead of `.markBar()` - to allow for tooltips <br />
222
+ * or passing an object to encode to support grouping)
223
+ *
224
+ * ```
225
+ * simpleData = [{fruit:'Apples',yield:20,year:'2020'},{fruit:'Apples',yield:22,year:'2021'},
226
+ * {fruit:'Bananas',yield:15,year:'2020'},{fruit:'Bananas',yield:12,year:'2021'},
227
+ * {fruit:'Pears',yield:18,year:'2020'},{fruit:'Pears',yield:19,year:'2021'}];
228
+ *
229
+ * utils.vega.svg((vl) => vl
230
+ * // render as points
231
+ * .mark({ type: 'bar', tooltip: true})
232
+ * .data(simpleData)
233
+ * .title('Fruit by Yield')
234
+ * .width(100)
235
+ * .encode({
236
+ * "x": {"field": "fruit"},
237
+ * "y": {"field": "yield", "type": "quantitative"},
238
+ * "xOffset": {"field": "year"},
239
+ * "color": {"field": "year"}
240
+ * })
241
+ * );
242
+ * ```
243
+ *
244
+ * ![Screenshot](img/vega_groupedBarCharts.jpg)
245
+ *
246
+ * ---
247
+ *
248
+ * ## Chart Series
249
+ *
250
+ * Instead of grouping values, you can also create a series of charts instead.
251
+ *
252
+ * ```
253
+ * utils.vega.svg((vl) => vl
254
+ * // render as points
255
+ * .mark({ type: 'arc', innerRadius: 30, tooltip: true})
256
+ * .data(simpleData)
257
+ * .title('Fruit by Yield')
258
+ * .width(100)
259
+ * .encode(
260
+ * // define the arc on the graph with the Qualitative / Numerical 'yield' property
261
+ * vl.theta().fieldQ('yield'),
262
+ * // define the y axis as the based on the Qualitative / Numerical 'year' property
263
+ * vl.color().fieldN('year'),
264
+ * // define the color series Nominative / TextBased 'fruit' property
265
+ * vl.column().fieldN('fruit')
266
+ * )
267
+ * );
268
+ * ```
269
+ *
270
+ * ![Screenshot](img/vega_chartColumns.jpg)
271
+ *
272
+ * Other examples can be found [under the vega-lite examples](https://vega.github.io/vega-lite/examples/#repeat--concatenation)
273
+ * and rendered with {@link vega.svgFromSpec|svgFromSpec} or {@link vega.ßembedFromSpec|embedFromSpec}
274
+ *
275
+ * ---
276
+ *
277
+ * ## Object Formatting / Conversion
278
+ *
279
+ * Note that vega-lite examples use data at the mark level:
280
+ *
281
+ * ```
282
+ * [{fruit:'Apples',yield:20,year:'2020'},{fruit:'Apples',yield:22,year:'2021'},
283
+ * {fruit:'Bananas',yield:15,year:'2020'},{fruit:'Bananas',yield:12,year:'2021'},
284
+ * {fruit:'Pears',yield:18,year:'2020'},{fruit:'Pears',yield:19,year:'2021'}];
285
+ * ```
286
+ *
287
+ * not at the series level:
288
+ *
289
+ * ```
290
+ * [{ year:'2020', apples:20, bananas:15, pears:18 },
291
+ * { year:'2021', apples:22, bananas:12, pears:19 }];
292
+ * ```
293
+ *
294
+ * If your data is at the series level, you can:
295
+ *
296
+ * * Transform the data with the {@link group#separateByFields|group.separateByFields}
297
+ * * or using the [vega-lite fold transform](https://vega.github.io/vega-lite/docs/fold.html)
298
+ *
299
+ * ```
300
+ * fruitSeriesData = [{ year:'2020', apples:20, bananas:15, pears:18 },
301
+ * { year:'2021', apples:22, bananas:12, pears:19 }];
302
+ *
303
+ * utils.vega.svg((vl) => vl
304
+ * .mark({ type: 'arc', innerRadius: 30, tooltip: true})
305
+ * .data(fruitSeriesData)
306
+ *
307
+ * //-- apples, bananas and pears will now be separate records
308
+ * //-- with the new `key` field as either 'apple', 'banana', or 'pears'
309
+ * //-- and the new `value` field storing the value of those fields.
310
+ * .transform([{ fold: ['apples', 'bananas', 'pears']}])
311
+ *
312
+ * .title('Fruit by Yield')
313
+ * .width(100)
314
+ * .encode(
315
+ * // define the arc on the graph with the Qualitative / Numerical 'yield' property
316
+ * vl.theta().fieldQ('value'),
317
+ * // define the y axis as the Nominative / TextBased 'fruit' property
318
+ * vl.color().fieldN('year'),
319
+ * // define the color series based on the Qualitative / Numerical 'year' property
320
+ * vl.column().fieldN('key')
321
+ * )
322
+ * )
323
+ * ```
324
+ * ![Screenshot](img/vega_chartColumns.jpg)
325
+ *
166
326
  * ---
167
327
  *
168
328
  * For more: