Repository navigation
feat: Remove Distributions, DataStructures, and MLJModelInterface/Tables dependencies #177
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
Merged
Merged
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
9333a54
refactor: Call StatsFuns directly for distribution functions
sethaxen 4c6e068
feat: Use our own categorical sampler instead of Distributions
sethaxen c5320b2
feat!: Move rstar Distributions integration to extension
sethaxen 0a2092f
chore: Remove Distributions as dependency
sethaxen 1601eae
feat: Remove DataStructures as dependency
sethaxen 0d3df58
fix: fix bug in _wsample
sethaxen a3d6ee9
test: Add _wsample tests
sethaxen f0a1550
feat: Roll our own _tiedrank!
sethaxen b54e546
feat: Roll our own _counts function
sethaxen eeb5d45
fix: Call Statistics.quantile directly
sethaxen f11c872
refactor: Move expectand proxy for mad to extension
sethaxen 7559149
chore: Remove StatsBase as package extension
sethaxen 4b8103d
chore: Remove unused DataAPI dependency
sethaxen 8d469ef
test: Bump EvoTrees test dep lower bound
sethaxen 59b41fa
test: Test trailing zeros walkback only on StatsBase versions that ha…
sethaxen 68b1a3f
chore: Increment minor version number
sethaxen 89f09e7
Revert "chore: Increment minor version number"
sethaxen 8291310
chore: Bump test and doc compat to match package
sethaxen 2279727
Reapply "chore: Increment minor version number"
sethaxen ab44c02
test: Add test for rstar error hint
sethaxen 3305b2f
perf: Use alias table for DAR(1) sampling in discretediag
sethaxen 8c04c72
perf: Use alias tables for Markov chain sampling in discretediag
sethaxen 8f502f6
perf: Replace broadcast over views with for loop
sethaxen 6eadc19
refactor: Move rstar implementation to MLJBase extension
sethaxen 12a1779
chore: Use patch instead of minor version bump
sethaxen 2d8d4c0
style: Apply formatting suggestions
sethaxen f8d7ee9
refactor: Drop package name from extension module names
sethaxen a03bcf0
refactor: Define model trait checks only in MLJBase extension
sethaxen 164bdc6
Revert "perf: Replace broadcast over views with for loop"
sethaxen 364143c
Revert "chore: Remove StatsBase as package extension"
sethaxen c789ffe
Revert "refactor: Move expectand proxy for mad to extension"
sethaxen a0b79de
Revert "feat: Roll our own _counts function"
sethaxen 638a9a0
Revert "feat: Roll our own _tiedrank!"
sethaxen 130dc81
test: Remove unused StatsBase import
sethaxen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| module MCMCDiagnosticToolsMLJBaseExt | ||
|
|
||
| using Distributions: Distributions | ||
| using MCMCDiagnosticTools: | ||
| MCMCDiagnosticTools, shuffle_split_stratified, split_chain_indices | ||
| using MLJModelInterface: MLJModelInterface as MMI | ||
| using Random: Random | ||
| using Statistics: Statistics | ||
| using Tables: Tables | ||
|
|
||
| import MCMCDiagnosticTools: | ||
| _check_model_supports_continuous_inputs, | ||
| _check_model_supports_multiclass_predictions, | ||
| _check_model_supports_multiclass_targets, | ||
| _rstar | ||
|
|
||
| function _rstar( | ||
| rng::Random.AbstractRNG, | ||
| classifier, | ||
| x, | ||
| y::AbstractVector{Int}; | ||
| subset::Real, | ||
| split_chains::Int, | ||
| verbosity::Int, | ||
| ) | ||
| # check the arguments | ||
| _check_model_supports_continuous_inputs(classifier) | ||
| _check_model_supports_multiclass_targets(classifier) | ||
| _check_model_supports_multiclass_predictions(classifier) | ||
| MMI.nrows(x) != length(y) && throw(DimensionMismatch()) | ||
| 0 < subset < 1 || throw(ArgumentError("`subset` must be a number in (0, 1)")) | ||
|
|
||
| # randomly sub-select training and testing set | ||
| ysplit = split_chain_indices(y, split_chains) | ||
| train_ids, test_ids = shuffle_split_stratified(rng, ysplit, subset) | ||
| 0 < length(train_ids) < length(y) || | ||
| throw(ArgumentError("training and test data subsets must not be empty")) | ||
|
|
||
| xtable = _astable(x) | ||
| ycategorical = MMI.categorical(ysplit) | ||
|
|
||
| # train classifier on training data | ||
| data = MMI.reformat(classifier, xtable, ycategorical) | ||
| train_data = MMI.selectrows(classifier, train_ids, data...) | ||
| fitresult, _ = MMI.fit(classifier, verbosity, train_data...) | ||
|
|
||
| # compute predictions on test data | ||
| # we exploit that MLJ demands that | ||
| # reformat(model, args...)[1] = reformat(model, args[1]) | ||
| # (https://alan-turing-institute.github.io/MLJ.jl/dev/adding_models_for_general_use/#Implementing-a-data-front-end) | ||
| test_data = MMI.selectrows(classifier, test_ids, data[1]) | ||
| predictions = _predict(classifier, fitresult, test_data...) | ||
|
|
||
| # compute statistic | ||
| ytest = ycategorical[test_ids] | ||
| result = _rstar(MMI.scitype(predictions), predictions, ytest) | ||
|
|
||
| return result | ||
| end | ||
|
|
||
| # check that the model supports the inputs and targets, and has predictions of the desired form | ||
| function _check_model_supports_continuous_inputs(classifier) | ||
| # ideally we would not allow MMI.Unknown but some models do not implement the traits | ||
| input_scitype_classifier = MMI.input_scitype(classifier) | ||
| if input_scitype_classifier !== MMI.Unknown && | ||
| !(MMI.Table(MMI.Continuous) <: input_scitype_classifier) | ||
| throw( | ||
| ArgumentError( | ||
| "classifier does not support tables of continuous values as inputs" | ||
| ), | ||
| ) | ||
| end | ||
| return nothing | ||
| end | ||
| function _check_model_supports_multiclass_targets(classifier) | ||
| target_scitype_classifier = MMI.target_scitype(classifier) | ||
| if target_scitype_classifier !== MMI.Unknown && | ||
| !(AbstractVector{<:MMI.Finite} <: target_scitype_classifier) | ||
| throw( | ||
| ArgumentError( | ||
| "classifier does not support vectors of multi-class labels as targets" | ||
| ), | ||
| ) | ||
| end | ||
| return nothing | ||
| end | ||
| function _check_model_supports_multiclass_predictions(classifier) | ||
| if !( | ||
| MMI.predict_scitype(classifier) <: Union{ | ||
| MMI.Unknown, | ||
| AbstractVector{<:MMI.Finite}, | ||
| AbstractVector{<:MMI.Density{<:MMI.Finite}}, | ||
| } | ||
| ) | ||
| throw( | ||
| ArgumentError( | ||
| "classifier does not support vectors of multi-class labels or their densities as predictions", | ||
| ), | ||
| ) | ||
| end | ||
| return nothing | ||
| end | ||
|
|
||
| _astable(x::AbstractVecOrMat) = Tables.table(x) | ||
| _astable(x) = Tables.istable(x) ? x : throw(ArgumentError("Argument is not a valid table")) | ||
|
|
||
| # Workaround for https://github.com/JuliaAI/MLJBase.jl/issues/863 | ||
| # `MLJModelInterface.predict` sometimes returns predictions and sometimes predictions + additional information | ||
| # TODO: Remove once the upstream issue is fixed | ||
| function _predict(model::MMI.Model, fitresult, x) | ||
| y = MMI.predict(model, fitresult, x) | ||
| return if :predict in MMI.reporting_operations(model) | ||
| first(y) | ||
| else | ||
| y | ||
| end | ||
| end | ||
|
|
||
| # R⋆ for deterministic predictions (algorithm 1) | ||
| function _rstar( | ||
| ::Type{<:AbstractVector{<:MMI.Finite}}, | ||
| predictions::AbstractVector, | ||
| ytest::AbstractVector, | ||
| ) | ||
| length(predictions) == length(ytest) || | ||
| error("numbers of predictions and targets must be equal") | ||
| mean_accuracy = Statistics.mean(p == y for (p, y) in zip(predictions, ytest)) | ||
| nclasses = length(MMI.classes(ytest)) | ||
| return nclasses * mean_accuracy | ||
| end | ||
|
|
||
| # R⋆ for probabilistic predictions (algorithm 2) | ||
| function _rstar( | ||
| ::Type{<:AbstractVector{<:MMI.Density{<:MMI.Finite}}}, | ||
| predictions::AbstractVector, | ||
| ytest::AbstractVector, | ||
| ) | ||
| length(predictions) == length(ytest) || | ||
| error("numbers of predictions and targets must be equal") | ||
|
|
||
| # create Poisson binomial distribution with support `0:length(predictions)` | ||
| distribution = Distributions.PoissonBinomial(map(Distributions.pdf, predictions, ytest)) | ||
|
|
||
| # scale distribution to support in `[0, nclasses]` | ||
| nclasses = length(MMI.classes(ytest)) | ||
| scaled_distribution = (nclasses//length(predictions)) * distribution | ||
|
|
||
| return scaled_distribution | ||
| end | ||
|
|
||
| # unsupported types of predictions and targets | ||
| function _rstar(::Any, predictions, targets) | ||
| return throw( | ||
| ArgumentError( | ||
| "unsupported types of predictions ($(typeof(predictions))) and targets ($(typeof(targets)))", | ||
| ), | ||
| ) | ||
| end | ||
|
|
||
| end | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| module MCMCDiagnosticToolsStatsBaseExt | ||
|
sethaxen marked this conversation as resolved.
Resolved
|
||
|
|
||
| using MCMCDiagnosticTools: MCMCDiagnosticTools | ||
| using Statistics: Statistics | ||
| using StatsBase: StatsBase | ||
|
|
||
| function MCMCDiagnosticTools._expectand_proxy(::typeof(StatsBase.mad), x) | ||
| x_folded = MCMCDiagnosticTools._fold_around_median(x) | ||
| return MCMCDiagnosticTools._expectand_proxy(Statistics.median, x_folded) | ||
| end | ||
|
|
||
| end | ||
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
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.