Remove intercept for a bare "+ -1" / "+ 0", matching "- 1" (fixes #158)#267
Open
deepakganesh78 wants to merge 1 commit into
Open
Remove intercept for a bare "+ -1" / "+ 0", matching "- 1" (fixes #158)#267deepakganesh78 wants to merge 1 commit into
deepakganesh78 wants to merge 1 commit into
Conversation
Root cause: _eval_binary_plus only propagated an *added* intercept from its right operand (via the `intercept` flag); it ignored the right operand's `intercept_removed` flag entirely, keeping only the left operand's intercept state. So "a + b - 1" removed the intercept (removal lives on the left) but "a + b + -1" did not, even though the docs imply appending "+ -1" is equivalent to appending "- 1". Fix: when the right operand of a binary "+" is a *bare* intercept literal (a chain of unary +/- ending directly in 0/1, as in "-1"), honor its intercept removal, mirroring the existing direct "+ 0" (ZERO) handling. A new _is_bare_intercept() helper inspects the parse node so that removal is NOT propagated out of a compound/parenthesized expression such as "1 + (a + 0)", which keeps the intercept by design (existing test). Adds regression cases to _eval_tests and a changelog entry. Fixes pydata#158
bashtage
requested changes
Jul 25, 2026
bashtage
left a comment
Contributor
There was a problem hiding this comment.
It would be appropriate to warn and let the user know since they can easily fix it.
| # "+ 0" (ZERO) case handled above. We deliberately do *not* | ||
| # propagate intercept removal out of a compound expression such as | ||
| # "1 + (a + 0)", which keeps the intercept by design. | ||
| return IntermediateExpr( |
Contributor
There was a problem hiding this comment.
In my opinion, this is a "won't fix" type of bug given where the project is. I think a warning would be OK, though. To fix this, one would need to go through a reasonably long deprecation cycle since this is a change to existing code, even if the original implementation is not great. Something that explains to the user what happens here and then tells the user how they can fix it (by slightly altering their formula) is better than a silent behaviour change.
statsmodels will be out with formulaic support next month, so this is moving to lower importance.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This fixes #158.
Root cause
_eval_binary_plusonly propagated an added intercept from its right operand (via theinterceptflag); it ignored the right operand'sintercept_removedflag entirely, keeping only the left operand's intercept state.So
a + b - 1removes the intercept (the removal lives on the left operand), buta + b + -1did not — even though the docs imply that appending+ -1is equivalent to appending- 1. The maintainer confirmed in the issue that this is a bug and that the successor, Formulaic, treatsa + b + -1asa + b.Fix
When the right operand of a binary
+is a bare intercept literal (a chain of unary+/-ending directly in0/1, as in-1), honor its intercept removal — mirroring the existing direct+ 0(ZERO) handling just above it.A small
_is_bare_intercept()helper inspects the parse node so that removal is not propagated out of a compound/parenthesized expression such as1 + (a + 0), which keeps the intercept by design (covered by the existing~ 1 + (a + 0)test, which still passes).Behavior after fix
Tests
Added regression cases to
_eval_testsand a changelog entry. Full suite is green:(scipy installed so the spline tests run.)