@rokkit/chart 1.3.5 → 1.3.7
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/dist/Plot/Axis.svelte +2 -0
- package/dist/Plot/Grid.svelte +32 -13
- package/dist/Plot/Grid.svelte.d.ts +6 -1
- package/dist/Plot.svelte +51 -4
- package/dist/Plot.svelte.d.ts +14 -2
- package/dist/PlotState.svelte.d.ts +7 -0
- package/dist/PlotState.svelte.js +37 -0
- package/dist/charts/AreaChart.svelte +33 -3
- package/dist/charts/AreaChart.svelte.d.ts +11 -2
- package/dist/charts/LineChart.svelte +34 -3
- package/dist/charts/LineChart.svelte.d.ts +11 -2
- package/dist/geoms/Area.svelte +18 -1
- package/dist/geoms/Bar.svelte +40 -7
- package/dist/geoms/Highlight.svelte +105 -0
- package/dist/geoms/Highlight.svelte.d.ts +10 -0
- package/dist/geoms/Line.svelte +25 -8
- package/dist/geoms/Point.svelte +30 -10
- package/dist/geoms/Trend.svelte +66 -0
- package/dist/geoms/Trend.svelte.d.ts +12 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -1
- package/dist/lib/brewing/brewer.svelte.d.ts +1 -0
- package/dist/lib/brewing/marks/lines.d.ts +2 -1
- package/dist/lib/brewing/marks/lines.js +11 -7
- package/dist/lib/highlight.d.ts +10 -0
- package/dist/lib/highlight.js +43 -0
- package/dist/lib/scale.d.ts +10 -0
- package/dist/lib/scale.js +14 -0
- package/dist/lib/select.d.ts +22 -0
- package/dist/lib/select.js +22 -0
- package/dist/lib/trend.d.ts +27 -0
- package/dist/lib/trend.js +113 -0
- package/dist/patterns/README.md +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export function mean(ys) {
|
|
2
|
+
if (!ys.length) return null
|
|
3
|
+
return ys.reduce((s, v) => s + v, 0) / ys.length
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function median(ys) {
|
|
7
|
+
if (!ys.length) return null
|
|
8
|
+
const sorted = [...ys].sort((a, b) => a - b)
|
|
9
|
+
const mid = Math.floor(sorted.length / 2)
|
|
10
|
+
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Least-squares line. Returns { m, b } or null (< 2 points or zero x-variance). */
|
|
14
|
+
export function linearRegression(xs, ys) {
|
|
15
|
+
const n = xs.length
|
|
16
|
+
if (n < 2) return null
|
|
17
|
+
const mx = mean(xs)
|
|
18
|
+
const my = mean(ys)
|
|
19
|
+
let num = 0
|
|
20
|
+
let den = 0
|
|
21
|
+
for (let i = 0; i < n; i++) {
|
|
22
|
+
const dx = xs[i] - mx
|
|
23
|
+
num += dx * (ys[i] - my)
|
|
24
|
+
den += dx * dx
|
|
25
|
+
}
|
|
26
|
+
if (den === 0) return null
|
|
27
|
+
const m = num / den
|
|
28
|
+
return { m, b: my - m * mx }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Trailing simple moving average; length preserved (partial window at the head). */
|
|
32
|
+
export function movingAverage(ys, window) {
|
|
33
|
+
const w = Math.max(1, Math.floor(window))
|
|
34
|
+
const out = []
|
|
35
|
+
for (let i = 0; i < ys.length; i++) {
|
|
36
|
+
const start = Math.max(0, i - w + 1)
|
|
37
|
+
const slice = ys.slice(start, i + 1)
|
|
38
|
+
out.push(slice.reduce((s, v) => s + v, 0) / slice.length)
|
|
39
|
+
}
|
|
40
|
+
return out
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Exponential moving average; alpha = alpha ?? 2/(span+1). Length preserved. */
|
|
44
|
+
export function ema(ys, { span, alpha } = {}) {
|
|
45
|
+
if (!ys.length) return []
|
|
46
|
+
const a = alpha ?? 2 / ((span ?? 7) + 1)
|
|
47
|
+
const out = [ys[0]]
|
|
48
|
+
for (let i = 1; i < ys.length; i++) out.push(a * ys[i] + (1 - a) * out[i - 1])
|
|
49
|
+
return out
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Fit y = a·e^(bx) via log-linear least squares. Null if any y ≤ 0 or < 2 points. */
|
|
53
|
+
export function expRegression(xs, ys) {
|
|
54
|
+
if (xs.length < 2) return null
|
|
55
|
+
if (ys.some((v) => v <= 0)) return null
|
|
56
|
+
const reg = linearRegression(xs, ys.map((v) => Math.log(v)))
|
|
57
|
+
if (!reg) return null
|
|
58
|
+
return { a: Math.exp(reg.b), b: reg.m }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function normalize(method) {
|
|
62
|
+
if (typeof method === 'number') return { type: 'value', value: method }
|
|
63
|
+
if (typeof method === 'string') return { type: method === 'mean' ? 'avg' : method }
|
|
64
|
+
if (method && typeof method === 'object' && method.type)
|
|
65
|
+
return { ...method, type: method.type === 'mean' ? 'avg' : method.type }
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Compute a trend for `rows` on channels {x, y}.
|
|
71
|
+
* @returns {{kind:'constant', value:number} | {kind:'series', values:number[]} | null}
|
|
72
|
+
*/
|
|
73
|
+
export function computeTrend(rows, channels, method) {
|
|
74
|
+
const cfg = normalize(method)
|
|
75
|
+
if (!cfg || !Array.isArray(rows) || rows.length === 0) return null
|
|
76
|
+
const { x, y } = channels
|
|
77
|
+
const ys = rows.map((r) => Number(r[y]))
|
|
78
|
+
const xs = rows.map((r, i) => {
|
|
79
|
+
const v = Number(r[x])
|
|
80
|
+
return Number.isFinite(v) ? v : i
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
switch (cfg.type) {
|
|
84
|
+
case 'avg':
|
|
85
|
+
return { kind: 'constant', value: mean(ys) }
|
|
86
|
+
case 'median':
|
|
87
|
+
return { kind: 'constant', value: median(ys) }
|
|
88
|
+
case 'min':
|
|
89
|
+
return { kind: 'constant', value: Math.min(...ys) }
|
|
90
|
+
case 'max':
|
|
91
|
+
return { kind: 'constant', value: Math.max(...ys) }
|
|
92
|
+
case 'value':
|
|
93
|
+
return typeof cfg.value === 'number' ? { kind: 'constant', value: cfg.value } : null
|
|
94
|
+
case 'linear': {
|
|
95
|
+
const reg = linearRegression(xs, ys)
|
|
96
|
+
if (!reg) return null
|
|
97
|
+
return { kind: 'series', values: xs.map((v) => reg.m * v + reg.b) }
|
|
98
|
+
}
|
|
99
|
+
case 'ma': {
|
|
100
|
+
if (!cfg.window) return null
|
|
101
|
+
return { kind: 'series', values: movingAverage(ys, cfg.window) }
|
|
102
|
+
}
|
|
103
|
+
case 'ema':
|
|
104
|
+
return { kind: 'series', values: ema(ys, { span: cfg.span, alpha: cfg.alpha }) }
|
|
105
|
+
case 'exp': {
|
|
106
|
+
const reg = expRegression(xs, ys)
|
|
107
|
+
if (!reg) return null
|
|
108
|
+
return { kind: 'series', values: xs.map((v) => reg.a * Math.exp(reg.b * v)) }
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
}
|
package/dist/patterns/README.md
CHANGED