Devaholic's picture
feat: UI for sentiment trend analysis
f8df83d
/**
* 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'
}