File size: 1,681 Bytes
f8df83d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * Centered rolling average over sorted array.
 * Returns array of { index, value, avg } where avg may be null at edges.
 */
export function rollingAverage(sorted, window = 7) {
  const half = Math.floor(window / 2)
  return sorted.map((item, i) => {
    const start = Math.max(0, i - half)
    const end = Math.min(sorted.length - 1, i + half)
    const slice = sorted.slice(start, end + 1)
    const avg = slice.reduce((s, x) => s + x.value, 0) / slice.length
    return { ...item, avg }
  })
}

/**
 * Pearson correlation coefficient for two arrays of equal length.
 */
export function pearsonR(x, y) {
  const n = x.length
  if (n < 2) return 0
  const mx = x.reduce((a, b) => a + b, 0) / n
  const my = y.reduce((a, b) => a + b, 0) / n
  const num = x.reduce((s, xi, i) => s + (xi - mx) * (y[i] - my), 0)
  const den = Math.sqrt(
    x.reduce((s, xi) => s + (xi - mx) ** 2, 0) *
    y.reduce((s, yi) => s + (yi - my) ** 2, 0)
  )
  return den === 0 ? 0 : num / den
}

/**
 * Simple linear regression. Returns { slope, intercept }.
 */
export function linearRegression(x, y) {
  const n = x.length
  if (n < 2) return { slope: 0, intercept: 0 }
  const mx = x.reduce((a, b) => a + b, 0) / n
  const my = y.reduce((a, b) => a + b, 0) / n
  const slope = x.reduce((s, xi, i) => s + (xi - mx) * (y[i] - my), 0) /
    x.reduce((s, xi) => s + (xi - mx) ** 2, 0)
  const intercept = my - slope * mx
  return { slope, intercept }
}

/**
 * Human-readable correlation description.
 */
export function describeCorrelation(r) {
  const abs = Math.abs(r)
  if (abs >= 0.7) return 'strong'
  if (abs >= 0.4) return 'moderate'
  if (abs >= 0.2) return 'weak'
  return 'no meaningful'
}