Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Negative r2 for linear model with no intercept #481

Closed
wants to merge 7 commits into from

Conversation

mousum-github
Copy link
Collaborator

@mousum-github mousum-github commented Apr 25, 2022

The Julia lm produces negative r2 (-0.15702479338842856) for the following:
data = DataFrame(x = 60:70, y = 130:140)
mdl = lm(@formula(y ~ 0 + x), data)
r2(mdl)

The certified value of r2 = 0.999365492298663
https://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/NoInt1.dat

While investigating the reason, we found that the nulldeviance calculation is different. Ideally, if the model has the intercept term, then the nulldeviance = y’y – n * mean(y)^2 and if the model does not have the intercept, then the nulldeviance = y’y.

In this PR, we have attempted to correct the nulldeviance calculation.

The following is the summary of the changes:

  • Added a new parameter hasintercept of type Bool in nulldeviance function with existing parameter type LinResp.
  • nulldeviance(obj::LinearModel) = nulldeviance(obj.rr, hasintercept(obj))
  • Added a test case to test beta, standard error of the estimate, dispersion and r2 with the certified values given in the above link

@codecov-commenter
Copy link

codecov-commenter commented Apr 25, 2022

Codecov Report

Merging #481 (7c52e8d) into master (42a0d04) will increase coverage by 2.00%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master     #481      +/-   ##
==========================================
+ Coverage   85.12%   87.13%   +2.00%     
==========================================
  Files           7        7              
  Lines         827      847      +20     
==========================================
+ Hits          704      738      +34     
+ Misses        123      109      -14     
Impacted Files Coverage Δ
src/lm.jl 96.18% <100.00%> (-0.06%) ⬇️
src/GLM.jl 50.00% <0.00%> (ø)
src/glmfit.jl 79.29% <0.00%> (+0.55%) ⬆️
src/glmtools.jl 92.99% <0.00%> (+10.28%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 42a0d04...7c52e8d. Read the comment docs.

@ayushpatnaikgit
Copy link
Contributor

@nalimilan @ViralBShah the PR is only failing on Julia nightly—windows. I don't think it's to do with anything that we've changed. What shall we do in this situation? It's happening with PR 482 as well.

@ViralBShah
Copy link
Contributor

Best would be to isolate a minimal test case and file a bug against Julia.

@ViralBShah
Copy link
Contributor

Actually do check the logs. This has to do with timezones, so ignore. Need to see what is going on though

@nalimilan
Copy link
Member

I think the TimeZones issue is a network problem, I've seen it before.

Regarding the PR itself, I think we'll have to adjust the definition of nulldeviance and nullloglikelihood to cover models with no intercept, as currently the null model is defined as the one with only the intercept, but this doesn't make much sense when there's no intercept. This is an issue with have recently discussed too at #479 (comment). Cc: @palday

src/lm.jl Outdated Show resolved Hide resolved
src/lm.jl Outdated Show resolved Hide resolved
src/lm.jl Outdated Show resolved Hide resolved
test/runtests.jl Outdated Show resolved Hide resolved
…defination of adjusted r-squared, added test cases, added more metrics in test cases
Copy link
Member

@kleinschmidt kleinschmidt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably good to also test teh case when the model is are not fit to a formula? e.g. the lm(X::Matrix, y::Vector) method. AFAICT it should Just Work TM (there are methods for hasintercept for formula-less models) but good to make sure it doesn't break :)

src/lm.jl Outdated Show resolved Hide resolved
src/lm.jl Outdated Show resolved Hide resolved
src/lm.jl Outdated Show resolved Hide resolved
@nalimilan
Copy link
Member

(For some reason I can't comment in the thread above.) I think eachindex is fine here. People shouldn't play fitting models where the response, predictors and/or weights have different axes. That would be asking for trouble. (Note that the current code is silently incorrect in that case anyway, so throwing an error would be an improvement.)

mousum-github and others added 3 commits May 20, 2022 17:06
Thanks for your suggestion. It has been committed.

Co-authored-by: Alex Arslan <[email protected]>
Removed indexing as suggested.

Co-authored-by: Alex Arslan <[email protected]>
…` function + added one more test case without providing any formula in `lm` function
@nalimilan
Copy link
Member

Thanks, looks good! I just wonder what release strategy we should adopt. Technically this could be considered as a breaking change (because it changes the definition of nulldeviance and nullloglikelihood, cf. JuliaStats/StatsAPI.jl#14) or as a bugfix (because the previous result didn't make a lot of sense, and was even misleading for the R²). We probably don't want to tag GLM 2.0 just for htis. We could adopt an intermediate approach and print a warning (at least temporarily) for models without an intercept. Opinions?

test/runtests.jl Outdated Show resolved Hide resolved
src/lm.jl Outdated Show resolved Hide resolved
test/runtests.jl Outdated Show resolved Hide resolved
mousum-github and others added 2 commits May 23, 2022 07:06
Co-authored-by: Milan Bouchet-Valat <[email protected]>
Removed `;`. The suggestion is commited.

Co-authored-by: Milan Bouchet-Valat <[email protected]>
@palday
Copy link
Member

palday commented May 23, 2022

@nalimilan I like the idea of issuing a warning for models without intercepts and a minor version bump -- it's a good thing to call out anyway that a lot of classical summary definitions are "weird" for models without intercepts.

m = mean(y, weights(wts))
end
else
m = zero(eltype(y))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, let's try something like this then? It would also be good to adapt Project.toml to require StatsAPI 1.4, which I'll tag shortly (JuliaStats/StatsAPI.jl#14), so that the docstring is consistent with what we do here.

Suggested change
m = zero(eltype(y))
@warn("Starting from GLM.jl 1.8, null model is defined as having no predictor at all " *
"when a model without an intercept is passed.")
m = zero(eltype(y))

@nalimilan
Copy link
Member

@kleinschmidt Technically, it's possible to fit a model which has an intercept but for which hasintercept returns false by passing a model matrix which applies full dummy coding without a column of ones, right? Should we try to detect this?

@nalimilan
Copy link
Member

I don't have rights to edit your branch so I pushed the commit directly to master with the warning. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants