CODAP-1471: omit LSRL statistics that aren't estimable - #2675
Conversation
Three related cases let undefined statistics reach the LSRL's output: - rSquared is 0/0 when the y values don't vary, so a valid horizontal line displayed "r = (not a number)" next to "r² = 0". The r² branch laundered the NaN through `rSquared || 0`, which is why the two disagreed. - The standard errors were reported as 0 with only two points, though the fit has zero degrees of freedom and they aren't estimable. V2 returns NaN here, so the special case was also a V2 divergence. - mse is likewise 0/0 with two points, and confidenceValues guarded with `== null`, which NaN passes. NaN reached the confidence band path coordinates and the browser rejected the paths, logging console errors. Statistics that aren't estimable are now omitted from the equation rather than displayed, and bands that can't be estimated clear their paths instead of drawing NaN coordinates. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## CODAP-1461-lsrl-degenerate-equation #2675 +/- ##
====================================================================
Coverage 87.69% 87.69%
====================================================================
Files 810 810
Lines 46938 46939 +1
Branches 12013 12004 -9
====================================================================
+ Hits 41162 41164 +2
+ Misses 5760 5759 -1
Partials 16 16
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
codap-v3
|
||||||||||||||||||||||||||||
| Project |
codap-v3
|
| Branch Review |
main
|
| Run status |
|
| Run duration | 07m 54s |
| Commit |
|
| Committer | null |
| View all properties for this run ↗︎ | |
| Test results | |
|---|---|
|
|
0
|
|
|
1
|
|
|
82
|
|
|
0
|
|
|
382
|
| View all changes introduced in this branch ↗︎ | |
There was a problem hiding this comment.
Pull request overview
This PR updates the v3 graph LSRL (least-squares regression line) display logic so that regression-related statistics and confidence-band geometry that are not mathematically estimable (e.g., NaN from degenerate inputs like two points or no variance) are omitted/cleared rather than rendered, preventing inconsistent UI output and browser console errors.
Changes:
- Leave LSRL standard errors as
NaNwhen degrees of freedom are zero (two-point fit), and update unit tests accordingly. - Update
lsrlEquationString()to omit r, r², and σ rows unless the computed values are finite (and stop laundering NaN through|| 0). - Guard confidence-band computation/rendering against non-finite inputs and clear SVG paths when bands can’t be estimated.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| v3/src/utilities/stats-utils.ts | Stops reporting 0 standard errors for 2-point fits; leaves them non-estimable (NaN). |
| v3/src/utilities/stats-utils.test.ts | Updates expectations to match non-estimable standard errors for 2-point data. |
| v3/src/components/graph/utilities/graph-utils.ts | Omits non-finite LSRL statistics from the equation string (r, r², σ terms). |
| v3/src/components/graph/utilities/graph-utils.test.ts | Adds tests asserting omitted/retained rows based on finiteness of stats. |
| v3/src/components/graph/adornments/lsrl/lsrl-adornment-model.ts | Switches confidence-band bounds gating from nullness to finiteness to avoid NaN propagation. |
| v3/src/components/graph/adornments/lsrl/lsrl-adornment-model.test.ts | Adds tests covering confidence bounds/band points behavior with sufficient vs insufficient degrees of freedom. |
| v3/src/components/graph/adornments/lsrl/lsrl-adornment-component.tsx | Clears band paths when unestimable (returns null paths; avoids empty/NaN path strings). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The guard reads `line?.intercept` before the unchained `line.slope`, which is safe because the leading `||` operand short-circuits. Add tests covering the no-line and unknown-category cases so the behavior is pinned rather than inferred from reading. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes CODAP-1471
main— retargetonce #2674 merges. Review only the latest commit; the rest is #2674's diff.
Problem
Three cases where a statistic that can't be estimated still reached the LSRL's output:
y = 5,r = (not a number),r² = 0y = 5σslope = 0,σintercept = 0, NaN band paths, 3 console errorsRoot causes
rSquaredis(sumOfProductDiffs)² / (xSumSquaredDeviations * ySumSquaredDeviations),which is
0/0when the y values don't vary. The r branch formatted that NaN directlywhile the r² branch passed it through
rSquared || 0— hence the two disagreeing.linRegrStdErrSlopeAndInterceptspecial-casedcount === 2to return{0, 0}. Withzero degrees of freedom the standard errors aren't estimable, so 0 asserts something
we can't know.
mse = sumSquaredErrors / (count - 2)is0/0with two points, andconfidenceValuesguarded with
== null, which NaN passes. NaN reached the band pathdattribute, sothe browser rejected the paths and logged
<path> attribute d: Expected number.Fix
Per the rule agreed with Bill: anything undefined is omitted — geometry that can't be
drawn isn't drawn, numbers that can't be computed get no row.
lsrlEquationStringomits the r, r², σslope and σintercept rows unless the value isfinite, and no longer launders NaN through
|| 0.linRegrStdErrSlopeAndInterceptdrops thecount === 2special case.confidenceValuesguards on finiteness rather than nullness, and the component clearsthe band paths instead of assigning empty/NaN ones.
V2 compatibility
I ran v2's own math in the live v2 app (build 0745). v2 produces the same NaNs for every
one of these cases and has no
count === 2 → 0special case — it returns NaN forseSlope/seIntercept with two points. So removing that special case restores v2 behavior
rather than diverging from it. On 5-point data v3 now yields σslope = 0.15 and
σintercept = 0.497, matching v2's 0.15000000000000005 / 0.49749371855331026.
Testing
lsrlEquationStringtests: r/r² omitted when rSquared is NaN, σ rows omitted whenthe standard errors are NaN, plus a positive control that finite values still render.
confidenceValues/confidenceBandsPointstests: no bounds and no band pointswith two points; finite bounds with four.
linRegrStdErrSlopeAndInterceptexpectation from 0 to NaN.console errors are gone.
🤖 Generated with Claude Code