@sjcrh/proteinpaint-shared 2.180.0 → 2.180.1

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/src/roundValue.js CHANGED
@@ -25,7 +25,8 @@ export function roundValue(value, digits) {
25
25
  export function roundValueAuto(value, format = false, defaultDigits = 2) {
26
26
  if (!value && value != 0) return value
27
27
  const dp = decimalPlacesUntilFirstNonZero(value)
28
- const digits = Math.abs(value) > 1 ? defaultDigits : dp > 0 ? dp + 1 : defaultDigits
28
+ const digits =
29
+ Math.abs(value) > 1 ? defaultDigits : dp > 0 ? dp + 1 : defaultDigits
29
30
  if (format) return formatValue(value, digits)
30
31
  return roundValue(value, digits)
31
32
  }
@@ -35,7 +36,7 @@ export function decimalPlacesUntilFirstNonZero(number) {
35
36
  const numberStr = number.toString()
36
37
 
37
38
  // Find the position of the decimal point
38
- const decimalIndex = numberStr.indexOf('.')
39
+ const decimalIndex = numberStr.indexOf(".")
39
40
 
40
41
  // If decimal point is not found or number is an integer, return 0
41
42
  if (decimalIndex === -1 || decimalIndex === numberStr.length - 1) {
@@ -46,9 +47,9 @@ export function decimalPlacesUntilFirstNonZero(number) {
46
47
  let decimalPlaces = 0
47
48
  for (let i = decimalIndex + 1; i < numberStr.length; i++) {
48
49
  // Increment the count of decimal places until a non-zero digit is found
49
- if (numberStr[i] === '0') {
50
+ if (numberStr[i] === "0") {
50
51
  decimalPlaces++
51
- } else if (numberStr[i] >= '1' && numberStr[i] <= '9') {
52
+ } else if (numberStr[i] >= "1" && numberStr[i] <= "9") {
52
53
  break
53
54
  }
54
55
  }
@@ -1,88 +1,92 @@
1
- import { format } from 'd3-format'
2
- import { getColors } from './common.js'
3
- import { isNumeric, isStrictNumeric, convertUnits } from './helpers.js'
1
+ import { format } from "d3-format"
2
+ import { getColors } from "./common.js"
3
+ import { isNumeric, isStrictNumeric, convertUnits } from "./helpers.js"
4
4
 
5
5
  export default function validate_bins(binconfig) {
6
6
  // Number.isFinite('1') returns false, which is desired
7
7
 
8
8
  const bc = binconfig
9
- if (!bc || typeof bc !== 'object') throw 'bin schema must be an object'
9
+ if (!bc || typeof bc !== "object") throw "bin schema must be an object"
10
10
  // assign default type
11
- if (!('type' in bc)) bc.type = 'regular-bin'
11
+ if (!("type" in bc)) bc.type = "regular-bin"
12
12
 
13
- if (bc.type == 'custom-bin') {
14
- if (!Array.isArray(bc.lst)) throw 'binconfig.lst must be an array'
15
- if (!bc.lst.length) throw 'binconfig.lst must have entries'
13
+ if (bc.type == "custom-bin") {
14
+ if (!Array.isArray(bc.lst)) throw "binconfig.lst must be an array"
15
+ if (!bc.lst.length) throw "binconfig.lst must have entries"
16
16
  const first_bin = bc.lst[0]
17
17
  const last_bin = bc.lst[bc.lst.length - 1]
18
18
 
19
19
  for (const bin of bc.lst) {
20
- if (!('startinclusive' in bin) && !('stopinclusive' in bin)) {
21
- throw 'custom bin.startinclusive and/or bin.stopinclusive must be defined'
20
+ if (!("startinclusive" in bin) && !("stopinclusive" in bin)) {
21
+ throw "custom bin.startinclusive and/or bin.stopinclusive must be defined"
22
22
  }
23
23
 
24
24
  if (bin == first_bin) {
25
- if ('startunbounded' in bin && !bin.startunbounded) {
25
+ if ("startunbounded" in bin && !bin.startunbounded) {
26
26
  throw `a custom first bin must not set bin.startunbounded to false`
27
27
  }
28
28
  bin.startunbounded = true
29
- if ('start' in bin) {
30
- throw 'a custom first bin must not set a bin.start value'
29
+ if ("start" in bin) {
30
+ throw "a custom first bin must not set a bin.start value"
31
31
  }
32
- if ('start_percentile' in bin) {
33
- throw 'the first bin must not set a bin.start_percentile value'
32
+ if ("start_percentile" in bin) {
33
+ throw "the first bin must not set a bin.start_percentile value"
34
34
  }
35
- if (!('stop' in bin)) {
35
+ if (!("stop" in bin)) {
36
36
  throw `a custom first bin must define a bin.stop value`
37
37
  }
38
38
  if (!isStrictNumeric(bin.stop)) {
39
39
  throw `a custom first bin.stop value should be numeric`
40
40
  }
41
41
  } else if (bin == last_bin) {
42
- if (!('start' in bin)) {
42
+ if (!("start" in bin)) {
43
43
  throw `a custom last bin must define a bin.start value`
44
44
  }
45
45
  if (!isStrictNumeric(bin.start)) {
46
46
  throw `a custom last bin.start must be numeric`
47
47
  }
48
- if ('stopunbounded' in bin && !bin.stopunbounded) {
49
- throw 'a custom last bin must not set bin.stopunbounded to false'
48
+ if ("stopunbounded" in bin && !bin.stopunbounded) {
49
+ throw "a custom last bin must not set bin.stopunbounded to false"
50
50
  }
51
51
  bin.stopunbounded = true
52
- if ('stop' in bin) {
53
- throw 'a custom last bin must not set a bin.stop value'
52
+ if ("stop" in bin) {
53
+ throw "a custom last bin must not set a bin.stop value"
54
54
  }
55
55
  } else {
56
56
  if (bin.startunbounded || bin.stopunbounded) {
57
- throw 'bin.startunbounded and bin.stopunbounded must not be set for non-first/non-last bins'
57
+ throw "bin.startunbounded and bin.stopunbounded must not be set for non-first/non-last bins"
58
58
  }
59
- if (!isStrictNumeric(bin.start)) throw 'bin.start must be numeric for a non-first bin'
60
- if (!isStrictNumeric(bin.stop)) throw 'bin.stop must be numeric for a non-last bin'
59
+ if (!isStrictNumeric(bin.start))
60
+ throw "bin.start must be numeric for a non-first bin"
61
+ if (!isStrictNumeric(bin.stop))
62
+ throw "bin.stop must be numeric for a non-last bin"
61
63
  }
62
64
  }
63
- } else if (bc.type == 'regular-bin') {
65
+ } else if (bc.type == "regular-bin") {
64
66
  // required custom_bin parameter
65
- if (!Number.isFinite(bc.bin_size)) throw 'non-numeric bin_size'
66
- if (bc.bin_size <= 0) throw 'bin_size must be greater than 0'
67
+ if (!Number.isFinite(bc.bin_size)) throw "non-numeric bin_size"
68
+ if (bc.bin_size <= 0) throw "bin_size must be greater than 0"
67
69
 
68
70
  if (!bc.startinclusive && !bc.stopinclusive) {
69
71
  bc.startinclusive = 1
70
72
  bc.stopinclusive = 0
71
73
  }
72
74
 
73
- if (!bc.first_bin) throw 'first_bin{} missing'
74
- if (typeof bc.first_bin != 'object') throw 'first_bin{} is not an object'
75
- if (!Object.keys(bc.first_bin).length) throw 'first_bin is an empty object'
75
+ if (!bc.first_bin) throw "first_bin{} missing"
76
+ if (typeof bc.first_bin != "object") throw "first_bin{} is not an object"
77
+ if (!Object.keys(bc.first_bin).length) throw "first_bin is an empty object"
76
78
 
77
79
  {
78
80
  const b = bc.first_bin
79
81
  b.startunbounded = true
80
82
  // requires stop_percentile, or stop
81
83
  if (b.stop_percentile) {
82
- if (!Number.isInteger(b.stop_percentile)) throw 'first_bin.stop_percentile should be integer'
83
- if (b.stop_percentile <= 0 || b.stop_percentile >= 100) throw 'first_bin.stop_percentile out of bound (0-100)'
84
+ if (!Number.isInteger(b.stop_percentile))
85
+ throw "first_bin.stop_percentile should be integer"
86
+ if (b.stop_percentile <= 0 || b.stop_percentile >= 100)
87
+ throw "first_bin.stop_percentile out of bound (0-100)"
84
88
  } else if (!Number.isFinite(b.stop)) {
85
- throw 'first_bin.stop not a number when stop_percentile is not set'
89
+ throw "first_bin.stop not a number when stop_percentile is not set"
86
90
  }
87
91
  }
88
92
 
@@ -90,15 +94,17 @@ export default function validate_bins(binconfig) {
90
94
  const b = bc.last_bin
91
95
  // requires start_percentile or start
92
96
  if (b.start_percentile) {
93
- if (!Number.isInteger(b.start_percentile)) throw 'last_bin.start_percentile should be integer'
94
- if (b.start_percentile <= 0 || b.start_percentile >= 100) throw 'last_bin.start_percentile out of bound (0-100)'
97
+ if (!Number.isInteger(b.start_percentile))
98
+ throw "last_bin.start_percentile should be integer"
99
+ if (b.start_percentile <= 0 || b.start_percentile >= 100)
100
+ throw "last_bin.start_percentile out of bound (0-100)"
95
101
  } else if (!Number.isFinite(b.start)) {
96
- throw 'last_bin.start not a number when start_percentile is not set'
102
+ throw "last_bin.start not a number when start_percentile is not set"
97
103
  }
98
104
 
99
105
  b.stopunbounded = true
100
- if ('stop' in b) {
101
- throw 'a regular last bin must not set a bin.stop value'
106
+ if ("stop" in b) {
107
+ throw "a regular last bin must not set a bin.stop value"
102
108
  }
103
109
  }
104
110
  } else {
@@ -132,11 +138,13 @@ summaryfxn (percentiles)=> return {min, max, pX, pY, ...}
132
138
  const k2c = getColors(bc.lst.length) //to color bins
133
139
  for (const bin of bc.lst) bin.color = k2c(bin.label)
134
140
  }
135
- if (bc.type == 'custom-bin') return JSON.parse(JSON.stringify(bc.lst))
136
- if (typeof summaryfxn != 'function') throw 'summaryfxn required for modules/termdb.bins.js compute_bins()'
141
+ if (bc.type == "custom-bin") return JSON.parse(JSON.stringify(bc.lst))
142
+ if (typeof summaryfxn != "function")
143
+ throw "summaryfxn required for modules/termdb.bins.js compute_bins()"
137
144
  const percentiles = target_percentiles(bc)
138
145
  const summary = summaryfxn(percentiles)
139
- if (!summary || typeof summary !== 'object') throw 'invalid returned value by summaryfxn'
146
+ if (!summary || typeof summary !== "object")
147
+ throw "invalid returned value by summaryfxn"
140
148
  bc.results = { summary }
141
149
  if (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)
142
150
 
@@ -147,7 +155,7 @@ summaryfxn (percentiles)=> return {min, max, pX, pY, ...}
147
155
  const min = bc.first_bin.startunbounded
148
156
  ? minFloor
149
157
  : bc.first_bin.start_percentile
150
- ? summary['p' + bc.first_bin.start_percentile]
158
+ ? summary["p" + bc.first_bin.start_percentile]
151
159
  : bc.first_bin.start
152
160
  let max = maxCeil, // in order to include the max value in the last bin
153
161
  last_start,
@@ -157,26 +165,27 @@ summaryfxn (percentiles)=> return {min, max, pX, pY, ...}
157
165
  max = bc.last_bin.stopunbounded
158
166
  ? maxCeil // in order to include the max value in the last bin
159
167
  : bc.last_bin.stop_percentile
160
- ? summary['p' + bc.last_bin.stop_percentile]
168
+ ? summary["p" + bc.last_bin.stop_percentile]
161
169
  : isNumeric(bc.last_bin.stop) && bc.last_bin.stop <= summary.max // '0.0088' < 0.0088
162
170
  ? bc.last_bin.stop
163
171
  : maxCeil // in order to include the max value in the last bin
164
172
  last_start = isStrictNumeric(bc.last_bin.start_percentile)
165
- ? summary['p' + bc.last_bin.start_percentile]
173
+ ? summary["p" + bc.last_bin.start_percentile]
166
174
  : isStrictNumeric(bc.last_bin.start)
167
175
  ? bc.last_bin.start
168
176
  : undefined
169
177
  last_stop = bc.last_bin.stopunbounded
170
178
  ? null
171
179
  : bc.last_bin.stop_percentile
172
- ? summary['p' + bc.last_bin.stop_percentile]
180
+ ? summary["p" + bc.last_bin.stop_percentile]
173
181
  : isStrictNumeric(bc.last_bin.stop)
174
182
  ? bc.last_bin.stop
175
183
  : null
176
184
  } else if (bc.lst) {
177
185
  const last_bin = bc.lst[bc.lst.length - 1]
178
186
  last_start = last_bin.start
179
- last_stop = 'stop' in last_bin && !last_bin.stopunbounded ? last_bin.stop : maxCeil
187
+ last_stop =
188
+ "stop" in last_bin && !last_bin.stopunbounded ? last_bin.stop : maxCeil
180
189
  max = last_stop
181
190
  } else {
182
191
  last_start = maxCeil
@@ -194,18 +203,23 @@ summaryfxn (percentiles)=> return {min, max, pX, pY, ...}
194
203
  startunbounded: bc.first_bin.startunbounded,
195
204
  start: bc.first_bin.startunbounded ? undefined : min,
196
205
  stop: isStrictNumeric(bc.first_bin.stop_percentile)
197
- ? +summary['p' + bc.first_bin.stop_percentile]
206
+ ? +summary["p" + bc.first_bin.stop_percentile]
198
207
  : isStrictNumeric(bc.first_bin.stop)
199
208
  ? +bc.first_bin.stop
200
209
  : min + bc.bin_size,
201
210
  startinclusive: bc.startinclusive,
202
- stopinclusive: bc.stopinclusive
211
+ stopinclusive: bc.stopinclusive,
203
212
  }
204
213
 
205
- if (!isStrictNumeric(currBin.stop)) throw 'the computed first_bin.stop is non-numeric' + currBin.stop
214
+ if (!isStrictNumeric(currBin.stop))
215
+ throw "the computed first_bin.stop is non-numeric" + currBin.stop
206
216
  const maxNumBins = 100 // harcoded limit for now to not stress sqlite
207
217
 
208
- while ((numericMax && currBin.stop <= max) || (currBin.startunbounded && !bins.length) || currBin.stopunbounded) {
218
+ while (
219
+ (numericMax && currBin.stop <= max) ||
220
+ (currBin.startunbounded && !bins.length) ||
221
+ currBin.stopunbounded
222
+ ) {
209
223
  bins.push(currBin)
210
224
  // force a computed last bin to have stopunbounded true
211
225
  if (currBin.stop >= max) {
@@ -228,7 +242,7 @@ summaryfxn (percentiles)=> return {min, max, pX, pY, ...}
228
242
  ? last_stop
229
243
  : numericLastStart && upper > last_start && previousStop != last_start
230
244
  ? last_start
231
- : upper
245
+ : upper,
232
246
  }
233
247
 
234
248
  if (currBin.stop >= max) {
@@ -240,12 +254,17 @@ summaryfxn (percentiles)=> return {min, max, pX, pY, ...}
240
254
  if (bc.last_bin && bc.last_bin.stopunbounded) currBin.stopunbounded = 1
241
255
  }
242
256
  if (currBin.start > currBin.stop) {
243
- if (numericLastStart && currBin.stop == last_start && bc.last_bin && bc.last_bin.stopunbounded)
257
+ if (
258
+ numericLastStart &&
259
+ currBin.stop == last_start &&
260
+ bc.last_bin &&
261
+ bc.last_bin.stopunbounded
262
+ )
244
263
  currBin.stopunbounded = true
245
264
  else break
246
265
  }
247
266
  if (bins.length + 1 >= maxNumBins) {
248
- bc.error = 'max_num_bins_reached'
267
+ bc.error = "max_num_bins_reached"
249
268
  break
250
269
  }
251
270
  }
@@ -260,65 +279,97 @@ summaryfxn (percentiles)=> return {min, max, pX, pY, ...}
260
279
 
261
280
  function getNumDecimalsFormatter(bc) {
262
281
  //return format('rounding' in bc ? bc.rounding : '')
263
- return 'rounding' in bc ? format(bc.rounding) : d => d // default to labeling using the start/stop value as-is
282
+ return "rounding" in bc ? format(bc.rounding) : (d) => d // default to labeling using the start/stop value as-is
264
283
  }
265
284
 
266
285
  export function get_bin_label(bin, binconfig, valueConversion) {
267
286
  /*
268
287
  Generate a numeric bin label given a bin configuration and an optional term valueConversion object
269
288
  */
270
- if (!bin) return 'missing bin.label'
289
+ if (!bin) return "missing bin.label"
271
290
  if (bin.label) return bin.label
272
291
 
273
292
  const bc = binconfig
274
293
  if (!bc.binLabelFormatter) bc.binLabelFormatter = getNumDecimalsFormatter(bc)
275
- if (!bin.startunbounded && !bin.stopunbounded && !('startinclusive' in bin) && !('stopinclusive' in bin)) {
294
+ if (
295
+ !bin.startunbounded &&
296
+ !bin.stopunbounded &&
297
+ !("startinclusive" in bin) &&
298
+ !("stopinclusive" in bin)
299
+ ) {
276
300
  if (bc.startinclusive) bin.startinclusive = true
277
301
  else if (bc.stopinclusive) bin.stopinclusive = true
278
302
  }
279
303
 
280
- const start = bc.use_as == 'bins' || bin.start
281
- const stop = bc.use_as == 'bins' || bin.stop
304
+ const start = bc.use_as == "bins" || bin.start
305
+ const stop = bc.use_as == "bins" || bin.stop
282
306
 
283
307
  let label_offset = 0
284
- if ('label_offset' in bc) {
285
- bc.label_offset_ignored = 'bin_size' in bc && bc.bin_size < bc.label_offset
308
+ if ("label_offset" in bc) {
309
+ bc.label_offset_ignored = "bin_size" in bc && bc.bin_size < bc.label_offset
286
310
  if (!bc.label_offset_ignored) label_offset = bc.label_offset
287
- } else if (bc.bin_size === 1 && bc.termtype == 'integer') {
311
+ } else if (bc.bin_size === 1 && bc.termtype == "integer") {
288
312
  label_offset = 1
289
313
  }
290
314
 
291
315
  // one side-unbounded bins
292
316
  // label will be ">v" or "<v"
293
317
  if (bin.startunbounded) {
294
- const oper = bin.stopinclusive ? '' : '<' // \u2264
318
+ const oper = bin.stopinclusive ? "" : "<" // \u2264
295
319
  const v1 = valueConversion
296
- ? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)
320
+ ? convertUnits(
321
+ stop,
322
+ valueConversion.fromUnit,
323
+ valueConversion.toUnit,
324
+ valueConversion.scaleFactor,
325
+ true
326
+ )
297
327
  : bc.binLabelFormatter(stop) //bin.startinclusive && label_offset ? stop - label_offset : stop)
298
328
  return oper + v1
299
329
  }
300
330
  // a data value may coincide with the last bin's start
301
331
  if (bin.stopunbounded || start === stop) {
302
- const oper = bin.startinclusive /*|| label_offset*/ ? '' : '>' // \u2265
332
+ const oper = bin.startinclusive /*|| label_offset*/ ? "" : ">" // \u2265
303
333
  const v0 = valueConversion
304
- ? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)
334
+ ? convertUnits(
335
+ start,
336
+ valueConversion.fromUnit,
337
+ valueConversion.toUnit,
338
+ valueConversion.scaleFactor,
339
+ true
340
+ )
305
341
  : bc.binLabelFormatter(start) //bin.startinclusive || start == min ? start : start + label_offset)
306
342
  return oper + v0
307
343
  }
308
344
 
309
345
  // two-sided bins
310
346
  if (label_offset && bin.startinclusive && !bin.stopinclusive) {
311
- if (Number.isInteger(bc.bin_size) && Math.abs(start - stop) === label_offset) {
347
+ if (
348
+ Number.isInteger(bc.bin_size) &&
349
+ Math.abs(start - stop) === label_offset
350
+ ) {
312
351
  // make a simpler label when the range simply spans the bin_size
313
352
  return (
314
- '' +
353
+ "" +
315
354
  (valueConversion
316
- ? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)
355
+ ? convertUnits(
356
+ start,
357
+ valueConversion.fromUnit,
358
+ valueConversion.toUnit,
359
+ valueConversion.scaleFactor,
360
+ true
361
+ )
317
362
  : bc.binLabelFormatter(start))
318
363
  )
319
364
  } else {
320
365
  const v0 = valueConversion
321
- ? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)
366
+ ? convertUnits(
367
+ start,
368
+ valueConversion.fromUnit,
369
+ valueConversion.toUnit,
370
+ valueConversion.scaleFactor,
371
+ true
372
+ )
322
373
  : bc.binLabelFormatter(start)
323
374
  const v1 = valueConversion
324
375
  ? convertUnits(
@@ -330,28 +381,40 @@ export function get_bin_label(bin, binconfig, valueConversion) {
330
381
  )
331
382
  : bc.binLabelFormatter(stop - label_offset)
332
383
  // ensure that last two bin labels make sense (the last is stopunbounded)
333
- return +v0 >= +v1 ? v0.toString() : v0 + ' to ' + v1
384
+ return +v0 >= +v1 ? v0.toString() : v0 + " to " + v1
334
385
  }
335
386
  } else {
336
387
  // stop_inclusive || label_offset == 0
337
- const oper0 = bin.startinclusive ? '' : '>'
338
- const oper1 = bin.stopinclusive ? '' : '<'
388
+ const oper0 = bin.startinclusive ? "" : ">"
389
+ const oper1 = bin.stopinclusive ? "" : "<"
339
390
  const v0 = valueConversion
340
- ? convertUnits(start, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)
391
+ ? convertUnits(
392
+ start,
393
+ valueConversion.fromUnit,
394
+ valueConversion.toUnit,
395
+ valueConversion.scaleFactor,
396
+ true
397
+ )
341
398
  : Number.isInteger(start)
342
399
  ? start
343
400
  : bc.binLabelFormatter(start)
344
401
  const v1 = valueConversion
345
- ? convertUnits(stop, valueConversion.fromUnit, valueConversion.toUnit, valueConversion.scaleFactor, true)
402
+ ? convertUnits(
403
+ stop,
404
+ valueConversion.fromUnit,
405
+ valueConversion.toUnit,
406
+ valueConversion.scaleFactor,
407
+ true
408
+ )
346
409
  : Number.isInteger(stop)
347
410
  ? stop
348
411
  : bc.binLabelFormatter(stop)
349
412
  // after rounding the bin labels, the bin start may equal the last bin stop as derived from actual data
350
413
  if (+v0 >= +v1) {
351
- const oper = bin.startinclusive ? '' : '>' // \u2265
414
+ const oper = bin.startinclusive ? "" : ">" // \u2265
352
415
  return oper + v0
353
416
  } else {
354
- return oper0 + v0 + ' to ' + oper1 + v1
417
+ return oper0 + v0 + " to " + oper1 + v1
355
418
  }
356
419
  }
357
420
  }
@@ -362,17 +425,17 @@ export function get_bin_range_equation(bin, binconfig) {
362
425
  let range_eq
363
426
  // should always use computed (not user-customized) bin label to determine bin range text
364
427
  const copy = structuredClone(bin)
365
- copy.label = '' // mutate only the copy, and not the original bin argument
428
+ copy.label = "" // mutate only the copy, and not the original bin argument
366
429
  const bin_label = get_bin_label(copy, binconfig)
367
430
  if (bin.startunbounded || bin.stopunbounded) {
368
431
  // first or last bins, e.g. x ≤ 14 and x > 16
369
- range_eq = x + ' ' + bin_label
432
+ range_eq = x + " " + bin_label
370
433
  } else if (bin.startinclusive) {
371
434
  // bins with startinclusive, e.g. 14 ≤ x < 16
372
- range_eq = bin_label.replace('to <', '' + x + ' <')
435
+ range_eq = bin_label.replace("to <", "" + x + " <")
373
436
  } else if (bin.stopinclusive) {
374
437
  // bins with stopinclusive, e.g. 14 < x ≤ 16
375
- range_eq = bin_label.replace('>', '').replace('to', '< ' + x + '')
438
+ range_eq = bin_label.replace(">", "").replace("to", "< " + x + "")
376
439
  }
377
440
  return range_eq
378
441
  }
@@ -380,10 +443,14 @@ export function get_bin_range_equation(bin, binconfig) {
380
443
  export function target_percentiles(binconfig) {
381
444
  const percentiles = []
382
445
  const f = binconfig.first_bin
383
- if (f && isStrictNumeric(f.start_percentile)) percentiles.push(f.start_percentile)
384
- if (f && isStrictNumeric(f.stop_percentile)) percentiles.push(f.stop_percentile)
446
+ if (f && isStrictNumeric(f.start_percentile))
447
+ percentiles.push(f.start_percentile)
448
+ if (f && isStrictNumeric(f.stop_percentile))
449
+ percentiles.push(f.stop_percentile)
385
450
  const l = binconfig.last_bin
386
- if (l && isStrictNumeric(l.start_percentile)) percentiles.push(l.start_percentile)
387
- if (l && isStrictNumeric(l.stop_percentile)) percentiles.push(l.stop_percentile)
451
+ if (l && isStrictNumeric(l.start_percentile))
452
+ percentiles.push(l.start_percentile)
453
+ if (l && isStrictNumeric(l.stop_percentile))
454
+ percentiles.push(l.stop_percentile)
388
455
  return percentiles
389
456
  }
@@ -7,8 +7,15 @@ Initialize a bin configuration for a numeric dataset
7
7
  */
8
8
  export default function initBinConfig(data, opts = {}) {
9
9
  if (!data.length)
10
- return { mode: 'discrete', type: 'regular-bin', startinclusive: true, bin_size: null, first_bin: { stop: null } }
11
- if (data.find(d => !Number.isFinite(d))) throw new Error('non-numeric values found')
10
+ return {
11
+ mode: "discrete",
12
+ type: "regular-bin",
13
+ startinclusive: true,
14
+ bin_size: null,
15
+ first_bin: { stop: null },
16
+ }
17
+ if (data.find((d) => !Number.isFinite(d)))
18
+ throw new Error("non-numeric values found")
12
19
 
13
20
  let binConfig
14
21
  const s = new Set(data)
@@ -21,12 +28,28 @@ export default function initBinConfig(data, opts = {}) {
21
28
  // all data values will fall into the second bin
22
29
  const value = [...s][0]
23
30
  binConfig = {
24
- type: 'custom-bin',
31
+ type: "custom-bin",
25
32
  lst: [
26
- { stop: value, stopinclusive: false, startunbounded: true, label: '<' + value },
27
- { start: value, stop: value, startinclusive: true, stopinclusive: true, label: '=' + value },
28
- { start: value, startinclusive: false, stopunbounded: true, label: '>' + value }
29
- ]
33
+ {
34
+ stop: value,
35
+ stopinclusive: false,
36
+ startunbounded: true,
37
+ label: "<" + value,
38
+ },
39
+ {
40
+ start: value,
41
+ stop: value,
42
+ startinclusive: true,
43
+ stopinclusive: true,
44
+ label: "=" + value,
45
+ },
46
+ {
47
+ start: value,
48
+ startinclusive: false,
49
+ stopunbounded: true,
50
+ label: ">" + value,
51
+ },
52
+ ],
30
53
  }
31
54
  } else {
32
55
  // multiple unique values in data array
@@ -47,22 +70,23 @@ export default function initBinConfig(data, opts = {}) {
47
70
  // first bin stop will equal either (minimum + bin size) or (5th percentile), whichever is larger.
48
71
  const firstBinStop = Math.max(min + binSize, p5)
49
72
  // round the bin values
50
- let [binSize_rnd, firstBinStop_rnd, lastBinStart_rnd, rounding] = roundBinVals(binSize, firstBinStop, max, min)
73
+ let [binSize_rnd, firstBinStop_rnd, lastBinStart_rnd, rounding] =
74
+ roundBinVals(binSize, firstBinStop, max, min)
51
75
  // generate the bin configuration
52
76
  binConfig = {
53
- type: 'regular-bin',
77
+ type: "regular-bin",
54
78
  startinclusive: true,
55
79
  bin_size: binSize_rnd,
56
- first_bin: { stop: firstBinStop_rnd }
80
+ first_bin: { stop: firstBinStop_rnd },
57
81
  }
58
82
  if (lastBinStart_rnd) binConfig.last_bin = { start: lastBinStart_rnd }
59
83
  if (rounding) binConfig.rounding = rounding
60
84
  }
61
- if ('format' in opts) {
62
- if (opts.format === 'string') {
85
+ if ("format" in opts) {
86
+ if (opts.format === "string") {
63
87
  return JSON.stringify(binConfig)
64
88
  } else {
65
- throw 'options are not in the correct format'
89
+ throw "options are not in the correct format"
66
90
  }
67
91
  } else {
68
92
  return binConfig
@@ -75,14 +99,17 @@ function roundBinVals(binSize, firstBinStop, max, min) {
75
99
  if (binSize >= 0.1 && binSize <= 2) {
76
100
  // Round to the nearest one for small bin sizes
77
101
  binSize_rnd = Math.round(binSize / (1 * 10 ** log)) * (1 * 10 ** log)
78
- firstBinStop_rnd = Math.round(firstBinStop / (1 * 10 ** log)) * (1 * 10 ** log)
102
+ firstBinStop_rnd =
103
+ Math.round(firstBinStop / (1 * 10 ** log)) * (1 * 10 ** log)
79
104
  } else {
80
105
  // Round to the nearest five for large bin sizes
81
106
  binSize_rnd = Math.round(binSize / (5 * 10 ** log)) * (5 * 10 ** log)
82
- firstBinStop_rnd = Math.round(firstBinStop / (5 * 10 ** log)) * (5 * 10 ** log)
107
+ firstBinStop_rnd =
108
+ Math.round(firstBinStop / (5 * 10 ** log)) * (5 * 10 ** log)
83
109
  if (binSize_rnd === 0) binSize_rnd = 1 * 10 ** log
84
110
  if (firstBinStop_rnd === 0) firstBinStop_rnd = 1 * 10 ** log
85
- if (binSize_rnd === 5 * 10 ** log && firstBinStop_rnd === 1 * 10 ** log) firstBinStop_rnd = 5 * 10 ** log
111
+ if (binSize_rnd === 5 * 10 ** log && firstBinStop_rnd === 1 * 10 ** log)
112
+ firstBinStop_rnd = 5 * 10 ** log
86
113
  }
87
114
  if (firstBinStop_rnd < min) firstBinStop_rnd = firstBinStop_rnd * 2
88
115
  // if the number of bins is above 8 after rounding, then set the last bin start to restrict the number of bins to 8
@@ -94,8 +121,9 @@ function roundBinVals(binSize, firstBinStop, max, min) {
94
121
  const digits = Math.abs(log)
95
122
  binSize_rnd = Number(binSize_rnd.toFixed(digits))
96
123
  firstBinStop_rnd = Number(firstBinStop_rnd.toFixed(digits))
97
- if (lastBinStart_rnd) lastBinStart_rnd = Number(lastBinStart_rnd.toFixed(digits))
98
- rounding = '.' + digits + 'f'
124
+ if (lastBinStart_rnd)
125
+ lastBinStart_rnd = Number(lastBinStart_rnd.toFixed(digits))
126
+ rounding = "." + digits + "f"
99
127
  }
100
128
  if (Object.is(firstBinStop_rnd, -0)) firstBinStop_rnd = 0
101
129
  return [binSize_rnd, firstBinStop_rnd, lastBinStart_rnd, rounding]