diff --git a/pr-preview/pr-276/.buildinfo b/pr-preview/pr-276/.buildinfo new file mode 100644 index 000000000..761eb8235 --- /dev/null +++ b/pr-preview/pr-276/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: b8cc9f9a7a49cac039167958d5de865e +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/pr-preview/pr-276/.doctrees/api/base.doctree b/pr-preview/pr-276/.doctrees/api/base.doctree new file mode 100644 index 000000000..0a000e212 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/base.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.PipelineCreator.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.PipelineCreator.doctree new file mode 100644 index 000000000..20c0f0277 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.PipelineCreator.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.TargetPipelineCreator.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.TargetPipelineCreator.doctree new file mode 100644 index 000000000..9bc478648 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.TargetPipelineCreator.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ColumnTypes.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ColumnTypes.doctree new file mode 100644 index 000000000..fd9126051 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ColumnTypes.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ColumnTypesLike.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ColumnTypesLike.doctree new file mode 100644 index 000000000..5f9211920 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ColumnTypesLike.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.JuBaseEstimator.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.JuBaseEstimator.doctree new file mode 100644 index 000000000..0ee35a4b3 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.JuBaseEstimator.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.JuTransformer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.JuTransformer.doctree new file mode 100644 index 000000000..bf42a385b Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.JuTransformer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.WrapModel.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.WrapModel.doctree new file mode 100644 index 000000000..51a72da6d Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.WrapModel.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.change_column_type.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.change_column_type.doctree new file mode 100644 index 000000000..f59492baf Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.change_column_type.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ensure_column_types.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ensure_column_types.doctree new file mode 100644 index 000000000..aef2aeb7c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.ensure_column_types.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.get_column_type.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.get_column_type.doctree new file mode 100644 index 000000000..4acf252eb Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.get_column_type.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.base.make_type_selector.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.make_type_selector.doctree new file mode 100644 index 000000000..5786a89be Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.base.make_type_selector.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.FoldsInspector.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.FoldsInspector.doctree new file mode 100644 index 000000000..72cff9283 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.FoldsInspector.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.Inspector.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.Inspector.doctree new file mode 100644 index 000000000..36fad9f65 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.Inspector.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.preprocess.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.preprocess.doctree new file mode 100644 index 000000000..0e1b67cec Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.inspect.preprocess.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.ContinuousStratifiedGroupKFold.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.ContinuousStratifiedGroupKFold.doctree new file mode 100644 index 000000000..bf319ce45 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.ContinuousStratifiedGroupKFold.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.ContinuousStratifiedKFold.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.ContinuousStratifiedKFold.doctree new file mode 100644 index 000000000..35ccafc85 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.ContinuousStratifiedKFold.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.RepeatedContinuousStratifiedGroupKFold.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.RepeatedContinuousStratifiedGroupKFold.doctree new file mode 100644 index 000000000..8488ac14c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.RepeatedContinuousStratifiedGroupKFold.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.RepeatedContinuousStratifiedKFold.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.RepeatedContinuousStratifiedKFold.doctree new file mode 100644 index 000000000..4cbd20f78 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.RepeatedContinuousStratifiedKFold.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.StratifiedBootstrap.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.StratifiedBootstrap.doctree new file mode 100644 index 000000000..00af32920 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.StratifiedBootstrap.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.get_searcher.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.get_searcher.doctree new file mode 100644 index 000000000..1e79e67f1 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.get_searcher.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.list_searchers.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.list_searchers.doctree new file mode 100644 index 000000000..43ea39b87 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.list_searchers.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.register_searcher.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.register_searcher.doctree new file mode 100644 index 000000000..52403de28 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.register_searcher.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.reset_searcher_register.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.reset_searcher_register.doctree new file mode 100644 index 000000000..ae219e740 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.model_selection.reset_searcher_register.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.models.dynamic.DynamicSelection.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.dynamic.DynamicSelection.doctree new file mode 100644 index 000000000..d703015d7 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.dynamic.DynamicSelection.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.models.get_model.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.get_model.doctree new file mode 100644 index 000000000..83463d643 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.get_model.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.models.list_models.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.list_models.doctree new file mode 100644 index 000000000..58c5ea78d Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.list_models.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.models.register_model.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.register_model.doctree new file mode 100644 index 000000000..57802c978 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.register_model.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.models.reset_model_register.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.reset_model_register.doctree new file mode 100644 index 000000000..0fdf3f682 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.models.reset_model_register.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.pipeline.JuTargetPipeline.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.pipeline.JuTargetPipeline.doctree new file mode 100644 index 000000000..458069330 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.pipeline.JuTargetPipeline.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.pipeline.pipeline_creator.Step.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.pipeline.pipeline_creator.Step.doctree new file mode 100644 index 000000000..e7131b15f Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.pipeline.pipeline_creator.Step.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.prepare.check_consistency.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.prepare.check_consistency.doctree new file mode 100644 index 000000000..8b76683a0 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.prepare.check_consistency.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.prepare.prepare_input_data.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.prepare.prepare_input_data.doctree new file mode 100644 index 000000000..5e864a516 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.prepare.prepare_input_data.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.run_cross_validation.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.run_cross_validation.doctree new file mode 100644 index 000000000..130f0b50c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.run_cross_validation.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.run_fit.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.run_fit.doctree new file mode 100644 index 000000000..6169ea5e5 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.run_fit.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.check_scoring.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.check_scoring.doctree new file mode 100644 index 000000000..8c266108f Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.check_scoring.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.get_scorer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.get_scorer.doctree new file mode 100644 index 000000000..70c20dcfd Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.get_scorer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.list_scorers.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.list_scorers.doctree new file mode 100644 index 000000000..fd9768dfb Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.list_scorers.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.metrics.r2_corr.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.metrics.r2_corr.doctree new file mode 100644 index 000000000..548de12a5 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.metrics.r2_corr.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.metrics.r_corr.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.metrics.r_corr.doctree new file mode 100644 index 000000000..04b9dfb5b Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.metrics.r_corr.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.register_scorer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.register_scorer.doctree new file mode 100644 index 000000000..8dc57de0d Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.register_scorer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.reset_scorer_register.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.reset_scorer_register.doctree new file mode 100644 index 000000000..5daf0230c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.scoring.reset_scorer_register.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.stats.corrected_ttest.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.stats.corrected_ttest.doctree new file mode 100644 index 000000000..742bcac91 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.stats.corrected_ttest.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.CBPM.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.CBPM.doctree new file mode 100644 index 000000000..d6d468352 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.CBPM.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.ChangeColumnTypes.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.ChangeColumnTypes.doctree new file mode 100644 index 000000000..04cffe21e Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.ChangeColumnTypes.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.DropColumns.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.DropColumns.doctree new file mode 100644 index 000000000..b99b4dffe Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.DropColumns.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.FilterColumns.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.FilterColumns.doctree new file mode 100644 index 000000000..4a0ee128c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.FilterColumns.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.JuColumnTransformer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.JuColumnTransformer.doctree new file mode 100644 index 000000000..076938654 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.JuColumnTransformer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.SetColumnTypes.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.SetColumnTypes.doctree new file mode 100644 index 000000000..a7855a350 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.SetColumnTypes.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.confound_remover.ConfoundRemover.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.confound_remover.ConfoundRemover.doctree new file mode 100644 index 000000000..3734e3257 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.confound_remover.ConfoundRemover.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.get_transformer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.get_transformer.doctree new file mode 100644 index 000000000..9ae071d8b Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.get_transformer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.list_transformers.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.list_transformers.doctree new file mode 100644 index 000000000..e43ebd104 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.list_transformers.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.register_transformer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.register_transformer.doctree new file mode 100644 index 000000000..52ae69d7a Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.register_transformer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.reset_transformer_register.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.reset_transformer_register.doctree new file mode 100644 index 000000000..8ed0d0c23 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.reset_transformer_register.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.JuTargetTransformer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.JuTargetTransformer.doctree new file mode 100644 index 000000000..613b6862b Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.JuTargetTransformer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.JuTransformedTargetModel.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.JuTransformedTargetModel.doctree new file mode 100644 index 000000000..5d6788e72 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.JuTransformedTargetModel.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.TargetConfoundRemover.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.TargetConfoundRemover.doctree new file mode 100644 index 000000000..f7ab3ea1a Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.TargetConfoundRemover.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.TransformedTargetWarning.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.TransformedTargetWarning.doctree new file mode 100644 index 000000000..80a900859 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.TransformedTargetWarning.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.get_target_transformer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.get_target_transformer.doctree new file mode 100644 index 000000000..0d86a973d Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.get_target_transformer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.list_target_transformers.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.list_target_transformers.doctree new file mode 100644 index 000000000..a95471ea5 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.list_target_transformers.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.register_target_transformer.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.register_target_transformer.doctree new file mode 100644 index 000000000..1ca1a6e1a Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.register_target_transformer.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.reset_target_transformer_register.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.reset_target_transformer_register.doctree new file mode 100644 index 000000000..0d89fb9f4 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.transformers.target.reset_target_transformer_register.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.configure_logging.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.configure_logging.doctree new file mode 100644 index 000000000..67322e797 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.configure_logging.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.logger.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.logger.doctree new file mode 100644 index 000000000..37d7cb561 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.logger.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.raise_error.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.raise_error.doctree new file mode 100644 index 000000000..f5bf3c4c9 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.raise_error.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLike.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLike.doctree new file mode 100644 index 000000000..43a21b0f8 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLike.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFit1.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFit1.doctree new file mode 100644 index 000000000..acd0534d7 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFit1.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFit2.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFit2.doctree new file mode 100644 index 000000000..4b946a35e Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFit2.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFity.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFity.doctree new file mode 100644 index 000000000..6474339e5 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.EstimatorLikeFity.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.JuEstimatorLike.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.JuEstimatorLike.doctree new file mode 100644 index 000000000..b7ff16cab Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.typing.JuEstimatorLike.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.warn_with_log.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.warn_with_log.doctree new file mode 100644 index 000000000..3f5ac5219 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.utils.warn_with_log.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/generated/julearn.viz.plot_scores.doctree b/pr-preview/pr-276/.doctrees/api/generated/julearn.viz.plot_scores.doctree new file mode 100644 index 000000000..3efe41f7f Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/generated/julearn.viz.plot_scores.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/index.doctree b/pr-preview/pr-276/.doctrees/api/index.doctree new file mode 100644 index 000000000..61f1f0c3d Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/inspect.doctree b/pr-preview/pr-276/.doctrees/api/inspect.doctree new file mode 100644 index 000000000..3dcfed4f8 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/inspect.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/main.doctree b/pr-preview/pr-276/.doctrees/api/main.doctree new file mode 100644 index 000000000..78a74d503 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/main.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/model_selection.doctree b/pr-preview/pr-276/.doctrees/api/model_selection.doctree new file mode 100644 index 000000000..2cfa3ee99 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/model_selection.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/models.doctree b/pr-preview/pr-276/.doctrees/api/models.doctree new file mode 100644 index 000000000..67cb1b083 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/models.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/pipeline.doctree b/pr-preview/pr-276/.doctrees/api/pipeline.doctree new file mode 100644 index 000000000..51f1ffaa3 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/pipeline.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/prepare.doctree b/pr-preview/pr-276/.doctrees/api/prepare.doctree new file mode 100644 index 000000000..d7132f9a5 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/prepare.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/scoring.doctree b/pr-preview/pr-276/.doctrees/api/scoring.doctree new file mode 100644 index 000000000..81238812c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/scoring.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/stats.doctree b/pr-preview/pr-276/.doctrees/api/stats.doctree new file mode 100644 index 000000000..cf3566cee Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/stats.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/transformers.doctree b/pr-preview/pr-276/.doctrees/api/transformers.doctree new file mode 100644 index 000000000..a4123a356 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/transformers.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/utils.doctree b/pr-preview/pr-276/.doctrees/api/utils.doctree new file mode 100644 index 000000000..c799cf7e6 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/utils.doctree differ diff --git a/pr-preview/pr-276/.doctrees/api/viz.doctree b/pr-preview/pr-276/.doctrees/api/viz.doctree new file mode 100644 index 000000000..22a8e4956 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/api/viz.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/index.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/index.doctree new file mode 100644 index 000000000..3d81cd8ec Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_cm_acc_multiclass.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_cm_acc_multiclass.doctree new file mode 100644 index 000000000..b4b07908f Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_cm_acc_multiclass.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_example_regression.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_example_regression.doctree new file mode 100644 index 000000000..509db3e72 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_example_regression.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_stratified_kfold_reg.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_stratified_kfold_reg.doctree new file mode 100644 index 000000000..76e7fad50 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/plot_stratified_kfold_reg.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_combine_pandas.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_combine_pandas.doctree new file mode 100644 index 000000000..54bb346f6 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_combine_pandas.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_grouped_cv.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_grouped_cv.doctree new file mode 100644 index 000000000..9ce8611d4 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_grouped_cv.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_simple_binary_classification.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_simple_binary_classification.doctree new file mode 100644 index 000000000..d1fabe49c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/run_simple_binary_classification.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/00_starting/sg_execution_times.doctree b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/sg_execution_times.doctree new file mode 100644 index 000000000..c82f99a7e Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/00_starting/sg_execution_times.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/index.doctree b/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/index.doctree new file mode 100644 index 000000000..e2a357b75 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/plot_simple_model_comparison.doctree b/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/plot_simple_model_comparison.doctree new file mode 100644 index 000000000..6c28620ee Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/plot_simple_model_comparison.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/sg_execution_times.doctree b/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/sg_execution_times.doctree new file mode 100644 index 000000000..363fc17d9 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/01_model_comparison/sg_execution_times.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/index.doctree b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/index.doctree new file mode 100644 index 000000000..52435a2b7 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_groupcv_inspect_svm.doctree b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_groupcv_inspect_svm.doctree new file mode 100644 index 000000000..54e61ac83 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_groupcv_inspect_svm.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_inspect_random_forest.doctree b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_inspect_random_forest.doctree new file mode 100644 index 000000000..0fd5fc9ab Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_inspect_random_forest.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_preprocess.doctree b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_preprocess.doctree new file mode 100644 index 000000000..385e0360d Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/plot_preprocess.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/run_binary_inspect_folds.doctree b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/run_binary_inspect_folds.doctree new file mode 100644 index 000000000..5ac2e9dd3 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/run_binary_inspect_folds.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/sg_execution_times.doctree b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/sg_execution_times.doctree new file mode 100644 index 000000000..031e195f7 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/02_inspection/sg_execution_times.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/index.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/index.doctree new file mode 100644 index 000000000..d463dd783 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_apply_to_target.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_apply_to_target.doctree new file mode 100644 index 000000000..afe354565 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_apply_to_target.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_example_pca_featsets.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_example_pca_featsets.doctree new file mode 100644 index 000000000..b0f624bbf Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_example_pca_featsets.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_multiple_grids.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_multiple_grids.doctree new file mode 100644 index 000000000..54d4097d6 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_multiple_grids.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_tuning.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_tuning.doctree new file mode 100644 index 000000000..36f53cc24 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_tuning.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch.doctree new file mode 100644 index 000000000..90ca0fe21 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_stacked_models.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_stacked_models.doctree new file mode 100644 index 000000000..7930dadd2 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/run_stacked_models.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/sg_execution_times.doctree b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/sg_execution_times.doctree new file mode 100644 index 000000000..932abeb8c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/03_complex_models/sg_execution_times.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/index.doctree b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/index.doctree new file mode 100644 index 000000000..801ea3ff4 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/plot_confound_removal_classification.doctree b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/plot_confound_removal_classification.doctree new file mode 100644 index 000000000..28e943843 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/plot_confound_removal_classification.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/run_return_confounds.doctree b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/run_return_confounds.doctree new file mode 100644 index 000000000..f003f9731 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/run_return_confounds.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/sg_execution_times.doctree b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/sg_execution_times.doctree new file mode 100644 index 000000000..34d209cd1 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/04_confounds/sg_execution_times.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/05_customization/index.doctree b/pr-preview/pr-276/.doctrees/auto_examples/05_customization/index.doctree new file mode 100644 index 000000000..b13c790bd Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/05_customization/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/05_customization/run_custom_scorers_regression.doctree b/pr-preview/pr-276/.doctrees/auto_examples/05_customization/run_custom_scorers_regression.doctree new file mode 100644 index 000000000..717bad44d Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/05_customization/run_custom_scorers_regression.doctree differ diff --git a/pr-preview/pr-276/.doctrees/auto_examples/05_customization/sg_execution_times.doctree b/pr-preview/pr-276/.doctrees/auto_examples/05_customization/sg_execution_times.doctree new file mode 100644 index 000000000..25f29f545 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/auto_examples/05_customization/sg_execution_times.doctree differ diff --git a/pr-preview/pr-276/.doctrees/available_pipeline_steps.doctree b/pr-preview/pr-276/.doctrees/available_pipeline_steps.doctree new file mode 100644 index 000000000..dfeea0182 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/available_pipeline_steps.doctree differ diff --git a/pr-preview/pr-276/.doctrees/bokeh_plot/bokeh-content-17f42cf4af004c0db5a67b9da92838dd-auto_examples-01_model_comparison-plot_simple_model_comparison.js b/pr-preview/pr-276/.doctrees/bokeh_plot/bokeh-content-17f42cf4af004c0db5a67b9da92838dd-auto_examples-01_model_comparison-plot_simple_model_comparison.js new file mode 100644 index 000000000..2ca7e231c --- /dev/null +++ b/pr-preview/pr-276/.doctrees/bokeh_plot/bokeh-content-17f42cf4af004c0db5a67b9da92838dd-auto_examples-01_model_comparison-plot_simple_model_comparison.js @@ -0,0 +1,154 @@ +(function() { + const fn = function() { + 'use strict'; + (function(root) { + function now() { + return new Date(); + } + + const force = false; + + if (typeof root._bokeh_onload_callbacks === "undefined" || force === true) { + root._bokeh_onload_callbacks = []; + root._bokeh_is_loading = undefined; + } + + + const element = document.getElementById("b1b02290-04dd-4f69-bbd9-3da83d0fb160"); + if (element == null) { + console.warn("Bokeh: autoload.js configured with elementid 'b1b02290-04dd-4f69-bbd9-3da83d0fb160' but no matching script tag was found.") + } + function run_callbacks() { + try { + root._bokeh_onload_callbacks.forEach(function(callback) { + if (callback != null) + callback(); + }); + } finally { + delete root._bokeh_onload_callbacks + } + console.debug("Bokeh: all callbacks have finished"); + } + + function load_libs(css_urls, js_urls, callback) { + if (css_urls == null) css_urls = []; + if (js_urls == null) js_urls = []; + + root._bokeh_onload_callbacks.push(callback); + if (root._bokeh_is_loading > 0) { + console.debug("Bokeh: BokehJS is being loaded, scheduling callback at", now()); + return null; + } + if (js_urls == null || js_urls.length === 0) { + run_callbacks(); + return null; + } + console.debug("Bokeh: BokehJS not loaded, scheduling load and callback at", now()); + root._bokeh_is_loading = css_urls.length + js_urls.length; + + function on_load() { + root._bokeh_is_loading--; + if (root._bokeh_is_loading === 0) { + console.debug("Bokeh: all BokehJS libraries/stylesheets loaded"); + run_callbacks() + } + } + + function on_error(url) { + console.error("failed to load " + url); + } + + for (let i = 0; i < css_urls.length; i++) { + const url = css_urls[i]; + const element = document.createElement("link"); + element.onload = on_load; + element.onerror = on_error.bind(null, url); + element.rel = "stylesheet"; + element.type = "text/css"; + element.href = url; + console.debug("Bokeh: injecting link tag for BokehJS stylesheet: ", url); + document.body.appendChild(element); + } + + for (let i = 0; i < js_urls.length; i++) { + const url = js_urls[i]; + const element = document.createElement('script'); + element.onload = on_load; + element.onerror = on_error.bind(null, url); + element.async = false; + element.src = url; + console.debug("Bokeh: injecting script tag for BokehJS library: ", url); + document.head.appendChild(element); + } + }; + + function inject_raw_css(css) { + const element = document.createElement("style"); + element.appendChild(document.createTextNode(css)); + document.body.appendChild(element); + } + + const js_urls = ["https://cdn.bokeh.org/bokeh/release/bokeh-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.6.0.min.js", "https://unpkg.com/@holoviz/panel@1.5.2/dist/panel.min.js"]; + const css_urls = []; + + const inline_js = [ function(Bokeh) { + Bokeh.set_log_level("info"); + }, + function(Bokeh) { + (function() { + const fn = function() { + Bokeh.safely(function() { + (function(root) { + function embed_document(root) { + const docs_json = '{"42c63395-ba27-42e4-b699-f28d8a30bbf2":{"version":"3.6.0","title":"Bokeh Application","roots":[{"type":"object","name":"panel.models.layout.Column","id":"p1236","attributes":{"name":"Column00258","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"type":"object","name":"ImportedStyleSheet","id":"p1240","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/loading.css"}},{"type":"object","name":"ImportedStyleSheet","id":"p1248","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/listpanel.css"}},{"type":"object","name":"ImportedStyleSheet","id":"p1238","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/theme/default.css"}},{"type":"object","name":"ImportedStyleSheet","id":"p1239","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/theme/native.css"}}],"margin":0,"align":"start","children":[{"type":"object","name":"Row","id":"p1237","attributes":{"name":"Row00223","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.markup.HTML","id":"p1241","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1238"},{"id":"p1239"}],"width":200,"height":70,"min_width":200,"min_height":70,"margin":[5,10],"align":"start","text":"&lt;img src=&quot;data:image/png;base64,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WY/zxEM/jzqIiIiIiIiIiIwc7bQVGRGx5cC4qFNII7Lrmbvg0KhTiIiIiIiIiMjIUdFWpNYS7W3Ae6KOIQ3rOPaO/1zUIURERERERERk5KhXokgtJTqbYOvDwOlRR5GG1gPBG0iveCLqICIiIiIiIiJSe9ppK1JTWy9DBVupXAvu10cdQkRERERERERGhnbaitTKuYumsje2BTg86igySpgn2LRyc9QxRERERERERKS2tNNWpFZ6Y3+PCrZSTW7LmT8/FnUMEREREREREaktFW1FaiHZ/kac9qhjyKhzJjunfDzqECIiIiIiIiJSWyraitSCswxoijqGjELOtbSlJkcdQ0RERERERERqR0VbkWpLdnwA7JyoY8goZRxFxr4QdQwRERERERERqR0dRCZSTfPnN7Nzyi+Ak6OOIqOY00sQO4NNtz4WdRQRERERERERqT7ttBWpph2HfQYVbKXWjGY8vDHqGCIiIiIiIiJSG9ppK1ItcxYfTSbYgjEp6igyVtg80l13RZ1CRERERERERKpLO21FqiUbXK+CrYws/wrTU/GoU4iIiIiIiIhIdaloK1INs1Nn4SyIOoaMOacxiaVRhxARERERERGR6lLRVqRyRshXMb2eJAJOJ4nUEVHHEBEREREREZHqUZFJpFKJjo8AZ0cdQ8asKbh9MeoQIiIiIiIiIlI9OohMpBIzrmihec+jGCdEHUXGtCyx8Cw2rHok6iAiIiIiIiIiUjnttBWpxCF7rlTBVupAjExsWdQhRERERERERKQ6YlEHEGlYsy45HsL/gxGPOooIxut4zVmP8ORDj0YdRUREREREREQqo522IuWKZb6MMT7qGCIH2I0kLj4k6hQiIiIiIiIiUhkVbUXKkVwyE+eDUccQyWOcCM2fijqGiIiIiIiIiFRGRVuRknUGeLgcHeQn9ekqzlk4LeoQIiIiIiIiIlI+FW1FSpXcthh4S9QxRIYwgb74tVGHEBEREREREZHyaaegSClmLpxIvGkLcEzUUUSG5ITEghlsXPFA1FFEREREREREpHTaaStSinjTNahgK/XOCAjVwkNERERERESkUcWiDiDSMBKLToJgNdAUdRSRIhzPa896nCce+nnUQURERERERESkNNppK1K02HJgXNQpRIpn1zN3waFRpxARERERERGR0qhoK1KMRHsb8J6oY4iU6Dj2jv9c1CFEREREREREpDTqdygynERnE2x9GDg96igiZeiB4A2kVzwRdRARERERERERKY522ooM6+lLUcFWGlcL7tdHHUJEREREREREiqedtiIHc+6iqeyNbQEOjzqKSEXME2xauTnqGCIiIiIiIiIyvP8G9ltcV3aamk4AAAAASUVORK5CYII=&quot; style=&quot;max-width: 100%; max-height: 100%; object-fit: contain; width: 200px; height: 70px;&quot;&gt;&lt;/img&gt;"}},{"type":"object","name":"Spacer","id":"p1243","attributes":{"name":"Spacer00222","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1238"},{"id":"p1239"}],"width":50,"min_width":50,"margin":0,"align":"start"}},{"type":"object","name":"panel.models.markup.HTML","id":"p1246","attributes":{"css_classes":["markdown"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"type":"object","name":"ImportedStyleSheet","id":"p1245","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/markdown.css"}},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","text":"&lt;h2 id=&quot;scores-viewer&quot;&gt;Scores Viewer &lt;a class=&quot;header-anchor&quot; href=&quot;#scores-viewer&quot;&gt;\\u00b6&lt;/a&gt;&lt;/h2&gt;\\n"}}]}},{"type":"object","name":"Row","id":"p1249","attributes":{"name":"Row00230","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.layout.Column","id":"p1250","attributes":{"name":"Metric","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1252","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","text":"<b>Metric</b>"}},{"type":"object","name":"panel.models.widgets.CustomSelect","id":"p1255","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"type":"object","name":"ImportedStyleSheet","id":"p1254","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/select.css"}},{"id":"p1238"},{"id":"p1239"}],"width":300,"min_width":300,"margin":[5,10],"align":"start","options":[["balanced_accuracy","balanced_accuracy"],["roc_auc","roc_auc"]],"value":"balanced_accuracy"}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1258","attributes":{"name":"Statistics","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1260","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","text":"<b>Statistics</b>"}},{"type":"object","name":"Toggle","id":"p1263","attributes":{"button_type":"primary","css_classes":["solid"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"type":"object","name":"ImportedStyleSheet","id":"p1262","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/button.css"}},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","label":"Show"}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1266","attributes":{"name":"Aggregate Repeats","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1268","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","text":"<b>Aggregate Repeats</b>"}},{"type":"object","name":"panel.models.widgets.RadioButtonGroup","id":"p1271","attributes":{"button_type":"primary","css_classes":["solid"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1262"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","labels":["no","median","mean"],"active":0}}]}}]}},{"type":"object","name":"Row","id":"p1276","attributes":{"name":"Row00250","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.layout.Column","id":"p1277","attributes":{"name":"Column00254","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":0,"align":"start","children":[{"type":"object","name":"Figure","id":"p1162","attributes":{"x_range":{"type":"object","name":"FactorRange","id":"p1158","attributes":{"factors":["svm","rf","svm_linear"]}},"y_range":{"type":"object","name":"DataRange1d","id":"p1164"},"x_scale":{"type":"object","name":"CategoricalScale","id":"p1172"},"y_scale":{"type":"object","name":"LinearScale","id":"p1173"},"title":{"type":"object","name":"Title","id":"p1165","attributes":{"text":"balanced_accuracy"}},"renderers":[{"type":"object","name":"GlyphRenderer","id":"p1204","attributes":{"data_source":{"type":"object","name":"ColumnDataSource","id":"p1159","attributes":{"selected":{"type":"object","name":"Selection","id":"p1160","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1161"},"data":{"type":"map","entries":[["x",["svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear"]],["score",{"type":"ndarray","array":{"type":"bytes","data":"V1VVVVVV7T9mZmZmZmbuP6qqqqqqquo/V1VVVVVV7T8AAAAAAADoP6qqqqqqquo/qqqqqqqq6j9XVVVVVVXtP2ZmZmZmZu4/ZmZmZmZm7j8AAAAAAADoP6qqqqqqquo/ERERERER6T9XVVVVVVXtP83MzMzMzOw/AAAAAAAA8D9XVVVVVVXtPxEREREREek/AAAAAAAA6D9XVVVVVVXtPwAAAAAAAPA/AAAAAAAA6D8RERERERHpP7y7u7u7u+s/V1VVVVVV7T8AAAAAAADwPwAAAAAAAOg/V1VVVVVV7T9XVVVVVVXtP2ZmZmZmZuY/qqqqqqqq6j+qqqqqqqrqP1dVVVVVVe0/ZmZmZmZm7j/NzMzMzMzsPwAAAAAAAOg/vLu7u7u76z+8u7u7u7vrP1dVVVVVVe0/zczMzMzM7D9mZmZmZmbuP1dVVVVVVe0/ERERERER6T9XVVVVVVXtPyIiIiIiIuo/AAAAAAAA8D+qqqqqqqrqP7y7u7u7u+s/vLu7u7u76z9XVVVVVVXtPwAAAAAAAPA/MzMzMzMz6z9XVVVVVVXtP1dVVVVVVe0/V1VVVVVV7T+qqqqqqqrqP6qqqqqqquo/V1VVVVVV7T8AAAAAAADwP83MzMzMzOw/qqqqqqqq6j9XVVVVVVXtP7y7u7u7u+s/V1VVVVVV7T9mZmZmZmbuPwAAAAAAAPA/V1VVVVVV7T+8u7u7u7vrP1dVVVVVVe0/vLu7u7u76z8AAAAAAADwP6qqqqqqquo/V1VVVVVV7T+8u7u7u7vrP1dVVVVVVe0/"},"shape":[75],"dtype":"float64","order":"little"}],["fold",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAA"},"shape":[75],"dtype":"int32","order":"little"}],["repeat",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAACAAAAAgAAAAIAAAACAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAADAAAABAAAAAQAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAQAAAAEAAAABAAAAAQAAAAEAAAA"},"shape":[75],"dtype":"int32","order":"little"}],["set",{"type":"ndarray","array":["test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test"],"shape":[75],"dtype":"object","order":"little"}]]}}},"view":{"type":"object","name":"CDSView","id":"p1205","attributes":{"filter":{"type":"object","name":"AllIndices","id":"p1206"}}},"glyph":{"type":"object","name":"Scatter","id":"p1201","attributes":{"x":{"type":"field","field":"x","transform":{"type":"object","name":"Jitter","id":"p1197","attributes":{"width":0.7,"range":{"id":"p1158"}}}},"y":{"type":"field","field":"score"},"size":{"type":"value","value":10},"line_color":{"type":"value","value":"white"},"line_alpha":{"type":"value","value":0.5},"fill_color":{"type":"value","value":"#0072B2"},"fill_alpha":{"type":"value","value":0.5},"hatch_color":{"type":"value","value":"#0072B2"},"hatch_alpha":{"type":"value","value":0.5}}},"nonselection_glyph":{"type":"object","name":"Scatter","id":"p1202","attributes":{"x":{"type":"field","field":"x","transform":{"id":"p1197"}},"y":{"type":"field","field":"score"},"size":{"type":"value","value":10},"line_color":{"type":"value","value":"white"},"line_alpha":{"type":"value","value":0.1},"fill_color":{"type":"value","value":"#0072B2"},"fill_alpha":{"type":"value","value":0.1},"hatch_color":{"type":"value","value":"#0072B2"},"hatch_alpha":{"type":"value","value":0.1}}},"muted_glyph":{"type":"object","name":"Scatter","id":"p1203","attributes":{"x":{"type":"field","field":"x","transform":{"id":"p1197"}},"y":{"type":"field","field":"score"},"size":{"type":"value","value":10},"line_color":{"type":"value","value":"white"},"line_alpha":{"type":"value","value":0.2},"fill_color":{"type":"value","value":"#0072B2"},"fill_alpha":{"type":"value","value":0.2},"hatch_color":{"type":"value","value":"#0072B2"},"hatch_alpha":{"type":"value","value":0.2}}}}}],"toolbar":{"type":"object","name":"Toolbar","id":"p1171","attributes":{"tools":[{"type":"object","name":"HoverTool","id":"p1184","attributes":{"renderers":"auto","tooltips":[["Fold","@fold"],["Repeat","@repeat"],["score","@score"]]}},{"type":"object","name":"SaveTool","id":"p1185"},{"type":"object","name":"PanTool","id":"p1186"},{"type":"object","name":"BoxZoomTool","id":"p1187","attributes":{"overlay":{"type":"object","name":"BoxAnnotation","id":"p1188","attributes":{"syncable":false,"line_color":"black","line_alpha":1.0,"line_width":2,"line_dash":[4,4],"fill_color":"lightgrey","fill_alpha":0.5,"level":"overlay","visible":false,"left":{"type":"number","value":"nan"},"right":{"type":"number","value":"nan"},"top":{"type":"number","value":"nan"},"bottom":{"type":"number","value":"nan"},"left_units":"canvas","right_units":"canvas","top_units":"canvas","bottom_units":"canvas","handles":{"type":"object","name":"BoxInteractionHandles","id":"p1194","attributes":{"all":{"type":"object","name":"AreaVisuals","id":"p1193","attributes":{"fill_color":"white","hover_fill_color":"lightgray"}}}}}}}},{"type":"object","name":"ResetTool","id":"p1195"},{"type":"object","name":"WheelZoomTool","id":"p1196","attributes":{"renderers":"auto"}}]}},"left":[{"type":"object","name":"LinearAxis","id":"p1179","attributes":{"ticker":{"type":"object","name":"BasicTicker","id":"p1180","attributes":{"mantissas":[1,2,5]}},"formatter":{"type":"object","name":"BasicTickFormatter","id":"p1181"},"major_label_policy":{"type":"object","name":"AllLabels","id":"p1182"}}}],"below":[{"type":"object","name":"CategoricalAxis","id":"p1174","attributes":{"ticker":{"type":"object","name":"CategoricalTicker","id":"p1175"},"formatter":{"type":"object","name":"CategoricalTickFormatter","id":"p1176"},"major_label_orientation":"vertical","major_label_policy":{"type":"object","name":"AllLabels","id":"p1177"},"major_label_text_color":"grey"}}],"center":[{"type":"object","name":"Grid","id":"p1178","attributes":{"axis":{"id":"p1174"},"grid_line_color":null}},{"type":"object","name":"Grid","id":"p1183","attributes":{"dimension":1,"axis":{"id":"p1179"}}},{"type":"object","name":"Legend","id":"p1207","attributes":{"visible":false,"items":[{"type":"object","name":"LegendItem","id":"p1208","attributes":{"label":{"type":"value","value":"test"},"renderers":[{"id":"p1204"}],"index":0}}]}},{"type":"object","name":"Whisker","id":"p1212","attributes":{"level":"annotation","source":{"type":"object","name":"ColumnDataSource","id":"p1209","attributes":{"selected":{"type":"object","name":"Selection","id":"p1210","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1211"},"data":{"type":"map","entries":[["base",{"type":"ndarray","array":["rf","svm","svm_linear"],"shape":[3],"dtype":"object","order":"little"}],["upper",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/"},"shape":[3],"dtype":"float64","order":"little"}],["lower",{"type":"ndarray","array":{"type":"bytes","data":"XI/C9Shc5z8AAAAAAADoP6qqqqqqquo/"},"shape":[3],"dtype":"float64","order":"little"}]]}}},"lower":{"type":"field","field":"lower"},"lower_head":{"type":"object","name":"TeeHead","id":"p1216","attributes":{"size":{"type":"value","value":20}}},"upper":{"type":"field","field":"upper"},"upper_head":{"type":"object","name":"TeeHead","id":"p1217","attributes":{"size":{"type":"value","value":20}}},"base":{"type":"field","field":"base"},"line_width":{"type":"value","value":2}}},{"type":"object","name":"Whisker","id":"p1221","attributes":{"level":"annotation","source":{"type":"object","name":"ColumnDataSource","id":"p1218","attributes":{"selected":{"type":"object","name":"Selection","id":"p1219","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1220"},"data":{"type":"map","entries":[["base",{"type":"ndarray","array":["rf","svm","svm_linear"],"shape":[3],"dtype":"object","order":"little"}],["upper",{"type":"ndarray","array":{"type":"bytes","data":"nH34DLsC7D9mrY4JHszrPwSdNtBpA+0/"},"shape":[3],"dtype":"float64","order":"little"}],["lower",{"type":"ndarray","array":{"type":"bytes","data":"nH34DLsC7D9mrY4JHszrPwSdNtBpA+0/"},"shape":[3],"dtype":"float64","order":"little"}]]}}},"lower":{"type":"field","field":"lower"},"lower_head":{"type":"object","name":"TeeHead","id":"p1225","attributes":{"size":{"type":"value","value":10}}},"upper":{"type":"field","field":"upper"},"upper_head":{"type":"object","name":"TeeHead","id":"p1226","attributes":{"size":{"type":"value","value":10}}},"base":{"type":"field","field":"base"},"line_width":{"type":"value","value":2}}},{"type":"object","name":"Whisker","id":"p1230","attributes":{"level":"annotation","source":{"type":"object","name":"ColumnDataSource","id":"p1227","attributes":{"selected":{"type":"object","name":"Selection","id":"p1228","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1229"},"data":{"type":"map","entries":[["base",[["svm",0.5],["rf",0.5]]],["lower",[0,0]],["upper",[1.05,1.05]]]}}},"lower":{"type":"field","field":"lower"},"lower_head":null,"upper":{"type":"field","field":"upper"},"upper_head":null,"base":{"type":"field","field":"base"},"line_color":{"type":"value","value":"lightgrey"},"line_width":{"type":"value","value":2}}}]}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1280","attributes":{"name":"Column00243","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.layout.Column","id":"p1281","attributes":{"name":"Models","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1283","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","text":"<b>Models</b>"}},{"type":"object","name":"panel.models.widgets.CheckboxButtonGroup","id":"p1286","attributes":{"button_type":"primary","css_classes":["solid"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1262"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start","labels":["svm","rf","svm_linear"],"orientation":"vertical","active":[0,1,2]}}]}}]}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1293","attributes":{"name":"Column00262","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1248"},{"id":"p1238"},{"id":"p1239"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.markup.HTML","id":"p1296","attributes":{"css_classes":["markdown"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1240"},{"id":"p1245"},{"id":"p1238"},{"id":"p1239"}],"margin":[5,10],"align":"start"}}]}}]}}],"defs":[{"type":"model","name":"ReactiveHTML1"},{"type":"model","name":"FlexBox1","properties":[{"name":"align_content","kind":"Any","default":"flex-start"},{"name":"align_items","kind":"Any","default":"flex-start"},{"name":"flex_direction","kind":"Any","default":"row"},{"name":"flex_wrap","kind":"Any","default":"wrap"},{"name":"gap","kind":"Any","default":""},{"name":"justify_content","kind":"Any","default":"flex-start"}]},{"type":"model","name":"FloatPanel1","properties":[{"name":"config","kind":"Any","default":{"type":"map"}},{"name":"contained","kind":"Any","default":true},{"name":"position","kind":"Any","default":"right-top"},{"name":"offsetx","kind":"Any","default":null},{"name":"offsety","kind":"Any","default":null},{"name":"theme","kind":"Any","default":"primary"},{"name":"status","kind":"Any","default":"normalized"}]},{"type":"model","name":"GridStack1","properties":[{"name":"mode","kind":"Any","default":"warn"},{"name":"ncols","kind":"Any","default":null},{"name":"nrows","kind":"Any","default":null},{"name":"allow_resize","kind":"Any","default":true},{"name":"allow_drag","kind":"Any","default":true},{"name":"state","kind":"Any","default":[]}]},{"type":"model","name":"drag1","properties":[{"name":"slider_width","kind":"Any","default":5},{"name":"slider_color","kind":"Any","default":"black"},{"name":"value","kind":"Any","default":50}]},{"type":"model","name":"click1","properties":[{"name":"terminal_output","kind":"Any","default":""},{"name":"debug_name","kind":"Any","default":""},{"name":"clears","kind":"Any","default":0}]},{"type":"model","name":"FastWrapper1","properties":[{"name":"object","kind":"Any","default":null},{"name":"style","kind":"Any","default":null}]},{"type":"model","name":"NotificationAreaBase1","properties":[{"name":"js_events","kind":"Any","default":{"type":"map"}},{"name":"position","kind":"Any","default":"bottom-right"},{"name":"_clear","kind":"Any","default":0}]},{"type":"model","name":"NotificationArea1","properties":[{"name":"js_events","kind":"Any","default":{"type":"map"}},{"name":"notifications","kind":"Any","default":[]},{"name":"position","kind":"Any","default":"bottom-right"},{"name":"_clear","kind":"Any","default":0},{"name":"types","kind":"Any","default":[{"type":"map","entries":[["type","warning"],["background","#ffc107"],["icon",{"type":"map","entries":[["className","fas fa-exclamation-triangle"],["tagName","i"],["color","white"]]}]]},{"type":"map","entries":[["type","info"],["background","#007bff"],["icon",{"type":"map","entries":[["className","fas fa-info-circle"],["tagName","i"],["color","white"]]}]]}]}]},{"type":"model","name":"Notification","properties":[{"name":"background","kind":"Any","default":null},{"name":"duration","kind":"Any","default":3000},{"name":"icon","kind":"Any","default":null},{"name":"message","kind":"Any","default":""},{"name":"notification_type","kind":"Any","default":null},{"name":"_destroyed","kind":"Any","default":false}]},{"type":"model","name":"TemplateActions1","properties":[{"name":"open_modal","kind":"Any","default":0},{"name":"close_modal","kind":"Any","default":0}]},{"type":"model","name":"BootstrapTemplateActions1","properties":[{"name":"open_modal","kind":"Any","default":0},{"name":"close_modal","kind":"Any","default":0}]},{"type":"model","name":"TemplateEditor1","properties":[{"name":"layout","kind":"Any","default":[]}]},{"type":"model","name":"MaterialTemplateActions1","properties":[{"name":"open_modal","kind":"Any","default":0},{"name":"close_modal","kind":"Any","default":0}]},{"type":"model","name":"ReactiveESM1"},{"type":"model","name":"JSComponent1"},{"type":"model","name":"ReactComponent1"},{"type":"model","name":"AnyWidgetComponent1"},{"type":"model","name":"request_value1","properties":[{"name":"fill","kind":"Any","default":"none"},{"name":"_synced","kind":"Any","default":null},{"name":"_request_sync","kind":"Any","default":0}]}]}}'; + const render_items = [{"docid":"42c63395-ba27-42e4-b699-f28d8a30bbf2","roots":{"p1236":"b1b02290-04dd-4f69-bbd9-3da83d0fb160"},"root_ids":["p1236"]}]; + root.Bokeh.embed.embed_items(docs_json, render_items); + } + if (root.Bokeh !== undefined) { + embed_document(root); + } else { + let attempts = 0; + const timer = setInterval(function(root) { + if (root.Bokeh !== undefined) { + clearInterval(timer); + embed_document(root); + } else { + attempts++; + if (attempts > 100) { + clearInterval(timer); + console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing"); + } + } + }, 10, root) + } + })(window); + }); + }; + if (document.readyState != "loading") fn(); + else document.addEventListener("DOMContentLoaded", fn, {once: true}); + })(); + }, + function(Bokeh) { + } + ]; + + function run_inline_js() { + for (let i = 0; i < inline_js.length; i++) { + inline_js[i].call(root, root.Bokeh); + } + } + + if (root._bokeh_is_loading === 0) { + console.debug("Bokeh: BokehJS loaded, going straight to plotting"); + run_inline_js(); + } else { + load_libs(css_urls, js_urls, function() { + console.debug("Bokeh: BokehJS plotting callback run at", now()); + run_inline_js(); + }); + } + }(window)); + }; + if (document.readyState != "loading") fn(); +else document.addEventListener("DOMContentLoaded", fn, {once: true}); +})(); \ No newline at end of file diff --git a/pr-preview/pr-276/.doctrees/bokeh_plot/bokeh-content-c3c7dc374b1c444286b2adc90284fbfe-what_really_need_know-model_comparison.js b/pr-preview/pr-276/.doctrees/bokeh_plot/bokeh-content-c3c7dc374b1c444286b2adc90284fbfe-what_really_need_know-model_comparison.js new file mode 100644 index 000000000..0103402eb --- /dev/null +++ b/pr-preview/pr-276/.doctrees/bokeh_plot/bokeh-content-c3c7dc374b1c444286b2adc90284fbfe-what_really_need_know-model_comparison.js @@ -0,0 +1,154 @@ +(function() { + const fn = function() { + 'use strict'; + (function(root) { + function now() { + return new Date(); + } + + const force = false; + + if (typeof root._bokeh_onload_callbacks === "undefined" || force === true) { + root._bokeh_onload_callbacks = []; + root._bokeh_is_loading = undefined; + } + + + const element = document.getElementById("e2e9be02-034b-45ee-93ec-94fb4d422bcc"); + if (element == null) { + console.warn("Bokeh: autoload.js configured with elementid 'e2e9be02-034b-45ee-93ec-94fb4d422bcc' but no matching script tag was found.") + } + function run_callbacks() { + try { + root._bokeh_onload_callbacks.forEach(function(callback) { + if (callback != null) + callback(); + }); + } finally { + delete root._bokeh_onload_callbacks + } + console.debug("Bokeh: all callbacks have finished"); + } + + function load_libs(css_urls, js_urls, callback) { + if (css_urls == null) css_urls = []; + if (js_urls == null) js_urls = []; + + root._bokeh_onload_callbacks.push(callback); + if (root._bokeh_is_loading > 0) { + console.debug("Bokeh: BokehJS is being loaded, scheduling callback at", now()); + return null; + } + if (js_urls == null || js_urls.length === 0) { + run_callbacks(); + return null; + } + console.debug("Bokeh: BokehJS not loaded, scheduling load and callback at", now()); + root._bokeh_is_loading = css_urls.length + js_urls.length; + + function on_load() { + root._bokeh_is_loading--; + if (root._bokeh_is_loading === 0) { + console.debug("Bokeh: all BokehJS libraries/stylesheets loaded"); + run_callbacks() + } + } + + function on_error(url) { + console.error("failed to load " + url); + } + + for (let i = 0; i < css_urls.length; i++) { + const url = css_urls[i]; + const element = document.createElement("link"); + element.onload = on_load; + element.onerror = on_error.bind(null, url); + element.rel = "stylesheet"; + element.type = "text/css"; + element.href = url; + console.debug("Bokeh: injecting link tag for BokehJS stylesheet: ", url); + document.body.appendChild(element); + } + + for (let i = 0; i < js_urls.length; i++) { + const url = js_urls[i]; + const element = document.createElement('script'); + element.onload = on_load; + element.onerror = on_error.bind(null, url); + element.async = false; + element.src = url; + console.debug("Bokeh: injecting script tag for BokehJS library: ", url); + document.head.appendChild(element); + } + }; + + function inject_raw_css(css) { + const element = document.createElement("style"); + element.appendChild(document.createTextNode(css)); + document.body.appendChild(element); + } + + const js_urls = ["https://cdn.bokeh.org/bokeh/release/bokeh-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.6.0.min.js", "https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.6.0.min.js", "https://unpkg.com/@holoviz/panel@1.5.2/dist/panel.min.js"]; + const css_urls = []; + + const inline_js = [ function(Bokeh) { + Bokeh.set_log_level("info"); + }, + function(Bokeh) { + (function() { + const fn = function() { + Bokeh.safely(function() { + (function(root) { + function embed_document(root) { + const docs_json = '{"21138b15-b3f7-4ce1-8b51-f8ccf069ecda":{"version":"3.6.0","title":"Bokeh Application","roots":[{"type":"object","name":"panel.models.layout.Column","id":"p1386","attributes":{"name":"Column00331","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"type":"object","name":"ImportedStyleSheet","id":"p1390","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/loading.css"}},{"type":"object","name":"ImportedStyleSheet","id":"p1398","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/listpanel.css"}},{"type":"object","name":"ImportedStyleSheet","id":"p1388","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/theme/default.css"}},{"type":"object","name":"ImportedStyleSheet","id":"p1389","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/bundled/theme/native.css"}}],"margin":0,"align":"start","children":[{"type":"object","name":"Row","id":"p1387","attributes":{"name":"Row00296","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.markup.HTML","id":"p1391","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1388"},{"id":"p1389"}],"width":200,"height":70,"min_width":200,"min_height":70,"margin":[5,10],"align":"start","text":"&lt;img src=&quot;data:image/png;base64,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WY/zxEM/jzqIiIiIiIiIiIwc7bQVGRGx5cC4qFNII7Lrmbvg0KhTiIiIiIiIiMjIUdFWpNYS7W3Ae6KOIQ3rOPaO/1zUIURERERERERk5KhXokgtJTqbYOvDwOlRR5GG1gPBG0iveCLqICIiIiIiIiJSe9ppK1JTWy9DBVupXAvu10cdQkRERERERERGhnbaitTKuYumsje2BTg86igySpgn2LRyc9QxRERERERERKS2tNNWpFZ6Y3+PCrZSTW7LmT8/FnUMEREREREREaktFW1FaiHZ/kac9qhjyKhzJjunfDzqECIiIiIiIiJSWyraitSCswxoijqGjELOtbSlJkcdQ0RERERERERqR0VbkWpLdnwA7JyoY8goZRxFxr4QdQwRERERERERqR0dRCZSTfPnN7Nzyi+Ak6OOIqOY00sQO4NNtz4WdRQRERERERERqT7ttBWpph2HfQYVbKXWjGY8vDHqGCIiIiIiIiJSG9ppK1ItcxYfTSbYgjEp6igyVtg80l13RZ1CRERERERERKpLO21FqiUbXK+CrYws/wrTU/GoU4iIiIiIiIhIdaloK1INs1Nn4SyIOoaMOacxiaVRhxARERERERGR6lLRVqRyRshXMb2eJAJOJ4nUEVHHEBEREREREZHqUZFJpFKJjo8AZ0cdQ8asKbh9MeoQIiIiIiIiIlI9OohMpBIzrmihec+jGCdEHUXGtCyx8Cw2rHok6iAiIiIiIiIiUjnttBWpxCF7rlTBVupAjExsWdQhRERERERERKQ6YlEHEGlYsy45HsL/gxGPOooIxut4zVmP8ORDj0YdRUREREREREQqo522IuWKZb6MMT7qGCIH2I0kLj4k6hQiIiIiIiIiUhkVbUXKkVwyE+eDUccQyWOcCM2fijqGiIiIiIiIiFRGRVuRknUGeLgcHeQn9ekqzlk4LeoQIiIiIiIiIlI+FW1FSpXcthh4S9QxRIYwgb74tVGHEBEREREREZHyaaegSClmLpxIvGkLcEzUUUSG5ITEghlsXPFA1FFEREREREREpHTaaStSinjTNahgK/XOCAjVwkNERERERESkUcWiDiDSMBKLToJgNdAUdRSRIhzPa896nCce+nnUQURERERERESkNNppK1K02HJgXNQpRIpn1zN3waFRpxARERERERGR0qhoK1KMRHsb8J6oY4iU6Dj2jv9c1CFEREREREREpDTqdygynERnE2x9GDg96igiZeiB4A2kVzwRdRARERERERERKY522ooM6+lLUcFWGlcL7tdHHUJEREREREREiqedtiIHc+6iqeyNbQEOjzqKSEXME2xauTnqGCIiIiIiIiIyvP8G9ltcV3aamk4AAAAASUVORK5CYII=&quot; style=&quot;max-width: 100%; max-height: 100%; object-fit: contain; width: 200px; height: 70px;&quot;&gt;&lt;/img&gt;"}},{"type":"object","name":"Spacer","id":"p1393","attributes":{"name":"Spacer00295","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1388"},{"id":"p1389"}],"width":50,"min_width":50,"margin":0,"align":"start"}},{"type":"object","name":"panel.models.markup.HTML","id":"p1396","attributes":{"css_classes":["markdown"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"type":"object","name":"ImportedStyleSheet","id":"p1395","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/markdown.css"}},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","text":"&lt;h2 id=&quot;scores-viewer&quot;&gt;Scores Viewer &lt;a class=&quot;header-anchor&quot; href=&quot;#scores-viewer&quot;&gt;\\u00b6&lt;/a&gt;&lt;/h2&gt;\\n"}}]}},{"type":"object","name":"Row","id":"p1399","attributes":{"name":"Row00303","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.layout.Column","id":"p1400","attributes":{"name":"Metric","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1402","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","text":"<b>Metric</b>"}},{"type":"object","name":"panel.models.widgets.CustomSelect","id":"p1405","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"type":"object","name":"ImportedStyleSheet","id":"p1404","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/select.css"}},{"id":"p1388"},{"id":"p1389"}],"width":300,"min_width":300,"margin":[5,10],"align":"start","options":[["balanced_accuracy","balanced_accuracy"],["roc_auc","roc_auc"]],"value":"balanced_accuracy"}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1408","attributes":{"name":"Statistics","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1410","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","text":"<b>Statistics</b>"}},{"type":"object","name":"Toggle","id":"p1413","attributes":{"button_type":"primary","css_classes":["solid"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"type":"object","name":"ImportedStyleSheet","id":"p1412","attributes":{"url":"https://cdn.holoviz.org/panel/1.5.2/dist/css/button.css"}},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","label":"Show"}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1416","attributes":{"name":"Aggregate Repeats","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1418","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","text":"<b>Aggregate Repeats</b>"}},{"type":"object","name":"panel.models.widgets.RadioButtonGroup","id":"p1421","attributes":{"button_type":"primary","css_classes":["solid"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1412"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","labels":["no","median","mean"],"active":0}}]}}]}},{"type":"object","name":"Row","id":"p1426","attributes":{"name":"Row00323","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.layout.Column","id":"p1427","attributes":{"name":"Column00327","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":0,"align":"start","children":[{"type":"object","name":"Figure","id":"p1312","attributes":{"x_range":{"type":"object","name":"FactorRange","id":"p1308","attributes":{"factors":["svm","rf","svm_linear"]}},"y_range":{"type":"object","name":"DataRange1d","id":"p1314"},"x_scale":{"type":"object","name":"CategoricalScale","id":"p1322"},"y_scale":{"type":"object","name":"LinearScale","id":"p1323"},"title":{"type":"object","name":"Title","id":"p1315","attributes":{"text":"balanced_accuracy"}},"renderers":[{"type":"object","name":"GlyphRenderer","id":"p1354","attributes":{"data_source":{"type":"object","name":"ColumnDataSource","id":"p1309","attributes":{"selected":{"type":"object","name":"Selection","id":"p1310","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1311"},"data":{"type":"map","entries":[["x",["svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","svm","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","rf","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear","svm_linear"]],["score",{"type":"ndarray","array":{"type":"bytes","data":"V1VVVVVV7T9mZmZmZmbuP6qqqqqqquo/V1VVVVVV7T8AAAAAAADoP6qqqqqqquo/qqqqqqqq6j9XVVVVVVXtP2ZmZmZmZu4/ZmZmZmZm7j8AAAAAAADoP6qqqqqqquo/ERERERER6T9XVVVVVVXtP83MzMzMzOw/AAAAAAAA8D9XVVVVVVXtPxEREREREek/AAAAAAAA6D9XVVVVVVXtPwAAAAAAAPA/AAAAAAAA6D8RERERERHpP7y7u7u7u+s/V1VVVVVV7T8AAAAAAADwPwAAAAAAAOg/V1VVVVVV7T9XVVVVVVXtP2ZmZmZmZuY/qqqqqqqq6j+qqqqqqqrqP1dVVVVVVe0/ZmZmZmZm7j/NzMzMzMzsPwAAAAAAAOg/vLu7u7u76z+8u7u7u7vrP1dVVVVVVe0/zczMzMzM7D9mZmZmZmbuP1dVVVVVVe0/ERERERER6T9XVVVVVVXtPyIiIiIiIuo/AAAAAAAA8D+qqqqqqqrqP7y7u7u7u+s/vLu7u7u76z9XVVVVVVXtPwAAAAAAAPA/MzMzMzMz6z9XVVVVVVXtP1dVVVVVVe0/V1VVVVVV7T+qqqqqqqrqP6qqqqqqquo/V1VVVVVV7T8AAAAAAADwP83MzMzMzOw/qqqqqqqq6j9XVVVVVVXtP7y7u7u7u+s/V1VVVVVV7T9mZmZmZmbuPwAAAAAAAPA/V1VVVVVV7T+8u7u7u7vrP1dVVVVVVe0/vLu7u7u76z8AAAAAAADwP6qqqqqqquo/V1VVVVVV7T+8u7u7u7vrP1dVVVVVVe0/"},"shape":[75],"dtype":"float64","order":"little"}],["fold",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAAQAAAAIAAAADAAAABAAAAAAAAAABAAAAAgAAAAMAAAAEAAAA"},"shape":[75],"dtype":"int32","order":"little"}],["repeat",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAACAAAAAgAAAAIAAAACAAAAAwAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAADAAAABAAAAAQAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAQAAAAEAAAABAAAAAQAAAAEAAAA"},"shape":[75],"dtype":"int32","order":"little"}],["set",{"type":"ndarray","array":["test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test","test"],"shape":[75],"dtype":"object","order":"little"}]]}}},"view":{"type":"object","name":"CDSView","id":"p1355","attributes":{"filter":{"type":"object","name":"AllIndices","id":"p1356"}}},"glyph":{"type":"object","name":"Scatter","id":"p1351","attributes":{"x":{"type":"field","field":"x","transform":{"type":"object","name":"Jitter","id":"p1347","attributes":{"width":0.7,"range":{"id":"p1308"}}}},"y":{"type":"field","field":"score"},"size":{"type":"value","value":10},"line_color":{"type":"value","value":"white"},"line_alpha":{"type":"value","value":0.5},"fill_color":{"type":"value","value":"#0072B2"},"fill_alpha":{"type":"value","value":0.5},"hatch_color":{"type":"value","value":"#0072B2"},"hatch_alpha":{"type":"value","value":0.5}}},"nonselection_glyph":{"type":"object","name":"Scatter","id":"p1352","attributes":{"x":{"type":"field","field":"x","transform":{"id":"p1347"}},"y":{"type":"field","field":"score"},"size":{"type":"value","value":10},"line_color":{"type":"value","value":"white"},"line_alpha":{"type":"value","value":0.1},"fill_color":{"type":"value","value":"#0072B2"},"fill_alpha":{"type":"value","value":0.1},"hatch_color":{"type":"value","value":"#0072B2"},"hatch_alpha":{"type":"value","value":0.1}}},"muted_glyph":{"type":"object","name":"Scatter","id":"p1353","attributes":{"x":{"type":"field","field":"x","transform":{"id":"p1347"}},"y":{"type":"field","field":"score"},"size":{"type":"value","value":10},"line_color":{"type":"value","value":"white"},"line_alpha":{"type":"value","value":0.2},"fill_color":{"type":"value","value":"#0072B2"},"fill_alpha":{"type":"value","value":0.2},"hatch_color":{"type":"value","value":"#0072B2"},"hatch_alpha":{"type":"value","value":0.2}}}}}],"toolbar":{"type":"object","name":"Toolbar","id":"p1321","attributes":{"tools":[{"type":"object","name":"HoverTool","id":"p1334","attributes":{"renderers":"auto","tooltips":[["Fold","@fold"],["Repeat","@repeat"],["score","@score"]]}},{"type":"object","name":"SaveTool","id":"p1335"},{"type":"object","name":"PanTool","id":"p1336"},{"type":"object","name":"BoxZoomTool","id":"p1337","attributes":{"overlay":{"type":"object","name":"BoxAnnotation","id":"p1338","attributes":{"syncable":false,"line_color":"black","line_alpha":1.0,"line_width":2,"line_dash":[4,4],"fill_color":"lightgrey","fill_alpha":0.5,"level":"overlay","visible":false,"left":{"type":"number","value":"nan"},"right":{"type":"number","value":"nan"},"top":{"type":"number","value":"nan"},"bottom":{"type":"number","value":"nan"},"left_units":"canvas","right_units":"canvas","top_units":"canvas","bottom_units":"canvas","handles":{"type":"object","name":"BoxInteractionHandles","id":"p1344","attributes":{"all":{"type":"object","name":"AreaVisuals","id":"p1343","attributes":{"fill_color":"white","hover_fill_color":"lightgray"}}}}}}}},{"type":"object","name":"ResetTool","id":"p1345"},{"type":"object","name":"WheelZoomTool","id":"p1346","attributes":{"renderers":"auto"}}]}},"left":[{"type":"object","name":"LinearAxis","id":"p1329","attributes":{"ticker":{"type":"object","name":"BasicTicker","id":"p1330","attributes":{"mantissas":[1,2,5]}},"formatter":{"type":"object","name":"BasicTickFormatter","id":"p1331"},"major_label_policy":{"type":"object","name":"AllLabels","id":"p1332"}}}],"below":[{"type":"object","name":"CategoricalAxis","id":"p1324","attributes":{"ticker":{"type":"object","name":"CategoricalTicker","id":"p1325"},"formatter":{"type":"object","name":"CategoricalTickFormatter","id":"p1326"},"major_label_orientation":"vertical","major_label_policy":{"type":"object","name":"AllLabels","id":"p1327"},"major_label_text_color":"grey"}}],"center":[{"type":"object","name":"Grid","id":"p1328","attributes":{"axis":{"id":"p1324"},"grid_line_color":null}},{"type":"object","name":"Grid","id":"p1333","attributes":{"dimension":1,"axis":{"id":"p1329"}}},{"type":"object","name":"Legend","id":"p1357","attributes":{"visible":false,"items":[{"type":"object","name":"LegendItem","id":"p1358","attributes":{"label":{"type":"value","value":"test"},"renderers":[{"id":"p1354"}],"index":0}}]}},{"type":"object","name":"Whisker","id":"p1362","attributes":{"level":"annotation","source":{"type":"object","name":"ColumnDataSource","id":"p1359","attributes":{"selected":{"type":"object","name":"Selection","id":"p1360","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1361"},"data":{"type":"map","entries":[["base",{"type":"ndarray","array":["rf","svm","svm_linear"],"shape":[3],"dtype":"object","order":"little"}],["upper",{"type":"ndarray","array":{"type":"bytes","data":"AAAAAAAA8D8AAAAAAADwPwAAAAAAAPA/"},"shape":[3],"dtype":"float64","order":"little"}],["lower",{"type":"ndarray","array":{"type":"bytes","data":"XI/C9Shc5z8AAAAAAADoP6qqqqqqquo/"},"shape":[3],"dtype":"float64","order":"little"}]]}}},"lower":{"type":"field","field":"lower"},"lower_head":{"type":"object","name":"TeeHead","id":"p1366","attributes":{"size":{"type":"value","value":20}}},"upper":{"type":"field","field":"upper"},"upper_head":{"type":"object","name":"TeeHead","id":"p1367","attributes":{"size":{"type":"value","value":20}}},"base":{"type":"field","field":"base"},"line_width":{"type":"value","value":2}}},{"type":"object","name":"Whisker","id":"p1371","attributes":{"level":"annotation","source":{"type":"object","name":"ColumnDataSource","id":"p1368","attributes":{"selected":{"type":"object","name":"Selection","id":"p1369","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1370"},"data":{"type":"map","entries":[["base",{"type":"ndarray","array":["rf","svm","svm_linear"],"shape":[3],"dtype":"object","order":"little"}],["upper",{"type":"ndarray","array":{"type":"bytes","data":"nH34DLsC7D9mrY4JHszrPwSdNtBpA+0/"},"shape":[3],"dtype":"float64","order":"little"}],["lower",{"type":"ndarray","array":{"type":"bytes","data":"nH34DLsC7D9mrY4JHszrPwSdNtBpA+0/"},"shape":[3],"dtype":"float64","order":"little"}]]}}},"lower":{"type":"field","field":"lower"},"lower_head":{"type":"object","name":"TeeHead","id":"p1375","attributes":{"size":{"type":"value","value":10}}},"upper":{"type":"field","field":"upper"},"upper_head":{"type":"object","name":"TeeHead","id":"p1376","attributes":{"size":{"type":"value","value":10}}},"base":{"type":"field","field":"base"},"line_width":{"type":"value","value":2}}},{"type":"object","name":"Whisker","id":"p1380","attributes":{"level":"annotation","source":{"type":"object","name":"ColumnDataSource","id":"p1377","attributes":{"selected":{"type":"object","name":"Selection","id":"p1378","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1379"},"data":{"type":"map","entries":[["base",[["svm",0.5],["rf",0.5]]],["lower",[0,0]],["upper",[1.05,1.05]]]}}},"lower":{"type":"field","field":"lower"},"lower_head":null,"upper":{"type":"field","field":"upper"},"upper_head":null,"base":{"type":"field","field":"base"},"line_color":{"type":"value","value":"lightgrey"},"line_width":{"type":"value","value":2}}}]}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1430","attributes":{"name":"Column00316","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.layout.Column","id":"p1431","attributes":{"name":"Models","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","children":[{"type":"object","name":"Div","id":"p1433","attributes":{"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","text":"<b>Models</b>"}},{"type":"object","name":"panel.models.widgets.CheckboxButtonGroup","id":"p1436","attributes":{"button_type":"primary","css_classes":["solid"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1412"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start","labels":["svm","rf","svm_linear"],"orientation":"vertical","active":[0,1,2]}}]}}]}}]}},{"type":"object","name":"panel.models.layout.Column","id":"p1443","attributes":{"name":"Column00335","stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1398"},{"id":"p1388"},{"id":"p1389"}],"margin":0,"align":"start","children":[{"type":"object","name":"panel.models.markup.HTML","id":"p1446","attributes":{"css_classes":["markdown"],"stylesheets":["\\n:host(.pn-loading):before, .pn-loading:before {\\n background-color: #c3c3c3;\\n mask-size: auto calc(min(50%, 400px));\\n -webkit-mask-size: auto calc(min(50%, 400px));\\n}",{"id":"p1390"},{"id":"p1395"},{"id":"p1388"},{"id":"p1389"}],"margin":[5,10],"align":"start"}}]}}]}}],"defs":[{"type":"model","name":"ReactiveHTML1"},{"type":"model","name":"FlexBox1","properties":[{"name":"align_content","kind":"Any","default":"flex-start"},{"name":"align_items","kind":"Any","default":"flex-start"},{"name":"flex_direction","kind":"Any","default":"row"},{"name":"flex_wrap","kind":"Any","default":"wrap"},{"name":"gap","kind":"Any","default":""},{"name":"justify_content","kind":"Any","default":"flex-start"}]},{"type":"model","name":"FloatPanel1","properties":[{"name":"config","kind":"Any","default":{"type":"map"}},{"name":"contained","kind":"Any","default":true},{"name":"position","kind":"Any","default":"right-top"},{"name":"offsetx","kind":"Any","default":null},{"name":"offsety","kind":"Any","default":null},{"name":"theme","kind":"Any","default":"primary"},{"name":"status","kind":"Any","default":"normalized"}]},{"type":"model","name":"GridStack1","properties":[{"name":"mode","kind":"Any","default":"warn"},{"name":"ncols","kind":"Any","default":null},{"name":"nrows","kind":"Any","default":null},{"name":"allow_resize","kind":"Any","default":true},{"name":"allow_drag","kind":"Any","default":true},{"name":"state","kind":"Any","default":[]}]},{"type":"model","name":"drag1","properties":[{"name":"slider_width","kind":"Any","default":5},{"name":"slider_color","kind":"Any","default":"black"},{"name":"value","kind":"Any","default":50}]},{"type":"model","name":"click1","properties":[{"name":"terminal_output","kind":"Any","default":""},{"name":"debug_name","kind":"Any","default":""},{"name":"clears","kind":"Any","default":0}]},{"type":"model","name":"FastWrapper1","properties":[{"name":"object","kind":"Any","default":null},{"name":"style","kind":"Any","default":null}]},{"type":"model","name":"NotificationAreaBase1","properties":[{"name":"js_events","kind":"Any","default":{"type":"map"}},{"name":"position","kind":"Any","default":"bottom-right"},{"name":"_clear","kind":"Any","default":0}]},{"type":"model","name":"NotificationArea1","properties":[{"name":"js_events","kind":"Any","default":{"type":"map"}},{"name":"notifications","kind":"Any","default":[]},{"name":"position","kind":"Any","default":"bottom-right"},{"name":"_clear","kind":"Any","default":0},{"name":"types","kind":"Any","default":[{"type":"map","entries":[["type","warning"],["background","#ffc107"],["icon",{"type":"map","entries":[["className","fas fa-exclamation-triangle"],["tagName","i"],["color","white"]]}]]},{"type":"map","entries":[["type","info"],["background","#007bff"],["icon",{"type":"map","entries":[["className","fas fa-info-circle"],["tagName","i"],["color","white"]]}]]}]}]},{"type":"model","name":"Notification","properties":[{"name":"background","kind":"Any","default":null},{"name":"duration","kind":"Any","default":3000},{"name":"icon","kind":"Any","default":null},{"name":"message","kind":"Any","default":""},{"name":"notification_type","kind":"Any","default":null},{"name":"_destroyed","kind":"Any","default":false}]},{"type":"model","name":"TemplateActions1","properties":[{"name":"open_modal","kind":"Any","default":0},{"name":"close_modal","kind":"Any","default":0}]},{"type":"model","name":"BootstrapTemplateActions1","properties":[{"name":"open_modal","kind":"Any","default":0},{"name":"close_modal","kind":"Any","default":0}]},{"type":"model","name":"TemplateEditor1","properties":[{"name":"layout","kind":"Any","default":[]}]},{"type":"model","name":"MaterialTemplateActions1","properties":[{"name":"open_modal","kind":"Any","default":0},{"name":"close_modal","kind":"Any","default":0}]},{"type":"model","name":"ReactiveESM1"},{"type":"model","name":"JSComponent1"},{"type":"model","name":"ReactComponent1"},{"type":"model","name":"AnyWidgetComponent1"},{"type":"model","name":"request_value1","properties":[{"name":"fill","kind":"Any","default":"none"},{"name":"_synced","kind":"Any","default":null},{"name":"_request_sync","kind":"Any","default":0}]}]}}'; + const render_items = [{"docid":"21138b15-b3f7-4ce1-8b51-f8ccf069ecda","roots":{"p1386":"e2e9be02-034b-45ee-93ec-94fb4d422bcc"},"root_ids":["p1386"]}]; + root.Bokeh.embed.embed_items(docs_json, render_items); + } + if (root.Bokeh !== undefined) { + embed_document(root); + } else { + let attempts = 0; + const timer = setInterval(function(root) { + if (root.Bokeh !== undefined) { + clearInterval(timer); + embed_document(root); + } else { + attempts++; + if (attempts > 100) { + clearInterval(timer); + console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing"); + } + } + }, 10, root) + } + })(window); + }); + }; + if (document.readyState != "loading") fn(); + else document.addEventListener("DOMContentLoaded", fn, {once: true}); + })(); + }, + function(Bokeh) { + } + ]; + + function run_inline_js() { + for (let i = 0; i < inline_js.length; i++) { + inline_js[i].call(root, root.Bokeh); + } + } + + if (root._bokeh_is_loading === 0) { + console.debug("Bokeh: BokehJS loaded, going straight to plotting"); + run_inline_js(); + } else { + load_libs(css_urls, js_urls, function() { + console.debug("Bokeh: BokehJS plotting callback run at", now()); + run_inline_js(); + }); + } + }(window)); + }; + if (document.readyState != "loading") fn(); +else document.addEventListener("DOMContentLoaded", fn, {once: true}); +})(); \ No newline at end of file diff --git a/pr-preview/pr-276/.doctrees/configuration.doctree b/pr-preview/pr-276/.doctrees/configuration.doctree new file mode 100644 index 000000000..fc1b69749 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/configuration.doctree differ diff --git a/pr-preview/pr-276/.doctrees/contributing.doctree b/pr-preview/pr-276/.doctrees/contributing.doctree new file mode 100644 index 000000000..149f49c76 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/contributing.doctree differ diff --git a/pr-preview/pr-276/.doctrees/environment.pickle b/pr-preview/pr-276/.doctrees/environment.pickle new file mode 100644 index 000000000..8f01fb22a Binary files /dev/null and b/pr-preview/pr-276/.doctrees/environment.pickle differ diff --git a/pr-preview/pr-276/.doctrees/examples.doctree b/pr-preview/pr-276/.doctrees/examples.doctree new file mode 100644 index 000000000..0107a533c Binary files /dev/null and b/pr-preview/pr-276/.doctrees/examples.doctree differ diff --git a/pr-preview/pr-276/.doctrees/faq.doctree b/pr-preview/pr-276/.doctrees/faq.doctree new file mode 100644 index 000000000..4ca15c110 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/faq.doctree differ diff --git a/pr-preview/pr-276/.doctrees/getting_started.doctree b/pr-preview/pr-276/.doctrees/getting_started.doctree new file mode 100644 index 000000000..579f84e85 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/getting_started.doctree differ diff --git a/pr-preview/pr-276/.doctrees/index.doctree b/pr-preview/pr-276/.doctrees/index.doctree new file mode 100644 index 000000000..a550ff6e5 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/maintaining.doctree b/pr-preview/pr-276/.doctrees/maintaining.doctree new file mode 100644 index 000000000..416ab6fa0 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/maintaining.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/CBPM.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/CBPM.doctree new file mode 100644 index 000000000..b166aa58e Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/CBPM.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/confound_removal.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/confound_removal.doctree new file mode 100644 index 000000000..6e43daf6e Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/confound_removal.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/cross_validation_splitter.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/cross_validation_splitter.doctree new file mode 100644 index 000000000..4a51be4bf Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/cross_validation_splitter.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/hyperparameter_tuning.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/hyperparameter_tuning.doctree new file mode 100644 index 000000000..a682f7558 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/hyperparameter_tuning.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/index.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/index.doctree new file mode 100644 index 000000000..d9db4ff94 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/model_inspect.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/model_inspect.doctree new file mode 100644 index 000000000..91036a8b2 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/model_inspect.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/stacked_models.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/stacked_models.doctree new file mode 100644 index 000000000..42c27ac1a Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/stacked_models.doctree differ diff --git a/pr-preview/pr-276/.doctrees/selected_deeper_topics/target_transformers.doctree b/pr-preview/pr-276/.doctrees/selected_deeper_topics/target_transformers.doctree new file mode 100644 index 000000000..dd726ae9a Binary files /dev/null and b/pr-preview/pr-276/.doctrees/selected_deeper_topics/target_transformers.doctree differ diff --git a/pr-preview/pr-276/.doctrees/what_really_need_know/cross_validation.doctree b/pr-preview/pr-276/.doctrees/what_really_need_know/cross_validation.doctree new file mode 100644 index 000000000..3ccd269ea Binary files /dev/null and b/pr-preview/pr-276/.doctrees/what_really_need_know/cross_validation.doctree differ diff --git a/pr-preview/pr-276/.doctrees/what_really_need_know/data.doctree b/pr-preview/pr-276/.doctrees/what_really_need_know/data.doctree new file mode 100644 index 000000000..f429d1fb2 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/what_really_need_know/data.doctree differ diff --git a/pr-preview/pr-276/.doctrees/what_really_need_know/index.doctree b/pr-preview/pr-276/.doctrees/what_really_need_know/index.doctree new file mode 100644 index 000000000..9a0179473 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/what_really_need_know/index.doctree differ diff --git a/pr-preview/pr-276/.doctrees/what_really_need_know/model_comparison.doctree b/pr-preview/pr-276/.doctrees/what_really_need_know/model_comparison.doctree new file mode 100644 index 000000000..afac7e8d9 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/what_really_need_know/model_comparison.doctree differ diff --git a/pr-preview/pr-276/.doctrees/what_really_need_know/model_evaluation.doctree b/pr-preview/pr-276/.doctrees/what_really_need_know/model_evaluation.doctree new file mode 100644 index 000000000..f5fb417e6 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/what_really_need_know/model_evaluation.doctree differ diff --git a/pr-preview/pr-276/.doctrees/what_really_need_know/pipeline.doctree b/pr-preview/pr-276/.doctrees/what_really_need_know/pipeline.doctree new file mode 100644 index 000000000..bf3a738de Binary files /dev/null and b/pr-preview/pr-276/.doctrees/what_really_need_know/pipeline.doctree differ diff --git a/pr-preview/pr-276/.doctrees/whats_new.doctree b/pr-preview/pr-276/.doctrees/whats_new.doctree new file mode 100644 index 000000000..1058ca2e6 Binary files /dev/null and b/pr-preview/pr-276/.doctrees/whats_new.doctree differ diff --git a/pr-preview/pr-276/_downloads/0fdc9a0d75a37f1ac1637ca54129dba7/plot_preprocess.py b/pr-preview/pr-276/_downloads/0fdc9a0d75a37f1ac1637ca54129dba7/plot_preprocess.py new file mode 100644 index 000000000..46ebabcbe --- /dev/null +++ b/pr-preview/pr-276/_downloads/0fdc9a0d75a37f1ac1637ca54129dba7/plot_preprocess.py @@ -0,0 +1,133 @@ +""" +Preprocessing with variance threshold, zscore and PCA +===================================================== + +This example uses the ``make_regression`` function to create a simple dataset, +performs a simple regression after the preprocessing of the features +including removal of low variance features, feature normalization for only +two features using zscore and feature reduction using PCA. +We will check the features after each preprocessing step. +""" +# Authors: Shammi More +# Leonard Sasse +# License: AGPL + +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns +from sklearn.datasets import make_regression + +from julearn import run_cross_validation +from julearn.inspect import preprocess +from julearn.pipeline import PipelineCreator +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Create a dataset using ``sklearn`` ``make_regression``. +df = pd.DataFrame() +X, y = [f"Feature {x}" for x in range(1, 5)], "y" +df[X], df[y] = make_regression( + n_samples=100, n_features=4, n_informative=3, noise=0.3, random_state=0 +) + +# We only want to zscore the first two features, so let's get their names. +first_two = X[:2] + +# We can define a dictionary, in which the 'key' defines the names of our +# different 'types' of 'X'. The 'value' determine, which features belong to +# this type. +X_types = {"X_to_zscore": first_two} + +############################################################################### +# Let's look at the summary statistics of the raw features. +print("Summary Statistics of the raw features : \n", df.describe()) + +############################################################################### +# We will preprocess all features using variance thresholding. +# We will only zscore the first two features, and then perform PCA using all +# features. We will zscore the target and then train a random forest model. +# Since we use the PipelineCreator object we have to explicitly declare which +# `X_types` each preprocessing step should be applied to. If we do not declare +# the type in the ``add`` method using the ``apply_to`` keyword argument, +# the step will default to ``"continuous"`` or to another type that can be +# declared in the constructor of the ``PipelineCreator``. +# To transform the target we could set ``apply_to="target"``, which is a special +# type, that cannot be user-defined. Please note also that if a step is added +# to transform the target, you also have to explicitly add the model that is +# to be used in the regression to the ``PipelineCreator``. + +# Define model parameters and preprocessing steps first +# The hyperparameters for each step can be added as a keyword argument and +# should be either one parameter or an iterable of multiple parameters for a +# search. + +# Setting the threshold for variance to 0.15, number of PCA components to 2 +# and number of trees for random forest to 200. + +# By setting "apply_to=*", we can apply the preprocessing step to all features. +pipeline_creator = PipelineCreator(problem_type="regression") + +pipeline_creator.add("select_variance", apply_to="*", threshold=0.15) +pipeline_creator.add("zscore", apply_to="X_to_zscore") +pipeline_creator.add("pca", apply_to="*", n_components=2) +pipeline_creator.add("rf", apply_to="*", n_estimators=200) + +# Because we have already added the model to the pipeline creator, we can +# simply drop in the ``pipeline_creator`` as a model. If we did not add a model +# here, we could add the ``pipeline_creator`` using the keyword argument +# ``preprocess`` and hand over a model separately. + +scores, model = run_cross_validation( + X=X, + y=y, + X_types=X_types, + data=df, + model=pipeline_creator, + scoring=["r2", "neg_mean_absolute_error"], + return_estimator="final", + seed=200, +) + +# We can use the final estimator to inspect the transformed features at a +# specific step of the pipeline. Since the PCA was the last step added to the +# pipeline, we can simply get the model up to this step by indexing as follows: + +X_after_pca = model[:-1].transform(df[X]) + +print("X after PCA:") +print("=" * 79) +print(X_after_pca) + +# We can select pipelines up to earlier steps by indexing previous elements +# in the final estimator. For example, to inspect features after the zscoring +# step: + +X_after_zscore = model[:-2].transform(df[X]) +print("X after zscore:") +print("=" * 79) +print(X_after_zscore) + +# However, to make this less confusing you can also simply use the high-level +# function ``preprocess`` to explicitly refer to a pipeline step by name: + +X_after_pca = preprocess(model, X=X, data=df, until="pca") +X_after_zscore = preprocess(model, X=X, data=df, until="zscore") + +# Let's plot scatter plots for raw features and the PCA components. +fig, axes = plt.subplots(1, 2, figsize=(12, 6)) +sns.scatterplot(x=X[0], y=X[1], data=df, ax=axes[0]) +axes[0].set_title("Raw features") +sns.scatterplot(x="pca0", y="pca1", data=X_after_pca, ax=axes[1]) +axes[1].set_title("PCA components") + +############################################################################### +# Let's look at the summary statistics of the zscored features. We see here +# that the mean of all the features is zero and standard deviation is one. +print( + "Summary Statistics of the zscored features : \n", + X_after_zscore.describe(), +) diff --git a/pr-preview/pr-276/_downloads/2c2781f9a637943101f9b9d122327ef8/run_hyperparameter_multiple_grids.ipynb b/pr-preview/pr-276/_downloads/2c2781f9a637943101f9b9d122327ef8/run_hyperparameter_multiple_grids.ipynb new file mode 100644 index 000000000..c71c8001a --- /dev/null +++ b/pr-preview/pr-276/_downloads/2c2781f9a637943101f9b9d122327ef8/run_hyperparameter_multiple_grids.ipynb @@ -0,0 +1,169 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Tuning Multiple Hyperparameters Grids\n\nThis example uses the ``fmri`` dataset, performs simple binary classification\nusing a Support Vector Machine classifier while tuning multiple hyperparameters\ngrids at the same time.\n\n## References\n\n Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of\n cognitive control in context-dependent decision-making. Cerebral Cortex.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# License: AGPL\n\nimport numpy as np\nfrom seaborn import load_dataset\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging\nfrom julearn.pipeline import PipelineCreator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the random seed to always have the same example.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "np.random.seed(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the dataset.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = load_dataset(\"fmri\")\ndf_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the dataframe in the right format.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.pivot(\n index=[\"subject\", \"timepoint\", \"event\"], columns=\"region\", values=\"signal\"\n)\n\ndf_fmri = df_fmri.reset_index()\ndf_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lets do a first attempt and use a linear SVM with the default parameters.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"frontal\", \"parietal\"]\ny = \"event\"\n\ncreator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\", kernel=\"linear\")\n\nscores = run_cross_validation(X=X, y=y, data=df_fmri, model=creator)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's tune a bit this SVM. We will use a grid search to tune the\nregularization parameter ``C`` and the kernel. We will also tune the ``gamma``.\nBut since the ``gamma`` is only used for the rbf kernel, we will use a\ndifferent grid for the ``\"rbf\"`` kernel.\n\nTo specify two different sets of parameters for the same step, we can\nexplicitly specify the name of the step. This is done by passing the\n``name`` parameter to the ``add`` method.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\", kernel=\"linear\", C=[0.01, 0.1], name=\"svm\")\ncreator.add(\n \"svm\",\n kernel=\"rbf\",\n C=[0.01, 0.1],\n gamma=[\"scale\", \"auto\", 1e-2, 1e-3],\n name=\"svm\",\n)\n\nsearch_params = {\n \"kind\": \"grid\",\n \"cv\": 2, # to speed up the example\n}\n\nscores, estimator = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n model=creator,\n search_params=search_params,\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It seems that we might have found a better model, but which one is it?\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(estimator.best_params_)\nprint(estimator.best_estimator_[\"svm\"]._gamma)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/3a8f305bb604c66843027e302d9f6752/plot_simple_model_comparison.py b/pr-preview/pr-276/_downloads/3a8f305bb604c66843027e302d9f6752/plot_simple_model_comparison.py new file mode 100644 index 000000000..358eee805 --- /dev/null +++ b/pr-preview/pr-276/_downloads/3a8f305bb604c66843027e302d9f6752/plot_simple_model_comparison.py @@ -0,0 +1,166 @@ +""" +Simple Model Comparison +======================= + +This example uses the ``iris`` dataset and performs binary classifications +using different models. At the end, it compares the performance of the models +using different scoring functions and performs a statistical test to assess +whether the difference in performance is significant. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# License: AGPL + +from seaborn import load_dataset +from sklearn.model_selection import RepeatedStratifiedKFold +from julearn import run_cross_validation +from julearn.utils import configure_logging +from julearn.stats.corrected_ttest import corrected_ttest + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +df_iris = load_dataset("iris") + +############################################################################### +# The dataset has three kind of species. We will keep two to perform a binary +# classification. +df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + +############################################################################### +# As features, we will use the sepal length, width and petal length. +# We will try to predict the species. + +X = ["sepal_length", "sepal_width", "petal_length"] +y = "species" +scores = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="svm", + problem_type="classification", + preprocess="zscore", +) + +print(scores["test_score"]) + +############################################################################### +# Additionally, we can choose to assess the performance of the model using +# different scoring functions. +# +# For example, we might have an unbalanced dataset: + +df_unbalanced = df_iris[20:] # drop the first 20 versicolor samples +print(df_unbalanced["species"].value_counts()) + +############################################################################### +# So we will choose to use the `balanced_accuracy` and `roc_auc` metrics. + +scoring = ["balanced_accuracy", "roc_auc"] + +############################################################################### +# Since we are comparing the performance of different models, we will need +# to use the same random seed to split the data in the same way. + +cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=42) + +############################################################################### +# First we will use a default SVM model. +scores1 = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + preprocess="zscore", + problem_type="classification", + scoring=scoring, + cv=cv, +) + +scores1["model"] = "svm" + +############################################################################### +# Second we will use a default Random Forest model. +scores2 = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="rf", + preprocess="zscore", + problem_type="classification", + scoring=scoring, + cv=cv, +) + +scores2["model"] = "rf" + +############################################################################### +# The third model will be a SVM with a linear kernel. +scores3 = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + model_params={"svm__kernel": "linear"}, + preprocess="zscore", + problem_type="classification", + scoring=scoring, + cv=cv, +) + +scores3["model"] = "svm_linear" + +############################################################################### +# We can now compare the performance of the models using corrected statistics. + +stats_df = corrected_ttest(scores1, scores2, scores3) +print(stats_df) + +############################################################################### +# .. rst-class:: hidden +# This block is hidden in the documentation. This files are used to generate +# the plots in the documentation. (not working for now) + +# sphinx_gallery_start_ignore +# The following lines are only meant for the documentation to work and not +# needed for the example to run. This will be removed as soon as sphix-gallery +# is able to hide code blocks. +scores1.to_csv("/tmp/scores1.csv") +scores2.to_csv("/tmp/scores2.csv") +scores3.to_csv("/tmp/scores3.csv") +# sphinx_gallery_end_ignore + +############################################################################### +# We can also plot the performance of the models using the ``julearn`` Score +# Viewer. + +from julearn.viz import plot_scores + +panel = plot_scores(scores1, scores2, scores3) +# panel.show() +# uncomment the previous line show the plot +# read the documentation for more information +# https://panel.holoviz.org/getting_started/build_app.html#deploying-panels + +############################################################################### +# This is how the plot looks like. +# +# .. note:: +# The plot is interactive. You can zoom in and out, and hover over. +# However, buttons will not work in this documentation. +# +# .. bokeh-plot:: +# :source-position: none +# +# from julearn.viz import plot_scores +# from bokeh.io import output_notebook, show +# import pandas as pd +# output_notebook() +# scores1 = pd.read_csv("/tmp/scores1.csv") +# scores2 = pd.read_csv("/tmp/scores2.csv") +# scores3 = pd.read_csv("/tmp/scores3.csv") +# panel = plot_scores(scores1, scores2, scores3, width=600) +# show(panel.get_root()) diff --git a/pr-preview/pr-276/_downloads/3f4409f285e23d1d8fb0b7dcdf8897e8/run_custom_scorers_regression.ipynb b/pr-preview/pr-276/_downloads/3f4409f285e23d1d8fb0b7dcdf8897e8/run_custom_scorers_regression.ipynb new file mode 100644 index 000000000..3e97b42cf --- /dev/null +++ b/pr-preview/pr-276/_downloads/3f4409f285e23d1d8fb0b7dcdf8897e8/run_custom_scorers_regression.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Custom Scoring Function for Regression\n\nThis example uses the ``diabetes`` data from ``sklearn datasets`` and performs\na regression analysis using a Ridge Regression model. As scorers, it uses\n``scikit-learn``, ``julearn`` and a custom metric defined by the user.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Shammi More \n# Federico Raimondo \n# License: AGPL\n\nimport pandas as pd\nimport scipy\nfrom sklearn.datasets import load_diabetes\n\nfrom sklearn.metrics import make_scorer\nfrom julearn.scoring import register_scorer\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "features, target = load_diabetes(return_X_y=True, as_frame=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dataset contains ten variables age, sex, body mass index, average blood\npressure, and six blood serum measurements (s1-s6) diabetes patients and\na quantitative measure of disease progression one year after baseline which\nis the target we are interested in predicting.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Features: \\n\", features.head())\nprint(\"Target: \\n\", target.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's combine features and target together in one dataframe and define X\nand y.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_diabetes = pd.concat([features, target], axis=1) # type: ignore\n\nX = [\"age\", \"sex\", \"bmi\", \"bp\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\"]\ny = \"target\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train a ridge regression model on train dataset and use mean absolute error\nfor scoring.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model = run_cross_validation(\n X=X,\n y=y,\n data=data_diabetes,\n preprocess=\"zscore\",\n problem_type=\"regression\",\n model=\"ridge\",\n return_estimator=\"final\",\n scoring=\"neg_mean_absolute_error\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The scores dataframe has all the values for each CV split.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mean value of mean absolute error across CV.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(scores[\"test_score\"].mean() * -1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now do the same thing, but use mean absolute error and Pearson product-moment\ncorrelation coefficient (squared) as scoring functions.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model = run_cross_validation(\n X=X,\n y=y,\n data=data_diabetes,\n preprocess=\"zscore\",\n problem_type=\"regression\",\n model=\"ridge\",\n return_estimator=\"final\",\n scoring=[\"neg_mean_absolute_error\", \"r2_corr\"],\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the scores dataframe has all the values for each CV split, but two scores\nunders the column names ``\"test_neg_mean_absolute_error\"`` and\n``\"test_r2_corr\"``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(scores[[\"test_neg_mean_absolute_error\", \"test_r2_corr\"]].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we want to define a custom scoring metric, we need to define a function\nthat takes the predicted and the actual values as input and returns a value.\nIn this case, we want to compute Pearson correlation coefficient (r).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def pearson_scorer(y_true, y_pred):\n return scipy.stats.pearsonr(y_true.squeeze(), y_pred.squeeze())[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before using it, we need to convert it to a ``sklearn scorer`` and register it\nwith ``julearn``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "register_scorer(scorer_name=\"pearsonr\", scorer=make_scorer(pearson_scorer))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can use it as another scoring metric.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model = run_cross_validation(\n X=X,\n y=y,\n data=data_diabetes,\n preprocess=\"zscore\",\n problem_type=\"regression\",\n model=\"ridge\",\n return_estimator=\"final\",\n scoring=[\"neg_mean_absolute_error\", \"r2_corr\", \"pearsonr\"],\n)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/3fba4d1cdc643e27b9b091dbcc9369cf/run_simple_binary_classification.ipynb b/pr-preview/pr-276/_downloads/3fba4d1cdc643e27b9b091dbcc9369cf/run_simple_binary_classification.ipynb new file mode 100644 index 000000000..3f9f2fbb1 --- /dev/null +++ b/pr-preview/pr-276/_downloads/3fba4d1cdc643e27b9b091dbcc9369cf/run_simple_binary_classification.ipynb @@ -0,0 +1,162 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Simple Binary Classification\n\nThis example uses the ``iris`` dataset and performs a simple binary\nclassification using a Support Vector Machine classifier.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n#\n# License: AGPL\n\nfrom seaborn import load_dataset\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = load_dataset(\"iris\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset has three kind of species. We will keep two to perform a binary\nclassification.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = df_iris[df_iris[\"species\"].isin([\"versicolor\", \"virginica\"])]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As features, we will use the sepal length, width and petal length.\nWe will try to predict the species.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"sepal_length\", \"sepal_width\", \"petal_length\"]\ny = \"species\"\nscores = run_cross_validation(\n X=X,\n y=y,\n data=df_iris,\n model=\"svm\",\n problem_type=\"classification\",\n preprocess=\"zscore\",\n)\n\nprint(scores[\"test_score\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, we can choose to assess the performance of the model using\ndifferent scoring functions.\n\nFor example, we might have an unbalanced dataset:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_unbalanced = df_iris[20:] # drop the first 20 versicolor samples\nprint(df_unbalanced[\"species\"].value_counts())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we compute the `accuracy`, we might not account for this imbalance. A more\nsuitable metric is the `balanced_accuracy`. More information in\n``scikit-learn``: :func:`~sklearn.metrics.balanced_accuracy_score`.\n\nWe will also set the random seed so we always split the data in the same way.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores = run_cross_validation(\n X=X,\n y=y,\n data=df_unbalanced,\n model=\"svm\",\n seed=42,\n preprocess=\"zscore\",\n problem_type=\"classification\",\n scoring=[\"accuracy\", \"balanced_accuracy\"],\n)\n\nprint(scores[\"test_accuracy\"].mean())\nprint(scores[\"test_balanced_accuracy\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Other kind of metrics allows us to evaluate how good our model is to detect\nspecific targets. Suppose we want to create a model that correctly identifies\nthe `versicolor` samples.\n\nNow we might want to evaluate the precision score, or the ratio of true\npositives (tp) over all positives (true and false positives). More\ninformation in ``scikit-learn``: :func:`~sklearn.metrics.precision_score`.\n\nFor this metric to work, we need to define which are our `positive` values.\nIn this example, we are interested in detecting `versicolor`.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "precision_scores = run_cross_validation(\n X=X,\n y=y,\n data=df_unbalanced,\n model=\"svm\",\n preprocess=\"zscore\",\n problem_type=\"classification\",\n seed=42,\n scoring=\"precision\",\n pos_labels=\"versicolor\",\n)\nprint(precision_scores[\"test_score\"].mean())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/42388118e4d8ea10bc2d3ca431517ea4/run_stacked_models.ipynb b/pr-preview/pr-276/_downloads/42388118e4d8ea10bc2d3ca431517ea4/run_stacked_models.ipynb new file mode 100644 index 000000000..ace37e749 --- /dev/null +++ b/pr-preview/pr-276/_downloads/42388118e4d8ea10bc2d3ca431517ea4/run_stacked_models.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Stacking Classification\n\nThis example uses the ``iris`` dataset and performs a complex stacking\nclassification. We will use two different classifiers, one applied to petal\nfeatures and one applied to sepal features. A final logistic regression\nclassifier will be applied on the predictions of the two classifiers.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# License: AGPL\n\nfrom seaborn import load_dataset\nfrom julearn import run_cross_validation\nfrom julearn.pipeline import PipelineCreator\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = load_dataset(\"iris\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset has three kind of species. We will keep two to perform a binary\nclassification.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = df_iris[df_iris[\"species\"].isin([\"versicolor\", \"virginica\"])]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As features, we will use the sepal length, width and petal length.\nWe will try to predict the species.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"sepal_length\", \"sepal_width\", \"petal_length\", \"petal_width\"]\ny = \"species\"\n\n# Define our feature types\nX_types = {\n \"sepal\": [\"sepal_length\", \"sepal_width\"],\n \"petal\": [\"petal_length\", \"petal_width\"],\n}\n\n# Create the pipeline for the sepal features, by default will apply to \"sepal\"\nmodel_sepal = PipelineCreator(problem_type=\"classification\", apply_to=\"sepal\")\nmodel_sepal.add(\"filter_columns\", apply_to=\"*\", keep=\"sepal\")\nmodel_sepal.add(\"zscore\")\nmodel_sepal.add(\"svm\")\n\n# Create the pipeline for the petal features, by default will apply to \"petal\"\nmodel_petal = PipelineCreator(problem_type=\"classification\", apply_to=\"petal\")\nmodel_petal.add(\"filter_columns\", apply_to=\"*\", keep=\"petal\")\nmodel_petal.add(\"zscore\")\nmodel_petal.add(\"rf\")\n\n# Create the stacking model\nmodel = PipelineCreator(problem_type=\"classification\")\nmodel.add(\n \"stacking\",\n estimators=[[(\"model_sepal\", model_sepal), (\"model_petal\", model_petal)]],\n apply_to=\"*\",\n)\n\nscores = run_cross_validation(\n X=X, y=y, X_types=X_types, data=df_iris, model=model\n)\n\nprint(scores[\"test_score\"])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/431544dd8767c79de542561d3d866115/plot_confound_removal_classification.py b/pr-preview/pr-276/_downloads/431544dd8767c79de542561d3d866115/plot_confound_removal_classification.py new file mode 100644 index 000000000..c179805f2 --- /dev/null +++ b/pr-preview/pr-276/_downloads/431544dd8767c79de542561d3d866115/plot_confound_removal_classification.py @@ -0,0 +1,240 @@ +""" +Confound Removal (model comparison) +=================================== + +This example uses the ``iris`` dataset, performs simple binary classification +with and without confound removal using a Random Forest classifier. + +""" +# Authors: Shammi More +# Federico Raimondo +# Leonard Sasse +# License: AGPL + +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns +from seaborn import load_dataset + +from julearn import run_cross_validation +from julearn.model_selection import StratifiedBootstrap +from julearn.pipeline import PipelineCreator +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Load the iris data from seaborn. +df_iris = load_dataset("iris") + +############################################################################### +# The dataset has three kind of species. We will keep two to perform a binary +# classification. +df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + +############################################################################### +# As features, we will use the sepal length, width and petal length and use +# petal width as confound. + +X = ["sepal_length", "sepal_width", "petal_length"] +y = "species" +confounds = ["petal_width"] + +############################################################################### +# Doing hypothesis testing in ML is not that simple. If we were to use +# classical frequentist statistics, we have the problem that using cross +# validation, the samples are not independent and the population (train + test) +# is always the same. +# +# If we want to compare two models, an alternative is to contrast, for each +# fold, the performance gap between the models. If we combine that approach +# with bootstrapping, we can then compare the confidence intervals of the +# difference. If the 95% CI is above 0 (or below), we can claim that the models +# are different with p < 0.05. +# +# Let's use a bootstrap CV. In the interest of time we do 20 iterations, +# change the number of bootstrap iterations to at least 2000 for a valid test. +n_bootstrap = 20 +n_elements = len(df_iris) +cv = StratifiedBootstrap(n_splits=n_bootstrap, test_size=0.3, random_state=42) + +############################################################################### +# First, we will train a model without performing confound removal on features. +# Note: confounds by default. +scores_ncr = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="rf", + cv=cv, + problem_type="classification", + preprocess="zscore", + scoring=["accuracy", "roc_auc"], + return_estimator="cv", + seed=200, +) + +############################################################################### +# Next, we train a model after performing confound removal on the features. +# Note: we initialize the CV again to use the same folds as before. +cv = StratifiedBootstrap(n_splits=n_bootstrap, test_size=0.3, random_state=42) + +# In order to tell ``run_cross_validation`` which columns are confounds, +# and which columns are features, we have to define the X_types: +X_types = {"features": X, "confound": confounds} + +############################################################################## +# We can now define a pipeline creator and add a confound removal step. +# The pipeline creator should apply all the steps, by default, to the +# features type. +# +# The first step will zscore both features and confounds. +# +# The second step will remove the confounds (type "confound") from the +# "features". +# +# Finally, a random forest will be trained. +# Given the default ``apply_to`` in the pipeline creator, +# the random forest will only be trained using "features". +creator = PipelineCreator(problem_type="classification", apply_to="features") +creator.add("zscore", apply_to=["features", "confound"]) +creator.add("confound_removal", apply_to="features", confounds="confound") +creator.add("rf") + +scores_cr = run_cross_validation( + X=X + confounds, + y=y, + data=df_iris, + model=creator, + cv=cv, + X_types=X_types, + scoring=["accuracy", "roc_auc"], + return_estimator="cv", + seed=200, +) + +############################################################################### +# Now we can compare the accuracies. We can combine the two outputs as +# ``pandas.DataFrame``. +scores_ncr["confounds"] = "Not Removed" +scores_cr["confounds"] = "Removed" + +############################################################################### +# Now we convert the metrics to a column for easier seaborn plotting (convert +# to long format). + +index = ["fold", "confounds"] +scorings = ["test_accuracy", "test_roc_auc"] + +df_ncr_metrics = scores_ncr.set_index(index)[scorings].stack() +df_ncr_metrics.index.names = ["fold", "confounds", "metric"] +df_ncr_metrics.name = "value" + +df_cr_metrics = scores_cr.set_index(index)[scorings].stack() +df_cr_metrics.index.names = ["fold", "confounds", "metric"] +df_cr_metrics.name = "value" + +df_metrics = pd.concat((df_ncr_metrics, df_cr_metrics)) + +df_metrics = df_metrics.reset_index() +df_metrics.head() + +############################################################################### +# And finally plot the results. +sns.catplot( + x="confounds", y="value", col="metric", data=df_metrics, kind="swarm" +) +plt.tight_layout() + +############################################################################### +# While this plot allows us to see the mean performance values and compare +# them, these samples are paired. In order to see if there is a systematic +# difference, we need to check the distribution of differences between the +# the models. +# +# First, we remove the column "confounds" from the index and make the difference +# between the metrics. +df_cr_metrics = df_cr_metrics.reset_index().set_index(["fold", "metric"]) +df_ncr_metrics = df_ncr_metrics.reset_index().set_index(["fold", "metric"]) + +df_diff_metrics = df_ncr_metrics["value"] - df_cr_metrics["value"] +df_diff_metrics = df_diff_metrics.reset_index() + +############################################################################### +# Now we can finally plot the difference, setting the whiskers of the box plot +# at 2.5 and 97.5 to see the 95% CI. +sns.boxplot( + x="metric", y="value", data=df_diff_metrics.reset_index(), whis=[2.5, 97.5] +) +plt.axhline(0, color="k", ls=":") +plt.tight_layout() + +############################################################################### +# We can see that while it seems that the accuracy and ROC AUC scores are +# higher when confounds are not removed. We can not really claim (using this +# test), that the models are different in terms of these metrics. +# +# Maybe the percentiles will be more accuracy with the proper amount of +# bootstrap iterations? +# +# But the main point of confound removal is for interpretability. Let's see +# if there is a change in the feature importances. +# +# First, we need to collect the feature importances for each model, for each +# fold. + +ncr_fi = [] +for i_fold, estimator in enumerate(scores_ncr["estimator"]): + this_importances = pd.DataFrame( + { + "feature": [x.replace("_", " ") for x in X], + "importance": estimator["rf"].feature_importances_, + "confounds": "Not Removed", + "fold": i_fold, + } + ) + ncr_fi.append(this_importances) +ncr_fi = pd.concat(ncr_fi) + +cr_fi = [] +for i_fold, estimator in enumerate(scores_cr["estimator"]): + this_importances = pd.DataFrame( + { + "feature": [x.replace("_", " ") for x in X], + "importance": estimator["rf"].model.feature_importances_, + "confounds": "Removed", + "fold": i_fold, + } + ) + cr_fi.append(this_importances) +cr_fi = pd.concat(cr_fi) + +feature_importance = pd.concat([cr_fi, ncr_fi]) + +############################################################################### +# We can now plot the importances. +sns.catplot( + x="feature", + y="importance", + hue="confounds", + dodge=True, + data=feature_importance, + kind="swarm", + s=3, +) +plt.tight_layout() + +############################################################################### +# And check the differences in importances. We can now see that there is +# a difference in importances. +diff_fi = ( + cr_fi.set_index(["feature", "fold"])["importance"] + - ncr_fi.set_index(["feature", "fold"])["importance"] +) +sns.boxplot( + x="importance", y="feature", data=diff_fi.reset_index(), whis=[2.5, 97.5] +) +plt.axvline(0, color="k", ls=":") +plt.tight_layout() diff --git a/pr-preview/pr-276/_downloads/49cd72af4c3c448b24084500a1dde058/plot_groupcv_inspect_svm.py b/pr-preview/pr-276/_downloads/49cd72af4c3c448b24084500a1dde058/plot_groupcv_inspect_svm.py new file mode 100644 index 000000000..bd13bf7a0 --- /dev/null +++ b/pr-preview/pr-276/_downloads/49cd72af4c3c448b24084500a1dde058/plot_groupcv_inspect_svm.py @@ -0,0 +1,217 @@ +""" +Inspecting SVM models +===================== + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier and analyse the model. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# Shammi More +# License: AGPL + +import numpy as np +import pandas as pd + +from sklearn.model_selection import GroupShuffleSplit + +import matplotlib.pyplot as plt +import seaborn as sns +from seaborn import load_dataset + +from julearn import run_cross_validation +from julearn.utils import configure_logging +from julearn.inspect import preprocess + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + + +############################################################################### +# Dealing with Cross-Validation techniques +# ---------------------------------------- + +df_fmri = load_dataset("fmri") + +############################################################################### +# First, lets get some information on what the dataset has: + +print(df_fmri.head()) + +############################################################################### +# From this information, we can infer that it is an fMRI study in which there +# were several subjects, timepoints, events and signal extracted from several +# brain regions. +# +# Lets check how many kinds of each we have. + +print(df_fmri["event"].unique()) +print(df_fmri["region"].unique()) +print(sorted(df_fmri["timepoint"].unique())) +print(df_fmri["subject"].unique()) + +############################################################################### +# We have data from parietal and frontal regions during 2 types of events +# (*cue* and *stim*) during 18 timepoints and for 14 subjects. +# Lets see how many samples we have for each condition + +print(df_fmri.groupby(["subject", "timepoint", "event", "region"]).count()) +print( + np.unique( + df_fmri.groupby(["subject", "timepoint", "event", "region"]) + .count() + .values + ) +) + +############################################################################### +# We have exactly one value per condition. +# +# Lets try to build a model, that uses parietal and frontal signal to predicts +# whether the event was a *cue* or a *stim*. +# +# First we define our X and y variables. +X = ["parietal", "frontal"] +y = "event" + +############################################################################### +# In order for this to work, both *parietal* and *frontal* must be columns. +# We need to *pivot* the table. +# +# The values of *region* will be the columns. The column *signal* will be the +# values. And the columns *subject*, *timepoint* and *event* will be the index +df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" +) + +df_fmri = df_fmri.reset_index() + +############################################################################### +# Here we want to zscore all the features and then train a Support Vector +# Machine classifier. + +scores = run_cross_validation( + X=X, + y=y, + data=df_fmri, + preprocess="zscore", + model="svm", + problem_type="classification", +) + +print(scores["test_score"].mean()) + +############################################################################### +# This results indicate that we can decode the kind of event by looking at +# the *parietal* and *frontal* signal. However, that claim is true only if we +# have some data from the same subject already acquired. +# +# The problem is that we split the data randomly into 5 folds (default, see +# :func:`.run_cross_validation`). This means that data from one subject could +# be both in the training and the testing set. If this is the case, then the +# model can learn the subjects' specific characteristics and apply it to the +# testing set. Thus, it is not true that we can decode it for an unseen +# subject, but for an unseen timepoint for a subject that for whom we already +# have data. +# +# To test for unseen subject, we need to make sure that all the data from each +# subject is either on the training or the testing set, but not in both. +# +# We can use ``scikit-learn``'s +# :class:`sklearn.model_selection.GroupShuffleSplit` and specify which is the +# grouping column using the ``group`` parameter. +# By setting ``return_estimator="final"``, the :func:`.run_cross_validation` +# function returns the estimator fitted with all the data. We will use this +# later to do some analyses. +cv = GroupShuffleSplit(n_splits=5, test_size=0.5, random_state=42) + +scores, model = run_cross_validation( + X=X, + y=y, + data=df_fmri, + preprocess="zscore", + model="svm", + cv=cv, + groups="subject", + problem_type="classification", + return_estimator="final", +) + +print(scores["test_score"].mean()) + +############################################################################### +# After testing on independent subjects, we can now claim that given a new +# subject, we can predict the kind of event. +# +# Let's do some visualization on how these two features interact and what +# the preprocessing part of the model does. + +# Plot the raw features +fig, axes = plt.subplots(1, 2, figsize=(8, 4)) +sns.scatterplot( + x="parietal", y="frontal", hue="event", data=df_fmri, ax=axes[0], s=5 +) +axes[0].set_title("Raw data") + +# Plot the preprocessed features +pre_X = preprocess( + model, X=X, data=df_fmri, until="zscore", with_column_types=True +) + +pre_df = pre_X.join(df_fmri[y]) + +sns.scatterplot( + x="parietal__:type:__continuous", + y="frontal__:type:__continuous", + hue="event", + data=pre_df, + ax=axes[1], + s=5, +) + +axes[1].set_title("Preprocessed data") + +############################################################################### +# In this case, the preprocessing is nothing more than a +# :class:`sklearn.preprocessing.StandardScaler`. +# +# It seems that the data is not quite linearly separable. Let's now visualize +# how the SVM does this complex task. + +# Get the model from the pipeline +clf = model[2] +fig = plt.figure() +ax = sns.scatterplot( + x="parietal__:type:__continuous", + y="frontal__:type:__continuous", + hue="event", + data=pre_df, + s=5, +) + +xlim = ax.get_xlim() +ylim = ax.get_ylim() + +# Create grid to evaluate model +xx = np.linspace(xlim[0], xlim[1], 30) +yy = np.linspace(ylim[0], ylim[1], 30) +YY, XX = np.meshgrid(yy, xx) +xy = np.vstack([XX.ravel(), YY.ravel()]).T + +# Create pandas.DataFrame +xy_df = pd.DataFrame( + data=xy, + columns=["parietal__:type:__continuous", "frontal__:type:__continuous"], +) + +Z = clf.decision_function(xy_df).reshape(XX.shape) +a = ax.contour(XX, YY, Z, colors="k", levels=[0], alpha=0.5, linestyles=["-"]) +ax.set_title("Preprocessed data with SVM decision function boundaries") diff --git a/pr-preview/pr-276/_downloads/615a4e32a4edeba925c5a5f195ca7401/plot_example_regression.ipynb b/pr-preview/pr-276/_downloads/615a4e32a4edeba925c5a5f195ca7401/plot_example_regression.ipynb new file mode 100644 index 000000000..79760bfd5 --- /dev/null +++ b/pr-preview/pr-276/_downloads/615a4e32a4edeba925c5a5f195ca7401/plot_example_regression.ipynb @@ -0,0 +1,277 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Regression Analysis\n\nThis example uses the ``diabetes`` data from ``sklearn datasets`` and performs\na regression analysis using a Ridge Regression model.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Shammi More \n# Federico Raimondo \n# License: AGPL\n\nimport pandas as pd\nimport seaborn as sns\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.datasets import load_diabetes\nfrom sklearn.metrics import mean_absolute_error\nfrom sklearn.model_selection import train_test_split\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "features, target = load_diabetes(return_X_y=True, as_frame=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dataset contains ten variables age, sex, body mass index, average blood\npressure, and six blood serum measurements (s1-s6) diabetes patients and\na quantitative measure of disease progression one year after baseline which\nis the target we are interested in predicting.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Features: \\n\", features.head())\nprint(\"Target: \\n\", target.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's combine features and target together in one dataframe and define X\nand y\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_diabetes = pd.concat([features, target], axis=1)\n\nX = [\"age\", \"sex\", \"bmi\", \"bp\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\"]\ny = \"target\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calculate correlations between the features/variables and plot it as heat map.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "corr = data_diabetes.corr()\n\nfig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.set(font_scale=1.2)\nsns.heatmap(\n corr,\n xticklabels=corr.columns,\n yticklabels=corr.columns,\n annot=True,\n fmt=\"0.1f\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Split the dataset into train and test.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train a ridge regression model on train dataset and use mean absolute error\nfor scoring.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model = run_cross_validation(\n X=X,\n y=y,\n data=train_diabetes,\n preprocess=\"zscore\",\n problem_type=\"regression\",\n model=\"ridge\",\n return_estimator=\"final\",\n scoring=\"neg_mean_absolute_error\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The scores dataframe has all the values for each CV split.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mean value of mean absolute error across CV\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(scores[\"test_score\"].mean() * -1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can get the MAE fold and repetition:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_mae = scores.set_index([\"repeat\", \"fold\"])[\"test_score\"].unstack() * -1\ndf_mae.index.name = \"Repeats\"\ndf_mae.columns.name = \"K-fold splits\"\n\ndf_mae" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot heatmap of mean absolute error (MAE) over all repeats and CV splits.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.heatmap(df_mae, cmap=\"YlGnBu\")\nplt.title(\"Cross-validation MAE\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's plot the feature importance using the coefficients of the trained model.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "features = pd.DataFrame({\"Features\": X, \"importance\": model[\"ridge\"].coef_})\nfeatures.sort_values(by=[\"importance\"], ascending=True, inplace=True)\nfeatures[\"positive\"] = features[\"importance\"] > 0\n\nfig, ax = plt.subplots(1, 1, figsize=(10, 7))\nfeatures.set_index(\"Features\", inplace=True)\nfeatures.importance.plot(\n kind=\"barh\", color=features.positive.map({True: \"blue\", False: \"red\"})\n)\nax.set(xlabel=\"Importance\", title=\"Variable importance for Ridge Regression\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the final model to make predictions on test data and plot scatterplot\nof true values vs predicted values.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "y_true = test_diabetes[y]\ny_pred = model.predict(test_diabetes[X])\nmae = format(mean_absolute_error(y_true, y_pred), \".2f\")\ncorr = format(np.corrcoef(y_pred, y_true)[1, 0], \".2f\")\n\nfig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.set_style(\"darkgrid\")\nplt.scatter(y_true, y_pred)\nplt.plot(y_true, y_true)\nxmin, xmax = ax.get_xlim()\nymin, ymax = ax.get_ylim()\ntext = \"MAE: \" + str(mae) + \" CORR: \" + str(corr)\nax.set(xlabel=\"True values\", ylabel=\"Predicted values\")\nplt.title(\"Actual vs Predicted\")\nplt.text(\n xmax - 0.01 * xmax,\n ymax - 0.01 * ymax,\n text,\n verticalalignment=\"top\",\n horizontalalignment=\"right\",\n fontsize=12,\n)\nplt.axis(\"scaled\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/642e7f78aab7ede209bd6f51a116541e/run_stacked_models.py b/pr-preview/pr-276/_downloads/642e7f78aab7ede209bd6f51a116541e/run_stacked_models.py new file mode 100644 index 000000000..241d42e69 --- /dev/null +++ b/pr-preview/pr-276/_downloads/642e7f78aab7ede209bd6f51a116541e/run_stacked_models.py @@ -0,0 +1,69 @@ +""" +Stacking Classification +======================= + +This example uses the ``iris`` dataset and performs a complex stacking +classification. We will use two different classifiers, one applied to petal +features and one applied to sepal features. A final logistic regression +classifier will be applied on the predictions of the two classifiers. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# License: AGPL + +from seaborn import load_dataset +from julearn import run_cross_validation +from julearn.pipeline import PipelineCreator +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +df_iris = load_dataset("iris") + +############################################################################### +# The dataset has three kind of species. We will keep two to perform a binary +# classification. +df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + +############################################################################### +# As features, we will use the sepal length, width and petal length. +# We will try to predict the species. + +X = ["sepal_length", "sepal_width", "petal_length", "petal_width"] +y = "species" + +# Define our feature types +X_types = { + "sepal": ["sepal_length", "sepal_width"], + "petal": ["petal_length", "petal_width"], +} + +# Create the pipeline for the sepal features, by default will apply to "sepal" +model_sepal = PipelineCreator(problem_type="classification", apply_to="sepal") +model_sepal.add("filter_columns", apply_to="*", keep="sepal") +model_sepal.add("zscore") +model_sepal.add("svm") + +# Create the pipeline for the petal features, by default will apply to "petal" +model_petal = PipelineCreator(problem_type="classification", apply_to="petal") +model_petal.add("filter_columns", apply_to="*", keep="petal") +model_petal.add("zscore") +model_petal.add("rf") + +# Create the stacking model +model = PipelineCreator(problem_type="classification") +model.add( + "stacking", + estimators=[[("model_sepal", model_sepal), ("model_petal", model_petal)]], + apply_to="*", +) + +scores = run_cross_validation( + X=X, y=y, X_types=X_types, data=df_iris, model=model +) + +print(scores["test_score"]) diff --git a/pr-preview/pr-276/_downloads/64386a197c5694c20381874773983d21/run_grouped_cv.py b/pr-preview/pr-276/_downloads/64386a197c5694c20381874773983d21/run_grouped_cv.py new file mode 100644 index 000000000..9363c4ef8 --- /dev/null +++ b/pr-preview/pr-276/_downloads/64386a197c5694c20381874773983d21/run_grouped_cv.py @@ -0,0 +1,139 @@ +""" +Grouped CV +========== + +This example uses the ``fMRI`` dataset and performs GroupKFold +Cross-Validation for classification using Random Forest Classifier. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc + +""" + +# Authors: Federico Raimondo +# Shammi More +# Kimia Nazarzadeh +# License: AGPL + +# Importing the necessary Python libraries +import numpy as np + +from seaborn import load_dataset +from sklearn.model_selection import GroupKFold, StratifiedGroupKFold + +from julearn.utils import configure_logging +from julearn import run_cross_validation + +############################################################################### +# Set the logging level to info to see extra information +configure_logging(level="INFO") + +############################################################################### +# Dealing with Cross-Validation techniques +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +df_fmri = load_dataset("fmri") + +############################################################################### +# First, lets get some information on what the dataset has: + +print(df_fmri.head()) + +############################################################################### +# From this information, we can infer that it is an fMRI study in which there +# were several subjects, timepoints, events and signal extracted from several +# brain regions. +# +# Lets check how many kinds of each we have. +print(df_fmri["event"].unique()) +print(df_fmri["region"].unique()) +print(sorted(df_fmri["timepoint"].unique())) +print(df_fmri["subject"].unique()) + +############################################################################### +# We have data from parietal and frontal regions during 2 types of events +# (*cue* and *stim*) during 18 timepoints and for 14 subjects. +# Lets see how many samples we have for each condition + +print(df_fmri.groupby(["subject", "timepoint", "event", "region"]).count()) +print( + np.unique( + df_fmri.groupby(["subject", "timepoint", "event", "region"]) + .count() + .values + ) +) + +############################################################################### +# We have exactly one value per condition. +# +# Lets try to build a model, that uses parietal and frontal signal to predicts +# whether the event was a *cue* or a *stim*. +# +# First we define our X and y variables. +X = ["parietal", "frontal"] +y = "event" + +############################################################################### +# In order for this to work, both *parietal* and *frontal* must be columns. +# We need to *pivot* the table. +# +# The values of *region* will be the columns. The column *signal* will be the +# values. And the columns *subject*, *timepoint* and *event* will be the index +df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" +) + +df_fmri = df_fmri.reset_index() + +############################################################################### +# Here we want to zscore all the features and then train a Support Vector +# Machine classifier. + +scores = run_cross_validation( + X=X, + y=y, + data=df_fmri, + preprocess="zscore", + model="rf", + problem_type="classification", +) + +print(scores["test_score"].mean()) + +############################################################################### +# Train classification model with stratification on data +cv_stratified = StratifiedGroupKFold(n_splits=2) +scores, model = run_cross_validation( + X=X, + y=y, + data=df_fmri, + groups="subject", + model="rf", + problem_type="classification", + cv=cv_stratified, + return_estimator="final", +) + +print(scores["test_score"].mean()) + +############################################################################### +# Train classification model without stratification on data +cv = GroupKFold(n_splits=2) +scores, model = run_cross_validation( + X=X, + y=y, + data=df_fmri, + groups="subject", + model="rf", + problem_type="classification", + cv=cv, + return_estimator="final", +) + +print(scores["test_score"].mean()) diff --git a/pr-preview/pr-276/_downloads/66cade0ddde300a7ce7f71599fd4d9a9/run_combine_pandas.py b/pr-preview/pr-276/_downloads/66cade0ddde300a7ce7f71599fd4d9a9/run_combine_pandas.py new file mode 100644 index 000000000..8cf7a18a3 --- /dev/null +++ b/pr-preview/pr-276/_downloads/66cade0ddde300a7ce7f71599fd4d9a9/run_combine_pandas.py @@ -0,0 +1,152 @@ +""" +Working with ``pandas`` +======================= + +This example uses the ``fmri`` dataset to transform and combine data in order +to prepare it to be used by ``julearn``. + + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# +# License: AGPL + +from seaborn import load_dataset +import pandas as pd + +############################################################################### +# One of the key elements that make ``julearn`` easy to use, is the possibility +# to work directly with ``pandas.DataFrame``, similar to MS Excel spreadsheets +# or csv files. +# +# Ideally, we will have everything tabulated and organised for ``julearn``, but +# it might not be your case. You might have some files with the fMRI values, some +# others with demographics, some other with diagnostic metrics or behavioral +# results. +# +# You need to prepare these files for ``julearn``. +# +# One option is to manually edit the files and make sure that everything is +# ready to do some machine-learning. However, this is error-prone. +# +# Fortunately, `pandas`_ provides several tools to deal with this task. +# +# This example is a collection of some of these useful methods. +# +# Let's start with the ``fmri`` dataset. + +df_fmri = load_dataset("fmri") + +############################################################################### +# Let's see what this dataset has. +# +df_fmri.head() + +############################################################################### +# From long to wide format +# We have seen this in other examples. If we want to use julearn, each feature +# must be a columns. In order to use the signals from different regions as +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# features, we need to convert this dataframe from the long format to the wide +# format. +# +# We will use the ``pivot`` method. +df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" +) + +############################################################################### +# This method reshapes the table, keeping the specified elements as index, +# columns and values. +# +# In our case, the values are extracted from the *signal* column. The columns +# from the *region* column and *subject*, *timepoint* and *event* becomes the +# index. +# +# The index is what identifies each sample. As a rule, the index can't be +# duplicated. If each subject has more than one timepoint, and each timepoint +# has more than one event, then these 3 elements are needed as the index. +# +# Let's see what we have here: +df_fmri.head() + +############################################################################### +# Now this is in the format we want. However, in order to access the index +# as columns ``df_fmri["subject"]`` we need to reset the index. +# +# Check the subtle but important difference: +df_fmri = df_fmri.reset_index() +df_fmri.head() + +############################################################################### +# Merging or joining ``DataFrame`` +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# So now we have our fMRI data tabulated for ``julearn``. However, it might be +# the case that we have some important information in another file. For example, +# the subjects' age and the place where they were scanned. +# +# For the purpose of the example, we'll create the dataframe here. +metadata = { + "subject": [f"s{i}" for i in range(14)], + "age": [23, 21, 31, 29, 43, 23, 43, 28, 48, 29, 35, 23, 34, 25], + "scanner": ["a"] * 6 + ["b"] * 8, +} +df_meta = pd.DataFrame(metadata) +df_meta + +############################################################################### +# We will use the ``join`` method. This method will join the two dataframes, +# matching elements by the *index*. +# +# In this case, the matching element (or index) will be the column ``subject``. +# We need to set the index in each dataframe before join. +df_fmri = df_fmri.set_index("subject") +df_meta = df_meta.set_index("subject") +df_fmri = df_fmri.join(df_meta) +df_fmri + +############################################################################### +# Finally, let's reset the index and have it ready for ``julearn``. +df_fmri = df_fmri.reset_index() + +############################################################################### +# Now we can use, for example, *age* and *scanner* as confounds. + +############################################################################### +# Reshaping data frames (more complex) +# Lets suppose that our prediction target is now the *age* and we want to use +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# as features the frontal and parietal value during each event. For this +# purpose, we need to convert the event values into columns. There are two +# events: *cue* and *stim*. So this will result in 4 columns. +# +# We will still use the pivot, but in this case, we will have two values: +df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "age", "scanner"], + columns="event", + values=["frontal", "parietal"], +) +df_fmri + +############################################################################### +# Since the column names are combinations of the values in the *event* column +# and the previous *frontal* and *parietal* columns, it is now a multi-level +# column name. +df_fmri.columns + +############################################################################### +# The following trick will join the different levels using an underscore (*_*) +df_fmri.columns = ["_".join(x) for x in df_fmri.columns] +df_fmri + +############################################################################### +# We have finally the information we want. We can now reset the index. +df_fmri = df_fmri.reset_index() diff --git a/pr-preview/pr-276/_downloads/6966d3604f95f27d385e5af970f5609e/plot_cm_acc_multiclass.py b/pr-preview/pr-276/_downloads/6966d3604f95f27d385e5af970f5609e/plot_cm_acc_multiclass.py new file mode 100644 index 000000000..2df333de0 --- /dev/null +++ b/pr-preview/pr-276/_downloads/6966d3604f95f27d385e5af970f5609e/plot_cm_acc_multiclass.py @@ -0,0 +1,115 @@ +""" +Multiclass Classification +========================= + +This example uses the ``iris`` dataset and performs multiclass +classification using a Support Vector Machine classifier and plots +heatmaps for cross-validation accuracies and plots confusion matrix +for the test data. + +""" +# Authors: Shammi More +# Federico Raimondo +# License: AGPL + +import pandas as pd +import seaborn as sns +import numpy as np +import matplotlib.pyplot as plt +from seaborn import load_dataset +from sklearn.model_selection import train_test_split, RepeatedKFold +from sklearn.metrics import confusion_matrix + +from julearn import run_cross_validation +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information +configure_logging(level="INFO") + +############################################################################### +# load the iris data from seaborn +df_iris = load_dataset("iris") +X = ["sepal_length", "sepal_width", "petal_length"] +y = "species" + +############################################################################### +# Split the dataset into train and test +train_iris, test_iris = train_test_split( + df_iris, test_size=0.2, stratify=df_iris[y], random_state=200 +) + +############################################################################### +# We want to perform multiclass classification as iris dataset contains 3 kinds +# of species. We will first zscore all the features and then train a support +# vector machine classifier. + +cv = RepeatedKFold(n_splits=5, n_repeats=5, random_state=200) +scores, model_iris = run_cross_validation( + X=X, + y=y, + data=train_iris, + model="svm", + preprocess="zscore", + problem_type="classification", + cv=cv, + scoring=["accuracy"], + return_estimator="final", +) + +############################################################################### +# The scores dataframe has all the values for each CV split. + +scores.head() + +############################################################################### +# Now we can get the accuracy per fold and repetition: + +df_accuracy = scores.set_index(["repeat", "fold"])["test_accuracy"].unstack() +df_accuracy.index.name = "Repeats" +df_accuracy.columns.name = "K-fold splits" +df_accuracy + +############################################################################### +# Plot heatmap of accuracy over all repeats and CV splits +sns.set(font_scale=1.2) +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.heatmap(df_accuracy, cmap="YlGnBu") +plt.title("Cross-validation Accuracy") + +############################################################################### +# We can also test our final model's accuracy and plot the confusion matrix +# for the test data as an annotated heatmap +y_true = test_iris[y] +y_pred = model_iris.predict(test_iris[X]) +cm = confusion_matrix(y_true, y_pred, labels=np.unique(y_true)) + +print(cm) + +############################################################################### +# Now that we have our confusion matrix, let's build another matrix with +# annotations. +cm_sum = np.sum(cm, axis=1, keepdims=True) +cm_perc = cm / cm_sum.astype(float) * 100 +annot = np.empty_like(cm).astype(str) +nrows, ncols = cm.shape +for i in range(nrows): + for j in range(ncols): + c = cm[i, j] + p = cm_perc[i, j] + if c == 0: + annot[i, j] = "" + else: + s = cm_sum[i] + annot[i, j] = "%.1f%%\n%d/%d" % (p, c, s) + +############################################################################### +# Finally we create another dataframe with the confusion matrix and plot +# the heatmap with annotations. +cm = pd.DataFrame(cm, index=np.unique(y_true), columns=np.unique(y_true)) +cm.index.name = "Actual" +cm.columns.name = "Predicted" + +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.heatmap(cm, cmap="YlGnBu", annot=annot, fmt="", ax=ax) +plt.title("Confusion matrix") diff --git a/pr-preview/pr-276/_downloads/69c2ca44a56fee8ffb7f1947c8eb82aa/plot_stratified_kfold_reg.ipynb b/pr-preview/pr-276/_downloads/69c2ca44a56fee8ffb7f1947c8eb82aa/plot_stratified_kfold_reg.ipynb new file mode 100644 index 000000000..cd3b45958 --- /dev/null +++ b/pr-preview/pr-276/_downloads/69c2ca44a56fee8ffb7f1947c8eb82aa/plot_stratified_kfold_reg.ipynb @@ -0,0 +1,223 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Stratified K-fold CV for regression analysis\n\nThis example uses the ``diabetes`` data from ``sklearn datasets`` to\nperform stratified Kfold CV for a regression problem,\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Shammi More \n# Federico Raimondo \n# Leonard Sasse \n# License: AGPL\n\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.datasets import load_diabetes\nfrom sklearn.model_selection import KFold\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging\nfrom julearn.model_selection import ContinuousStratifiedKFold" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "features, target = load_diabetes(return_X_y=True, as_frame=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dataset contains ten variables age, sex, body mass index, average blood\npressure, and six blood serum measurements (s1-s6) diabetes patients and\na quantitative measure of disease progression one year after baseline which\nis the target we are interested in predicting.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Features: \\n\", features.head())\nprint(\"Target: \\n\", target.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's combine features and target together in one dataframe and create some\noutliers to see the difference in model performance with and without\nstratification.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_df = pd.concat([features, target], axis=1)\n\n# Create outliers for test purpose\nnew_df = data_df[(data_df.target > 145) & (data_df.target <= 150)]\nnew_df[\"target\"] = [590, 580, 597, 595, 590, 590, 600]\ndata_df = pd.concat([data_df, new_df], axis=0)\ndata_df = data_df.reset_index(drop=True)\n\n# Define X, y\nX = [\"age\", \"sex\", \"bmi\", \"bp\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\"]\ny = \"target\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define number of bins/group for stratification. The idea is that each \"bin\"\nwill be equally represented in each fold. The number of bins should be\nchosen such that each bin has a sufficient number of samples so that each\nfold has more than one sample from each bin.\nLet's see a couple of histrograms with different number of bins.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.displot(data_df, x=\"target\", bins=60)\n\nsns.displot(data_df, x=\"target\", bins=40)\n\nsns.displot(data_df, x=\"target\", bins=20)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From the histogram above, we can see that the data is not uniformly\ndistributed. We can see that the data is skewed towards the lower end of\nthe target variable. We can also see that there are some outliers in the\ndata. In any case, even with a low number of splits, some groups will not be\nrepresented in each fold. Lets continue with 40 bins which gives a good\ngranularity.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv_stratified = ContinuousStratifiedKFold(n_bins=40, n_splits=5, shuffle=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train a linear regression model with stratification on target.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores_strat, model = run_cross_validation(\n X=X,\n y=y,\n data=data_df,\n preprocess=\"zscore\",\n cv=cv_stratified,\n problem_type=\"regression\",\n model=\"linreg\",\n return_estimator=\"final\",\n scoring=\"neg_mean_absolute_error\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train a linear regression model without stratification on target.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv = KFold(n_splits=5, shuffle=False, random_state=None)\nscores, model = run_cross_validation(\n X=X,\n y=y,\n data=data_df,\n preprocess=\"zscore\",\n cv=cv,\n problem_type=\"regression\",\n model=\"linreg\",\n return_estimator=\"final\",\n scoring=\"neg_mean_absolute_error\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can compare the test score for model trained with and without\nstratification. We can combine the two outputs as ``pandas.DataFrame``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores_strat[\"model\"] = \"With stratification\"\nscores[\"model\"] = \"Without stratification\"\ndf_scores = scores_strat[[\"test_score\", \"model\"]]\ndf_scores = pd.concat([df_scores, scores[[\"test_score\", \"model\"]]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot a boxplot with test scores from both the models. We see here that\nthe test score is higher when CV splits were not stratified.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.set_style(\"darkgrid\")\nax = sns.boxplot(x=\"model\", y=\"test_score\", data=df_scores)\nax = sns.swarmplot(x=\"model\", y=\"test_score\", data=df_scores, color=\".25\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/6c774f4eca75f20ba6b46ff437a810e9/run_return_confounds.ipynb b/pr-preview/pr-276/_downloads/6c774f4eca75f20ba6b46ff437a810e9/run_return_confounds.ipynb new file mode 100644 index 000000000..599c98b9b --- /dev/null +++ b/pr-preview/pr-276/_downloads/6c774f4eca75f20ba6b46ff437a810e9/run_return_confounds.ipynb @@ -0,0 +1,302 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Return Confounds in Confound Removal\n\nIn most cases confound removal is a simple operation.\nYou regress out the confound from the features and only continue working with\nthese new confound removed features. This is also the default setting for\n``julearn``'s ``remove_confound`` step. But sometimes you want to work with the\nconfound even after removing it from the features. In this example, we\nwill discuss the options you have.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Sami Hamdan \n# License: AGPL\n\nfrom sklearn.datasets import load_diabetes # to load data\nfrom julearn.pipeline import PipelineCreator\nfrom julearn import run_cross_validation\nfrom julearn.inspect import preprocess\n\n# Load in the data\ndf_features, target = load_diabetes(return_X_y=True, as_frame=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we can have a look at our features.\nYou can see it includes Age, BMI, average blood pressure (bp) and 6 other\nmeasures from s1 to s6. Furthermore, it includes sex which will be considered\nas a confound in this example.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Features: \", df_features.head())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Second, we can have a look at the target.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Target: \", target.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can put both into one DataFrame:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data = df_features.copy()\ndata[\"target\"] = target" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the following we will explore different settings of confound removal\nusing ``julearn``'s pipeline functionalities.\n\n## Confound Removal Typical Use Case\nHere, we want to deconfound the features and not include the confound as a\nfeature into our last model. We will use the ``remove_confound`` step for this.\nThen we will use the ``pca`` step to reduce the dimensionality of the features.\nFinally, we will fit a linear regression model.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"regression\", apply_to=\"continuous\")\ncreator.add(\"confound_removal\")\ncreator.add(\"pca\")\ncreator.add(\"linreg\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we need to set the ``X_types`` argument of the ``run_cross_validation``\nfunction. This argument is a dictionary that maps the names of the different\ntypes of X to the features that belong to this type. In this example, we\nhave two types of features: `continuous` and `confound`. The `continuous`\nfeatures are the features that we want to deconfound and the `confound`\nfeatures are the features that we want to remove from the `continuous`.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "feature_names = list(df_features.drop(columns=\"sex\").columns)\nX_types = {\"continuous\": feature_names, \"confound\": \"sex\"}\n\nX = feature_names + [\"sex\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can run the cross validation and get the scores.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model = run_cross_validation(\n X=X,\n y=\"target\",\n X_types=X_types,\n data=data,\n model=creator,\n return_estimator=\"final\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use the ``preprocess`` method of the ``inspect`` module to inspect the\ntransformations steps of the returned estimator.\nBy providing a step name to the ``until`` argument of the\n``preprocess`` method we return the transformed X and y up to\nthe provided step (inclusive).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_deconfounded = preprocess(model, X=X, data=data, until=\"confound_removal\")\ndf_deconfounded.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see the confound ``sex`` was dropped and only the confound removed\nfeatures are used in the following PCA.\n\nBut what if you want to keep the confound after removal for\nother transformations?\n\nFor example, let's assume that you want to do a PCA on the confound removed\nfeature, but want to keep the confound for the actual modelling step.\nLet us have a closer look to the confound remover in order to understand\nhow we could achieve such a task:\n\n.. autoclass:: julearn.transformers.confound_remover.ConfoundRemover\n :noindex:\n :exclude-members: transform, get_support, get_feature_names_out,\n filter_columns, fit, fit_transform, get_apply_to,\n get_needed_types, get_params, set_output, set_params\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example, we will set the ``keep_confounds`` argument to True.\nThis will keep the confounds after confound removal.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"regression\", apply_to=\"continuous\")\ncreator.add(\"confound_removal\", keep_confounds=True)\ncreator.add(\"pca\")\ncreator.add(\"linreg\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can run the cross validation and get the scores.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model = run_cross_validation(\n X=X,\n y=\"target\",\n X_types=X_types,\n data=data,\n model=creator,\n return_estimator=\"final\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see this kept the confound variable ``sex`` in the data.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_deconfounded = preprocess(model, X=X, data=data, until=\"confound_removal\")\ndf_deconfounded.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Even after the PCA, the confound will still be present.\nThis is the case because by default transformers only transform continuous\nfeatures (including features without a specified type) and ignore confounds\nand categorical variables.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_transformed = preprocess(model, X=X, data=data)\ndf_transformed.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This means that the resulting Linear Regression can use the deconfounded\nfeatures together with the confound to predict the target. However, in the\npipeline creator, the model is only applied to the continuous features.\nThis means that the confound is not used in the model.\nHere we can see that the model is using 9 features.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(len(model.steps[-1][1].model.coef_))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, you can also use the confound as a normal feature after confound\nremoval.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"regression\", apply_to=\"continuous\")\ncreator.add(\"confound_removal\", keep_confounds=True)\ncreator.add(\"pca\")\ncreator.add(\"linreg\", apply_to=\"*\")\n\nscores, model = run_cross_validation(\n X=X,\n y=\"target\",\n X_types=X_types,\n data=data,\n model=creator,\n return_estimator=\"final\",\n)\nscores" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see the confound is now used in the linear regression model.\nThis is the case because we set the ``apply_to`` argument of the ``linreg``\nstep to ``*``. This means that the step will be applied to all features\n(including confounds and categorical variables).\nHere we can see that the model is using 10 features (9 deconfounded features\nand the confound).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(len(model.steps[-1][1].coef_))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/6ff8a2e7b9dbfdf57a938dd25776ebac/plot_confound_removal_classification.ipynb b/pr-preview/pr-276/_downloads/6ff8a2e7b9dbfdf57a938dd25776ebac/plot_confound_removal_classification.ipynb new file mode 100644 index 000000000..14298111a --- /dev/null +++ b/pr-preview/pr-276/_downloads/6ff8a2e7b9dbfdf57a938dd25776ebac/plot_confound_removal_classification.ipynb @@ -0,0 +1,331 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Confound Removal (model comparison)\n\nThis example uses the ``iris`` dataset, performs simple binary classification\nwith and without confound removal using a Random Forest classifier.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Shammi More \n# Federico Raimondo \n# Leonard Sasse \n# License: AGPL\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\nfrom seaborn import load_dataset\n\nfrom julearn import run_cross_validation\nfrom julearn.model_selection import StratifiedBootstrap\nfrom julearn.pipeline import PipelineCreator\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the iris data from seaborn.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = load_dataset(\"iris\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset has three kind of species. We will keep two to perform a binary\nclassification.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = df_iris[df_iris[\"species\"].isin([\"versicolor\", \"virginica\"])]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As features, we will use the sepal length, width and petal length and use\npetal width as confound.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"sepal_length\", \"sepal_width\", \"petal_length\"]\ny = \"species\"\nconfounds = [\"petal_width\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Doing hypothesis testing in ML is not that simple. If we were to use\nclassical frequentist statistics, we have the problem that using cross\nvalidation, the samples are not independent and the population (train + test)\nis always the same.\n\nIf we want to compare two models, an alternative is to contrast, for each\nfold, the performance gap between the models. If we combine that approach\nwith bootstrapping, we can then compare the confidence intervals of the\ndifference. If the 95% CI is above 0 (or below), we can claim that the models\nare different with p < 0.05.\n\nLet's use a bootstrap CV. In the interest of time we do 20 iterations,\nchange the number of bootstrap iterations to at least 2000 for a valid test.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "n_bootstrap = 20\nn_elements = len(df_iris)\ncv = StratifiedBootstrap(n_splits=n_bootstrap, test_size=0.3, random_state=42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we will train a model without performing confound removal on features.\nNote: confounds by default.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores_ncr = run_cross_validation(\n X=X,\n y=y,\n data=df_iris,\n model=\"rf\",\n cv=cv,\n problem_type=\"classification\",\n preprocess=\"zscore\",\n scoring=[\"accuracy\", \"roc_auc\"],\n return_estimator=\"cv\",\n seed=200,\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we train a model after performing confound removal on the features.\nNote: we initialize the CV again to use the same folds as before.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv = StratifiedBootstrap(n_splits=n_bootstrap, test_size=0.3, random_state=42)\n\n# In order to tell ``run_cross_validation`` which columns are confounds,\n# and which columns are features, we have to define the X_types:\nX_types = {\"features\": X, \"confound\": confounds}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now define a pipeline creator and add a confound removal step.\nThe pipeline creator should apply all the steps, by default, to the\nfeatures type.\n\nThe first step will zscore both features and confounds.\n\nThe second step will remove the confounds (type \"confound\") from the\n\"features\".\n\nFinally, a random forest will be trained.\nGiven the default ``apply_to`` in the pipeline creator,\nthe random forest will only be trained using \"features\".\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"classification\", apply_to=\"features\")\ncreator.add(\"zscore\", apply_to=[\"features\", \"confound\"])\ncreator.add(\"confound_removal\", apply_to=\"features\", confounds=\"confound\")\ncreator.add(\"rf\")\n\nscores_cr = run_cross_validation(\n X=X + confounds,\n y=y,\n data=df_iris,\n model=creator,\n cv=cv,\n X_types=X_types,\n scoring=[\"accuracy\", \"roc_auc\"],\n return_estimator=\"cv\",\n seed=200,\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can compare the accuracies. We can combine the two outputs as\n``pandas.DataFrame``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores_ncr[\"confounds\"] = \"Not Removed\"\nscores_cr[\"confounds\"] = \"Removed\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we convert the metrics to a column for easier seaborn plotting (convert\nto long format).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "index = [\"fold\", \"confounds\"]\nscorings = [\"test_accuracy\", \"test_roc_auc\"]\n\ndf_ncr_metrics = scores_ncr.set_index(index)[scorings].stack()\ndf_ncr_metrics.index.names = [\"fold\", \"confounds\", \"metric\"]\ndf_ncr_metrics.name = \"value\"\n\ndf_cr_metrics = scores_cr.set_index(index)[scorings].stack()\ndf_cr_metrics.index.names = [\"fold\", \"confounds\", \"metric\"]\ndf_cr_metrics.name = \"value\"\n\ndf_metrics = pd.concat((df_ncr_metrics, df_cr_metrics))\n\ndf_metrics = df_metrics.reset_index()\ndf_metrics.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And finally plot the results.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.catplot(\n x=\"confounds\", y=\"value\", col=\"metric\", data=df_metrics, kind=\"swarm\"\n)\nplt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While this plot allows us to see the mean performance values and compare\nthem, these samples are paired. In order to see if there is a systematic\ndifference, we need to check the distribution of differences between the\nthe models.\n\nFirst, we remove the column \"confounds\" from the index and make the difference\nbetween the metrics.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_cr_metrics = df_cr_metrics.reset_index().set_index([\"fold\", \"metric\"])\ndf_ncr_metrics = df_ncr_metrics.reset_index().set_index([\"fold\", \"metric\"])\n\ndf_diff_metrics = df_ncr_metrics[\"value\"] - df_cr_metrics[\"value\"]\ndf_diff_metrics = df_diff_metrics.reset_index()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can finally plot the difference, setting the whiskers of the box plot\nat 2.5 and 97.5 to see the 95% CI.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.boxplot(\n x=\"metric\", y=\"value\", data=df_diff_metrics.reset_index(), whis=[2.5, 97.5]\n)\nplt.axhline(0, color=\"k\", ls=\":\")\nplt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that while it seems that the accuracy and ROC AUC scores are\nhigher when confounds are not removed. We can not really claim (using this\ntest), that the models are different in terms of these metrics.\n\nMaybe the percentiles will be more accuracy with the proper amount of\nbootstrap iterations?\n\nBut the main point of confound removal is for interpretability. Let's see\nif there is a change in the feature importances.\n\nFirst, we need to collect the feature importances for each model, for each\nfold.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ncr_fi = []\nfor i_fold, estimator in enumerate(scores_ncr[\"estimator\"]):\n this_importances = pd.DataFrame(\n {\n \"feature\": [x.replace(\"_\", \" \") for x in X],\n \"importance\": estimator[\"rf\"].feature_importances_,\n \"confounds\": \"Not Removed\",\n \"fold\": i_fold,\n }\n )\n ncr_fi.append(this_importances)\nncr_fi = pd.concat(ncr_fi)\n\ncr_fi = []\nfor i_fold, estimator in enumerate(scores_cr[\"estimator\"]):\n this_importances = pd.DataFrame(\n {\n \"feature\": [x.replace(\"_\", \" \") for x in X],\n \"importance\": estimator[\"rf\"].model.feature_importances_,\n \"confounds\": \"Removed\",\n \"fold\": i_fold,\n }\n )\n cr_fi.append(this_importances)\ncr_fi = pd.concat(cr_fi)\n\nfeature_importance = pd.concat([cr_fi, ncr_fi])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now plot the importances.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.catplot(\n x=\"feature\",\n y=\"importance\",\n hue=\"confounds\",\n dodge=True,\n data=feature_importance,\n kind=\"swarm\",\n s=3,\n)\nplt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And check the differences in importances. We can now see that there is\na difference in importances.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "diff_fi = (\n cr_fi.set_index([\"feature\", \"fold\"])[\"importance\"]\n - ncr_fi.set_index([\"feature\", \"fold\"])[\"importance\"]\n)\nsns.boxplot(\n x=\"importance\", y=\"feature\", data=diff_fi.reset_index(), whis=[2.5, 97.5]\n)\nplt.axvline(0, color=\"k\", ls=\":\")\nplt.tight_layout()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/7f7a2eaac0e31ab1ac030855137b6e21/run_binary_inspect_folds.ipynb b/pr-preview/pr-276/_downloads/7f7a2eaac0e31ab1ac030855137b6e21/run_binary_inspect_folds.ipynb new file mode 100644 index 000000000..ac21d7e58 --- /dev/null +++ b/pr-preview/pr-276/_downloads/7f7a2eaac0e31ab1ac030855137b6e21/run_binary_inspect_folds.ipynb @@ -0,0 +1,137 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Inspecting the fold-wise predictions\n\nThis example uses the ``iris`` dataset and performs a simple binary\nclassification using a Support Vector Machine classifier.\n\nWe later inspect the predictions of the model for each fold.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# License: AGPL\n\nfrom seaborn import load_dataset\n\nfrom sklearn.model_selection import RepeatedStratifiedKFold, ShuffleSplit\n\nfrom julearn import run_cross_validation\nfrom julearn.pipeline import PipelineCreator\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = load_dataset(\"iris\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset has three kind of species. We will keep two to perform a binary\nclassification.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = df_iris[df_iris[\"species\"].isin([\"versicolor\", \"virginica\"])]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As features, we will use the sepal length, width and petal length.\nWe will try to predict the species.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"sepal_length\", \"sepal_width\", \"petal_length\"]\ny = \"species\"\nX_types = {\"continuous\": X}\n\ncreator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\")\n\ncv = RepeatedStratifiedKFold(n_splits=5, n_repeats=4, random_state=200)\n\nscores, model, inspector = run_cross_validation(\n X=X,\n y=y,\n data=df_iris,\n model=creator,\n return_inspector=True,\n cv=cv,\n)\n\nprint(scores)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now inspect the predictions of the model for each fold.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv_predictions = inspector.folds.predict()\n\nprint(cv_predictions)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "inspector.folds[0].model" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/877ac2e363ae97702387f111c911090b/run_combine_pandas.ipynb b/pr-preview/pr-276/_downloads/877ac2e363ae97702387f111c911090b/run_combine_pandas.ipynb new file mode 100644 index 000000000..2fd1f7a4b --- /dev/null +++ b/pr-preview/pr-276/_downloads/877ac2e363ae97702387f111c911090b/run_combine_pandas.ipynb @@ -0,0 +1,266 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Working with ``pandas``\n\nThis example uses the ``fmri`` dataset to transform and combine data in order\nto prepare it to be used by ``julearn``.\n\n\n## References\n\n Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of\n cognitive control in context-dependent decision-making. Cerebral Cortex.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n#\n# License: AGPL\n\nfrom seaborn import load_dataset\nimport pandas as pd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One of the key elements that make ``julearn`` easy to use, is the possibility\nto work directly with ``pandas.DataFrame``, similar to MS Excel spreadsheets\nor csv files.\n\nIdeally, we will have everything tabulated and organised for ``julearn``, but\nit might not be your case. You might have some files with the fMRI values, some\nothers with demographics, some other with diagnostic metrics or behavioral\nresults.\n\nYou need to prepare these files for ``julearn``.\n\nOne option is to manually edit the files and make sure that everything is\nready to do some machine-learning. However, this is error-prone.\n\nFortunately, `pandas`_ provides several tools to deal with this task.\n\nThis example is a collection of some of these useful methods.\n\nLet's start with the ``fmri`` dataset.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = load_dataset(\"fmri\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see what this dataset has.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From long to wide format\nWe have seen this in other examples. If we want to use julearn, each feature\nmust be a columns. In order to use the signals from different regions as\n~~~~~~~~~~~~~~~~~~~~~~~~\nfeatures, we need to convert this dataframe from the long format to the wide\nformat.\n\nWe will use the ``pivot`` method.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.pivot(\n index=[\"subject\", \"timepoint\", \"event\"], columns=\"region\", values=\"signal\"\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This method reshapes the table, keeping the specified elements as index,\ncolumns and values.\n\nIn our case, the values are extracted from the *signal* column. The columns\nfrom the *region* column and *subject*, *timepoint* and *event* becomes the\nindex.\n\nThe index is what identifies each sample. As a rule, the index can't be\nduplicated. If each subject has more than one timepoint, and each timepoint\nhas more than one event, then these 3 elements are needed as the index.\n\nLet's see what we have here:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now this is in the format we want. However, in order to access the index\nas columns ``df_fmri[\"subject\"]`` we need to reset the index.\n\nCheck the subtle but important difference:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.reset_index()\ndf_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Merging or joining ``DataFrame``\n\nSo now we have our fMRI data tabulated for ``julearn``. However, it might be\nthe case that we have some important information in another file. For example,\nthe subjects' age and the place where they were scanned.\n\nFor the purpose of the example, we'll create the dataframe here.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "metadata = {\n \"subject\": [f\"s{i}\" for i in range(14)],\n \"age\": [23, 21, 31, 29, 43, 23, 43, 28, 48, 29, 35, 23, 34, 25],\n \"scanner\": [\"a\"] * 6 + [\"b\"] * 8,\n}\ndf_meta = pd.DataFrame(metadata)\ndf_meta" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use the ``join`` method. This method will join the two dataframes,\nmatching elements by the *index*.\n\nIn this case, the matching element (or index) will be the column ``subject``.\nWe need to set the index in each dataframe before join.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.set_index(\"subject\")\ndf_meta = df_meta.set_index(\"subject\")\ndf_fmri = df_fmri.join(df_meta)\ndf_fmri" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, let's reset the index and have it ready for ``julearn``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.reset_index()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can use, for example, *age* and *scanner* as confounds.\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Reshaping data frames (more complex)\nLets suppose that our prediction target is now the *age* and we want to use\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nas features the frontal and parietal value during each event. For this\npurpose, we need to convert the event values into columns. There are two\nevents: *cue* and *stim*. So this will result in 4 columns.\n\nWe will still use the pivot, but in this case, we will have two values:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.pivot(\n index=[\"subject\", \"timepoint\", \"age\", \"scanner\"],\n columns=\"event\",\n values=[\"frontal\", \"parietal\"],\n)\ndf_fmri" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the column names are combinations of the values in the *event* column\nand the previous *frontal* and *parietal* columns, it is now a multi-level\ncolumn name.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri.columns" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following trick will join the different levels using an underscore (*_*)\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri.columns = [\"_\".join(x) for x in df_fmri.columns]\ndf_fmri" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have finally the information we want. We can now reset the index.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.reset_index()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/8f1fb433f30609bc682628827dedb66e/run_apply_to_target.ipynb b/pr-preview/pr-276/_downloads/8f1fb433f30609bc682628827dedb66e/run_apply_to_target.ipynb new file mode 100644 index 000000000..9bb65d8c1 --- /dev/null +++ b/pr-preview/pr-276/_downloads/8f1fb433f30609bc682628827dedb66e/run_apply_to_target.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Transforming target variable with z-score\n\nThis example uses the sklearn ``diabetes`` regression dataset, and transforms the\ntarget variable, in this case, using z-score. Then, we perform a regression\nanalysis using Ridge Regression model.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Lya K. Paas Oliveros \n# Sami Hamdan \n#\n# License: AGPL\n\nimport pandas as pd\nfrom sklearn.datasets import load_diabetes\nfrom sklearn.model_selection import train_test_split\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging\n\nfrom julearn.pipeline import PipelineCreator, TargetPipelineCreator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the diabetes dataset from ``sklearn`` as a ``pandas.DataFrame``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "features, target = load_diabetes(return_X_y=True, as_frame=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dataset contains ten variables age, sex, body mass index, average blood\npressure, and six blood serum measurements (s1-s6) diabetes patients and\na quantitative measure of disease progression one year after baseline which\nis the target we are interested in predicting.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Features: \\n\", features.head())\nprint(\"Target: \\n\", target.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's combine features and target together in one dataframe and define X\nand y.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_diabetes = pd.concat([features, target], axis=1)\n\nX = [\"age\", \"sex\", \"bmi\", \"bp\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\"]\ny = \"target\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Split the dataset into train and test.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's create the model. Since we will be transforming the target variable\nwe will first need to create a TargetPipelineCreator for this.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "target_creator = TargetPipelineCreator()\ntarget_creator.add(\"zscore\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can create the pipeline using a PipelineCreator.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"regression\")\ncreator.add(target_creator, apply_to=\"target\")\ncreator.add(\"ridge\")\n\nscores, model = run_cross_validation(\n X=X,\n y=y,\n data=train_diabetes,\n model=creator,\n return_estimator=\"final\",\n scoring=\"neg_mean_absolute_error\",\n)\n\nprint(scores.head(5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mean value of mean absolute error across CV\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(scores[\"test_score\"].mean() * -1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/9be317669764567291e95e7a41efa962/run_custom_scorers_regression.py b/pr-preview/pr-276/_downloads/9be317669764567291e95e7a41efa962/run_custom_scorers_regression.py new file mode 100644 index 000000000..6c53cf9d0 --- /dev/null +++ b/pr-preview/pr-276/_downloads/9be317669764567291e95e7a41efa962/run_custom_scorers_regression.py @@ -0,0 +1,117 @@ +""" +Custom Scoring Function for Regression +====================================== + +This example uses the ``diabetes`` data from ``sklearn datasets`` and performs +a regression analysis using a Ridge Regression model. As scorers, it uses +``scikit-learn``, ``julearn`` and a custom metric defined by the user. + +""" +# Authors: Shammi More +# Federico Raimondo +# License: AGPL + +import pandas as pd +import scipy +from sklearn.datasets import load_diabetes + +from sklearn.metrics import make_scorer +from julearn.scoring import register_scorer + +from julearn import run_cross_validation +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. +features, target = load_diabetes(return_X_y=True, as_frame=True) + +############################################################################### +# Dataset contains ten variables age, sex, body mass index, average blood +# pressure, and six blood serum measurements (s1-s6) diabetes patients and +# a quantitative measure of disease progression one year after baseline which +# is the target we are interested in predicting. +print("Features: \n", features.head()) +print("Target: \n", target.describe()) + +############################################################################### +# Let's combine features and target together in one dataframe and define X +# and y. +data_diabetes = pd.concat([features, target], axis=1) # type: ignore + +X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] +y = "target" + +############################################################################### +# Train a ridge regression model on train dataset and use mean absolute error +# for scoring. +scores, model = run_cross_validation( + X=X, + y=y, + data=data_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring="neg_mean_absolute_error", +) + +############################################################################### +# The scores dataframe has all the values for each CV split. +scores.head() + +############################################################################### +# Mean value of mean absolute error across CV. +print(scores["test_score"].mean() * -1) + +############################################################################### +# Now do the same thing, but use mean absolute error and Pearson product-moment +# correlation coefficient (squared) as scoring functions. +scores, model = run_cross_validation( + X=X, + y=y, + data=data_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring=["neg_mean_absolute_error", "r2_corr"], +) + +############################################################################### +# Now the scores dataframe has all the values for each CV split, but two scores +# unders the column names ``"test_neg_mean_absolute_error"`` and +# ``"test_r2_corr"``. +print(scores[["test_neg_mean_absolute_error", "test_r2_corr"]].mean()) + +############################################################################### +# If we want to define a custom scoring metric, we need to define a function +# that takes the predicted and the actual values as input and returns a value. +# In this case, we want to compute Pearson correlation coefficient (r). + + +def pearson_scorer(y_true, y_pred): + return scipy.stats.pearsonr(y_true.squeeze(), y_pred.squeeze())[0] + + +############################################################################### +# Before using it, we need to convert it to a ``sklearn scorer`` and register it +# with ``julearn``. + +register_scorer(scorer_name="pearsonr", scorer=make_scorer(pearson_scorer)) + +############################################################################### +# Now we can use it as another scoring metric. +scores, model = run_cross_validation( + X=X, + y=y, + data=data_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring=["neg_mean_absolute_error", "r2_corr", "pearsonr"], +) diff --git a/pr-preview/pr-276/_downloads/9bec4977b6944e6a410f67726ba6437f/run_return_confounds.py b/pr-preview/pr-276/_downloads/9bec4977b6944e6a410f67726ba6437f/run_return_confounds.py new file mode 100644 index 000000000..3749074a5 --- /dev/null +++ b/pr-preview/pr-276/_downloads/9bec4977b6944e6a410f67726ba6437f/run_return_confounds.py @@ -0,0 +1,175 @@ +""" +Return Confounds in Confound Removal +==================================== + +In most cases confound removal is a simple operation. +You regress out the confound from the features and only continue working with +these new confound removed features. This is also the default setting for +``julearn``'s ``remove_confound`` step. But sometimes you want to work with the +confound even after removing it from the features. In this example, we +will discuss the options you have. + +.. include:: ../../links.inc +""" +# Authors: Sami Hamdan +# License: AGPL + +from sklearn.datasets import load_diabetes # to load data +from julearn.pipeline import PipelineCreator +from julearn import run_cross_validation +from julearn.inspect import preprocess + +# Load in the data +df_features, target = load_diabetes(return_X_y=True, as_frame=True) + +############################################################################### +# First, we can have a look at our features. +# You can see it includes Age, BMI, average blood pressure (bp) and 6 other +# measures from s1 to s6. Furthermore, it includes sex which will be considered +# as a confound in this example. +print("Features: ", df_features.head()) + +############################################################################### +# Second, we can have a look at the target. +print("Target: ", target.describe()) + +############################################################################### +# Now, we can put both into one DataFrame: +data = df_features.copy() +data["target"] = target + +############################################################################### +# In the following we will explore different settings of confound removal +# using ``julearn``'s pipeline functionalities. +# +# Confound Removal Typical Use Case +# --------------------------------- +# Here, we want to deconfound the features and not include the confound as a +# feature into our last model. We will use the ``remove_confound`` step for this. +# Then we will use the ``pca`` step to reduce the dimensionality of the features. +# Finally, we will fit a linear regression model. + +creator = PipelineCreator(problem_type="regression", apply_to="continuous") +creator.add("confound_removal") +creator.add("pca") +creator.add("linreg") + +############################################################################### +# Now we need to set the ``X_types`` argument of the ``run_cross_validation`` +# function. This argument is a dictionary that maps the names of the different +# types of X to the features that belong to this type. In this example, we +# have two types of features: `continuous` and `confound`. The `continuous` +# features are the features that we want to deconfound and the `confound` +# features are the features that we want to remove from the `continuous`. + +feature_names = list(df_features.drop(columns="sex").columns) +X_types = {"continuous": feature_names, "confound": "sex"} + +X = feature_names + ["sex"] + +############################################################################### +# Now we can run the cross validation and get the scores. +scores, model = run_cross_validation( + X=X, + y="target", + X_types=X_types, + data=data, + model=creator, + return_estimator="final", +) + +############################################################################### +# We can use the ``preprocess`` method of the ``inspect`` module to inspect the +# transformations steps of the returned estimator. +# By providing a step name to the ``until`` argument of the +# ``preprocess`` method we return the transformed X and y up to +# the provided step (inclusive). +df_deconfounded = preprocess(model, X=X, data=data, until="confound_removal") +df_deconfounded.head() + +############################################################################### +# As you can see the confound ``sex`` was dropped and only the confound removed +# features are used in the following PCA. +# +# But what if you want to keep the confound after removal for +# other transformations? +# +# For example, let's assume that you want to do a PCA on the confound removed +# feature, but want to keep the confound for the actual modelling step. +# Let us have a closer look to the confound remover in order to understand +# how we could achieve such a task: +# +# .. autoclass:: julearn.transformers.confound_remover.ConfoundRemover +# :noindex: +# :exclude-members: transform, get_support, get_feature_names_out, +# filter_columns, fit, fit_transform, get_apply_to, +# get_needed_types, get_params, set_output, set_params + +############################################################################### +# In this example, we will set the ``keep_confounds`` argument to True. +# This will keep the confounds after confound removal. + +creator = PipelineCreator(problem_type="regression", apply_to="continuous") +creator.add("confound_removal", keep_confounds=True) +creator.add("pca") +creator.add("linreg") + +############################################################################### +# Now we can run the cross validation and get the scores. +scores, model = run_cross_validation( + X=X, + y="target", + X_types=X_types, + data=data, + model=creator, + return_estimator="final", +) + +############################################################################### +# As you can see this kept the confound variable ``sex`` in the data. +df_deconfounded = preprocess(model, X=X, data=data, until="confound_removal") +df_deconfounded.head() + +############################################################################### +# Even after the PCA, the confound will still be present. +# This is the case because by default transformers only transform continuous +# features (including features without a specified type) and ignore confounds +# and categorical variables. +df_transformed = preprocess(model, X=X, data=data) +df_transformed.head() + +############################################################################### +# This means that the resulting Linear Regression can use the deconfounded +# features together with the confound to predict the target. However, in the +# pipeline creator, the model is only applied to the continuous features. +# This means that the confound is not used in the model. +# Here we can see that the model is using 9 features. + +print(len(model.steps[-1][1].model.coef_)) + +############################################################################### +# Lastly, you can also use the confound as a normal feature after confound +# removal. +creator = PipelineCreator(problem_type="regression", apply_to="continuous") +creator.add("confound_removal", keep_confounds=True) +creator.add("pca") +creator.add("linreg", apply_to="*") + +scores, model = run_cross_validation( + X=X, + y="target", + X_types=X_types, + data=data, + model=creator, + return_estimator="final", +) +scores + +############################################################################### +# As you can see the confound is now used in the linear regression model. +# This is the case because we set the ``apply_to`` argument of the ``linreg`` +# step to ``*``. This means that the step will be applied to all features +# (including confounds and categorical variables). +# Here we can see that the model is using 10 features (9 deconfounded features +# and the confound). +print(len(model.steps[-1][1].coef_)) diff --git a/pr-preview/pr-276/_downloads/b04556cf71bbccfddd9a00ccfa5c2c20/run_grouped_cv.ipynb b/pr-preview/pr-276/_downloads/b04556cf71bbccfddd9a00ccfa5c2c20/run_grouped_cv.ipynb new file mode 100644 index 000000000..3647dea26 --- /dev/null +++ b/pr-preview/pr-276/_downloads/b04556cf71bbccfddd9a00ccfa5c2c20/run_grouped_cv.ipynb @@ -0,0 +1,223 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Grouped CV\n\nThis example uses the ``fMRI`` dataset and performs GroupKFold\nCross-Validation for classification using Random Forest Classifier.\n\n## References\n\n Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of\n cognitive control in context-dependent decision-making. Cerebral Cortex.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# Shammi More \n# Kimia Nazarzadeh \n# License: AGPL\n\n# Importing the necessary Python libraries\nimport numpy as np\n\nfrom seaborn import load_dataset\nfrom sklearn.model_selection import GroupKFold, StratifiedGroupKFold\n\nfrom julearn.utils import configure_logging\nfrom julearn import run_cross_validation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dealing with Cross-Validation techniques\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = load_dataset(\"fmri\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, lets get some information on what the dataset has:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(df_fmri.head())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From this information, we can infer that it is an fMRI study in which there\nwere several subjects, timepoints, events and signal extracted from several\nbrain regions.\n\nLets check how many kinds of each we have.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(df_fmri[\"event\"].unique())\nprint(df_fmri[\"region\"].unique())\nprint(sorted(df_fmri[\"timepoint\"].unique()))\nprint(df_fmri[\"subject\"].unique())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have data from parietal and frontal regions during 2 types of events\n(*cue* and *stim*) during 18 timepoints and for 14 subjects.\nLets see how many samples we have for each condition\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(df_fmri.groupby([\"subject\", \"timepoint\", \"event\", \"region\"]).count())\nprint(\n np.unique(\n df_fmri.groupby([\"subject\", \"timepoint\", \"event\", \"region\"])\n .count()\n .values\n )\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have exactly one value per condition.\n\nLets try to build a model, that uses parietal and frontal signal to predicts\nwhether the event was a *cue* or a *stim*.\n\nFirst we define our X and y variables.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"parietal\", \"frontal\"]\ny = \"event\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order for this to work, both *parietal* and *frontal* must be columns.\nWe need to *pivot* the table.\n\nThe values of *region* will be the columns. The column *signal* will be the\nvalues. And the columns *subject*, *timepoint* and *event* will be the index\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.pivot(\n index=[\"subject\", \"timepoint\", \"event\"], columns=\"region\", values=\"signal\"\n)\n\ndf_fmri = df_fmri.reset_index()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we want to zscore all the features and then train a Support Vector\nMachine classifier.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n preprocess=\"zscore\",\n model=\"rf\",\n problem_type=\"classification\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train classification model with stratification on data\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv_stratified = StratifiedGroupKFold(n_splits=2)\nscores, model = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n groups=\"subject\",\n model=\"rf\",\n problem_type=\"classification\",\n cv=cv_stratified,\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train classification model without stratification on data\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv = GroupKFold(n_splits=2)\nscores, model = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n groups=\"subject\",\n model=\"rf\",\n problem_type=\"classification\",\n cv=cv,\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/b72a553cc1a921289d38c9c7f22a453c/plot_inspect_random_forest.ipynb b/pr-preview/pr-276/_downloads/b72a553cc1a921289d38c9c7f22a453c/plot_inspect_random_forest.ipynb new file mode 100644 index 000000000..6841d3905 --- /dev/null +++ b/pr-preview/pr-276/_downloads/b72a553cc1a921289d38c9c7f22a453c/plot_inspect_random_forest.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Inspecting Random Forest models\n\nThis example uses the ``iris`` dataset, performs simple binary classification\nusing a Random Forest classifier and analyse the model.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# License: AGPL\n\nimport pandas as pd\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom seaborn import load_dataset\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Random Forest variable importance\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = load_dataset(\"iris\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset has three kind of species. We will keep two to perform a binary\nclassification.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = df_iris[df_iris[\"species\"].isin([\"versicolor\", \"virginica\"])]\n\nX = [\"sepal_length\", \"sepal_width\", \"petal_length\"]\ny = \"species\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use a Random Forest classifier. By setting\n`return_estimator='final'`, the :func:`.run_cross_validation` function\nreturns the estimator fitted with all the data.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model_iris = run_cross_validation(\n X=X,\n y=y,\n data=df_iris,\n model=\"rf\",\n preprocess=\"zscore\",\n problem_type=\"classification\",\n return_estimator=\"final\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This type of classifier has an internal variable that can inform us on how\n*important* is each of the features. Caution: read the proper ``scikit-learn``\ndocumentation :class:`~sklearn.ensemble.RandomForestClassifier` to understand\nhow this learning algorithm works.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "rf = model_iris[\"rf\"]\n\nto_plot = pd.DataFrame(\n {\n \"variable\": [x.replace(\"_\", \" \") for x in X],\n \"importance\": rf.feature_importances_,\n }\n)\n\nfig, ax = plt.subplots(1, 1, figsize=(6, 4))\nsns.barplot(x=\"importance\", y=\"variable\", data=to_plot, ax=ax)\nax.set_title(\"Variable Importances for Random Forest Classifier\")\nfig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, some reviewers (including us), might wander about the\nvariability of the importance of these features. In the previous example\nall the feature importances were obtained by fitting on the entire dataset,\nwhile the performance was estimated using cross validation.\n\nBy specifying `return_estimator='cv'`, we can get, for each fold, the fitted\nestimator.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores = run_cross_validation(\n X=X,\n y=y,\n data=df_iris,\n model=\"rf\",\n preprocess=\"zscore\",\n problem_type=\"classification\",\n return_estimator=\"cv\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can obtain the feature importance for each estimator (CV fold).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "to_plot = []\nfor i_fold, estimator in enumerate(scores[\"estimator\"]):\n this_importances = pd.DataFrame(\n {\n \"variable\": [x.replace(\"_\", \" \") for x in X],\n \"importance\": estimator[\"rf\"].feature_importances_,\n \"fold\": i_fold,\n }\n )\n to_plot.append(this_importances)\n\nto_plot = pd.concat(to_plot)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we can plot the variable importances for each fold.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(6, 4))\nsns.swarmplot(x=\"importance\", y=\"variable\", data=to_plot, ax=ax)\nax.set_title(\n \"Distribution of variable Importances for Random Forest \"\n \"Classifier across folds\"\n)\nfig.tight_layout()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/b77fe0c98c3f48e1a6f571944c788ca2/run_hyperparameter_multiple_grids.py b/pr-preview/pr-276/_downloads/b77fe0c98c3f48e1a6f571944c788ca2/run_hyperparameter_multiple_grids.py new file mode 100644 index 000000000..db656be6f --- /dev/null +++ b/pr-preview/pr-276/_downloads/b77fe0c98c3f48e1a6f571944c788ca2/run_hyperparameter_multiple_grids.py @@ -0,0 +1,102 @@ +""" +Tuning Multiple Hyperparameters Grids +===================================== + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier while tuning multiple hyperparameters +grids at the same time. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# License: AGPL + +import numpy as np +from seaborn import load_dataset + +from julearn import run_cross_validation +from julearn.utils import configure_logging +from julearn.pipeline import PipelineCreator + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Set the random seed to always have the same example. +np.random.seed(42) + +############################################################################### +# Load the dataset. +df_fmri = load_dataset("fmri") +df_fmri.head() + +############################################################################### +# Set the dataframe in the right format. +df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" +) + +df_fmri = df_fmri.reset_index() +df_fmri.head() + +############################################################################### +# Lets do a first attempt and use a linear SVM with the default parameters. + +X = ["frontal", "parietal"] +y = "event" + +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm", kernel="linear") + +scores = run_cross_validation(X=X, y=y, data=df_fmri, model=creator) + +print(scores["test_score"].mean()) + +############################################################################### +# Now let's tune a bit this SVM. We will use a grid search to tune the +# regularization parameter ``C`` and the kernel. We will also tune the ``gamma``. +# But since the ``gamma`` is only used for the rbf kernel, we will use a +# different grid for the ``"rbf"`` kernel. +# +# To specify two different sets of parameters for the same step, we can +# explicitly specify the name of the step. This is done by passing the +# ``name`` parameter to the ``add`` method. +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm", kernel="linear", C=[0.01, 0.1], name="svm") +creator.add( + "svm", + kernel="rbf", + C=[0.01, 0.1], + gamma=["scale", "auto", 1e-2, 1e-3], + name="svm", +) + +search_params = { + "kind": "grid", + "cv": 2, # to speed up the example +} + +scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", +) + +print(scores["test_score"].mean()) + +############################################################################### +# It seems that we might have found a better model, but which one is it? +print(estimator.best_params_) +print(estimator.best_estimator_["svm"]._gamma) diff --git a/pr-preview/pr-276/_downloads/ba53d606479a3cb55560049f69488102/run_hyperparameter_tuning_bayessearch.ipynb b/pr-preview/pr-276/_downloads/ba53d606479a3cb55560049f69488102/run_hyperparameter_tuning_bayessearch.ipynb new file mode 100644 index 000000000..63f7666d6 --- /dev/null +++ b/pr-preview/pr-276/_downloads/ba53d606479a3cb55560049f69488102/run_hyperparameter_tuning_bayessearch.ipynb @@ -0,0 +1,151 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Tuning Hyperparameters using Bayesian Search\n\nThis example uses the ``fmri`` dataset, performs simple binary classification\nusing a Support Vector Machine classifier and analyzes the model.\n\n## References\n\n Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of\n cognitive control in context-dependent decision-making. Cerebral Cortex.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# License: AGPL\n\nimport numpy as np\nfrom seaborn import load_dataset\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging, logger\nfrom julearn.pipeline import PipelineCreator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the random seed to always have the same example.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "np.random.seed(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the dataset.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = load_dataset(\"fmri\")\ndf_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the dataframe in the right format.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.pivot(\n index=[\"subject\", \"timepoint\", \"event\"], columns=\"region\", values=\"signal\"\n)\n\ndf_fmri = df_fmri.reset_index()\ndf_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Following the hyperparamter tuning example, we will now use a Bayesian\nsearch to find the best hyperparameters for the SVM model.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"frontal\", \"parietal\"]\ny = \"event\"\n\ncreator1 = PipelineCreator(problem_type=\"classification\")\ncreator1.add(\"zscore\")\ncreator1.add(\n \"svm\",\n kernel=[\"linear\"],\n C=(1e-6, 1e3, \"log-uniform\"),\n)\n\ncreator2 = PipelineCreator(problem_type=\"classification\")\ncreator2.add(\"zscore\")\ncreator2.add(\n \"svm\",\n kernel=[\"rbf\"],\n C=(1e-6, 1e3, \"log-uniform\"),\n gamma=(1e-6, 1e1, \"log-uniform\"),\n)\n\nsearch_params = {\n \"kind\": \"bayes\",\n \"cv\": 2, # to speed up the example\n \"n_iter\": 10, # 10 iterations of bayesian search to speed up example\n}\n\n\nscores, estimator = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n model=[creator1, creator2],\n cv=2, # to speed up the example\n search_params=search_params,\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It seems that we might have found a better model, but which one is it?\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(estimator.best_params_)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/bd5abb8fbda27a3ede92c46be10d52d3/plot_inspect_random_forest.py b/pr-preview/pr-276/_downloads/bd5abb8fbda27a3ede92c46be10d52d3/plot_inspect_random_forest.py new file mode 100644 index 000000000..ddfeb70b4 --- /dev/null +++ b/pr-preview/pr-276/_downloads/bd5abb8fbda27a3ede92c46be10d52d3/plot_inspect_random_forest.py @@ -0,0 +1,118 @@ +""" +Inspecting Random Forest models +=============================== + +This example uses the ``iris`` dataset, performs simple binary classification +using a Random Forest classifier and analyse the model. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# License: AGPL + +import pandas as pd + +import matplotlib.pyplot as plt +import seaborn as sns +from seaborn import load_dataset + +from julearn import run_cross_validation +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Random Forest variable importance +# --------------------------------- + +df_iris = load_dataset("iris") + +############################################################################### +# The dataset has three kind of species. We will keep two to perform a binary +# classification. +df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + +X = ["sepal_length", "sepal_width", "petal_length"] +y = "species" + + +############################################################################### +# We will use a Random Forest classifier. By setting +# `return_estimator='final'`, the :func:`.run_cross_validation` function +# returns the estimator fitted with all the data. + +scores, model_iris = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="rf", + preprocess="zscore", + problem_type="classification", + return_estimator="final", +) + +############################################################################### +# This type of classifier has an internal variable that can inform us on how +# *important* is each of the features. Caution: read the proper ``scikit-learn`` +# documentation :class:`~sklearn.ensemble.RandomForestClassifier` to understand +# how this learning algorithm works. +rf = model_iris["rf"] + +to_plot = pd.DataFrame( + { + "variable": [x.replace("_", " ") for x in X], + "importance": rf.feature_importances_, + } +) + +fig, ax = plt.subplots(1, 1, figsize=(6, 4)) +sns.barplot(x="importance", y="variable", data=to_plot, ax=ax) +ax.set_title("Variable Importances for Random Forest Classifier") +fig.tight_layout() + +############################################################################### +# However, some reviewers (including us), might wander about the +# variability of the importance of these features. In the previous example +# all the feature importances were obtained by fitting on the entire dataset, +# while the performance was estimated using cross validation. +# +# By specifying `return_estimator='cv'`, we can get, for each fold, the fitted +# estimator. + +scores = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="rf", + preprocess="zscore", + problem_type="classification", + return_estimator="cv", +) + +############################################################################### +# Now we can obtain the feature importance for each estimator (CV fold). +to_plot = [] +for i_fold, estimator in enumerate(scores["estimator"]): + this_importances = pd.DataFrame( + { + "variable": [x.replace("_", " ") for x in X], + "importance": estimator["rf"].feature_importances_, + "fold": i_fold, + } + ) + to_plot.append(this_importances) + +to_plot = pd.concat(to_plot) + +############################################################################### +# Finally, we can plot the variable importances for each fold. + +fig, ax = plt.subplots(1, 1, figsize=(6, 4)) +sns.swarmplot(x="importance", y="variable", data=to_plot, ax=ax) +ax.set_title( + "Distribution of variable Importances for Random Forest " + "Classifier across folds" +) +fig.tight_layout() diff --git a/pr-preview/pr-276/_downloads/c1f7d27c7acc6f2f34834c7c2732f95d/run_binary_inspect_folds.py b/pr-preview/pr-276/_downloads/c1f7d27c7acc6f2f34834c7c2732f95d/run_binary_inspect_folds.py new file mode 100644 index 000000000..a95aea6e8 --- /dev/null +++ b/pr-preview/pr-276/_downloads/c1f7d27c7acc6f2f34834c7c2732f95d/run_binary_inspect_folds.py @@ -0,0 +1,68 @@ +""" +Inspecting the fold-wise predictions +==================================== + +This example uses the ``iris`` dataset and performs a simple binary +classification using a Support Vector Machine classifier. + +We later inspect the predictions of the model for each fold. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# License: AGPL + +from seaborn import load_dataset + +from sklearn.model_selection import RepeatedStratifiedKFold, ShuffleSplit + +from julearn import run_cross_validation +from julearn.pipeline import PipelineCreator +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +df_iris = load_dataset("iris") + +############################################################################### +# The dataset has three kind of species. We will keep two to perform a binary +# classification. +df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + +############################################################################### +# As features, we will use the sepal length, width and petal length. +# We will try to predict the species. + +X = ["sepal_length", "sepal_width", "petal_length"] +y = "species" +X_types = {"continuous": X} + +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm") + +cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=4, random_state=200) + +scores, model, inspector = run_cross_validation( + X=X, + y=y, + data=df_iris, + model=creator, + return_inspector=True, + cv=cv, +) + +print(scores) + +############################################################################### +# We can now inspect the predictions of the model for each fold. + +cv_predictions = inspector.folds.predict() + +print(cv_predictions) + +############################################################################### +inspector.folds[0].model diff --git a/pr-preview/pr-276/_downloads/c49d88d09ef11db04561dd7cf170906f/plot_simple_model_comparison.ipynb b/pr-preview/pr-276/_downloads/c49d88d09ef11db04561dd7cf170906f/plot_simple_model_comparison.ipynb new file mode 100644 index 000000000..ba688d41f --- /dev/null +++ b/pr-preview/pr-276/_downloads/c49d88d09ef11db04561dd7cf170906f/plot_simple_model_comparison.ipynb @@ -0,0 +1,277 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Simple Model Comparison\n\nThis example uses the ``iris`` dataset and performs binary classifications\nusing different models. At the end, it compares the performance of the models\nusing different scoring functions and performs a statistical test to assess\nwhether the difference in performance is significant.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# License: AGPL\n\nfrom seaborn import load_dataset\nfrom sklearn.model_selection import RepeatedStratifiedKFold\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging\nfrom julearn.stats.corrected_ttest import corrected_ttest" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = load_dataset(\"iris\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset has three kind of species. We will keep two to perform a binary\nclassification.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = df_iris[df_iris[\"species\"].isin([\"versicolor\", \"virginica\"])]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As features, we will use the sepal length, width and petal length.\nWe will try to predict the species.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"sepal_length\", \"sepal_width\", \"petal_length\"]\ny = \"species\"\nscores = run_cross_validation(\n X=X,\n y=y,\n data=df_iris,\n model=\"svm\",\n problem_type=\"classification\",\n preprocess=\"zscore\",\n)\n\nprint(scores[\"test_score\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, we can choose to assess the performance of the model using\ndifferent scoring functions.\n\nFor example, we might have an unbalanced dataset:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_unbalanced = df_iris[20:] # drop the first 20 versicolor samples\nprint(df_unbalanced[\"species\"].value_counts())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So we will choose to use the `balanced_accuracy` and `roc_auc` metrics.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scoring = [\"balanced_accuracy\", \"roc_auc\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since we are comparing the performance of different models, we will need\nto use the same random seed to split the data in the same way.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we will use a default SVM model.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores1 = run_cross_validation(\n X=X,\n y=y,\n data=df_unbalanced,\n model=\"svm\",\n preprocess=\"zscore\",\n problem_type=\"classification\",\n scoring=scoring,\n cv=cv,\n)\n\nscores1[\"model\"] = \"svm\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Second we will use a default Random Forest model.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores2 = run_cross_validation(\n X=X,\n y=y,\n data=df_unbalanced,\n model=\"rf\",\n preprocess=\"zscore\",\n problem_type=\"classification\",\n scoring=scoring,\n cv=cv,\n)\n\nscores2[\"model\"] = \"rf\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The third model will be a SVM with a linear kernel.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores3 = run_cross_validation(\n X=X,\n y=y,\n data=df_unbalanced,\n model=\"svm\",\n model_params={\"svm__kernel\": \"linear\"},\n preprocess=\"zscore\",\n problem_type=\"classification\",\n scoring=scoring,\n cv=cv,\n)\n\nscores3[\"model\"] = \"svm_linear\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now compare the performance of the models using corrected statistics.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "stats_df = corrected_ttest(scores1, scores2, scores3)\nprint(stats_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ".. rst-class:: hidden\n This block is hidden in the documentation. This files are used to generate\n the plots in the documentation. (not working for now)\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also plot the performance of the models using the ``julearn`` Score\nViewer.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from julearn.viz import plot_scores\n\npanel = plot_scores(scores1, scores2, scores3)\n# panel.show()\n# uncomment the previous line show the plot\n# read the documentation for more information\n# https://panel.holoviz.org/getting_started/build_app.html#deploying-panels" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is how the plot looks like.\n\n

Note

The plot is interactive. You can zoom in and out, and hover over.\n However, buttons will not work in this documentation.

\n\n.. bokeh-plot::\n :source-position: none\n\n from julearn.viz import plot_scores\n from bokeh.io import output_notebook, show\n import pandas as pd\n output_notebook()\n scores1 = pd.read_csv(\"/tmp/scores1.csv\")\n scores2 = pd.read_csv(\"/tmp/scores2.csv\")\n scores3 = pd.read_csv(\"/tmp/scores3.csv\")\n panel = plot_scores(scores1, scores2, scores3, width=600)\n show(panel.get_root())\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/c7811f5cf46eb64580ee2fb3736b828f/run_hyperparameter_tuning_bayessearch.py b/pr-preview/pr-276/_downloads/c7811f5cf46eb64580ee2fb3736b828f/run_hyperparameter_tuning_bayessearch.py new file mode 100644 index 000000000..05cf4cdad --- /dev/null +++ b/pr-preview/pr-276/_downloads/c7811f5cf46eb64580ee2fb3736b828f/run_hyperparameter_tuning_bayessearch.py @@ -0,0 +1,95 @@ +""" +Tuning Hyperparameters using Bayesian Search +============================================ + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier and analyzes the model. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc +""" + +# Authors: Federico Raimondo +# License: AGPL + +import numpy as np +from seaborn import load_dataset + +from julearn import run_cross_validation +from julearn.utils import configure_logging, logger +from julearn.pipeline import PipelineCreator + + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Set the random seed to always have the same example. +np.random.seed(42) + +############################################################################### +# Load the dataset. +df_fmri = load_dataset("fmri") +df_fmri.head() + +############################################################################### +# Set the dataframe in the right format. +df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" +) + +df_fmri = df_fmri.reset_index() +df_fmri.head() + +############################################################################### +# Following the hyperparamter tuning example, we will now use a Bayesian +# search to find the best hyperparameters for the SVM model. +X = ["frontal", "parietal"] +y = "event" + +creator1 = PipelineCreator(problem_type="classification") +creator1.add("zscore") +creator1.add( + "svm", + kernel=["linear"], + C=(1e-6, 1e3, "log-uniform"), +) + +creator2 = PipelineCreator(problem_type="classification") +creator2.add("zscore") +creator2.add( + "svm", + kernel=["rbf"], + C=(1e-6, 1e3, "log-uniform"), + gamma=(1e-6, 1e1, "log-uniform"), +) + +search_params = { + "kind": "bayes", + "cv": 2, # to speed up the example + "n_iter": 10, # 10 iterations of bayesian search to speed up example +} + + +scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=[creator1, creator2], + cv=2, # to speed up the example + search_params=search_params, + return_estimator="final", +) + +print(scores["test_score"].mean()) + + +############################################################################### +# It seems that we might have found a better model, but which one is it? +print(estimator.best_params_) diff --git a/pr-preview/pr-276/_downloads/c8ced6d3b1e6a1327ae1ec5f6d6352a4/run_apply_to_target.py b/pr-preview/pr-276/_downloads/c8ced6d3b1e6a1327ae1ec5f6d6352a4/run_apply_to_target.py new file mode 100644 index 000000000..a24ccc42a --- /dev/null +++ b/pr-preview/pr-276/_downloads/c8ced6d3b1e6a1327ae1ec5f6d6352a4/run_apply_to_target.py @@ -0,0 +1,78 @@ +""" +Transforming target variable with z-score +========================================= + +This example uses the sklearn ``diabetes`` regression dataset, and transforms the +target variable, in this case, using z-score. Then, we perform a regression +analysis using Ridge Regression model. + +""" +# Authors: Lya K. Paas Oliveros +# Sami Hamdan +# +# License: AGPL + +import pandas as pd +from sklearn.datasets import load_diabetes +from sklearn.model_selection import train_test_split + +from julearn import run_cross_validation +from julearn.utils import configure_logging + +from julearn.pipeline import PipelineCreator, TargetPipelineCreator + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Load the diabetes dataset from ``sklearn`` as a ``pandas.DataFrame``. +features, target = load_diabetes(return_X_y=True, as_frame=True) + +############################################################################### +# Dataset contains ten variables age, sex, body mass index, average blood +# pressure, and six blood serum measurements (s1-s6) diabetes patients and +# a quantitative measure of disease progression one year after baseline which +# is the target we are interested in predicting. +print("Features: \n", features.head()) +print("Target: \n", target.describe()) + +############################################################################### +# Let's combine features and target together in one dataframe and define X +# and y. +data_diabetes = pd.concat([features, target], axis=1) + +X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] +y = "target" + +############################################################################### +# Split the dataset into train and test. +train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3) + +############################################################################### +# Let's create the model. Since we will be transforming the target variable +# we will first need to create a TargetPipelineCreator for this. + +target_creator = TargetPipelineCreator() +target_creator.add("zscore") + +############################################################################## +# Now we can create the pipeline using a PipelineCreator. +creator = PipelineCreator(problem_type="regression") +creator.add(target_creator, apply_to="target") +creator.add("ridge") + +scores, model = run_cross_validation( + X=X, + y=y, + data=train_diabetes, + model=creator, + return_estimator="final", + scoring="neg_mean_absolute_error", +) + +print(scores.head(5)) + +############################################################################### +# Mean value of mean absolute error across CV +print(scores["test_score"].mean() * -1) diff --git a/pr-preview/pr-276/_downloads/c8d2ebd68279e9f248f65513a9baeb9b/plot_groupcv_inspect_svm.ipynb b/pr-preview/pr-276/_downloads/c8d2ebd68279e9f248f65513a9baeb9b/plot_groupcv_inspect_svm.ipynb new file mode 100644 index 000000000..73dbf2ec7 --- /dev/null +++ b/pr-preview/pr-276/_downloads/c8d2ebd68279e9f248f65513a9baeb9b/plot_groupcv_inspect_svm.ipynb @@ -0,0 +1,241 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Inspecting SVM models\n\nThis example uses the ``fmri`` dataset, performs simple binary classification\nusing a Support Vector Machine classifier and analyse the model.\n\n## References\n\n Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of\n cognitive control in context-dependent decision-making. Cerebral Cortex.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# Shammi More \n# License: AGPL\n\nimport numpy as np\nimport pandas as pd\n\nfrom sklearn.model_selection import GroupShuffleSplit\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom seaborn import load_dataset\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging\nfrom julearn.inspect import preprocess" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dealing with Cross-Validation techniques\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = load_dataset(\"fmri\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, lets get some information on what the dataset has:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(df_fmri.head())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From this information, we can infer that it is an fMRI study in which there\nwere several subjects, timepoints, events and signal extracted from several\nbrain regions.\n\nLets check how many kinds of each we have.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(df_fmri[\"event\"].unique())\nprint(df_fmri[\"region\"].unique())\nprint(sorted(df_fmri[\"timepoint\"].unique()))\nprint(df_fmri[\"subject\"].unique())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have data from parietal and frontal regions during 2 types of events\n(*cue* and *stim*) during 18 timepoints and for 14 subjects.\nLets see how many samples we have for each condition\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(df_fmri.groupby([\"subject\", \"timepoint\", \"event\", \"region\"]).count())\nprint(\n np.unique(\n df_fmri.groupby([\"subject\", \"timepoint\", \"event\", \"region\"])\n .count()\n .values\n )\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have exactly one value per condition.\n\nLets try to build a model, that uses parietal and frontal signal to predicts\nwhether the event was a *cue* or a *stim*.\n\nFirst we define our X and y variables.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"parietal\", \"frontal\"]\ny = \"event\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order for this to work, both *parietal* and *frontal* must be columns.\nWe need to *pivot* the table.\n\nThe values of *region* will be the columns. The column *signal* will be the\nvalues. And the columns *subject*, *timepoint* and *event* will be the index\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.pivot(\n index=[\"subject\", \"timepoint\", \"event\"], columns=\"region\", values=\"signal\"\n)\n\ndf_fmri = df_fmri.reset_index()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we want to zscore all the features and then train a Support Vector\nMachine classifier.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n preprocess=\"zscore\",\n model=\"svm\",\n problem_type=\"classification\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This results indicate that we can decode the kind of event by looking at\nthe *parietal* and *frontal* signal. However, that claim is true only if we\nhave some data from the same subject already acquired.\n\nThe problem is that we split the data randomly into 5 folds (default, see\n:func:`.run_cross_validation`). This means that data from one subject could\nbe both in the training and the testing set. If this is the case, then the\nmodel can learn the subjects' specific characteristics and apply it to the\ntesting set. Thus, it is not true that we can decode it for an unseen\nsubject, but for an unseen timepoint for a subject that for whom we already\nhave data.\n\nTo test for unseen subject, we need to make sure that all the data from each\nsubject is either on the training or the testing set, but not in both.\n\nWe can use ``scikit-learn``'s\n:class:`sklearn.model_selection.GroupShuffleSplit` and specify which is the\ngrouping column using the ``group`` parameter.\nBy setting ``return_estimator=\"final\"``, the :func:`.run_cross_validation`\nfunction returns the estimator fitted with all the data. We will use this\nlater to do some analyses.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv = GroupShuffleSplit(n_splits=5, test_size=0.5, random_state=42)\n\nscores, model = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n preprocess=\"zscore\",\n model=\"svm\",\n cv=cv,\n groups=\"subject\",\n problem_type=\"classification\",\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After testing on independent subjects, we can now claim that given a new\nsubject, we can predict the kind of event.\n\nLet's do some visualization on how these two features interact and what\nthe preprocessing part of the model does.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Plot the raw features\nfig, axes = plt.subplots(1, 2, figsize=(8, 4))\nsns.scatterplot(\n x=\"parietal\", y=\"frontal\", hue=\"event\", data=df_fmri, ax=axes[0], s=5\n)\naxes[0].set_title(\"Raw data\")\n\n# Plot the preprocessed features\npre_X = preprocess(\n model, X=X, data=df_fmri, until=\"zscore\", with_column_types=True\n)\n\npre_df = pre_X.join(df_fmri[y])\n\nsns.scatterplot(\n x=\"parietal__:type:__continuous\",\n y=\"frontal__:type:__continuous\",\n hue=\"event\",\n data=pre_df,\n ax=axes[1],\n s=5,\n)\n\naxes[1].set_title(\"Preprocessed data\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, the preprocessing is nothing more than a\n:class:`sklearn.preprocessing.StandardScaler`.\n\nIt seems that the data is not quite linearly separable. Let's now visualize\nhow the SVM does this complex task.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Get the model from the pipeline\nclf = model[2]\nfig = plt.figure()\nax = sns.scatterplot(\n x=\"parietal__:type:__continuous\",\n y=\"frontal__:type:__continuous\",\n hue=\"event\",\n data=pre_df,\n s=5,\n)\n\nxlim = ax.get_xlim()\nylim = ax.get_ylim()\n\n# Create grid to evaluate model\nxx = np.linspace(xlim[0], xlim[1], 30)\nyy = np.linspace(ylim[0], ylim[1], 30)\nYY, XX = np.meshgrid(yy, xx)\nxy = np.vstack([XX.ravel(), YY.ravel()]).T\n\n# Create pandas.DataFrame\nxy_df = pd.DataFrame(\n data=xy,\n columns=[\"parietal__:type:__continuous\", \"frontal__:type:__continuous\"],\n)\n\nZ = clf.decision_function(xy_df).reshape(XX.shape)\na = ax.contour(XX, YY, Z, colors=\"k\", levels=[0], alpha=0.5, linestyles=[\"-\"])\nax.set_title(\"Preprocessed data with SVM decision function boundaries\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/ca0f48eb9cb2ca7b282830f39addaf1d/run_hyperparameter_tuning.py b/pr-preview/pr-276/_downloads/ca0f48eb9cb2ca7b282830f39addaf1d/run_hyperparameter_tuning.py new file mode 100644 index 000000000..82e206fa5 --- /dev/null +++ b/pr-preview/pr-276/_downloads/ca0f48eb9cb2ca7b282830f39addaf1d/run_hyperparameter_tuning.py @@ -0,0 +1,152 @@ +""" +Tuning Hyperparameters +======================= + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier and analyze the model. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# License: AGPL + +import numpy as np +from seaborn import load_dataset + +from julearn import run_cross_validation +from julearn.utils import configure_logging +from julearn.pipeline import PipelineCreator + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Set the random seed to always have the same example. +np.random.seed(42) + +############################################################################### +# Load the dataset. +df_fmri = load_dataset("fmri") +df_fmri.head() + +############################################################################### +# Set the dataframe in the right format. +df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" +) + +df_fmri = df_fmri.reset_index() +df_fmri.head() + +############################################################################### +# Let's do a first attempt and use a linear SVM with the default parameters. +X = ["frontal", "parietal"] +y = "event" + +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm", kernel="linear") + +scores = run_cross_validation(X=X, y=y, data=df_fmri, model=creator) + +print(scores["test_score"].mean()) + +############################################################################### +# The score is not so good. Let's try to see if there is an optimal +# regularization parameter (C) for the linear SVM. +# We will use a grid search to find the best ``C``. + +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm", kernel="linear", C=[0.01, 0.1]) + +search_params = { + "kind": "grid", + "cv": 2, # to speed up the example +} + +scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", +) + +print(scores["test_score"].mean()) + +############################################################################### +# This did not change much, lets explore other kernels too. + +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm", kernel=["linear", "rbf", "poly"], C=[0.01, 0.1]) + +scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", +) + +print(scores["test_score"].mean()) +############################################################################### +# It seems that we might have found a better model, but which one is it? +print(estimator.best_params_) + +############################################################################### +# Now that we know that a RBF kernel is better, lest test different *gamma* +# parameters. + +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm", kernel="rbf", C=[0.01, 0.1], gamma=[1e-2, 1e-3]) + +scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", +) + +print(scores["test_score"].mean()) +print(estimator.best_params_) + +############################################################################### +# It seems that without tuning the gamma parameter we had a better accuracy. +# Let's add the default value and see what happens. + +creator = PipelineCreator(problem_type="classification") +creator.add("zscore") +creator.add("svm", kernel="rbf", C=[0.01, 0.1], gamma=[1e-2, 1e-3, "scale"]) +X = ["frontal", "parietal"] +y = "event" + +search_params = {"cv": 2} + +scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + return_estimator="final", + search_params=search_params, +) + +print(scores["test_score"].mean()) +print(estimator.best_params_) + +############################################################################### +print(estimator.best_estimator_["svm"]._gamma) diff --git a/pr-preview/pr-276/_downloads/cc4726f1c6598af17d3357bdbb13371d/run_simple_binary_classification.py b/pr-preview/pr-276/_downloads/cc4726f1c6598af17d3357bdbb13371d/run_simple_binary_classification.py new file mode 100644 index 000000000..3d2d2a168 --- /dev/null +++ b/pr-preview/pr-276/_downloads/cc4726f1c6598af17d3357bdbb13371d/run_simple_binary_classification.py @@ -0,0 +1,98 @@ +""" +Simple Binary Classification +============================ + +This example uses the ``iris`` dataset and performs a simple binary +classification using a Support Vector Machine classifier. + +.. include:: ../../links.inc +""" +# Authors: Federico Raimondo +# +# License: AGPL + +from seaborn import load_dataset +from julearn import run_cross_validation +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information +configure_logging(level="INFO") + +############################################################################### +df_iris = load_dataset("iris") + +############################################################################### +# The dataset has three kind of species. We will keep two to perform a binary +# classification. +df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + +############################################################################### +# As features, we will use the sepal length, width and petal length. +# We will try to predict the species. + +X = ["sepal_length", "sepal_width", "petal_length"] +y = "species" +scores = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="svm", + problem_type="classification", + preprocess="zscore", +) + +print(scores["test_score"]) + +############################################################################### +# Additionally, we can choose to assess the performance of the model using +# different scoring functions. +# +# For example, we might have an unbalanced dataset: + +df_unbalanced = df_iris[20:] # drop the first 20 versicolor samples +print(df_unbalanced["species"].value_counts()) + +############################################################################### +# If we compute the `accuracy`, we might not account for this imbalance. A more +# suitable metric is the `balanced_accuracy`. More information in +# ``scikit-learn``: :func:`~sklearn.metrics.balanced_accuracy_score`. +# +# We will also set the random seed so we always split the data in the same way. +scores = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + seed=42, + preprocess="zscore", + problem_type="classification", + scoring=["accuracy", "balanced_accuracy"], +) + +print(scores["test_accuracy"].mean()) +print(scores["test_balanced_accuracy"].mean()) + +############################################################################### +# Other kind of metrics allows us to evaluate how good our model is to detect +# specific targets. Suppose we want to create a model that correctly identifies +# the `versicolor` samples. +# +# Now we might want to evaluate the precision score, or the ratio of true +# positives (tp) over all positives (true and false positives). More +# information in ``scikit-learn``: :func:`~sklearn.metrics.precision_score`. +# +# For this metric to work, we need to define which are our `positive` values. +# In this example, we are interested in detecting `versicolor`. +precision_scores = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + preprocess="zscore", + problem_type="classification", + seed=42, + scoring="precision", + pos_labels="versicolor", +) +print(precision_scores["test_score"].mean()) diff --git a/pr-preview/pr-276/_downloads/d0389f5847861aaa8d73951eceed9f91/run_hyperparameter_tuning.ipynb b/pr-preview/pr-276/_downloads/d0389f5847861aaa8d73951eceed9f91/run_hyperparameter_tuning.ipynb new file mode 100644 index 000000000..adf3448f9 --- /dev/null +++ b/pr-preview/pr-276/_downloads/d0389f5847861aaa8d73951eceed9f91/run_hyperparameter_tuning.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Tuning Hyperparameters\n\nThis example uses the ``fmri`` dataset, performs simple binary classification\nusing a Support Vector Machine classifier and analyze the model.\n\n## References\n\n Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of\n cognitive control in context-dependent decision-making. Cerebral Cortex.\n\n.. include:: ../../links.inc\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Federico Raimondo \n# License: AGPL\n\nimport numpy as np\nfrom seaborn import load_dataset\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging\nfrom julearn.pipeline import PipelineCreator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the random seed to always have the same example.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "np.random.seed(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the dataset.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = load_dataset(\"fmri\")\ndf_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the dataframe in the right format.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_fmri = df_fmri.pivot(\n index=[\"subject\", \"timepoint\", \"event\"], columns=\"region\", values=\"signal\"\n)\n\ndf_fmri = df_fmri.reset_index()\ndf_fmri.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's do a first attempt and use a linear SVM with the default parameters.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = [\"frontal\", \"parietal\"]\ny = \"event\"\n\ncreator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\", kernel=\"linear\")\n\nscores = run_cross_validation(X=X, y=y, data=df_fmri, model=creator)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The score is not so good. Let's try to see if there is an optimal\nregularization parameter (C) for the linear SVM.\nWe will use a grid search to find the best ``C``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\", kernel=\"linear\", C=[0.01, 0.1])\n\nsearch_params = {\n \"kind\": \"grid\",\n \"cv\": 2, # to speed up the example\n}\n\nscores, estimator = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n model=creator,\n search_params=search_params,\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This did not change much, lets explore other kernels too.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\", kernel=[\"linear\", \"rbf\", \"poly\"], C=[0.01, 0.1])\n\nscores, estimator = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n model=creator,\n search_params=search_params,\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It seems that we might have found a better model, but which one is it?\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(estimator.best_params_)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we know that a RBF kernel is better, lest test different *gamma*\nparameters.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\", kernel=\"rbf\", C=[0.01, 0.1], gamma=[1e-2, 1e-3])\n\nscores, estimator = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n model=creator,\n search_params=search_params,\n return_estimator=\"final\",\n)\n\nprint(scores[\"test_score\"].mean())\nprint(estimator.best_params_)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It seems that without tuning the gamma parameter we had a better accuracy.\nLet's add the default value and see what happens.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"classification\")\ncreator.add(\"zscore\")\ncreator.add(\"svm\", kernel=\"rbf\", C=[0.01, 0.1], gamma=[1e-2, 1e-3, \"scale\"])\nX = [\"frontal\", \"parietal\"]\ny = \"event\"\n\nsearch_params = {\"cv\": 2}\n\nscores, estimator = run_cross_validation(\n X=X,\n y=y,\n data=df_fmri,\n model=creator,\n return_estimator=\"final\",\n search_params=search_params,\n)\n\nprint(scores[\"test_score\"].mean())\nprint(estimator.best_params_)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(estimator.best_estimator_[\"svm\"]._gamma)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/d48d37efb46ccabf7a609809e1f1c990/run_example_pca_featsets.py b/pr-preview/pr-276/_downloads/d48d37efb46ccabf7a609809e1f1c990/run_example_pca_featsets.py new file mode 100644 index 000000000..14571735e --- /dev/null +++ b/pr-preview/pr-276/_downloads/d48d37efb46ccabf7a609809e1f1c990/run_example_pca_featsets.py @@ -0,0 +1,154 @@ +""" +Regression Analysis +=================== + +This example uses the ``diabetes`` data from ``sklearn datasets`` and performs +a regression analysis using a Ridge Regression model. We'll use the +``julearn.PipelineCreator`` to create a pipeline with two different PCA steps and +reduce the dimensionality of the data, each one computed on a different +subset of features. + +""" +# Authors: Georgios Antonopoulos +# Kaustubh R. Patil +# Shammi More +# Federico Raimondo +# License: AGPL + +import pandas as pd +import seaborn as sns +import numpy as np +import matplotlib.pyplot as plt +from sklearn.datasets import load_diabetes +from sklearn.metrics import mean_absolute_error +from sklearn.model_selection import train_test_split + +from julearn import run_cross_validation +from julearn.utils import configure_logging +from julearn.pipeline import PipelineCreator +from julearn.inspect import preprocess + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. +features, target = load_diabetes(return_X_y=True, as_frame=True) + +############################################################################### +# Dataset contains ten variables age, sex, body mass index, average blood +# pressure, and six blood serum measurements (s1-s6) diabetes patients and +# a quantitative measure of disease progression one year after baseline which +# is the target we are interested in predicting. + +print("Features: \n", features.head()) +print("Target: \n", target.describe()) + +############################################################################### +# Let's combine features and target together in one dataframe and define X +# and y +data_diabetes = pd.concat([features, target], axis=1) + +X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] +y = "target" + +############################################################################### +# Assign types to the features and create feature groups for PCA. +# We will keep 1 component per PCA group. +X_types = { + "pca1": ["age", "bmi", "bp"], + "pca2": ["s1", "s2", "s3", "s4", "s5", "s6"], + "categorical": ["sex"], +} + +############################################################################### +# Create a pipeline to process the data and the fit a model. We must specify +# how each ``X_type`` will be used. For example if in the last step we do not +# specify ``apply_to=["continuous", "categorical"]``, then the pipeline will not +# know what to do with the categorical features. +creator = PipelineCreator(problem_type="regression") +creator.add("pca", apply_to="pca1", n_components=1, name="pca_feats1") +creator.add("pca", apply_to="pca2", n_components=1, name="pca_feats2") +creator.add("ridge", apply_to=["continuous", "categorical"]) + +############################################################################### +# Split the dataset into train and test. +train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3) + +############################################################################### +# Train a ridge regression model on train dataset and use mean absolute error +# for scoring. +scores, model = run_cross_validation( + X=X, + y=y, + X_types=X_types, + data=train_diabetes, + model=creator, + scoring="r2", + return_estimator="final", +) + +############################################################################### +# The scores dataframe has all the values for each CV split. +print(scores.head()) + +############################################################################### +# Mean value of mean absolute error across CV. +print(scores["test_score"].mean()) + +############################################################################### +# Let's see how the data looks like after preprocessing. We will process the +# data until the first PCA step. We should get the first PCA component for +# ["age", "bmi", "bp"] and leave other features untouched. +data_processed1 = preprocess(model, X, data=train_diabetes, until="pca_feats1") +print("Data after preprocessing until PCA step 1") +data_processed1.head() + +############################################################################### +# We will process the data until the second PCA step. We should now also get +# one PCA component for ["s1", "s2", "s3", "s4", "s5", "s6"]. +data_processed2 = preprocess(model, X, data=train_diabetes, until="pca_feats2") +print("Data after preprocessing until PCA step 2") +data_processed2.head() + +############################################################################### +# Now we can get the MAE fold and repetition: +df_mae = scores.set_index(["repeat", "fold"])["test_score"].unstack() * -1 +df_mae.index.name = "Repeats" +df_mae.columns.name = "K-fold splits" + +print(df_mae) + +############################################################################### +# Plot heatmap of mean absolute error (MAE) over all repeats and CV splits. +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.heatmap(df_mae, cmap="YlGnBu") +plt.title("Cross-validation MAE") + +############################################################################### +# Use the final model to make predictions on test data and plot scatterplot +# of true values vs predicted values. +y_true = test_diabetes[y] +y_pred = model.predict(test_diabetes[X]) +mae = format(mean_absolute_error(y_true, y_pred), ".2f") +corr = format(np.corrcoef(y_pred, y_true)[1, 0], ".2f") + +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.set_style("darkgrid") +plt.scatter(y_true, y_pred) +plt.plot(y_true, y_true) +xmin, xmax = ax.get_xlim() +ymin, ymax = ax.get_ylim() +text = "MAE: " + str(mae) + " CORR: " + str(corr) +ax.set(xlabel="True values", ylabel="Predicted values") +plt.title("Actual vs Predicted") +plt.text( + xmax - 0.01 * xmax, + ymax - 0.01 * ymax, + text, + verticalalignment="top", + horizontalalignment="right", + fontsize=12, +) +plt.axis("scaled") diff --git a/pr-preview/pr-276/_downloads/d7e247dae7dd8764398372ebe6109e27/run_example_pca_featsets.ipynb b/pr-preview/pr-276/_downloads/d7e247dae7dd8764398372ebe6109e27/run_example_pca_featsets.ipynb new file mode 100644 index 000000000..7fbaf24d7 --- /dev/null +++ b/pr-preview/pr-276/_downloads/d7e247dae7dd8764398372ebe6109e27/run_example_pca_featsets.ipynb @@ -0,0 +1,313 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Regression Analysis\n\nThis example uses the ``diabetes`` data from ``sklearn datasets`` and performs\na regression analysis using a Ridge Regression model. We'll use the\n``julearn.PipelineCreator`` to create a pipeline with two different PCA steps and\nreduce the dimensionality of the data, each one computed on a different\nsubset of features.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Georgios Antonopoulos \n# Kaustubh R. Patil \n# Shammi More \n# Federico Raimondo \n# License: AGPL\n\nimport pandas as pd\nimport seaborn as sns\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.datasets import load_diabetes\nfrom sklearn.metrics import mean_absolute_error\nfrom sklearn.model_selection import train_test_split\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging\nfrom julearn.pipeline import PipelineCreator\nfrom julearn.inspect import preprocess" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "features, target = load_diabetes(return_X_y=True, as_frame=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dataset contains ten variables age, sex, body mass index, average blood\npressure, and six blood serum measurements (s1-s6) diabetes patients and\na quantitative measure of disease progression one year after baseline which\nis the target we are interested in predicting.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Features: \\n\", features.head())\nprint(\"Target: \\n\", target.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's combine features and target together in one dataframe and define X\nand y\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_diabetes = pd.concat([features, target], axis=1)\n\nX = [\"age\", \"sex\", \"bmi\", \"bp\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\"]\ny = \"target\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Assign types to the features and create feature groups for PCA.\nWe will keep 1 component per PCA group.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_types = {\n \"pca1\": [\"age\", \"bmi\", \"bp\"],\n \"pca2\": [\"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\"],\n \"categorical\": [\"sex\"],\n}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a pipeline to process the data and the fit a model. We must specify\nhow each ``X_type`` will be used. For example if in the last step we do not\nspecify ``apply_to=[\"continuous\", \"categorical\"]``, then the pipeline will not\nknow what to do with the categorical features.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "creator = PipelineCreator(problem_type=\"regression\")\ncreator.add(\"pca\", apply_to=\"pca1\", n_components=1, name=\"pca_feats1\")\ncreator.add(\"pca\", apply_to=\"pca2\", n_components=1, name=\"pca_feats2\")\ncreator.add(\"ridge\", apply_to=[\"continuous\", \"categorical\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Split the dataset into train and test.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Train a ridge regression model on train dataset and use mean absolute error\nfor scoring.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores, model = run_cross_validation(\n X=X,\n y=y,\n X_types=X_types,\n data=train_diabetes,\n model=creator,\n scoring=\"r2\",\n return_estimator=\"final\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The scores dataframe has all the values for each CV split.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(scores.head())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mean value of mean absolute error across CV.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(scores[\"test_score\"].mean())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see how the data looks like after preprocessing. We will process the\ndata until the first PCA step. We should get the first PCA component for\n[\"age\", \"bmi\", \"bp\"] and leave other features untouched.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_processed1 = preprocess(model, X, data=train_diabetes, until=\"pca_feats1\")\nprint(\"Data after preprocessing until PCA step 1\")\ndata_processed1.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will process the data until the second PCA step. We should now also get\none PCA component for [\"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\"].\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_processed2 = preprocess(model, X, data=train_diabetes, until=\"pca_feats2\")\nprint(\"Data after preprocessing until PCA step 2\")\ndata_processed2.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can get the MAE fold and repetition:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_mae = scores.set_index([\"repeat\", \"fold\"])[\"test_score\"].unstack() * -1\ndf_mae.index.name = \"Repeats\"\ndf_mae.columns.name = \"K-fold splits\"\n\nprint(df_mae)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot heatmap of mean absolute error (MAE) over all repeats and CV splits.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.heatmap(df_mae, cmap=\"YlGnBu\")\nplt.title(\"Cross-validation MAE\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the final model to make predictions on test data and plot scatterplot\nof true values vs predicted values.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "y_true = test_diabetes[y]\ny_pred = model.predict(test_diabetes[X])\nmae = format(mean_absolute_error(y_true, y_pred), \".2f\")\ncorr = format(np.corrcoef(y_pred, y_true)[1, 0], \".2f\")\n\nfig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.set_style(\"darkgrid\")\nplt.scatter(y_true, y_pred)\nplt.plot(y_true, y_true)\nxmin, xmax = ax.get_xlim()\nymin, ymax = ax.get_ylim()\ntext = \"MAE: \" + str(mae) + \" CORR: \" + str(corr)\nax.set(xlabel=\"True values\", ylabel=\"Predicted values\")\nplt.title(\"Actual vs Predicted\")\nplt.text(\n xmax - 0.01 * xmax,\n ymax - 0.01 * ymax,\n text,\n verticalalignment=\"top\",\n horizontalalignment=\"right\",\n fontsize=12,\n)\nplt.axis(\"scaled\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/d7f2e43dc1b648794b2ea12f865cd8ed/plot_example_regression.py b/pr-preview/pr-276/_downloads/d7f2e43dc1b648794b2ea12f865cd8ed/plot_example_regression.py new file mode 100644 index 000000000..87bebc032 --- /dev/null +++ b/pr-preview/pr-276/_downloads/d7f2e43dc1b648794b2ea12f865cd8ed/plot_example_regression.py @@ -0,0 +1,144 @@ +""" +Regression Analysis +=================== + +This example uses the ``diabetes`` data from ``sklearn datasets`` and performs +a regression analysis using a Ridge Regression model. + +""" +# Authors: Shammi More +# Federico Raimondo +# License: AGPL + +import pandas as pd +import seaborn as sns +import numpy as np +import matplotlib.pyplot as plt +from sklearn.datasets import load_diabetes +from sklearn.metrics import mean_absolute_error +from sklearn.model_selection import train_test_split + +from julearn import run_cross_validation +from julearn.utils import configure_logging + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. +features, target = load_diabetes(return_X_y=True, as_frame=True) + +############################################################################### +# Dataset contains ten variables age, sex, body mass index, average blood +# pressure, and six blood serum measurements (s1-s6) diabetes patients and +# a quantitative measure of disease progression one year after baseline which +# is the target we are interested in predicting. + +print("Features: \n", features.head()) +print("Target: \n", target.describe()) + +############################################################################### +# Let's combine features and target together in one dataframe and define X +# and y +data_diabetes = pd.concat([features, target], axis=1) + +X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] +y = "target" + +############################################################################### +# Calculate correlations between the features/variables and plot it as heat map. +corr = data_diabetes.corr() + +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.set(font_scale=1.2) +sns.heatmap( + corr, + xticklabels=corr.columns, + yticklabels=corr.columns, + annot=True, + fmt="0.1f", +) + +############################################################################### +# Split the dataset into train and test. +train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3) + +############################################################################### +# Train a ridge regression model on train dataset and use mean absolute error +# for scoring. +scores, model = run_cross_validation( + X=X, + y=y, + data=train_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring="neg_mean_absolute_error", +) + +############################################################################### +# The scores dataframe has all the values for each CV split. + +scores.head() + +############################################################################### +# Mean value of mean absolute error across CV +print(scores["test_score"].mean() * -1) + +############################################################################### +# Now we can get the MAE fold and repetition: + +df_mae = scores.set_index(["repeat", "fold"])["test_score"].unstack() * -1 +df_mae.index.name = "Repeats" +df_mae.columns.name = "K-fold splits" + +df_mae + +############################################################################### +# Plot heatmap of mean absolute error (MAE) over all repeats and CV splits. +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.heatmap(df_mae, cmap="YlGnBu") +plt.title("Cross-validation MAE") + +############################################################################### +# Let's plot the feature importance using the coefficients of the trained model. +features = pd.DataFrame({"Features": X, "importance": model["ridge"].coef_}) +features.sort_values(by=["importance"], ascending=True, inplace=True) +features["positive"] = features["importance"] > 0 + +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +features.set_index("Features", inplace=True) +features.importance.plot( + kind="barh", color=features.positive.map({True: "blue", False: "red"}) +) +ax.set(xlabel="Importance", title="Variable importance for Ridge Regression") + +############################################################################### +# Use the final model to make predictions on test data and plot scatterplot +# of true values vs predicted values. + +y_true = test_diabetes[y] +y_pred = model.predict(test_diabetes[X]) +mae = format(mean_absolute_error(y_true, y_pred), ".2f") +corr = format(np.corrcoef(y_pred, y_true)[1, 0], ".2f") + +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.set_style("darkgrid") +plt.scatter(y_true, y_pred) +plt.plot(y_true, y_true) +xmin, xmax = ax.get_xlim() +ymin, ymax = ax.get_ylim() +text = "MAE: " + str(mae) + " CORR: " + str(corr) +ax.set(xlabel="True values", ylabel="Predicted values") +plt.title("Actual vs Predicted") +plt.text( + xmax - 0.01 * xmax, + ymax - 0.01 * ymax, + text, + verticalalignment="top", + horizontalalignment="right", + fontsize=12, +) +plt.axis("scaled") diff --git a/pr-preview/pr-276/_downloads/e19d16e33ffa29cdb1e56852178d5909/plot_stratified_kfold_reg.py b/pr-preview/pr-276/_downloads/e19d16e33ffa29cdb1e56852178d5909/plot_stratified_kfold_reg.py new file mode 100644 index 000000000..6f5c0e419 --- /dev/null +++ b/pr-preview/pr-276/_downloads/e19d16e33ffa29cdb1e56852178d5909/plot_stratified_kfold_reg.py @@ -0,0 +1,130 @@ +""" +Stratified K-fold CV for regression analysis +============================================ + +This example uses the ``diabetes`` data from ``sklearn datasets`` to +perform stratified Kfold CV for a regression problem, + +.. include:: ../../links.inc +""" +# Authors: Shammi More +# Federico Raimondo +# Leonard Sasse +# License: AGPL + +import pandas as pd +import seaborn as sns +import matplotlib.pyplot as plt +from sklearn.datasets import load_diabetes +from sklearn.model_selection import KFold + +from julearn import run_cross_validation +from julearn.utils import configure_logging +from julearn.model_selection import ContinuousStratifiedKFold + +############################################################################### +# Set the logging level to info to see extra information. +configure_logging(level="INFO") + +############################################################################### +# Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. +features, target = load_diabetes(return_X_y=True, as_frame=True) + +############################################################################### +# Dataset contains ten variables age, sex, body mass index, average blood +# pressure, and six blood serum measurements (s1-s6) diabetes patients and +# a quantitative measure of disease progression one year after baseline which +# is the target we are interested in predicting. + +print("Features: \n", features.head()) +print("Target: \n", target.describe()) + +############################################################################### +# Let's combine features and target together in one dataframe and create some +# outliers to see the difference in model performance with and without +# stratification. + +data_df = pd.concat([features, target], axis=1) + +# Create outliers for test purpose +new_df = data_df[(data_df.target > 145) & (data_df.target <= 150)] +new_df["target"] = [590, 580, 597, 595, 590, 590, 600] +data_df = pd.concat([data_df, new_df], axis=0) +data_df = data_df.reset_index(drop=True) + +# Define X, y +X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] +y = "target" + + +############################################################################### +# Define number of bins/group for stratification. The idea is that each "bin" +# will be equally represented in each fold. The number of bins should be +# chosen such that each bin has a sufficient number of samples so that each +# fold has more than one sample from each bin. +# Let's see a couple of histrograms with different number of bins. + +sns.displot(data_df, x="target", bins=60) + +sns.displot(data_df, x="target", bins=40) + +sns.displot(data_df, x="target", bins=20) + +############################################################################### +# From the histogram above, we can see that the data is not uniformly +# distributed. We can see that the data is skewed towards the lower end of +# the target variable. We can also see that there are some outliers in the +# data. In any case, even with a low number of splits, some groups will not be +# represented in each fold. Lets continue with 40 bins which gives a good +# granularity. + +cv_stratified = ContinuousStratifiedKFold(n_bins=40, n_splits=5, shuffle=False) + +############################################################################### +# Train a linear regression model with stratification on target. + +scores_strat, model = run_cross_validation( + X=X, + y=y, + data=data_df, + preprocess="zscore", + cv=cv_stratified, + problem_type="regression", + model="linreg", + return_estimator="final", + scoring="neg_mean_absolute_error", +) + +############################################################################### +# Train a linear regression model without stratification on target. + +cv = KFold(n_splits=5, shuffle=False, random_state=None) +scores, model = run_cross_validation( + X=X, + y=y, + data=data_df, + preprocess="zscore", + cv=cv, + problem_type="regression", + model="linreg", + return_estimator="final", + scoring="neg_mean_absolute_error", +) + +############################################################################### +# Now we can compare the test score for model trained with and without +# stratification. We can combine the two outputs as ``pandas.DataFrame``. + +scores_strat["model"] = "With stratification" +scores["model"] = "Without stratification" +df_scores = scores_strat[["test_score", "model"]] +df_scores = pd.concat([df_scores, scores[["test_score", "model"]]]) + +############################################################################### +# Plot a boxplot with test scores from both the models. We see here that +# the test score is higher when CV splits were not stratified. + +fig, ax = plt.subplots(1, 1, figsize=(10, 7)) +sns.set_style("darkgrid") +ax = sns.boxplot(x="model", y="test_score", data=df_scores) +ax = sns.swarmplot(x="model", y="test_score", data=df_scores, color=".25") diff --git a/pr-preview/pr-276/_downloads/e2d40aaa5cca65f87881373fea370df4/plot_cm_acc_multiclass.ipynb b/pr-preview/pr-276/_downloads/e2d40aaa5cca65f87881373fea370df4/plot_cm_acc_multiclass.ipynb new file mode 100644 index 000000000..b4ba34d43 --- /dev/null +++ b/pr-preview/pr-276/_downloads/e2d40aaa5cca65f87881373fea370df4/plot_cm_acc_multiclass.ipynb @@ -0,0 +1,223 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Multiclass Classification\n\nThis example uses the ``iris`` dataset and performs multiclass\nclassification using a Support Vector Machine classifier and plots\nheatmaps for cross-validation accuracies and plots confusion matrix\nfor the test data.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Shammi More \n# Federico Raimondo \n# License: AGPL\n\nimport pandas as pd\nimport seaborn as sns\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom seaborn import load_dataset\nfrom sklearn.model_selection import train_test_split, RepeatedKFold\nfrom sklearn.metrics import confusion_matrix\n\nfrom julearn import run_cross_validation\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "load the iris data from seaborn\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_iris = load_dataset(\"iris\")\nX = [\"sepal_length\", \"sepal_width\", \"petal_length\"]\ny = \"species\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Split the dataset into train and test\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "train_iris, test_iris = train_test_split(\n df_iris, test_size=0.2, stratify=df_iris[y], random_state=200\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We want to perform multiclass classification as iris dataset contains 3 kinds\nof species. We will first zscore all the features and then train a support\nvector machine classifier.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cv = RepeatedKFold(n_splits=5, n_repeats=5, random_state=200)\nscores, model_iris = run_cross_validation(\n X=X,\n y=y,\n data=train_iris,\n model=\"svm\",\n preprocess=\"zscore\",\n problem_type=\"classification\",\n cv=cv,\n scoring=[\"accuracy\"],\n return_estimator=\"final\",\n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The scores dataframe has all the values for each CV split.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scores.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can get the accuracy per fold and repetition:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df_accuracy = scores.set_index([\"repeat\", \"fold\"])[\"test_accuracy\"].unstack()\ndf_accuracy.index.name = \"Repeats\"\ndf_accuracy.columns.name = \"K-fold splits\"\ndf_accuracy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plot heatmap of accuracy over all repeats and CV splits\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.set(font_scale=1.2)\nfig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.heatmap(df_accuracy, cmap=\"YlGnBu\")\nplt.title(\"Cross-validation Accuracy\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also test our final model's accuracy and plot the confusion matrix\nfor the test data as an annotated heatmap\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "y_true = test_iris[y]\ny_pred = model_iris.predict(test_iris[X])\ncm = confusion_matrix(y_true, y_pred, labels=np.unique(y_true))\n\nprint(cm)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have our confusion matrix, let's build another matrix with\nannotations.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cm_sum = np.sum(cm, axis=1, keepdims=True)\ncm_perc = cm / cm_sum.astype(float) * 100\nannot = np.empty_like(cm).astype(str)\nnrows, ncols = cm.shape\nfor i in range(nrows):\n for j in range(ncols):\n c = cm[i, j]\n p = cm_perc[i, j]\n if c == 0:\n annot[i, j] = \"\"\n else:\n s = cm_sum[i]\n annot[i, j] = \"%.1f%%\\n%d/%d\" % (p, c, s)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally we create another dataframe with the confusion matrix and plot\nthe heatmap with annotations.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "cm = pd.DataFrame(cm, index=np.unique(y_true), columns=np.unique(y_true))\ncm.index.name = \"Actual\"\ncm.columns.name = \"Predicted\"\n\nfig, ax = plt.subplots(1, 1, figsize=(10, 7))\nsns.heatmap(cm, cmap=\"YlGnBu\", annot=annot, fmt=\"\", ax=ax)\nplt.title(\"Confusion matrix\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_downloads/fc4804a4f78788e476ed705a2c0a5cb0/plot_preprocess.ipynb b/pr-preview/pr-276/_downloads/fc4804a4f78788e476ed705a2c0a5cb0/plot_preprocess.ipynb new file mode 100644 index 000000000..88779bd16 --- /dev/null +++ b/pr-preview/pr-276/_downloads/fc4804a4f78788e476ed705a2c0a5cb0/plot_preprocess.ipynb @@ -0,0 +1,133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Preprocessing with variance threshold, zscore and PCA\n\nThis example uses the ``make_regression`` function to create a simple dataset,\nperforms a simple regression after the preprocessing of the features\nincluding removal of low variance features, feature normalization for only\ntwo features using zscore and feature reduction using PCA.\nWe will check the features after each preprocessing step.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Authors: Shammi More \n# Leonard Sasse \n# License: AGPL\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\nfrom sklearn.datasets import make_regression\n\nfrom julearn import run_cross_validation\nfrom julearn.inspect import preprocess\nfrom julearn.pipeline import PipelineCreator\nfrom julearn.utils import configure_logging" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set the logging level to info to see extra information.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "configure_logging(level=\"INFO\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a dataset using ``sklearn`` ``make_regression``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df = pd.DataFrame()\nX, y = [f\"Feature {x}\" for x in range(1, 5)], \"y\"\ndf[X], df[y] = make_regression(\n n_samples=100, n_features=4, n_informative=3, noise=0.3, random_state=0\n)\n\n# We only want to zscore the first two features, so let's get their names.\nfirst_two = X[:2]\n\n# We can define a dictionary, in which the 'key' defines the names of our\n# different 'types' of 'X'. The 'value' determine, which features belong to\n# this type.\nX_types = {\"X_to_zscore\": first_two}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look at the summary statistics of the raw features.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Summary Statistics of the raw features : \\n\", df.describe())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will preprocess all features using variance thresholding.\nWe will only zscore the first two features, and then perform PCA using all\nfeatures. We will zscore the target and then train a random forest model.\nSince we use the PipelineCreator object we have to explicitly declare which\n`X_types` each preprocessing step should be applied to. If we do not declare\nthe type in the ``add`` method using the ``apply_to`` keyword argument,\nthe step will default to ``\"continuous\"`` or to another type that can be\ndeclared in the constructor of the ``PipelineCreator``.\nTo transform the target we could set ``apply_to=\"target\"``, which is a special\ntype, that cannot be user-defined. Please note also that if a step is added\nto transform the target, you also have to explicitly add the model that is\nto be used in the regression to the ``PipelineCreator``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Define model parameters and preprocessing steps first\n# The hyperparameters for each step can be added as a keyword argument and\n# should be either one parameter or an iterable of multiple parameters for a\n# search.\n\n# Setting the threshold for variance to 0.15, number of PCA components to 2\n# and number of trees for random forest to 200.\n\n# By setting \"apply_to=*\", we can apply the preprocessing step to all features.\npipeline_creator = PipelineCreator(problem_type=\"regression\")\n\npipeline_creator.add(\"select_variance\", apply_to=\"*\", threshold=0.15)\npipeline_creator.add(\"zscore\", apply_to=\"X_to_zscore\")\npipeline_creator.add(\"pca\", apply_to=\"*\", n_components=2)\npipeline_creator.add(\"rf\", apply_to=\"*\", n_estimators=200)\n\n# Because we have already added the model to the pipeline creator, we can\n# simply drop in the ``pipeline_creator`` as a model. If we did not add a model\n# here, we could add the ``pipeline_creator`` using the keyword argument\n# ``preprocess`` and hand over a model separately.\n\nscores, model = run_cross_validation(\n X=X,\n y=y,\n X_types=X_types,\n data=df,\n model=pipeline_creator,\n scoring=[\"r2\", \"neg_mean_absolute_error\"],\n return_estimator=\"final\",\n seed=200,\n)\n\n# We can use the final estimator to inspect the transformed features at a\n# specific step of the pipeline. Since the PCA was the last step added to the\n# pipeline, we can simply get the model up to this step by indexing as follows:\n\nX_after_pca = model[:-1].transform(df[X])\n\nprint(\"X after PCA:\")\nprint(\"=\" * 79)\nprint(X_after_pca)\n\n# We can select pipelines up to earlier steps by indexing previous elements\n# in the final estimator. For example, to inspect features after the zscoring\n# step:\n\nX_after_zscore = model[:-2].transform(df[X])\nprint(\"X after zscore:\")\nprint(\"=\" * 79)\nprint(X_after_zscore)\n\n# However, to make this less confusing you can also simply use the high-level\n# function ``preprocess`` to explicitly refer to a pipeline step by name:\n\nX_after_pca = preprocess(model, X=X, data=df, until=\"pca\")\nX_after_zscore = preprocess(model, X=X, data=df, until=\"zscore\")\n\n# Let's plot scatter plots for raw features and the PCA components.\nfig, axes = plt.subplots(1, 2, figsize=(12, 6))\nsns.scatterplot(x=X[0], y=X[1], data=df, ax=axes[0])\naxes[0].set_title(\"Raw features\")\nsns.scatterplot(x=\"pca0\", y=\"pca1\", data=X_after_pca, ax=axes[1])\naxes[1].set_title(\"PCA components\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look at the summary statistics of the zscored features. We see here\nthat the mean of all the features is zero and standard deviation is one.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\n \"Summary Statistics of the zscored features : \\n\",\n X_after_zscore.describe(),\n)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/pr-preview/pr-276/_images/julearn_logo_it.png b/pr-preview/pr-276/_images/julearn_logo_it.png new file mode 100644 index 000000000..e02e6ee88 Binary files /dev/null and b/pr-preview/pr-276/_images/julearn_logo_it.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_001.png b/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_001.png new file mode 100644 index 000000000..f135ea803 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_002.png b/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_002.png new file mode 100644 index 000000000..36780a00a Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_thumb.png new file mode 100644 index 000000000..134f37820 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_cm_acc_multiclass_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_001.png b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_001.png new file mode 100644 index 000000000..dbba7ebbb Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_002.png b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_002.png new file mode 100644 index 000000000..695c59a69 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_003.png b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_003.png new file mode 100644 index 000000000..fbcb3f305 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_003.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_004.png b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_004.png new file mode 100644 index 000000000..f52fb31ac Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_004.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_thumb.png new file mode 100644 index 000000000..bf9e54df5 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_confound_removal_classification_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_001.png b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_001.png new file mode 100644 index 000000000..14a85ca95 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_002.png b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_002.png new file mode 100644 index 000000000..375c10cb0 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_003.png b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_003.png new file mode 100644 index 000000000..30747abc0 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_003.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_004.png b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_004.png new file mode 100644 index 000000000..a66306f2f Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_004.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_thumb.png new file mode 100644 index 000000000..aa54ceeac Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_example_regression_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_001.png b/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_001.png new file mode 100644 index 000000000..3a4095702 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_002.png b/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_002.png new file mode 100644 index 000000000..d741adbb5 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_thumb.png new file mode 100644 index 000000000..63f26af14 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_groupcv_inspect_svm_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_001.png b/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_001.png new file mode 100644 index 000000000..7397a0f5c Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_002.png b/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_002.png new file mode 100644 index 000000000..e77693519 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_thumb.png new file mode 100644 index 000000000..c44ca6498 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_inspect_random_forest_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_preprocess_001.png b/pr-preview/pr-276/_images/sphx_glr_plot_preprocess_001.png new file mode 100644 index 000000000..1d8e54e43 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_preprocess_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_preprocess_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_preprocess_thumb.png new file mode 100644 index 000000000..df033a7a0 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_preprocess_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_simple_model_comparison_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_simple_model_comparison_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_simple_model_comparison_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_001.png b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_001.png new file mode 100644 index 000000000..dd5becfe0 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_002.png b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_002.png new file mode 100644 index 000000000..49a9ba28b Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_003.png b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_003.png new file mode 100644 index 000000000..5d39f87f5 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_003.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_004.png b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_004.png new file mode 100644 index 000000000..3f8782090 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_004.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_thumb.png b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_thumb.png new file mode 100644 index 000000000..e9ec80e71 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_plot_stratified_kfold_reg_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_apply_to_target_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_apply_to_target_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_apply_to_target_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_binary_inspect_folds_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_binary_inspect_folds_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_binary_inspect_folds_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cbpm_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_cbpm_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cbpm_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_combine_pandas_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_combine_pandas_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_combine_pandas_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_confound_removal_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_confound_removal_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_confound_removal_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_custom_scorers_regression_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_custom_scorers_regression_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_custom_scorers_regression_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_001.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_001.png new file mode 100644 index 000000000..88839b770 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_002.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_002.png new file mode 100644 index 000000000..1140cce34 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_003.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_003.png new file mode 100644 index 000000000..8049d70e2 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_003.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_004.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_004.png new file mode 100644 index 000000000..e02ba2898 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_004.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_005.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_005.png new file mode 100644 index 000000000..606666395 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_005.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_006.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_006.png new file mode 100644 index 000000000..eb19889ce Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_006.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_007.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_007.png new file mode 100644 index 000000000..699559d16 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_007.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_008.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_008.png new file mode 100644 index 000000000..34fa9dc45 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_008.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_thumb.png new file mode 100644 index 000000000..cfe2089e3 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_cv_splitters_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_001.png b/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_001.png new file mode 100644 index 000000000..77860275f Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_001.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_002.png b/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_002.png new file mode 100644 index 000000000..1e2522a95 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_002.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_thumb.png new file mode 100644 index 000000000..0f3ba76e6 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_example_pca_featsets_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_grouped_cv_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_grouped_cv_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_grouped_cv_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_multiple_grids_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_multiple_grids_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_multiple_grids_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_tuning_bayessearch_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_tuning_bayessearch_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_tuning_bayessearch_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_tuning_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_tuning_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameter_tuning_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_hyperparameters_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameters_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_hyperparameters_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_model_comparison_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_model_comparison_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_model_comparison_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_model_evaluation_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_model_evaluation_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_model_evaluation_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_model_inspection_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_model_inspection_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_model_inspection_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_pipeline_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_pipeline_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_pipeline_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_return_confounds_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_return_confounds_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_return_confounds_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_simple_binary_classification_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_simple_binary_classification_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_simple_binary_classification_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_stacked_models_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_stacked_models_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_stacked_models_docs_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_stacked_models_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_stacked_models_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_stacked_models_thumb.png differ diff --git a/pr-preview/pr-276/_images/sphx_glr_run_target_transformer_docs_thumb.png b/pr-preview/pr-276/_images/sphx_glr_run_target_transformer_docs_thumb.png new file mode 100644 index 000000000..8a5fed589 Binary files /dev/null and b/pr-preview/pr-276/_images/sphx_glr_run_target_transformer_docs_thumb.png differ diff --git a/pr-preview/pr-276/_sources/api/base.rst.txt b/pr-preview/pr-276/_sources/api/base.rst.txt new file mode 100644 index 000000000..03c96a00f --- /dev/null +++ b/pr-preview/pr-276/_sources/api/base.rst.txt @@ -0,0 +1,36 @@ +Base +==== + +.. automodule:: julearn.base + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.base + +.. autosummary:: + :nosignatures: + :toctree: generated/ + :template: class.rst + + JuBaseEstimator + JuTransformer + WrapModel + ColumnTypes + ColumnTypesLike + +Functions +--------- + +.. currentmodule:: julearn.base + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + change_column_type + get_column_type + make_type_selector + ensure_column_types diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.PipelineCreator.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.PipelineCreator.rst.txt new file mode 100644 index 000000000..b96740e5a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.PipelineCreator.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.PipelineCreator +----------------------- + +.. currentmodule:: julearn + +.. autoclass:: PipelineCreator + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.PipelineCreator: + +.. minigallery:: julearn.PipelineCreator + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.TargetPipelineCreator.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.TargetPipelineCreator.rst.txt new file mode 100644 index 000000000..817e3f7ea --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.TargetPipelineCreator.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.TargetPipelineCreator +----------------------------- + +.. currentmodule:: julearn + +.. autoclass:: TargetPipelineCreator + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.TargetPipelineCreator: + +.. minigallery:: julearn.TargetPipelineCreator + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.ColumnTypes.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.ColumnTypes.rst.txt new file mode 100644 index 000000000..5228a2417 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.ColumnTypes.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.base.ColumnTypes +------------------------ + +.. currentmodule:: julearn.base + +.. autoclass:: ColumnTypes + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.base.ColumnTypes: + +.. minigallery:: julearn.base.ColumnTypes + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.ColumnTypesLike.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.ColumnTypesLike.rst.txt new file mode 100644 index 000000000..f8ac79c56 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.ColumnTypesLike.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.base.ColumnTypesLike +---------------------------- + +.. currentmodule:: julearn.base + +.. autoclass:: ColumnTypesLike + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.base.ColumnTypesLike: + +.. minigallery:: julearn.base.ColumnTypesLike + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.JuBaseEstimator.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.JuBaseEstimator.rst.txt new file mode 100644 index 000000000..b3e60de26 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.JuBaseEstimator.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.base.JuBaseEstimator +---------------------------- + +.. currentmodule:: julearn.base + +.. autoclass:: JuBaseEstimator + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.base.JuBaseEstimator: + +.. minigallery:: julearn.base.JuBaseEstimator + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.JuTransformer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.JuTransformer.rst.txt new file mode 100644 index 000000000..a5f02997a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.JuTransformer.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.base.JuTransformer +-------------------------- + +.. currentmodule:: julearn.base + +.. autoclass:: JuTransformer + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.base.JuTransformer: + +.. minigallery:: julearn.base.JuTransformer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.WrapModel.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.WrapModel.rst.txt new file mode 100644 index 000000000..4d93de35e --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.WrapModel.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.base.WrapModel +---------------------- + +.. currentmodule:: julearn.base + +.. autoclass:: WrapModel + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.base.WrapModel: + +.. minigallery:: julearn.base.WrapModel + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.change_column_type.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.change_column_type.rst.txt new file mode 100644 index 000000000..a03874b45 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.change_column_type.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.base.change_column_type +------------------------------- + +.. currentmodule:: julearn.base + +.. autofunction:: change_column_type + +.. _sphx_glr_backreferences_julearn.base.change_column_type: + +.. minigallery:: julearn.base.change_column_type + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.ensure_column_types.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.ensure_column_types.rst.txt new file mode 100644 index 000000000..07d1963b6 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.ensure_column_types.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.base.ensure_column_types +-------------------------------- + +.. currentmodule:: julearn.base + +.. autofunction:: ensure_column_types + +.. _sphx_glr_backreferences_julearn.base.ensure_column_types: + +.. minigallery:: julearn.base.ensure_column_types + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.get_column_type.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.get_column_type.rst.txt new file mode 100644 index 000000000..13b060976 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.get_column_type.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.base.get_column_type +---------------------------- + +.. currentmodule:: julearn.base + +.. autofunction:: get_column_type + +.. _sphx_glr_backreferences_julearn.base.get_column_type: + +.. minigallery:: julearn.base.get_column_type + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.base.make_type_selector.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.base.make_type_selector.rst.txt new file mode 100644 index 000000000..fcdcfa955 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.base.make_type_selector.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.base.make_type_selector +------------------------------- + +.. currentmodule:: julearn.base + +.. autofunction:: make_type_selector + +.. _sphx_glr_backreferences_julearn.base.make_type_selector: + +.. minigallery:: julearn.base.make_type_selector + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.inspect.FoldsInspector.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.inspect.FoldsInspector.rst.txt new file mode 100644 index 000000000..6c4ed29e7 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.inspect.FoldsInspector.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.inspect.FoldsInspector +------------------------------ + +.. currentmodule:: julearn.inspect + +.. autoclass:: FoldsInspector + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.inspect.FoldsInspector: + +.. minigallery:: julearn.inspect.FoldsInspector + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.inspect.Inspector.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.inspect.Inspector.rst.txt new file mode 100644 index 000000000..d06848730 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.inspect.Inspector.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.inspect.Inspector +------------------------- + +.. currentmodule:: julearn.inspect + +.. autoclass:: Inspector + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.inspect.Inspector: + +.. minigallery:: julearn.inspect.Inspector + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.inspect.preprocess.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.inspect.preprocess.rst.txt new file mode 100644 index 000000000..426df70c9 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.inspect.preprocess.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.inspect.preprocess +-------------------------- + +.. currentmodule:: julearn.inspect + +.. autofunction:: preprocess + +.. _sphx_glr_backreferences_julearn.inspect.preprocess: + +.. minigallery:: julearn.inspect.preprocess + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.ContinuousStratifiedGroupKFold.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.ContinuousStratifiedGroupKFold.rst.txt new file mode 100644 index 000000000..001c6fbd5 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.ContinuousStratifiedGroupKFold.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.model_selection.ContinuousStratifiedGroupKFold +------------------------------------------------------ + +.. currentmodule:: julearn.model_selection + +.. autoclass:: ContinuousStratifiedGroupKFold + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.model_selection.ContinuousStratifiedGroupKFold: + +.. minigallery:: julearn.model_selection.ContinuousStratifiedGroupKFold + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.ContinuousStratifiedKFold.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.ContinuousStratifiedKFold.rst.txt new file mode 100644 index 000000000..34dedc3d5 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.ContinuousStratifiedKFold.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.model_selection.ContinuousStratifiedKFold +------------------------------------------------- + +.. currentmodule:: julearn.model_selection + +.. autoclass:: ContinuousStratifiedKFold + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.model_selection.ContinuousStratifiedKFold: + +.. minigallery:: julearn.model_selection.ContinuousStratifiedKFold + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.RepeatedContinuousStratifiedGroupKFold.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.RepeatedContinuousStratifiedGroupKFold.rst.txt new file mode 100644 index 000000000..7250a4d38 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.RepeatedContinuousStratifiedGroupKFold.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.model_selection.RepeatedContinuousStratifiedGroupKFold +-------------------------------------------------------------- + +.. currentmodule:: julearn.model_selection + +.. autoclass:: RepeatedContinuousStratifiedGroupKFold + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.model_selection.RepeatedContinuousStratifiedGroupKFold: + +.. minigallery:: julearn.model_selection.RepeatedContinuousStratifiedGroupKFold + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.RepeatedContinuousStratifiedKFold.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.RepeatedContinuousStratifiedKFold.rst.txt new file mode 100644 index 000000000..e0da745c7 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.RepeatedContinuousStratifiedKFold.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.model_selection.RepeatedContinuousStratifiedKFold +--------------------------------------------------------- + +.. currentmodule:: julearn.model_selection + +.. autoclass:: RepeatedContinuousStratifiedKFold + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.model_selection.RepeatedContinuousStratifiedKFold: + +.. minigallery:: julearn.model_selection.RepeatedContinuousStratifiedKFold + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.StratifiedBootstrap.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.StratifiedBootstrap.rst.txt new file mode 100644 index 000000000..18b67f395 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.StratifiedBootstrap.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.model_selection.StratifiedBootstrap +------------------------------------------- + +.. currentmodule:: julearn.model_selection + +.. autoclass:: StratifiedBootstrap + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.model_selection.StratifiedBootstrap: + +.. minigallery:: julearn.model_selection.StratifiedBootstrap + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.get_searcher.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.get_searcher.rst.txt new file mode 100644 index 000000000..a071ddb81 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.get_searcher.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.model_selection.get_searcher +------------------------------------ + +.. currentmodule:: julearn.model_selection + +.. autofunction:: get_searcher + +.. _sphx_glr_backreferences_julearn.model_selection.get_searcher: + +.. minigallery:: julearn.model_selection.get_searcher + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.list_searchers.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.list_searchers.rst.txt new file mode 100644 index 000000000..b27e2513e --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.list_searchers.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.model_selection.list_searchers +-------------------------------------- + +.. currentmodule:: julearn.model_selection + +.. autofunction:: list_searchers + +.. _sphx_glr_backreferences_julearn.model_selection.list_searchers: + +.. minigallery:: julearn.model_selection.list_searchers + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.register_searcher.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.register_searcher.rst.txt new file mode 100644 index 000000000..1d79723e4 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.register_searcher.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.model_selection.register_searcher +----------------------------------------- + +.. currentmodule:: julearn.model_selection + +.. autofunction:: register_searcher + +.. _sphx_glr_backreferences_julearn.model_selection.register_searcher: + +.. minigallery:: julearn.model_selection.register_searcher + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.reset_searcher_register.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.reset_searcher_register.rst.txt new file mode 100644 index 000000000..576adeb47 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.model_selection.reset_searcher_register.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.model_selection.reset_searcher_register +----------------------------------------------- + +.. currentmodule:: julearn.model_selection + +.. autofunction:: reset_searcher_register + +.. _sphx_glr_backreferences_julearn.model_selection.reset_searcher_register: + +.. minigallery:: julearn.model_selection.reset_searcher_register + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.models.dynamic.DynamicSelection.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.models.dynamic.DynamicSelection.rst.txt new file mode 100644 index 000000000..47ba2b9b4 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.models.dynamic.DynamicSelection.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.models.dynamic.DynamicSelection +--------------------------------------- + +.. currentmodule:: julearn.models.dynamic + +.. autoclass:: DynamicSelection + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.models.dynamic.DynamicSelection: + +.. minigallery:: julearn.models.dynamic.DynamicSelection + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.models.get_model.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.models.get_model.rst.txt new file mode 100644 index 000000000..0a58705f1 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.models.get_model.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.models.get_model +------------------------ + +.. currentmodule:: julearn.models + +.. autofunction:: get_model + +.. _sphx_glr_backreferences_julearn.models.get_model: + +.. minigallery:: julearn.models.get_model + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.models.list_models.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.models.list_models.rst.txt new file mode 100644 index 000000000..94a0ad5d4 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.models.list_models.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.models.list_models +-------------------------- + +.. currentmodule:: julearn.models + +.. autofunction:: list_models + +.. _sphx_glr_backreferences_julearn.models.list_models: + +.. minigallery:: julearn.models.list_models + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.models.register_model.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.models.register_model.rst.txt new file mode 100644 index 000000000..9a63603b5 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.models.register_model.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.models.register_model +----------------------------- + +.. currentmodule:: julearn.models + +.. autofunction:: register_model + +.. _sphx_glr_backreferences_julearn.models.register_model: + +.. minigallery:: julearn.models.register_model + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.models.reset_model_register.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.models.reset_model_register.rst.txt new file mode 100644 index 000000000..f90515c99 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.models.reset_model_register.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.models.reset_model_register +----------------------------------- + +.. currentmodule:: julearn.models + +.. autofunction:: reset_model_register + +.. _sphx_glr_backreferences_julearn.models.reset_model_register: + +.. minigallery:: julearn.models.reset_model_register + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.pipeline.JuTargetPipeline.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.pipeline.JuTargetPipeline.rst.txt new file mode 100644 index 000000000..5356521f1 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.pipeline.JuTargetPipeline.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.pipeline.JuTargetPipeline +--------------------------------- + +.. currentmodule:: julearn.pipeline + +.. autoclass:: JuTargetPipeline + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.pipeline.JuTargetPipeline: + +.. minigallery:: julearn.pipeline.JuTargetPipeline + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.pipeline.pipeline_creator.Step.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.pipeline.pipeline_creator.Step.rst.txt new file mode 100644 index 000000000..21498a5a4 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.pipeline.pipeline_creator.Step.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.pipeline.pipeline_creator.Step +-------------------------------------- + +.. currentmodule:: julearn.pipeline.pipeline_creator + +.. autoclass:: Step + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.pipeline.pipeline_creator.Step: + +.. minigallery:: julearn.pipeline.pipeline_creator.Step + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.prepare.check_consistency.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.prepare.check_consistency.rst.txt new file mode 100644 index 000000000..99ec72019 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.prepare.check_consistency.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.prepare.check_consistency +--------------------------------- + +.. currentmodule:: julearn.prepare + +.. autofunction:: check_consistency + +.. _sphx_glr_backreferences_julearn.prepare.check_consistency: + +.. minigallery:: julearn.prepare.check_consistency + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.prepare.prepare_input_data.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.prepare.prepare_input_data.rst.txt new file mode 100644 index 000000000..4e2ac6a9e --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.prepare.prepare_input_data.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.prepare.prepare_input_data +---------------------------------- + +.. currentmodule:: julearn.prepare + +.. autofunction:: prepare_input_data + +.. _sphx_glr_backreferences_julearn.prepare.prepare_input_data: + +.. minigallery:: julearn.prepare.prepare_input_data + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.run_cross_validation.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.run_cross_validation.rst.txt new file mode 100644 index 000000000..7d1bdc377 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.run_cross_validation.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.run_cross_validation +---------------------------- + +.. currentmodule:: julearn + +.. autofunction:: run_cross_validation + +.. _sphx_glr_backreferences_julearn.run_cross_validation: + +.. minigallery:: julearn.run_cross_validation + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.run_fit.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.run_fit.rst.txt new file mode 100644 index 000000000..af0cb272a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.run_fit.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.run_fit +--------------- + +.. currentmodule:: julearn + +.. autofunction:: run_fit + +.. _sphx_glr_backreferences_julearn.run_fit: + +.. minigallery:: julearn.run_fit + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.scoring.check_scoring.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.check_scoring.rst.txt new file mode 100644 index 000000000..57d01c2e4 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.check_scoring.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.scoring.check_scoring +----------------------------- + +.. currentmodule:: julearn.scoring + +.. autofunction:: check_scoring + +.. _sphx_glr_backreferences_julearn.scoring.check_scoring: + +.. minigallery:: julearn.scoring.check_scoring + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.scoring.get_scorer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.get_scorer.rst.txt new file mode 100644 index 000000000..ce9ac7b3a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.get_scorer.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.scoring.get_scorer +-------------------------- + +.. currentmodule:: julearn.scoring + +.. autofunction:: get_scorer + +.. _sphx_glr_backreferences_julearn.scoring.get_scorer: + +.. minigallery:: julearn.scoring.get_scorer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.scoring.list_scorers.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.list_scorers.rst.txt new file mode 100644 index 000000000..300ed7df4 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.list_scorers.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.scoring.list_scorers +---------------------------- + +.. currentmodule:: julearn.scoring + +.. autofunction:: list_scorers + +.. _sphx_glr_backreferences_julearn.scoring.list_scorers: + +.. minigallery:: julearn.scoring.list_scorers + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.scoring.metrics.r2_corr.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.metrics.r2_corr.rst.txt new file mode 100644 index 000000000..ef5beab29 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.metrics.r2_corr.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.scoring.metrics.r2_corr +------------------------------- + +.. currentmodule:: julearn.scoring.metrics + +.. autofunction:: r2_corr + +.. _sphx_glr_backreferences_julearn.scoring.metrics.r2_corr: + +.. minigallery:: julearn.scoring.metrics.r2_corr + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.scoring.metrics.r_corr.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.metrics.r_corr.rst.txt new file mode 100644 index 000000000..69c85ffcb --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.metrics.r_corr.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.scoring.metrics.r_corr +------------------------------ + +.. currentmodule:: julearn.scoring.metrics + +.. autofunction:: r_corr + +.. _sphx_glr_backreferences_julearn.scoring.metrics.r_corr: + +.. minigallery:: julearn.scoring.metrics.r_corr + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.scoring.register_scorer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.register_scorer.rst.txt new file mode 100644 index 000000000..89f626bea --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.register_scorer.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.scoring.register_scorer +------------------------------- + +.. currentmodule:: julearn.scoring + +.. autofunction:: register_scorer + +.. _sphx_glr_backreferences_julearn.scoring.register_scorer: + +.. minigallery:: julearn.scoring.register_scorer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.scoring.reset_scorer_register.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.reset_scorer_register.rst.txt new file mode 100644 index 000000000..0ee8f5a1f --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.scoring.reset_scorer_register.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.scoring.reset_scorer_register +------------------------------------- + +.. currentmodule:: julearn.scoring + +.. autofunction:: reset_scorer_register + +.. _sphx_glr_backreferences_julearn.scoring.reset_scorer_register: + +.. minigallery:: julearn.scoring.reset_scorer_register + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.stats.corrected_ttest.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.stats.corrected_ttest.rst.txt new file mode 100644 index 000000000..829b3fc57 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.stats.corrected_ttest.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.stats.corrected_ttest +----------------------------- + +.. currentmodule:: julearn.stats + +.. autofunction:: corrected_ttest + +.. _sphx_glr_backreferences_julearn.stats.corrected_ttest: + +.. minigallery:: julearn.stats.corrected_ttest + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.CBPM.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.CBPM.rst.txt new file mode 100644 index 000000000..043fe7c57 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.CBPM.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.CBPM +------------------------- + +.. currentmodule:: julearn.transformers + +.. autoclass:: CBPM + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.CBPM: + +.. minigallery:: julearn.transformers.CBPM + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.ChangeColumnTypes.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.ChangeColumnTypes.rst.txt new file mode 100644 index 000000000..8e87771b0 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.ChangeColumnTypes.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.ChangeColumnTypes +-------------------------------------- + +.. currentmodule:: julearn.transformers + +.. autoclass:: ChangeColumnTypes + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.ChangeColumnTypes: + +.. minigallery:: julearn.transformers.ChangeColumnTypes + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.DropColumns.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.DropColumns.rst.txt new file mode 100644 index 000000000..aeacf84fe --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.DropColumns.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.DropColumns +-------------------------------- + +.. currentmodule:: julearn.transformers + +.. autoclass:: DropColumns + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.DropColumns: + +.. minigallery:: julearn.transformers.DropColumns + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.FilterColumns.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.FilterColumns.rst.txt new file mode 100644 index 000000000..9a9be618a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.FilterColumns.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.FilterColumns +---------------------------------- + +.. currentmodule:: julearn.transformers + +.. autoclass:: FilterColumns + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.FilterColumns: + +.. minigallery:: julearn.transformers.FilterColumns + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.JuColumnTransformer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.JuColumnTransformer.rst.txt new file mode 100644 index 000000000..8162bdab6 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.JuColumnTransformer.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.JuColumnTransformer +---------------------------------------- + +.. currentmodule:: julearn.transformers + +.. autoclass:: JuColumnTransformer + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.JuColumnTransformer: + +.. minigallery:: julearn.transformers.JuColumnTransformer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.SetColumnTypes.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.SetColumnTypes.rst.txt new file mode 100644 index 000000000..bd8e715c8 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.SetColumnTypes.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.SetColumnTypes +----------------------------------- + +.. currentmodule:: julearn.transformers + +.. autoclass:: SetColumnTypes + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.SetColumnTypes: + +.. minigallery:: julearn.transformers.SetColumnTypes + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.confound_remover.ConfoundRemover.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.confound_remover.ConfoundRemover.rst.txt new file mode 100644 index 000000000..d498a7af2 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.confound_remover.ConfoundRemover.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.confound_remover.ConfoundRemover +----------------------------------------------------- + +.. currentmodule:: julearn.transformers.confound_remover + +.. autoclass:: ConfoundRemover + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.confound_remover.ConfoundRemover: + +.. minigallery:: julearn.transformers.confound_remover.ConfoundRemover + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.get_transformer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.get_transformer.rst.txt new file mode 100644 index 000000000..000cdd53d --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.get_transformer.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.get_transformer +------------------------------------ + +.. currentmodule:: julearn.transformers + +.. autofunction:: get_transformer + +.. _sphx_glr_backreferences_julearn.transformers.get_transformer: + +.. minigallery:: julearn.transformers.get_transformer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.list_transformers.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.list_transformers.rst.txt new file mode 100644 index 000000000..2e7e96ac7 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.list_transformers.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.list_transformers +-------------------------------------- + +.. currentmodule:: julearn.transformers + +.. autofunction:: list_transformers + +.. _sphx_glr_backreferences_julearn.transformers.list_transformers: + +.. minigallery:: julearn.transformers.list_transformers + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.register_transformer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.register_transformer.rst.txt new file mode 100644 index 000000000..80f57f5fc --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.register_transformer.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.register_transformer +----------------------------------------- + +.. currentmodule:: julearn.transformers + +.. autofunction:: register_transformer + +.. _sphx_glr_backreferences_julearn.transformers.register_transformer: + +.. minigallery:: julearn.transformers.register_transformer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.reset_transformer_register.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.reset_transformer_register.rst.txt new file mode 100644 index 000000000..40f25324d --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.reset_transformer_register.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.reset_transformer_register +----------------------------------------------- + +.. currentmodule:: julearn.transformers + +.. autofunction:: reset_transformer_register + +.. _sphx_glr_backreferences_julearn.transformers.reset_transformer_register: + +.. minigallery:: julearn.transformers.reset_transformer_register + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.JuTargetTransformer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.JuTargetTransformer.rst.txt new file mode 100644 index 000000000..d3631ce34 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.JuTargetTransformer.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.target.JuTargetTransformer +----------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autoclass:: JuTargetTransformer + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.target.JuTargetTransformer: + +.. minigallery:: julearn.transformers.target.JuTargetTransformer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.JuTransformedTargetModel.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.JuTransformedTargetModel.rst.txt new file mode 100644 index 000000000..a0df625c5 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.JuTransformedTargetModel.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.target.JuTransformedTargetModel +---------------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autoclass:: JuTransformedTargetModel + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.target.JuTransformedTargetModel: + +.. minigallery:: julearn.transformers.target.JuTransformedTargetModel + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.TargetConfoundRemover.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.TargetConfoundRemover.rst.txt new file mode 100644 index 000000000..9029cd3c2 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.TargetConfoundRemover.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.target.TargetConfoundRemover +------------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autoclass:: TargetConfoundRemover + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.target.TargetConfoundRemover: + +.. minigallery:: julearn.transformers.target.TargetConfoundRemover + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.TransformedTargetWarning.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.TransformedTargetWarning.rst.txt new file mode 100644 index 000000000..01d5cc050 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.TransformedTargetWarning.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.transformers.target.TransformedTargetWarning +---------------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autoclass:: TransformedTargetWarning + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.transformers.target.TransformedTargetWarning: + +.. minigallery:: julearn.transformers.target.TransformedTargetWarning + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.get_target_transformer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.get_target_transformer.rst.txt new file mode 100644 index 000000000..525db41e3 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.get_target_transformer.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.target.get_target_transformer +-------------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autofunction:: get_target_transformer + +.. _sphx_glr_backreferences_julearn.transformers.target.get_target_transformer: + +.. minigallery:: julearn.transformers.target.get_target_transformer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.list_target_transformers.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.list_target_transformers.rst.txt new file mode 100644 index 000000000..4aff46d05 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.list_target_transformers.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.target.list_target_transformers +---------------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autofunction:: list_target_transformers + +.. _sphx_glr_backreferences_julearn.transformers.target.list_target_transformers: + +.. minigallery:: julearn.transformers.target.list_target_transformers + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.register_target_transformer.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.register_target_transformer.rst.txt new file mode 100644 index 000000000..011c0dc7a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.register_target_transformer.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.target.register_target_transformer +------------------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autofunction:: register_target_transformer + +.. _sphx_glr_backreferences_julearn.transformers.target.register_target_transformer: + +.. minigallery:: julearn.transformers.target.register_target_transformer + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.reset_target_transformer_register.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.reset_target_transformer_register.rst.txt new file mode 100644 index 000000000..85fce578b --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.transformers.target.reset_target_transformer_register.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.transformers.target.reset_target_transformer_register +------------------------------------------------------------- + +.. currentmodule:: julearn.transformers.target + +.. autofunction:: reset_target_transformer_register + +.. _sphx_glr_backreferences_julearn.transformers.target.reset_target_transformer_register: + +.. minigallery:: julearn.transformers.target.reset_target_transformer_register + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.configure_logging.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.configure_logging.rst.txt new file mode 100644 index 000000000..bfc7c1a20 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.configure_logging.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.utils.configure_logging +------------------------------- + +.. currentmodule:: julearn.utils + +.. autofunction:: configure_logging + +.. _sphx_glr_backreferences_julearn.utils.configure_logging: + +.. minigallery:: julearn.utils.configure_logging + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.logger.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.logger.rst.txt new file mode 100644 index 000000000..4c7a00bd2 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.logger.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.utils.logger +-------------------- + +.. currentmodule:: julearn.utils + +.. autoclass:: logger + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.utils.logger: + +.. minigallery:: julearn.utils.logger + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.raise_error.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.raise_error.rst.txt new file mode 100644 index 000000000..f21c6a64a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.raise_error.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.utils.raise_error +------------------------- + +.. currentmodule:: julearn.utils + +.. autofunction:: raise_error + +.. _sphx_glr_backreferences_julearn.utils.raise_error: + +.. minigallery:: julearn.utils.raise_error + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLike.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLike.rst.txt new file mode 100644 index 000000000..9fc338f8a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLike.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.utils.typing.EstimatorLike +---------------------------------- + +.. currentmodule:: julearn.utils.typing + +.. autoclass:: EstimatorLike + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.utils.typing.EstimatorLike: + +.. minigallery:: julearn.utils.typing.EstimatorLike + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFit1.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFit1.rst.txt new file mode 100644 index 000000000..b45cf024b --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFit1.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.utils.typing.EstimatorLikeFit1 +-------------------------------------- + +.. currentmodule:: julearn.utils.typing + +.. autoclass:: EstimatorLikeFit1 + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.utils.typing.EstimatorLikeFit1: + +.. minigallery:: julearn.utils.typing.EstimatorLikeFit1 + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFit2.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFit2.rst.txt new file mode 100644 index 000000000..cf1509c7d --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFit2.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.utils.typing.EstimatorLikeFit2 +-------------------------------------- + +.. currentmodule:: julearn.utils.typing + +.. autoclass:: EstimatorLikeFit2 + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.utils.typing.EstimatorLikeFit2: + +.. minigallery:: julearn.utils.typing.EstimatorLikeFit2 + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFity.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFity.rst.txt new file mode 100644 index 000000000..e22f70346 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.EstimatorLikeFity.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.utils.typing.EstimatorLikeFity +-------------------------------------- + +.. currentmodule:: julearn.utils.typing + +.. autoclass:: EstimatorLikeFity + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.utils.typing.EstimatorLikeFity: + +.. minigallery:: julearn.utils.typing.EstimatorLikeFity + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.JuEstimatorLike.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.JuEstimatorLike.rst.txt new file mode 100644 index 000000000..4d1a2c51a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.typing.JuEstimatorLike.rst.txt @@ -0,0 +1,27 @@ + +.. note:: + + This page is a reference documentation. It only explains the class signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + +julearn.utils.typing.JuEstimatorLike +------------------------------------ + +.. currentmodule:: julearn.utils.typing + +.. autoclass:: JuEstimatorLike + :inherited-members: + + + .. automethod:: __init__ + + +.. _sphx_glr_backreferences_julearn.utils.typing.JuEstimatorLike: + +.. minigallery:: julearn.utils.typing.JuEstimatorLike + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.utils.warn_with_log.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.utils.warn_with_log.rst.txt new file mode 100644 index 000000000..33a7aee88 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.utils.warn_with_log.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.utils.warn_with_log +--------------------------- + +.. currentmodule:: julearn.utils + +.. autofunction:: warn_with_log + +.. _sphx_glr_backreferences_julearn.utils.warn_with_log: + +.. minigallery:: julearn.utils.warn_with_log + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/generated/julearn.viz.plot_scores.rst.txt b/pr-preview/pr-276/_sources/api/generated/julearn.viz.plot_scores.rst.txt new file mode 100644 index 000000000..18e937a60 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/generated/julearn.viz.plot_scores.rst.txt @@ -0,0 +1,22 @@ +.. note:: + + This page is a reference documentation. It only explains the function signature, and not how to use it. + Please refer to the :ref:`What you really need to know ` section for the big picture. + + +julearn.viz.plot_scores +----------------------- + +.. currentmodule:: julearn.viz + +.. autofunction:: plot_scores + +.. _sphx_glr_backreferences_julearn.viz.plot_scores: + +.. minigallery:: julearn.viz.plot_scores + :add-heading: + + +.. raw:: html + +
\ No newline at end of file diff --git a/pr-preview/pr-276/_sources/api/index.rst.txt b/pr-preview/pr-276/_sources/api/index.rst.txt new file mode 100644 index 000000000..31d1f1f17 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/index.rst.txt @@ -0,0 +1,20 @@ +.. _api: + +API Reference +============= + +.. toctree:: + :maxdepth: 1 + + main.rst + pipeline.rst + model_selection.rst + base.rst + inspect.rst + models.rst + scoring.rst + transformers.rst + utils.rst + prepare.rst + stats.rst + viz.rst diff --git a/pr-preview/pr-276/_sources/api/inspect.rst.txt b/pr-preview/pr-276/_sources/api/inspect.rst.txt new file mode 100644 index 000000000..4768d930a --- /dev/null +++ b/pr-preview/pr-276/_sources/api/inspect.rst.txt @@ -0,0 +1,30 @@ +Inspect +======= + +.. automodule:: julearn.inspect + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.inspect + +.. autosummary:: + :nosignatures: + :toctree: generated/ + :template: class.rst + + Inspector + FoldsInspector + +Functions +--------- + +.. currentmodule:: julearn.inspect + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + preprocess diff --git a/pr-preview/pr-276/_sources/api/main.rst.txt b/pr-preview/pr-276/_sources/api/main.rst.txt new file mode 100644 index 000000000..7b2a5a9a6 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/main.rst.txt @@ -0,0 +1,18 @@ +Main API +======== + +.. automodule:: julearn.api + :no-members: + :no-inherited-members: + +Functions +--------- + +.. currentmodule:: julearn + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + run_cross_validation + run_fit diff --git a/pr-preview/pr-276/_sources/api/model_selection.rst.txt b/pr-preview/pr-276/_sources/api/model_selection.rst.txt new file mode 100644 index 000000000..19a5bbe2c --- /dev/null +++ b/pr-preview/pr-276/_sources/api/model_selection.rst.txt @@ -0,0 +1,38 @@ +.. _model_selection: + +Model Selection +=============== + +.. automodule:: julearn.model_selection + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.model_selection + +.. autosummary:: + :nosignatures: + :toctree: generated/ + :template: class.rst + + ContinuousStratifiedKFold + RepeatedContinuousStratifiedKFold + ContinuousStratifiedGroupKFold + RepeatedContinuousStratifiedGroupKFold + StratifiedBootstrap + +Functions +--------- + +.. currentmodule:: julearn.model_selection + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + get_searcher + list_searchers + register_searcher + reset_searcher_register diff --git a/pr-preview/pr-276/_sources/api/models.rst.txt b/pr-preview/pr-276/_sources/api/models.rst.txt new file mode 100644 index 000000000..7fcb9c2e2 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/models.rst.txt @@ -0,0 +1,44 @@ +Models +====== + +.. automodule:: julearn.models + :no-members: + :no-inherited-members: + +Functions +--------- + +.. currentmodule:: julearn.models + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + list_models + get_model + register_model + reset_model_register + +Dynamic Selection (DESLib) +========================== + +.. important:: + This module requires the ``deslib`` optional dependencies. Please install + ``julearn`` with the ``deslib`` extra to use this module + (see :ref:`install_optional_dependencies`). + +.. automodule:: julearn.models.dynamic + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.models.dynamic + +.. autosummary:: + :nosignatures: + :toctree: generated/ + :template: class.rst + + DynamicSelection diff --git a/pr-preview/pr-276/_sources/api/pipeline.rst.txt b/pr-preview/pr-276/_sources/api/pipeline.rst.txt new file mode 100644 index 000000000..17ba8b499 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/pipeline.rst.txt @@ -0,0 +1,21 @@ +Pipeline +======== + +.. automodule:: julearn.pipeline + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn + +.. autosummary:: + :nosignatures: + :toctree: generated/ + :template: class.rst + + PipelineCreator + TargetPipelineCreator + pipeline.JuTargetPipeline + pipeline.pipeline_creator.Step diff --git a/pr-preview/pr-276/_sources/api/prepare.rst.txt b/pr-preview/pr-276/_sources/api/prepare.rst.txt new file mode 100644 index 000000000..69dcacdf1 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/prepare.rst.txt @@ -0,0 +1,18 @@ +Prepare +======= + +.. automodule:: julearn.prepare + :no-members: + :no-inherited-members: + +Functions +--------- + +.. currentmodule:: julearn.prepare + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + prepare_input_data + check_consistency diff --git a/pr-preview/pr-276/_sources/api/scoring.rst.txt b/pr-preview/pr-276/_sources/api/scoring.rst.txt new file mode 100644 index 000000000..26d12dd03 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/scoring.rst.txt @@ -0,0 +1,40 @@ +Scoring +======= + +.. automodule:: julearn.scoring + :no-members: + :no-inherited-members: + +Functions +--------- + +.. currentmodule:: julearn.scoring + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + get_scorer + list_scorers + register_scorer + reset_scorer_register + check_scoring + +Scoring Metrics +=============== + +.. automodule:: julearn.scoring.metrics + :no-members: + :no-inherited-members: + +Functions +--------- + +.. currentmodule:: julearn.scoring.metrics + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + r_corr + r2_corr diff --git a/pr-preview/pr-276/_sources/api/stats.rst.txt b/pr-preview/pr-276/_sources/api/stats.rst.txt new file mode 100644 index 000000000..b32dcd9d9 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/stats.rst.txt @@ -0,0 +1,17 @@ +Stats +===== + +.. automodule:: julearn.stats + :no-members: + :no-inherited-members: + +Functions +--------- + +.. currentmodule:: julearn.stats + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + corrected_ttest diff --git a/pr-preview/pr-276/_sources/api/transformers.rst.txt b/pr-preview/pr-276/_sources/api/transformers.rst.txt new file mode 100644 index 000000000..cc547f423 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/transformers.rst.txt @@ -0,0 +1,72 @@ +Transformers +============ + +.. automodule:: julearn.transformers + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.transformers + +.. autosummary:: + :toctree: generated/ + :template: class.rst + + DropColumns + ChangeColumnTypes + SetColumnTypes + FilterColumns + CBPM + JuColumnTransformer + confound_remover.ConfoundRemover + +Functions +--------- + +.. currentmodule:: julearn.transformers + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + list_transformers + get_transformer + register_transformer + reset_transformer_register + +Target Transformers +=================== + +.. automodule:: julearn.transformers.target + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.transformers.target + +.. autosummary:: + :toctree: generated/ + :template: class.rst + + JuTransformedTargetModel + JuTargetTransformer + TargetConfoundRemover + TransformedTargetWarning + +Functions +--------- + +.. currentmodule:: julearn.transformers.target + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + get_target_transformer + list_target_transformers + register_target_transformer + reset_target_transformer_register diff --git a/pr-preview/pr-276/_sources/api/utils.rst.txt b/pr-preview/pr-276/_sources/api/utils.rst.txt new file mode 100644 index 000000000..d3888ace1 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/utils.rst.txt @@ -0,0 +1,52 @@ +Utils +===== + +.. automodule:: julearn.utils + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.utils + +.. autosummary:: + :toctree: generated/ + :template: class.rst + + logger + +Functions +--------- +.. currentmodule:: julearn.utils + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + configure_logging + raise_error + warn_with_log + + +Typing +====== + +.. automodule:: julearn.utils.typing + :no-members: + :no-inherited-members: + +Classes +------- + +.. currentmodule:: julearn.utils.typing + +.. autosummary:: + :toctree: generated/ + :template: class.rst + + JuEstimatorLike + EstimatorLike + EstimatorLikeFit1 + EstimatorLikeFit2 + EstimatorLikeFity diff --git a/pr-preview/pr-276/_sources/api/viz.rst.txt b/pr-preview/pr-276/_sources/api/viz.rst.txt new file mode 100644 index 000000000..712986e09 --- /dev/null +++ b/pr-preview/pr-276/_sources/api/viz.rst.txt @@ -0,0 +1,27 @@ +Visualization +============= + +.. warning:: + This module is experimental. This means that it may change in the future + and the documentation is not complete. Particularly, the dependencies on + which this module relies might be beta releases of other packages. + +.. important:: + This module requires the ``viz`` optional dependencies. Please install + ``julearn`` with the ``viz`` extra to use this module + (see :ref:`install_optional_dependencies`). + +.. automodule:: julearn.viz + :no-members: + :no-inherited-members: + +Functions +--------- + +.. currentmodule:: julearn.viz + +.. autosummary:: + :toctree: generated/ + :template: function.rst + + plot_scores diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/index.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/index.rst.txt new file mode 100644 index 000000000..f47bbbd31 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/index.rst.txt @@ -0,0 +1,138 @@ +:orphan: + +Starting with ``julearn`` +========================= + +Examples showing how to use basic ``julearn`` functionality. + + + +.. raw:: html + +
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/00_starting/images/thumb/sphx_glr_run_combine_pandas_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_00_starting_run_combine_pandas.py` + +.. raw:: html + +
Working with pandas
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/00_starting/images/thumb/sphx_glr_run_simple_binary_classification_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_00_starting_run_simple_binary_classification.py` + +.. raw:: html + +
Simple Binary Classification
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/00_starting/images/thumb/sphx_glr_run_grouped_cv_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_00_starting_run_grouped_cv.py` + +.. raw:: html + +
Grouped CV
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/00_starting/images/thumb/sphx_glr_plot_cm_acc_multiclass_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_00_starting_plot_cm_acc_multiclass.py` + +.. raw:: html + +
Multiclass Classification
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/00_starting/images/thumb/sphx_glr_plot_stratified_kfold_reg_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_00_starting_plot_stratified_kfold_reg.py` + +.. raw:: html + +
Stratified K-fold CV for regression analysis
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/00_starting/images/thumb/sphx_glr_plot_example_regression_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_00_starting_plot_example_regression.py` + +.. raw:: html + +
Regression Analysis
+
+ + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /auto_examples/00_starting/run_combine_pandas + /auto_examples/00_starting/run_simple_binary_classification + /auto_examples/00_starting/run_grouped_cv + /auto_examples/00_starting/plot_cm_acc_multiclass + /auto_examples/00_starting/plot_stratified_kfold_reg + /auto_examples/00_starting/plot_example_regression + + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_cm_acc_multiclass.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_cm_acc_multiclass.rst.txt new file mode 100644 index 000000000..a13a75cb8 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_cm_acc_multiclass.rst.txt @@ -0,0 +1,566 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/00_starting/plot_cm_acc_multiclass.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_00_starting_plot_cm_acc_multiclass.py: + + +Multiclass Classification +========================= + +This example uses the ``iris`` dataset and performs multiclass +classification using a Support Vector Machine classifier and plots +heatmaps for cross-validation accuracies and plots confusion matrix +for the test data. + +.. GENERATED FROM PYTHON SOURCE LINES 11-26 + +.. code-block:: default + + # Authors: Shammi More + # Federico Raimondo + # License: AGPL + + import pandas as pd + import seaborn as sns + import numpy as np + import matplotlib.pyplot as plt + from seaborn import load_dataset + from sklearn.model_selection import train_test_split, RepeatedKFold + from sklearn.metrics import confusion_matrix + + from julearn import run_cross_validation + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 27-28 + +Set the logging level to info to see extra information + +.. GENERATED FROM PYTHON SOURCE LINES 28-30 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:25,244 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:25,244 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:25,244 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:25,244 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:25,244 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:25,244 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:25,244 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 31-32 + +load the iris data from seaborn + +.. GENERATED FROM PYTHON SOURCE LINES 32-36 + +.. code-block:: default + + df_iris = load_dataset("iris") + X = ["sepal_length", "sepal_width", "petal_length"] + y = "species" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 37-38 + +Split the dataset into train and test + +.. GENERATED FROM PYTHON SOURCE LINES 38-42 + +.. code-block:: default + + train_iris, test_iris = train_test_split( + df_iris, test_size=0.2, stratify=df_iris[y], random_state=200 + ) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 43-46 + +We want to perform multiclass classification as iris dataset contains 3 kinds +of species. We will first zscore all the features and then train a support +vector machine classifier. + +.. GENERATED FROM PYTHON SOURCE LINES 46-60 + +.. code-block:: default + + + cv = RepeatedKFold(n_splits=5, n_repeats=5, random_state=200) + scores, model_iris = run_cross_validation( + X=X, + y=y, + data=train_iris, + model="svm", + preprocess="zscore", + problem_type="classification", + cv=cv, + scoring=["accuracy"], + return_estimator="final", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:25,247 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:25,247 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:25,247 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:25,248 - julearn - INFO - Target: species + 2024-10-17 13:53:25,248 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:25,248 - julearn - INFO - X_types:{} + 2024-10-17 13:53:25,248 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:25,248 - julearn - INFO - ==================== + 2024-10-17 13:53:25,249 - julearn - INFO - + 2024-10-17 13:53:25,249 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:25,249 - julearn - INFO - Step added + 2024-10-17 13:53:25,249 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:25,249 - julearn - INFO - Step added + 2024-10-17 13:53:25,249 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:25,249 - julearn - INFO - ==================== + 2024-10-17 13:53:25,250 - julearn - INFO - + 2024-10-17 13:53:25,250 - julearn - INFO - = Data Information = + 2024-10-17 13:53:25,250 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:25,250 - julearn - INFO - Number of samples: 120 + 2024-10-17 13:53:25,250 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:25,250 - julearn - INFO - ==================== + 2024-10-17 13:53:25,250 - julearn - INFO - + 2024-10-17 13:53:25,250 - julearn - INFO - Number of classes: 3 + 2024-10-17 13:53:25,250 - julearn - INFO - Target type: object + 2024-10-17 13:53:25,250 - julearn - INFO - Class distributions: species + versicolor 40 + virginica 40 + setosa 40 + Name: count, dtype: int64 + 2024-10-17 13:53:25,251 - julearn - INFO - Using outer CV scheme RepeatedKFold(n_repeats=5, n_splits=5, random_state=200) (incl. final model) + 2024-10-17 13:53:25,251 - julearn - INFO - Multi-class classification problem detected #classes = 3. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 61-62 + +The scores dataframe has all the values for each CV split. + +.. GENERATED FROM PYTHON SOURCE LINES 62-65 + +.. code-block:: default + + + scores.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
fit_timescore_timetest_accuracyn_trainn_testrepeatfoldcv_mdsum
00.0048490.0026960.916667962400fa5ab7a2b930761687a8e82d9971ebca
10.0044680.0025400.833333962401fa5ab7a2b930761687a8e82d9971ebca
20.0044220.0025570.958333962402fa5ab7a2b930761687a8e82d9971ebca
30.0044230.0025430.916667962403fa5ab7a2b930761687a8e82d9971ebca
40.0043640.0025510.833333962404fa5ab7a2b930761687a8e82d9971ebca
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 66-67 + +Now we can get the accuracy per fold and repetition: + +.. GENERATED FROM PYTHON SOURCE LINES 67-73 + +.. code-block:: default + + + df_accuracy = scores.set_index(["repeat", "fold"])["test_accuracy"].unstack() + df_accuracy.index.name = "Repeats" + df_accuracy.columns.name = "K-fold splits" + df_accuracy + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
K-fold splits01234
Repeats
00.9166670.8333330.9583330.9166670.833333
10.8750000.8333330.9166670.8333330.833333
20.7500000.9166670.9166670.9583330.916667
31.0000000.7916670.8750001.0000000.791667
40.8750000.8333330.8750000.9166670.958333
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 74-75 + +Plot heatmap of accuracy over all repeats and CV splits + +.. GENERATED FROM PYTHON SOURCE LINES 75-80 + +.. code-block:: default + + sns.set(font_scale=1.2) + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.heatmap(df_accuracy, cmap="YlGnBu") + plt.title("Cross-validation Accuracy") + + + + +.. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_cm_acc_multiclass_001.png + :alt: Cross-validation Accuracy + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_cm_acc_multiclass_001.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Text(0.5, 1.0, 'Cross-validation Accuracy') + + + +.. GENERATED FROM PYTHON SOURCE LINES 81-83 + +We can also test our final model's accuracy and plot the confusion matrix +for the test data as an annotated heatmap + +.. GENERATED FROM PYTHON SOURCE LINES 83-89 + +.. code-block:: default + + y_true = test_iris[y] + y_pred = model_iris.predict(test_iris[X]) + cm = confusion_matrix(y_true, y_pred, labels=np.unique(y_true)) + + print(cm) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + [[9 1 0] + [0 9 1] + [0 2 8]] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 90-92 + +Now that we have our confusion matrix, let's build another matrix with +annotations. + +.. GENERATED FROM PYTHON SOURCE LINES 92-106 + +.. code-block:: default + + cm_sum = np.sum(cm, axis=1, keepdims=True) + cm_perc = cm / cm_sum.astype(float) * 100 + annot = np.empty_like(cm).astype(str) + nrows, ncols = cm.shape + for i in range(nrows): + for j in range(ncols): + c = cm[i, j] + p = cm_perc[i, j] + if c == 0: + annot[i, j] = "" + else: + s = cm_sum[i] + annot[i, j] = "%.1f%%\n%d/%d" % (p, c, s) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/examples/00_starting/plot_cm_acc_multiclass.py:104: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.) + annot[i, j] = "%.1f%%\n%d/%d" % (p, c, s) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 107-109 + +Finally we create another dataframe with the confusion matrix and plot +the heatmap with annotations. + +.. GENERATED FROM PYTHON SOURCE LINES 109-116 + +.. code-block:: default + + cm = pd.DataFrame(cm, index=np.unique(y_true), columns=np.unique(y_true)) + cm.index.name = "Actual" + cm.columns.name = "Predicted" + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.heatmap(cm, cmap="YlGnBu", annot=annot, fmt="", ax=ax) + plt.title("Confusion matrix") + + + +.. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_cm_acc_multiclass_002.png + :alt: Confusion matrix + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_cm_acc_multiclass_002.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Text(0.5, 1.0, 'Confusion matrix') + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.534 seconds) + + +.. _sphx_glr_download_auto_examples_00_starting_plot_cm_acc_multiclass.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_cm_acc_multiclass.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_cm_acc_multiclass.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_example_regression.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_example_regression.rst.txt new file mode 100644 index 000000000..035f685fa --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_example_regression.rst.txt @@ -0,0 +1,632 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/00_starting/plot_example_regression.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_00_starting_plot_example_regression.py: + + +Regression Analysis +=================== + +This example uses the ``diabetes`` data from ``sklearn datasets`` and performs +a regression analysis using a Ridge Regression model. + +.. GENERATED FROM PYTHON SOURCE LINES 9-24 + +.. code-block:: default + + # Authors: Shammi More + # Federico Raimondo + # License: AGPL + + import pandas as pd + import seaborn as sns + import numpy as np + import matplotlib.pyplot as plt + from sklearn.datasets import load_diabetes + from sklearn.metrics import mean_absolute_error + from sklearn.model_selection import train_test_split + + from julearn import run_cross_validation + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 25-26 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 26-28 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:26,944 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:26,944 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:26,944 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:26,945 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:26,945 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:26,945 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:26,945 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 29-30 + +Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +.. code-block:: default + + features, target = load_diabetes(return_X_y=True, as_frame=True) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-37 + +Dataset contains ten variables age, sex, body mass index, average blood +pressure, and six blood serum measurements (s1-s6) diabetes patients and +a quantitative measure of disease progression one year after baseline which +is the target we are interested in predicting. + +.. GENERATED FROM PYTHON SOURCE LINES 37-41 + +.. code-block:: default + + + print("Features: \n", features.head()) + print("Target: \n", target.describe()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Features: + age sex bmi ... s4 s5 s6 + 0 0.038076 0.050680 0.061696 ... -0.002592 0.019907 -0.017646 + 1 -0.001882 -0.044642 -0.051474 ... -0.039493 -0.068332 -0.092204 + 2 0.085299 0.050680 0.044451 ... -0.002592 0.002861 -0.025930 + 3 -0.089063 -0.044642 -0.011595 ... 0.034309 0.022688 -0.009362 + 4 0.005383 -0.044642 -0.036385 ... -0.002592 -0.031988 -0.046641 + + [5 rows x 10 columns] + Target: + count 442.000000 + mean 152.133484 + std 77.093005 + min 25.000000 + 25% 87.000000 + 50% 140.500000 + 75% 211.500000 + max 346.000000 + Name: target, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 42-44 + +Let's combine features and target together in one dataframe and define X +and y + +.. GENERATED FROM PYTHON SOURCE LINES 44-49 + +.. code-block:: default + + data_diabetes = pd.concat([features, target], axis=1) + + X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] + y = "target" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 50-51 + +Calculate correlations between the features/variables and plot it as heat map. + +.. GENERATED FROM PYTHON SOURCE LINES 51-63 + +.. code-block:: default + + corr = data_diabetes.corr() + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.set(font_scale=1.2) + sns.heatmap( + corr, + xticklabels=corr.columns, + yticklabels=corr.columns, + annot=True, + fmt="0.1f", + ) + + + + +.. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_001.png + :alt: plot example regression + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_001.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 64-65 + +Split the dataset into train and test. + +.. GENERATED FROM PYTHON SOURCE LINES 65-67 + +.. code-block:: default + + train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 68-70 + +Train a ridge regression model on train dataset and use mean absolute error +for scoring. + +.. GENERATED FROM PYTHON SOURCE LINES 70-81 + +.. code-block:: default + + scores, model = run_cross_validation( + X=X, + y=y, + data=train_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring="neg_mean_absolute_error", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:27,143 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:27,143 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:27,143 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:27,143 - julearn - INFO - Target: target + 2024-10-17 13:53:27,143 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:27,143 - julearn - INFO - X_types:{} + 2024-10-17 13:53:27,144 - julearn - WARNING - The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:27,144 - julearn - INFO - ==================== + 2024-10-17 13:53:27,144 - julearn - INFO - + 2024-10-17 13:53:27,144 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:27,145 - julearn - INFO - Step added + 2024-10-17 13:53:27,145 - julearn - INFO - Adding step ridge that applies to ColumnTypes + 2024-10-17 13:53:27,145 - julearn - INFO - Step added + 2024-10-17 13:53:27,145 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:27,145 - julearn - INFO - ==================== + 2024-10-17 13:53:27,145 - julearn - INFO - + 2024-10-17 13:53:27,145 - julearn - INFO - = Data Information = + 2024-10-17 13:53:27,145 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:27,146 - julearn - INFO - Number of samples: 309 + 2024-10-17 13:53:27,146 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:27,146 - julearn - INFO - ==================== + 2024-10-17 13:53:27,146 - julearn - INFO - + 2024-10-17 13:53:27,146 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:27,146 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 82-83 + +The scores dataframe has all the values for each CV split. + +.. GENERATED FROM PYTHON SOURCE LINES 83-86 + +.. code-block:: default + + + scores.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
fit_timescore_timetest_scoren_trainn_testrepeatfoldcv_mdsum
00.0051480.002323-48.7838742476200b10eef89b4192178d482d7a1587a248a
10.0044560.002264-47.5735682476201b10eef89b4192178d482d7a1587a248a
20.0045190.002297-37.6174742476202b10eef89b4192178d482d7a1587a248a
30.0044820.002329-47.6868522476203b10eef89b4192178d482d7a1587a248a
40.0043980.002251-45.5586552486104b10eef89b4192178d482d7a1587a248a
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 87-88 + +Mean value of mean absolute error across CV + +.. GENERATED FROM PYTHON SOURCE LINES 88-90 + +.. code-block:: default + + print(scores["test_score"].mean() * -1) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 45.444084441470615 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 91-92 + +Now we can get the MAE fold and repetition: + +.. GENERATED FROM PYTHON SOURCE LINES 92-99 + +.. code-block:: default + + + df_mae = scores.set_index(["repeat", "fold"])["test_score"].unstack() * -1 + df_mae.index.name = "Repeats" + df_mae.columns.name = "K-fold splits" + + df_mae + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
K-fold splits01234
Repeats
048.78387447.57356837.61747447.68685245.558655
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 100-101 + +Plot heatmap of mean absolute error (MAE) over all repeats and CV splits. + +.. GENERATED FROM PYTHON SOURCE LINES 101-105 + +.. code-block:: default + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.heatmap(df_mae, cmap="YlGnBu") + plt.title("Cross-validation MAE") + + + + +.. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_002.png + :alt: Cross-validation MAE + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_002.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Text(0.5, 1.0, 'Cross-validation MAE') + + + +.. GENERATED FROM PYTHON SOURCE LINES 106-107 + +Let's plot the feature importance using the coefficients of the trained model. + +.. GENERATED FROM PYTHON SOURCE LINES 107-118 + +.. code-block:: default + + features = pd.DataFrame({"Features": X, "importance": model["ridge"].coef_}) + features.sort_values(by=["importance"], ascending=True, inplace=True) + features["positive"] = features["importance"] > 0 + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + features.set_index("Features", inplace=True) + features.importance.plot( + kind="barh", color=features.positive.map({True: "blue", False: "red"}) + ) + ax.set(xlabel="Importance", title="Variable importance for Ridge Regression") + + + + +.. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_003.png + :alt: Variable importance for Ridge Regression + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_003.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + [Text(0.5, 40.249999999999986, 'Importance'), Text(0.5, 1.0, 'Variable importance for Ridge Regression')] + + + +.. GENERATED FROM PYTHON SOURCE LINES 119-121 + +Use the final model to make predictions on test data and plot scatterplot +of true values vs predicted values. + +.. GENERATED FROM PYTHON SOURCE LINES 121-145 + +.. code-block:: default + + + y_true = test_diabetes[y] + y_pred = model.predict(test_diabetes[X]) + mae = format(mean_absolute_error(y_true, y_pred), ".2f") + corr = format(np.corrcoef(y_pred, y_true)[1, 0], ".2f") + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.set_style("darkgrid") + plt.scatter(y_true, y_pred) + plt.plot(y_true, y_true) + xmin, xmax = ax.get_xlim() + ymin, ymax = ax.get_ylim() + text = "MAE: " + str(mae) + " CORR: " + str(corr) + ax.set(xlabel="True values", ylabel="Predicted values") + plt.title("Actual vs Predicted") + plt.text( + xmax - 0.01 * xmax, + ymax - 0.01 * ymax, + text, + verticalalignment="top", + horizontalalignment="right", + fontsize=12, + ) + plt.axis("scaled") + + + +.. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_004.png + :alt: Actual vs Predicted + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_example_regression_004.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + (9.649999999999999, 347.35, 9.649999999999999, 347.35) + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.669 seconds) + + +.. _sphx_glr_download_auto_examples_00_starting_plot_example_regression.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_example_regression.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_example_regression.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_stratified_kfold_reg.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_stratified_kfold_reg.rst.txt new file mode 100644 index 000000000..fe9f5c54b --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/plot_stratified_kfold_reg.rst.txt @@ -0,0 +1,487 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/00_starting/plot_stratified_kfold_reg.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_00_starting_plot_stratified_kfold_reg.py: + + +Stratified K-fold CV for regression analysis +============================================ + +This example uses the ``diabetes`` data from ``sklearn datasets`` to +perform stratified Kfold CV for a regression problem, + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 10-25 + +.. code-block:: default + + # Authors: Shammi More + # Federico Raimondo + # Leonard Sasse + # License: AGPL + + import pandas as pd + import seaborn as sns + import matplotlib.pyplot as plt + from sklearn.datasets import load_diabetes + from sklearn.model_selection import KFold + + from julearn import run_cross_validation + from julearn.utils import configure_logging + from julearn.model_selection import ContinuousStratifiedKFold + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 26-27 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 27-29 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:25,966 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:25,966 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:25,966 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:25,966 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:25,966 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:25,966 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:25,966 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 30-31 + +Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. + +.. GENERATED FROM PYTHON SOURCE LINES 31-33 + +.. code-block:: default + + features, target = load_diabetes(return_X_y=True, as_frame=True) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 34-38 + +Dataset contains ten variables age, sex, body mass index, average blood +pressure, and six blood serum measurements (s1-s6) diabetes patients and +a quantitative measure of disease progression one year after baseline which +is the target we are interested in predicting. + +.. GENERATED FROM PYTHON SOURCE LINES 38-42 + +.. code-block:: default + + + print("Features: \n", features.head()) + print("Target: \n", target.describe()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Features: + age sex bmi ... s4 s5 s6 + 0 0.038076 0.050680 0.061696 ... -0.002592 0.019907 -0.017646 + 1 -0.001882 -0.044642 -0.051474 ... -0.039493 -0.068332 -0.092204 + 2 0.085299 0.050680 0.044451 ... -0.002592 0.002861 -0.025930 + 3 -0.089063 -0.044642 -0.011595 ... 0.034309 0.022688 -0.009362 + 4 0.005383 -0.044642 -0.036385 ... -0.002592 -0.031988 -0.046641 + + [5 rows x 10 columns] + Target: + count 442.000000 + mean 152.133484 + std 77.093005 + min 25.000000 + 25% 87.000000 + 50% 140.500000 + 75% 211.500000 + max 346.000000 + Name: target, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 43-46 + +Let's combine features and target together in one dataframe and create some +outliers to see the difference in model performance with and without +stratification. + +.. GENERATED FROM PYTHON SOURCE LINES 46-60 + +.. code-block:: default + + + data_df = pd.concat([features, target], axis=1) + + # Create outliers for test purpose + new_df = data_df[(data_df.target > 145) & (data_df.target <= 150)] + new_df["target"] = [590, 580, 597, 595, 590, 590, 600] + data_df = pd.concat([data_df, new_df], axis=0) + data_df = data_df.reset_index(drop=True) + + # Define X, y + X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] + y = "target" + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/examples/00_starting/plot_stratified_kfold_reg.py:51: SettingWithCopyWarning: + A value is trying to be set on a copy of a slice from a DataFrame. + Try using .loc[row_indexer,col_indexer] = value instead + + See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy + new_df["target"] = [590, 580, 597, 595, 590, 590, 600] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 61-66 + +Define number of bins/group for stratification. The idea is that each "bin" +will be equally represented in each fold. The number of bins should be +chosen such that each bin has a sufficient number of samples so that each +fold has more than one sample from each bin. +Let's see a couple of histrograms with different number of bins. + +.. GENERATED FROM PYTHON SOURCE LINES 66-73 + +.. code-block:: default + + + sns.displot(data_df, x="target", bins=60) + + sns.displot(data_df, x="target", bins=40) + + sns.displot(data_df, x="target", bins=20) + + + + +.. rst-class:: sphx-glr-horizontal + + + * + + .. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_001.png + :alt: plot stratified kfold reg + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_001.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_002.png + :alt: plot stratified kfold reg + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_002.png + :class: sphx-glr-multi-img + + * + + .. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_003.png + :alt: plot stratified kfold reg + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_003.png + :class: sphx-glr-multi-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 74-80 + +From the histogram above, we can see that the data is not uniformly +distributed. We can see that the data is skewed towards the lower end of +the target variable. We can also see that there are some outliers in the +data. In any case, even with a low number of splits, some groups will not be +represented in each fold. Lets continue with 40 bins which gives a good +granularity. + +.. GENERATED FROM PYTHON SOURCE LINES 80-83 + +.. code-block:: default + + + cv_stratified = ContinuousStratifiedKFold(n_bins=40, n_splits=5, shuffle=False) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 84-85 + +Train a linear regression model with stratification on target. + +.. GENERATED FROM PYTHON SOURCE LINES 85-98 + +.. code-block:: default + + + scores_strat, model = run_cross_validation( + X=X, + y=y, + data=data_df, + preprocess="zscore", + cv=cv_stratified, + problem_type="regression", + model="linreg", + return_estimator="final", + scoring="neg_mean_absolute_error", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:26,560 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:26,560 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:26,561 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:26,561 - julearn - INFO - Target: target + 2024-10-17 13:53:26,561 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:26,561 - julearn - INFO - X_types:{} + 2024-10-17 13:53:26,561 - julearn - WARNING - The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:26,562 - julearn - INFO - ==================== + 2024-10-17 13:53:26,562 - julearn - INFO - + 2024-10-17 13:53:26,562 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:26,562 - julearn - INFO - Step added + 2024-10-17 13:53:26,562 - julearn - INFO - Adding step linreg that applies to ColumnTypes + 2024-10-17 13:53:26,563 - julearn - INFO - Step added + 2024-10-17 13:53:26,563 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:26,563 - julearn - INFO - ==================== + 2024-10-17 13:53:26,563 - julearn - INFO - + 2024-10-17 13:53:26,563 - julearn - INFO - = Data Information = + 2024-10-17 13:53:26,563 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:26,563 - julearn - INFO - Number of samples: 449 + 2024-10-17 13:53:26,563 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:26,563 - julearn - INFO - ==================== + 2024-10-17 13:53:26,563 - julearn - INFO - + 2024-10-17 13:53:26,563 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:26,564 - julearn - INFO - Using outer CV scheme ContinuousStratifiedKFold(method='binning', n_bins=40, n_splits=5, + random_state=None, shuffle=False) (incl. final model) + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/sklearn/model_selection/_split.py:776: UserWarning: The least populated class in y has only 1 members, which is less than n_splits=5. + warnings.warn( + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/sklearn/model_selection/_split.py:776: UserWarning: The least populated class in y has only 1 members, which is less than n_splits=5. + warnings.warn( + + + + +.. GENERATED FROM PYTHON SOURCE LINES 99-100 + +Train a linear regression model without stratification on target. + +.. GENERATED FROM PYTHON SOURCE LINES 100-114 + +.. code-block:: default + + + cv = KFold(n_splits=5, shuffle=False, random_state=None) + scores, model = run_cross_validation( + X=X, + y=y, + data=data_df, + preprocess="zscore", + cv=cv, + problem_type="regression", + model="linreg", + return_estimator="final", + scoring="neg_mean_absolute_error", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:26,611 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:26,611 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:26,611 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:26,611 - julearn - INFO - Target: target + 2024-10-17 13:53:26,611 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:26,611 - julearn - INFO - X_types:{} + 2024-10-17 13:53:26,611 - julearn - WARNING - The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:26,612 - julearn - INFO - ==================== + 2024-10-17 13:53:26,612 - julearn - INFO - + 2024-10-17 13:53:26,612 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:26,612 - julearn - INFO - Step added + 2024-10-17 13:53:26,612 - julearn - INFO - Adding step linreg that applies to ColumnTypes + 2024-10-17 13:53:26,612 - julearn - INFO - Step added + 2024-10-17 13:53:26,612 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:26,612 - julearn - INFO - ==================== + 2024-10-17 13:53:26,612 - julearn - INFO - + 2024-10-17 13:53:26,612 - julearn - INFO - = Data Information = + 2024-10-17 13:53:26,613 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:26,613 - julearn - INFO - Number of samples: 449 + 2024-10-17 13:53:26,613 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:26,613 - julearn - INFO - ==================== + 2024-10-17 13:53:26,613 - julearn - INFO - + 2024-10-17 13:53:26,613 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:26,613 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 115-117 + +Now we can compare the test score for model trained with and without +stratification. We can combine the two outputs as ``pandas.DataFrame``. + +.. GENERATED FROM PYTHON SOURCE LINES 117-123 + +.. code-block:: default + + + scores_strat["model"] = "With stratification" + scores["model"] = "Without stratification" + df_scores = scores_strat[["test_score", "model"]] + df_scores = pd.concat([df_scores, scores[["test_score", "model"]]]) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 124-126 + +Plot a boxplot with test scores from both the models. We see here that +the test score is higher when CV splits were not stratified. + +.. GENERATED FROM PYTHON SOURCE LINES 126-131 + +.. code-block:: default + + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.set_style("darkgrid") + ax = sns.boxplot(x="model", y="test_score", data=df_scores) + ax = sns.swarmplot(x="model", y="test_score", data=df_scores, color=".25") + + + +.. image-sg:: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_004.png + :alt: plot stratified kfold reg + :srcset: /auto_examples/00_starting/images/sphx_glr_plot_stratified_kfold_reg_004.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1075: FutureWarning: When grouping with a length-1 list-like, you will need to pass a length-1 tuple to get_group in a future version of pandas. Pass `(name,)` instead of `name` to silence this warning. + data_subset = grouped_data.get_group(pd_key) + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.855 seconds) + + +.. _sphx_glr_download_auto_examples_00_starting_plot_stratified_kfold_reg.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_stratified_kfold_reg.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_stratified_kfold_reg.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/run_combine_pandas.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/run_combine_pandas.rst.txt new file mode 100644 index 000000000..11e422d36 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/run_combine_pandas.rst.txt @@ -0,0 +1,1165 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/00_starting/run_combine_pandas.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_00_starting_run_combine_pandas.py: + + +Working with ``pandas`` +======================= + +This example uses the ``fmri`` dataset to transform and combine data in order +to prepare it to be used by ``julearn``. + + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 17-24 + +.. code-block:: default + + # Authors: Federico Raimondo + # + # License: AGPL + + from seaborn import load_dataset + import pandas as pd + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 25-44 + +One of the key elements that make ``julearn`` easy to use, is the possibility +to work directly with ``pandas.DataFrame``, similar to MS Excel spreadsheets +or csv files. + +Ideally, we will have everything tabulated and organised for ``julearn``, but +it might not be your case. You might have some files with the fMRI values, some +others with demographics, some other with diagnostic metrics or behavioral +results. + +You need to prepare these files for ``julearn``. + +One option is to manually edit the files and make sure that everything is +ready to do some machine-learning. However, this is error-prone. + +Fortunately, `pandas`_ provides several tools to deal with this task. + +This example is a collection of some of these useful methods. + +Let's start with the ``fmri`` dataset. + +.. GENERATED FROM PYTHON SOURCE LINES 44-47 + +.. code-block:: default + + + df_fmri = load_dataset("fmri") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 48-50 + +Let's see what this dataset has. + + +.. GENERATED FROM PYTHON SOURCE LINES 50-52 + +.. code-block:: default + + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
subjecttimepointeventregionsignal
0s1318stimparietal-0.017552
1s514stimparietal-0.080883
2s1218stimparietal-0.081033
3s1118stimparietal-0.046134
4s1018stimparietal-0.037970
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 53-61 + +From long to wide format +We have seen this in other examples. If we want to use julearn, each feature +must be a columns. In order to use the signals from different regions as +~~~~~~~~~~~~~~~~~~~~~~~~ +features, we need to convert this dataframe from the long format to the wide +format. + +We will use the ``pivot`` method. + +.. GENERATED FROM PYTHON SOURCE LINES 61-65 + +.. code-block:: default + + df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" + ) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 66-78 + +This method reshapes the table, keeping the specified elements as index, +columns and values. + +In our case, the values are extracted from the *signal* column. The columns +from the *region* column and *subject*, *timepoint* and *event* becomes the +index. + +The index is what identifies each sample. As a rule, the index can't be +duplicated. If each subject has more than one timepoint, and each timepoint +has more than one event, then these 3 elements are needed as the index. + +Let's see what we have here: + +.. GENERATED FROM PYTHON SOURCE LINES 78-80 + +.. code-block:: default + + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
regionfrontalparietal
subjecttimepointevent
s00cue0.007766-0.006899
stim-0.021452-0.039327
1cue0.0164400.000300
stim-0.021054-0.035735
2cue0.0242960.033220
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 81-85 + +Now this is in the format we want. However, in order to access the index +as columns ``df_fmri["subject"]`` we need to reset the index. + +Check the subtle but important difference: + +.. GENERATED FROM PYTHON SOURCE LINES 85-88 + +.. code-block:: default + + df_fmri = df_fmri.reset_index() + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
regionsubjecttimepointeventfrontalparietal
0s00cue0.007766-0.006899
1s00stim-0.021452-0.039327
2s01cue0.0164400.000300
3s01stim-0.021054-0.035735
4s02cue0.0242960.033220
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 89-97 + +Merging or joining ``DataFrame`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +So now we have our fMRI data tabulated for ``julearn``. However, it might be +the case that we have some important information in another file. For example, +the subjects' age and the place where they were scanned. + +For the purpose of the example, we'll create the dataframe here. + +.. GENERATED FROM PYTHON SOURCE LINES 97-105 + +.. code-block:: default + + metadata = { + "subject": [f"s{i}" for i in range(14)], + "age": [23, 21, 31, 29, 43, 23, 43, 28, 48, 29, 35, 23, 34, 25], + "scanner": ["a"] * 6 + ["b"] * 8, + } + df_meta = pd.DataFrame(metadata) + df_meta + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
subjectagescanner
0s023a
1s121a
2s231a
3s329a
4s443a
5s523a
6s643b
7s728b
8s848b
9s929b
10s1035b
11s1123b
12s1234b
13s1325b
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 106-111 + +We will use the ``join`` method. This method will join the two dataframes, +matching elements by the *index*. + +In this case, the matching element (or index) will be the column ``subject``. +We need to set the index in each dataframe before join. + +.. GENERATED FROM PYTHON SOURCE LINES 111-116 + +.. code-block:: default + + df_fmri = df_fmri.set_index("subject") + df_meta = df_meta.set_index("subject") + df_fmri = df_fmri.join(df_meta) + df_fmri + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
timepointeventfrontalparietalagescanner
subject
s00cue0.007766-0.00689923a
s00stim-0.021452-0.03932723a
s01cue0.0164400.00030023a
s01stim-0.021054-0.03573523a
s02cue0.0242960.03322023a
.....................
s916stim-0.036739-0.13164129b
s917cue-0.004900-0.03636229b
s917stim-0.030099-0.12157429b
s918cue-0.000643-0.05104029b
s918stim-0.009959-0.10351329b
+

532 rows × 6 columns

+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 117-118 + +Finally, let's reset the index and have it ready for ``julearn``. + +.. GENERATED FROM PYTHON SOURCE LINES 118-120 + +.. code-block:: default + + df_fmri = df_fmri.reset_index() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 121-122 + +Now we can use, for example, *age* and *scanner* as confounds. + +.. GENERATED FROM PYTHON SOURCE LINES 124-132 + +Reshaping data frames (more complex) +Lets suppose that our prediction target is now the *age* and we want to use +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +as features the frontal and parietal value during each event. For this +purpose, we need to convert the event values into columns. There are two +events: *cue* and *stim*. So this will result in 4 columns. + +We will still use the pivot, but in this case, we will have two values: + +.. GENERATED FROM PYTHON SOURCE LINES 132-139 + +.. code-block:: default + + df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "age", "scanner"], + columns="event", + values=["frontal", "parietal"], + ) + df_fmri + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
frontalparietal
eventcuestimcuestim
subjecttimepointagescanner
s0023a0.007766-0.021452-0.006899-0.039327
123a0.016440-0.0210540.000300-0.035735
223a0.024296-0.0090380.0332200.009642
323a0.0478590.0267270.0850400.086399
423a0.0697750.0705580.1153210.154058
........................
s91429b0.010535-0.061817-0.034386-0.130267
1529b0.002170-0.048007-0.038257-0.134828
1629b-0.004290-0.036739-0.035395-0.131641
1729b-0.004900-0.030099-0.036362-0.121574
1829b-0.000643-0.009959-0.051040-0.103513
+

266 rows × 4 columns

+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 140-143 + +Since the column names are combinations of the values in the *event* column +and the previous *frontal* and *parietal* columns, it is now a multi-level +column name. + +.. GENERATED FROM PYTHON SOURCE LINES 143-145 + +.. code-block:: default + + df_fmri.columns + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + MultiIndex([( 'frontal', 'cue'), + ( 'frontal', 'stim'), + ('parietal', 'cue'), + ('parietal', 'stim')], + names=[None, 'event']) + + + +.. GENERATED FROM PYTHON SOURCE LINES 146-147 + +The following trick will join the different levels using an underscore (*_*) + +.. GENERATED FROM PYTHON SOURCE LINES 147-150 + +.. code-block:: default + + df_fmri.columns = ["_".join(x) for x in df_fmri.columns] + df_fmri + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
frontal_cuefrontal_stimparietal_cueparietal_stim
subjecttimepointagescanner
s0023a0.007766-0.021452-0.006899-0.039327
123a0.016440-0.0210540.000300-0.035735
223a0.024296-0.0090380.0332200.009642
323a0.0478590.0267270.0850400.086399
423a0.0697750.0705580.1153210.154058
........................
s91429b0.010535-0.061817-0.034386-0.130267
1529b0.002170-0.048007-0.038257-0.134828
1629b-0.004290-0.036739-0.035395-0.131641
1729b-0.004900-0.030099-0.036362-0.121574
1829b-0.000643-0.009959-0.051040-0.103513
+

266 rows × 4 columns

+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 151-152 + +We have finally the information we want. We can now reset the index. + +.. GENERATED FROM PYTHON SOURCE LINES 152-153 + +.. code-block:: default + + df_fmri = df_fmri.reset_index() + + + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.413 seconds) + + +.. _sphx_glr_download_auto_examples_00_starting_run_combine_pandas.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_combine_pandas.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_combine_pandas.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/run_grouped_cv.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/run_grouped_cv.rst.txt new file mode 100644 index 000000000..287366110 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/run_grouped_cv.rst.txt @@ -0,0 +1,494 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/00_starting/run_grouped_cv.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_00_starting_run_grouped_cv.py: + + +Grouped CV +========== + +This example uses the ``fMRI`` dataset and performs GroupKFold +Cross-Validation for classification using Random Forest Classifier. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 17-32 + +.. code-block:: default + + + # Authors: Federico Raimondo + # Shammi More + # Kimia Nazarzadeh + # License: AGPL + + # Importing the necessary Python libraries + import numpy as np + + from seaborn import load_dataset + from sklearn.model_selection import GroupKFold, StratifiedGroupKFold + + from julearn.utils import configure_logging + from julearn import run_cross_validation + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-34 + +Set the logging level to info to see extra information + +.. GENERATED FROM PYTHON SOURCE LINES 34-36 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:23,756 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:23,756 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:23,756 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:23,756 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:23,756 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:23,756 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:23,756 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 37-39 + +Dealing with Cross-Validation techniques +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. GENERATED FROM PYTHON SOURCE LINES 39-42 + +.. code-block:: default + + + df_fmri = load_dataset("fmri") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 43-44 + +First, lets get some information on what the dataset has: + +.. GENERATED FROM PYTHON SOURCE LINES 44-47 + +.. code-block:: default + + + print(df_fmri.head()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + subject timepoint event region signal + 0 s13 18 stim parietal -0.017552 + 1 s5 14 stim parietal -0.080883 + 2 s12 18 stim parietal -0.081033 + 3 s11 18 stim parietal -0.046134 + 4 s10 18 stim parietal -0.037970 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 48-53 + +From this information, we can infer that it is an fMRI study in which there +were several subjects, timepoints, events and signal extracted from several +brain regions. + +Lets check how many kinds of each we have. + +.. GENERATED FROM PYTHON SOURCE LINES 53-58 + +.. code-block:: default + + print(df_fmri["event"].unique()) + print(df_fmri["region"].unique()) + print(sorted(df_fmri["timepoint"].unique())) + print(df_fmri["subject"].unique()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + ['stim' 'cue'] + ['parietal' 'frontal'] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] + ['s13' 's5' 's12' 's11' 's10' 's9' 's8' 's7' 's6' 's4' 's3' 's2' 's1' 's0'] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 59-62 + +We have data from parietal and frontal regions during 2 types of events +(*cue* and *stim*) during 18 timepoints and for 14 subjects. +Lets see how many samples we have for each condition + +.. GENERATED FROM PYTHON SOURCE LINES 62-72 + +.. code-block:: default + + + print(df_fmri.groupby(["subject", "timepoint", "event", "region"]).count()) + print( + np.unique( + df_fmri.groupby(["subject", "timepoint", "event", "region"]) + .count() + .values + ) + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + signal + subject timepoint event region + s0 0 cue frontal 1 + parietal 1 + stim frontal 1 + parietal 1 + 1 cue frontal 1 + ... ... + s9 17 stim parietal 1 + 18 cue frontal 1 + parietal 1 + stim frontal 1 + parietal 1 + + [1064 rows x 1 columns] + [1] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 73-79 + +We have exactly one value per condition. + +Lets try to build a model, that uses parietal and frontal signal to predicts +whether the event was a *cue* or a *stim*. + +First we define our X and y variables. + +.. GENERATED FROM PYTHON SOURCE LINES 79-82 + +.. code-block:: default + + X = ["parietal", "frontal"] + y = "event" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 83-88 + +In order for this to work, both *parietal* and *frontal* must be columns. +We need to *pivot* the table. + +The values of *region* will be the columns. The column *signal* will be the +values. And the columns *subject*, *timepoint* and *event* will be the index + +.. GENERATED FROM PYTHON SOURCE LINES 88-94 + +.. code-block:: default + + df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" + ) + + df_fmri = df_fmri.reset_index() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 95-97 + +Here we want to zscore all the features and then train a Support Vector +Machine classifier. + +.. GENERATED FROM PYTHON SOURCE LINES 97-109 + +.. code-block:: default + + + scores = run_cross_validation( + X=X, + y=y, + data=df_fmri, + preprocess="zscore", + model="rf", + problem_type="classification", + ) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:23,775 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:23,775 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:23,775 - julearn - INFO - Features: ['parietal', 'frontal'] + 2024-10-17 13:53:23,775 - julearn - INFO - Target: event + 2024-10-17 13:53:23,775 - julearn - INFO - Expanded features: ['parietal', 'frontal'] + 2024-10-17 13:53:23,775 - julearn - INFO - X_types:{} + 2024-10-17 13:53:23,775 - julearn - WARNING - The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:23,776 - julearn - INFO - ==================== + 2024-10-17 13:53:23,776 - julearn - INFO - + 2024-10-17 13:53:23,776 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:23,776 - julearn - INFO - Step added + 2024-10-17 13:53:23,776 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:23,777 - julearn - INFO - Step added + 2024-10-17 13:53:23,777 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:23,777 - julearn - INFO - ==================== + 2024-10-17 13:53:23,777 - julearn - INFO - + 2024-10-17 13:53:23,777 - julearn - INFO - = Data Information = + 2024-10-17 13:53:23,777 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:23,777 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:23,777 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:23,777 - julearn - INFO - ==================== + 2024-10-17 13:53:23,777 - julearn - INFO - + 2024-10-17 13:53:23,778 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:23,778 - julearn - INFO - Target type: object + 2024-10-17 13:53:23,778 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:23,778 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:23,779 - julearn - INFO - Binary classification problem detected. + 0.6841826838300122 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 110-111 + +Train classification model with stratification on data + +.. GENERATED FROM PYTHON SOURCE LINES 111-125 + +.. code-block:: default + + cv_stratified = StratifiedGroupKFold(n_splits=2) + scores, model = run_cross_validation( + X=X, + y=y, + data=df_fmri, + groups="subject", + model="rf", + problem_type="classification", + cv=cv_stratified, + return_estimator="final", + ) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:24,399 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:24,399 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:24,399 - julearn - INFO - Features: ['parietal', 'frontal'] + 2024-10-17 13:53:24,399 - julearn - INFO - Target: event + 2024-10-17 13:53:24,399 - julearn - INFO - Expanded features: ['parietal', 'frontal'] + 2024-10-17 13:53:24,399 - julearn - INFO - X_types:{} + 2024-10-17 13:53:24,399 - julearn - WARNING - The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:24,400 - julearn - INFO - Using subject as groups + 2024-10-17 13:53:24,400 - julearn - INFO - ==================== + 2024-10-17 13:53:24,400 - julearn - INFO - + 2024-10-17 13:53:24,400 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:24,400 - julearn - INFO - Step added + 2024-10-17 13:53:24,401 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:24,401 - julearn - INFO - ==================== + 2024-10-17 13:53:24,401 - julearn - INFO - + 2024-10-17 13:53:24,401 - julearn - INFO - = Data Information = + 2024-10-17 13:53:24,401 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:24,401 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:24,401 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:24,401 - julearn - INFO - ==================== + 2024-10-17 13:53:24,401 - julearn - INFO - + 2024-10-17 13:53:24,401 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:24,401 - julearn - INFO - Target type: object + 2024-10-17 13:53:24,402 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:24,402 - julearn - INFO - Using outer CV scheme StratifiedGroupKFold(n_splits=2, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:24,402 - julearn - INFO - Binary classification problem detected. + 0.6898496240601504 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 126-127 + +Train classification model without stratification on data + +.. GENERATED FROM PYTHON SOURCE LINES 127-140 + +.. code-block:: default + + cv = GroupKFold(n_splits=2) + scores, model = run_cross_validation( + X=X, + y=y, + data=df_fmri, + groups="subject", + model="rf", + problem_type="classification", + cv=cv, + return_estimator="final", + ) + + print(scores["test_score"].mean()) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:24,761 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:24,761 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:24,761 - julearn - INFO - Features: ['parietal', 'frontal'] + 2024-10-17 13:53:24,761 - julearn - INFO - Target: event + 2024-10-17 13:53:24,762 - julearn - INFO - Expanded features: ['parietal', 'frontal'] + 2024-10-17 13:53:24,762 - julearn - INFO - X_types:{} + 2024-10-17 13:53:24,762 - julearn - WARNING - The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:24,762 - julearn - INFO - Using subject as groups + 2024-10-17 13:53:24,762 - julearn - INFO - ==================== + 2024-10-17 13:53:24,762 - julearn - INFO - + 2024-10-17 13:53:24,763 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:24,763 - julearn - INFO - Step added + 2024-10-17 13:53:24,763 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:24,763 - julearn - INFO - ==================== + 2024-10-17 13:53:24,763 - julearn - INFO - + 2024-10-17 13:53:24,763 - julearn - INFO - = Data Information = + 2024-10-17 13:53:24,763 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:24,763 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:24,763 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:24,763 - julearn - INFO - ==================== + 2024-10-17 13:53:24,763 - julearn - INFO - + 2024-10-17 13:53:24,764 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:24,764 - julearn - INFO - Target type: object + 2024-10-17 13:53:24,764 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:24,764 - julearn - INFO - Using outer CV scheme GroupKFold(n_splits=2) (incl. final model) + 2024-10-17 13:53:24,765 - julearn - INFO - Binary classification problem detected. + 0.6879699248120301 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 1.366 seconds) + + +.. _sphx_glr_download_auto_examples_00_starting_run_grouped_cv.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_grouped_cv.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_grouped_cv.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/run_simple_binary_classification.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/run_simple_binary_classification.rst.txt new file mode 100644 index 000000000..1aa9d73e0 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/run_simple_binary_classification.rst.txt @@ -0,0 +1,399 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/00_starting/run_simple_binary_classification.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_00_starting_run_simple_binary_classification.py: + + +Simple Binary Classification +============================ + +This example uses the ``iris`` dataset and performs a simple binary +classification using a Support Vector Machine classifier. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 10-18 + +.. code-block:: default + + # Authors: Federico Raimondo + # + # License: AGPL + + from seaborn import load_dataset + from julearn import run_cross_validation + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 19-20 + +Set the logging level to info to see extra information + +.. GENERATED FROM PYTHON SOURCE LINES 20-22 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:23,429 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:23,429 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:23,429 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:23,429 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:23,429 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:23,429 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:23,429 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 23-25 + +.. code-block:: default + + df_iris = load_dataset("iris") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 26-28 + +The dataset has three kind of species. We will keep two to perform a binary +classification. + +.. GENERATED FROM PYTHON SOURCE LINES 28-30 + +.. code-block:: default + + df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 31-33 + +As features, we will use the sepal length, width and petal length. +We will try to predict the species. + +.. GENERATED FROM PYTHON SOURCE LINES 33-47 + +.. code-block:: default + + + X = ["sepal_length", "sepal_width", "petal_length"] + y = "species" + scores = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="svm", + problem_type="classification", + preprocess="zscore", + ) + + print(scores["test_score"]) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:23,503 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:23,503 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:23,503 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:23,503 - julearn - INFO - Target: species + 2024-10-17 13:53:23,503 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:23,503 - julearn - INFO - X_types:{} + 2024-10-17 13:53:23,503 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:23,504 - julearn - INFO - ==================== + 2024-10-17 13:53:23,504 - julearn - INFO - + 2024-10-17 13:53:23,504 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:23,505 - julearn - INFO - Step added + 2024-10-17 13:53:23,505 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:23,505 - julearn - INFO - Step added + 2024-10-17 13:53:23,506 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:23,506 - julearn - INFO - ==================== + 2024-10-17 13:53:23,506 - julearn - INFO - + 2024-10-17 13:53:23,506 - julearn - INFO - = Data Information = + 2024-10-17 13:53:23,506 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:23,506 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:23,506 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:23,506 - julearn - INFO - ==================== + 2024-10-17 13:53:23,506 - julearn - INFO - + 2024-10-17 13:53:23,506 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:23,506 - julearn - INFO - Target type: object + 2024-10-17 13:53:23,507 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:23,507 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:23,507 - julearn - INFO - Binary classification problem detected. + 0 0.90 + 1 0.75 + 2 0.95 + 3 0.70 + 4 0.90 + Name: test_score, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 48-52 + +Additionally, we can choose to assess the performance of the model using +different scoring functions. + +For example, we might have an unbalanced dataset: + +.. GENERATED FROM PYTHON SOURCE LINES 52-56 + +.. code-block:: default + + + df_unbalanced = df_iris[20:] # drop the first 20 versicolor samples + print(df_unbalanced["species"].value_counts()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + species + virginica 50 + versicolor 30 + Name: count, dtype: int64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 57-62 + +If we compute the `accuracy`, we might not account for this imbalance. A more +suitable metric is the `balanced_accuracy`. More information in +``scikit-learn``: :func:`~sklearn.metrics.balanced_accuracy_score`. + +We will also set the random seed so we always split the data in the same way. + +.. GENERATED FROM PYTHON SOURCE LINES 62-76 + +.. code-block:: default + + scores = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + seed=42, + preprocess="zscore", + problem_type="classification", + scoring=["accuracy", "balanced_accuracy"], + ) + + print(scores["test_accuracy"].mean()) + print(scores["test_balanced_accuracy"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:23,549 - julearn - INFO - Setting random seed to 42 + 2024-10-17 13:53:23,549 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:23,549 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:23,549 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:23,549 - julearn - INFO - Target: species + 2024-10-17 13:53:23,549 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:23,549 - julearn - INFO - X_types:{} + 2024-10-17 13:53:23,549 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:23,550 - julearn - INFO - ==================== + 2024-10-17 13:53:23,550 - julearn - INFO - + 2024-10-17 13:53:23,550 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:23,550 - julearn - INFO - Step added + 2024-10-17 13:53:23,550 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:23,550 - julearn - INFO - Step added + 2024-10-17 13:53:23,551 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:23,551 - julearn - INFO - ==================== + 2024-10-17 13:53:23,551 - julearn - INFO - + 2024-10-17 13:53:23,551 - julearn - INFO - = Data Information = + 2024-10-17 13:53:23,551 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:23,551 - julearn - INFO - Number of samples: 80 + 2024-10-17 13:53:23,551 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:23,551 - julearn - INFO - ==================== + 2024-10-17 13:53:23,551 - julearn - INFO - + 2024-10-17 13:53:23,551 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:23,551 - julearn - INFO - Target type: object + 2024-10-17 13:53:23,552 - julearn - INFO - Class distributions: species + virginica 50 + versicolor 30 + Name: count, dtype: int64 + 2024-10-17 13:53:23,552 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:23,552 - julearn - INFO - Binary classification problem detected. + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/sklearn/metrics/_classification.py:2480: UserWarning: y_pred contains classes not in y_true + warnings.warn("y_pred contains classes not in y_true") + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/sklearn/metrics/_classification.py:2480: UserWarning: y_pred contains classes not in y_true + warnings.warn("y_pred contains classes not in y_true") + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/sklearn/metrics/_classification.py:2480: UserWarning: y_pred contains classes not in y_true + warnings.warn("y_pred contains classes not in y_true") + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/sklearn/metrics/_classification.py:2480: UserWarning: y_pred contains classes not in y_true + warnings.warn("y_pred contains classes not in y_true") + 0.8625 + 0.8678571428571429 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 77-87 + +Other kind of metrics allows us to evaluate how good our model is to detect +specific targets. Suppose we want to create a model that correctly identifies +the `versicolor` samples. + +Now we might want to evaluate the precision score, or the ratio of true +positives (tp) over all positives (true and false positives). More +information in ``scikit-learn``: :func:`~sklearn.metrics.precision_score`. + +For this metric to work, we need to define which are our `positive` values. +In this example, we are interested in detecting `versicolor`. + +.. GENERATED FROM PYTHON SOURCE LINES 87-99 + +.. code-block:: default + + precision_scores = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + preprocess="zscore", + problem_type="classification", + seed=42, + scoring="precision", + pos_labels="versicolor", + ) + print(precision_scores["test_score"].mean()) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:23,597 - julearn - INFO - Setting random seed to 42 + 2024-10-17 13:53:23,597 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:23,597 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:23,597 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:23,597 - julearn - INFO - Target: species + 2024-10-17 13:53:23,597 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:23,597 - julearn - INFO - X_types:{} + 2024-10-17 13:53:23,597 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:23,597 - julearn - INFO - Setting the following as positive labels ['versicolor'] + 2024-10-17 13:53:23,598 - julearn - INFO - ==================== + 2024-10-17 13:53:23,598 - julearn - INFO - + 2024-10-17 13:53:23,598 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:23,598 - julearn - INFO - Step added + 2024-10-17 13:53:23,598 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:23,598 - julearn - INFO - Step added + 2024-10-17 13:53:23,599 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:23,599 - julearn - INFO - ==================== + 2024-10-17 13:53:23,599 - julearn - INFO - + 2024-10-17 13:53:23,599 - julearn - INFO - = Data Information = + 2024-10-17 13:53:23,599 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:23,599 - julearn - INFO - Number of samples: 80 + 2024-10-17 13:53:23,599 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:23,599 - julearn - INFO - ==================== + 2024-10-17 13:53:23,599 - julearn - INFO - + 2024-10-17 13:53:23,599 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:23,599 - julearn - INFO - Target type: int64 + 2024-10-17 13:53:23,600 - julearn - INFO - Class distributions: species + 0 50 + 1 30 + Name: count, dtype: int64 + 2024-10-17 13:53:23,600 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:23,600 - julearn - INFO - Binary classification problem detected. + 0.4 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.221 seconds) + + +.. _sphx_glr_download_auto_examples_00_starting_run_simple_binary_classification.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_simple_binary_classification.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_simple_binary_classification.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/00_starting/sg_execution_times.rst.txt b/pr-preview/pr-276/_sources/auto_examples/00_starting/sg_execution_times.rst.txt new file mode 100644 index 000000000..5d27172ad --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/00_starting/sg_execution_times.rst.txt @@ -0,0 +1,23 @@ + +:orphan: + +.. _sphx_glr_auto_examples_00_starting_sg_execution_times: + + +Computation times +================= +**00:04.057** total execution time for **auto_examples_00_starting** files: + ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_00_starting_run_grouped_cv.py` (``run_grouped_cv.py``) | 00:01.366 | 0.0 MB | ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_00_starting_plot_stratified_kfold_reg.py` (``plot_stratified_kfold_reg.py``) | 00:00.855 | 0.0 MB | ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_00_starting_plot_example_regression.py` (``plot_example_regression.py``) | 00:00.669 | 0.0 MB | ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_00_starting_plot_cm_acc_multiclass.py` (``plot_cm_acc_multiclass.py``) | 00:00.534 | 0.0 MB | ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_00_starting_run_combine_pandas.py` (``run_combine_pandas.py``) | 00:00.413 | 0.0 MB | ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_00_starting_run_simple_binary_classification.py` (``run_simple_binary_classification.py``) | 00:00.221 | 0.0 MB | ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ diff --git a/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/index.rst.txt b/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/index.rst.txt new file mode 100644 index 000000000..58360a131 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/index.rst.txt @@ -0,0 +1,48 @@ +:orphan: + +Model Comparison +================ + +Examples that perform model comparisons. + + + +.. raw:: html + +
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/01_model_comparison/images/thumb/sphx_glr_plot_simple_model_comparison_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_01_model_comparison_plot_simple_model_comparison.py` + +.. raw:: html + +
Simple Model Comparison
+
+ + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /auto_examples/01_model_comparison/plot_simple_model_comparison + + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/plot_simple_model_comparison.rst.txt b/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/plot_simple_model_comparison.rst.txt new file mode 100644 index 000000000..19c926cee --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/plot_simple_model_comparison.rst.txt @@ -0,0 +1,577 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/01_model_comparison/plot_simple_model_comparison.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_01_model_comparison_plot_simple_model_comparison.py: + + +Simple Model Comparison +======================= + +This example uses the ``iris`` dataset and performs binary classifications +using different models. At the end, it compares the performance of the models +using different scoring functions and performs a statistical test to assess +whether the difference in performance is significant. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 12-21 + +.. code-block:: default + + # Authors: Federico Raimondo + # License: AGPL + + from seaborn import load_dataset + from sklearn.model_selection import RepeatedStratifiedKFold + from julearn import run_cross_validation + from julearn.utils import configure_logging + from julearn.stats.corrected_ttest import corrected_ttest + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 22-23 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 23-25 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:27,787 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:27,787 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:27,787 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:27,787 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:27,787 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:27,787 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:27,787 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 26-28 + +.. code-block:: default + + df_iris = load_dataset("iris") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 29-31 + +The dataset has three kind of species. We will keep two to perform a binary +classification. + +.. GENERATED FROM PYTHON SOURCE LINES 31-33 + +.. code-block:: default + + df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 34-36 + +As features, we will use the sepal length, width and petal length. +We will try to predict the species. + +.. GENERATED FROM PYTHON SOURCE LINES 36-50 + +.. code-block:: default + + + X = ["sepal_length", "sepal_width", "petal_length"] + y = "species" + scores = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="svm", + problem_type="classification", + preprocess="zscore", + ) + + print(scores["test_score"]) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:27,789 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:27,789 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:27,790 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:27,790 - julearn - INFO - Target: species + 2024-10-17 13:53:27,790 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:27,790 - julearn - INFO - X_types:{} + 2024-10-17 13:53:27,790 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:27,790 - julearn - INFO - ==================== + 2024-10-17 13:53:27,790 - julearn - INFO - + 2024-10-17 13:53:27,791 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:27,791 - julearn - INFO - Step added + 2024-10-17 13:53:27,791 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:27,791 - julearn - INFO - Step added + 2024-10-17 13:53:27,791 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:27,791 - julearn - INFO - ==================== + 2024-10-17 13:53:27,792 - julearn - INFO - + 2024-10-17 13:53:27,792 - julearn - INFO - = Data Information = + 2024-10-17 13:53:27,792 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:27,792 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:27,792 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:27,792 - julearn - INFO - ==================== + 2024-10-17 13:53:27,792 - julearn - INFO - + 2024-10-17 13:53:27,792 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:27,792 - julearn - INFO - Target type: object + 2024-10-17 13:53:27,793 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:27,793 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:27,793 - julearn - INFO - Binary classification problem detected. + 0 0.90 + 1 0.75 + 2 0.95 + 3 0.70 + 4 0.90 + Name: test_score, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 51-55 + +Additionally, we can choose to assess the performance of the model using +different scoring functions. + +For example, we might have an unbalanced dataset: + +.. GENERATED FROM PYTHON SOURCE LINES 55-59 + +.. code-block:: default + + + df_unbalanced = df_iris[20:] # drop the first 20 versicolor samples + print(df_unbalanced["species"].value_counts()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + species + virginica 50 + versicolor 30 + Name: count, dtype: int64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 60-61 + +So we will choose to use the `balanced_accuracy` and `roc_auc` metrics. + +.. GENERATED FROM PYTHON SOURCE LINES 61-64 + +.. code-block:: default + + + scoring = ["balanced_accuracy", "roc_auc"] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 65-67 + +Since we are comparing the performance of different models, we will need +to use the same random seed to split the data in the same way. + +.. GENERATED FROM PYTHON SOURCE LINES 67-70 + +.. code-block:: default + + + cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=5, random_state=42) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 71-72 + +First we will use a default SVM model. + +.. GENERATED FROM PYTHON SOURCE LINES 72-85 + +.. code-block:: default + + scores1 = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + preprocess="zscore", + problem_type="classification", + scoring=scoring, + cv=cv, + ) + + scores1["model"] = "svm" + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:27,833 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:27,833 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:27,833 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:27,833 - julearn - INFO - Target: species + 2024-10-17 13:53:27,833 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:27,833 - julearn - INFO - X_types:{} + 2024-10-17 13:53:27,833 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:27,834 - julearn - INFO - ==================== + 2024-10-17 13:53:27,834 - julearn - INFO - + 2024-10-17 13:53:27,834 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:27,834 - julearn - INFO - Step added + 2024-10-17 13:53:27,834 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:27,834 - julearn - INFO - Step added + 2024-10-17 13:53:27,835 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:27,835 - julearn - INFO - ==================== + 2024-10-17 13:53:27,835 - julearn - INFO - + 2024-10-17 13:53:27,835 - julearn - INFO - = Data Information = + 2024-10-17 13:53:27,835 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:27,835 - julearn - INFO - Number of samples: 80 + 2024-10-17 13:53:27,835 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:27,835 - julearn - INFO - ==================== + 2024-10-17 13:53:27,835 - julearn - INFO - + 2024-10-17 13:53:27,835 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:27,835 - julearn - INFO - Target type: object + 2024-10-17 13:53:27,836 - julearn - INFO - Class distributions: species + virginica 50 + versicolor 30 + Name: count, dtype: int64 + 2024-10-17 13:53:27,836 - julearn - INFO - Using outer CV scheme RepeatedStratifiedKFold(n_repeats=5, n_splits=5, random_state=42) + 2024-10-17 13:53:27,836 - julearn - INFO - Binary classification problem detected. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 86-87 + +Second we will use a default Random Forest model. + +.. GENERATED FROM PYTHON SOURCE LINES 87-100 + +.. code-block:: default + + scores2 = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="rf", + preprocess="zscore", + problem_type="classification", + scoring=scoring, + cv=cv, + ) + + scores2["model"] = "rf" + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:28,116 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:28,116 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:28,116 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:28,116 - julearn - INFO - Target: species + 2024-10-17 13:53:28,116 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:28,116 - julearn - INFO - X_types:{} + 2024-10-17 13:53:28,116 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:28,117 - julearn - INFO - ==================== + 2024-10-17 13:53:28,117 - julearn - INFO - + 2024-10-17 13:53:28,117 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:28,117 - julearn - INFO - Step added + 2024-10-17 13:53:28,117 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:28,117 - julearn - INFO - Step added + 2024-10-17 13:53:28,118 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:28,118 - julearn - INFO - ==================== + 2024-10-17 13:53:28,118 - julearn - INFO - + 2024-10-17 13:53:28,118 - julearn - INFO - = Data Information = + 2024-10-17 13:53:28,118 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:28,118 - julearn - INFO - Number of samples: 80 + 2024-10-17 13:53:28,118 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:28,118 - julearn - INFO - ==================== + 2024-10-17 13:53:28,118 - julearn - INFO - + 2024-10-17 13:53:28,118 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:28,118 - julearn - INFO - Target type: object + 2024-10-17 13:53:28,119 - julearn - INFO - Class distributions: species + virginica 50 + versicolor 30 + Name: count, dtype: int64 + 2024-10-17 13:53:28,119 - julearn - INFO - Using outer CV scheme RepeatedStratifiedKFold(n_repeats=5, n_splits=5, random_state=42) + 2024-10-17 13:53:28,119 - julearn - INFO - Binary classification problem detected. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 101-102 + +The third model will be a SVM with a linear kernel. + +.. GENERATED FROM PYTHON SOURCE LINES 102-116 + +.. code-block:: default + + scores3 = run_cross_validation( + X=X, + y=y, + data=df_unbalanced, + model="svm", + model_params={"svm__kernel": "linear"}, + preprocess="zscore", + problem_type="classification", + scoring=scoring, + cv=cv, + ) + + scores3["model"] = "svm_linear" + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:30,734 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:30,734 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:30,735 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:30,735 - julearn - INFO - Target: species + 2024-10-17 13:53:30,735 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:30,735 - julearn - INFO - X_types:{} + 2024-10-17 13:53:30,735 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:30,735 - julearn - INFO - ==================== + 2024-10-17 13:53:30,735 - julearn - INFO - + 2024-10-17 13:53:30,736 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:30,736 - julearn - INFO - Step added + 2024-10-17 13:53:30,736 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:30,736 - julearn - INFO - Setting hyperparameter kernel = linear + 2024-10-17 13:53:30,736 - julearn - INFO - Step added + 2024-10-17 13:53:30,736 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:30,736 - julearn - INFO - ==================== + 2024-10-17 13:53:30,737 - julearn - INFO - + 2024-10-17 13:53:30,737 - julearn - INFO - = Data Information = + 2024-10-17 13:53:30,737 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:30,737 - julearn - INFO - Number of samples: 80 + 2024-10-17 13:53:30,737 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:30,737 - julearn - INFO - ==================== + 2024-10-17 13:53:30,737 - julearn - INFO - + 2024-10-17 13:53:30,737 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:30,737 - julearn - INFO - Target type: object + 2024-10-17 13:53:30,738 - julearn - INFO - Class distributions: species + virginica 50 + versicolor 30 + Name: count, dtype: int64 + 2024-10-17 13:53:30,738 - julearn - INFO - Using outer CV scheme RepeatedStratifiedKFold(n_repeats=5, n_splits=5, random_state=42) + 2024-10-17 13:53:30,738 - julearn - INFO - Binary classification problem detected. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 117-118 + +We can now compare the performance of the models using corrected statistics. + +.. GENERATED FROM PYTHON SOURCE LINES 118-122 + +.. code-block:: default + + + stats_df = corrected_ttest(scores1, scores2, scores3) + print(stats_df) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + metric t-stat ... model_2 p-val-corrected + 0 test_balanced_accuracy -0.175075 ... rf 1.000000 + 2 test_balanced_accuracy -1.062567 ... svm_linear 0.895662 + 4 test_balanced_accuracy -1.151390 ... svm_linear 0.782741 + 1 test_roc_auc 1.108944 ... rf 0.835331 + 3 test_roc_auc -1.236153 ... svm_linear 0.685092 + 5 test_roc_auc -1.669010 ... svm_linear 0.324331 + + [6 rows x 6 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 123-126 + +.. rst-class:: hidden + This block is hidden in the documentation. This files are used to generate + the plots in the documentation. (not working for now) + +.. GENERATED FROM PYTHON SOURCE LINES 126-128 + +.. code-block:: default + + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 137-139 + +We can also plot the performance of the models using the ``julearn`` Score +Viewer. + +.. GENERATED FROM PYTHON SOURCE LINES 139-148 + +.. code-block:: default + + + from julearn.viz import plot_scores + + panel = plot_scores(scores1, scores2, scores3) + # panel.show() + # uncomment the previous line show the plot + # read the documentation for more information + # https://panel.holoviz.org/getting_started/build_app.html#deploying-panels + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 149-167 + +This is how the plot looks like. + +.. note:: + The plot is interactive. You can zoom in and out, and hover over. + However, buttons will not work in this documentation. + +.. bokeh-plot:: + :source-position: none + + from julearn.viz import plot_scores + from bokeh.io import output_notebook, show + import pandas as pd + output_notebook() + scores1 = pd.read_csv("/tmp/scores1.csv") + scores2 = pd.read_csv("/tmp/scores2.csv") + scores3 = pd.read_csv("/tmp/scores3.csv") + panel = plot_scores(scores1, scores2, scores3, width=600) + show(panel.get_root()) + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 3.353 seconds) + + +.. _sphx_glr_download_auto_examples_01_model_comparison_plot_simple_model_comparison.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_simple_model_comparison.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_simple_model_comparison.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/sg_execution_times.rst.txt b/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/sg_execution_times.rst.txt new file mode 100644 index 000000000..67c970b52 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/01_model_comparison/sg_execution_times.rst.txt @@ -0,0 +1,13 @@ + +:orphan: + +.. _sphx_glr_auto_examples_01_model_comparison_sg_execution_times: + + +Computation times +================= +**00:03.353** total execution time for **auto_examples_01_model_comparison** files: + ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_01_model_comparison_plot_simple_model_comparison.py` (``plot_simple_model_comparison.py``) | 00:03.353 | 0.0 MB | ++-------------------------------------------------------------------------------------------------------------------------+-----------+--------+ diff --git a/pr-preview/pr-276/_sources/auto_examples/02_inspection/index.rst.txt b/pr-preview/pr-276/_sources/auto_examples/02_inspection/index.rst.txt new file mode 100644 index 000000000..48c84ebbb --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/02_inspection/index.rst.txt @@ -0,0 +1,102 @@ +:orphan: + +Inspection +========== + +Examples that demonstrate how to inspect the models. + + + +.. raw:: html + +
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/02_inspection/images/thumb/sphx_glr_run_binary_inspect_folds_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_02_inspection_run_binary_inspect_folds.py` + +.. raw:: html + +
Inspecting the fold-wise predictions
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/02_inspection/images/thumb/sphx_glr_plot_inspect_random_forest_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_02_inspection_plot_inspect_random_forest.py` + +.. raw:: html + +
Inspecting Random Forest models
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/02_inspection/images/thumb/sphx_glr_plot_groupcv_inspect_svm_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_02_inspection_plot_groupcv_inspect_svm.py` + +.. raw:: html + +
Inspecting SVM models
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/02_inspection/images/thumb/sphx_glr_plot_preprocess_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_02_inspection_plot_preprocess.py` + +.. raw:: html + +
Preprocessing with variance threshold, zscore and PCA
+
+ + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /auto_examples/02_inspection/run_binary_inspect_folds + /auto_examples/02_inspection/plot_inspect_random_forest + /auto_examples/02_inspection/plot_groupcv_inspect_svm + /auto_examples/02_inspection/plot_preprocess + + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_groupcv_inspect_svm.rst.txt b/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_groupcv_inspect_svm.rst.txt new file mode 100644 index 000000000..844be5754 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_groupcv_inspect_svm.rst.txt @@ -0,0 +1,573 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/02_inspection/plot_groupcv_inspect_svm.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_02_inspection_plot_groupcv_inspect_svm.py: + + +Inspecting SVM models +===================== + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier and analyse the model. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 16-33 + +.. code-block:: default + + # Authors: Federico Raimondo + # Shammi More + # License: AGPL + + import numpy as np + import pandas as pd + + from sklearn.model_selection import GroupShuffleSplit + + import matplotlib.pyplot as plt + import seaborn as sns + from seaborn import load_dataset + + from julearn import run_cross_validation + from julearn.utils import configure_logging + from julearn.inspect import preprocess + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 34-35 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 35-38 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:33,207 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:33,207 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:33,207 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:33,207 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:33,207 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:33,207 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:33,207 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 39-41 + +Dealing with Cross-Validation techniques +---------------------------------------- + +.. GENERATED FROM PYTHON SOURCE LINES 41-44 + +.. code-block:: default + + + df_fmri = load_dataset("fmri") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 45-46 + +First, lets get some information on what the dataset has: + +.. GENERATED FROM PYTHON SOURCE LINES 46-49 + +.. code-block:: default + + + print(df_fmri.head()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + subject timepoint event region signal + 0 s13 18 stim parietal -0.017552 + 1 s5 14 stim parietal -0.080883 + 2 s12 18 stim parietal -0.081033 + 3 s11 18 stim parietal -0.046134 + 4 s10 18 stim parietal -0.037970 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 50-55 + +From this information, we can infer that it is an fMRI study in which there +were several subjects, timepoints, events and signal extracted from several +brain regions. + +Lets check how many kinds of each we have. + +.. GENERATED FROM PYTHON SOURCE LINES 55-61 + +.. code-block:: default + + + print(df_fmri["event"].unique()) + print(df_fmri["region"].unique()) + print(sorted(df_fmri["timepoint"].unique())) + print(df_fmri["subject"].unique()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + ['stim' 'cue'] + ['parietal' 'frontal'] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] + ['s13' 's5' 's12' 's11' 's10' 's9' 's8' 's7' 's6' 's4' 's3' 's2' 's1' 's0'] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 62-65 + +We have data from parietal and frontal regions during 2 types of events +(*cue* and *stim*) during 18 timepoints and for 14 subjects. +Lets see how many samples we have for each condition + +.. GENERATED FROM PYTHON SOURCE LINES 65-75 + +.. code-block:: default + + + print(df_fmri.groupby(["subject", "timepoint", "event", "region"]).count()) + print( + np.unique( + df_fmri.groupby(["subject", "timepoint", "event", "region"]) + .count() + .values + ) + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + signal + subject timepoint event region + s0 0 cue frontal 1 + parietal 1 + stim frontal 1 + parietal 1 + 1 cue frontal 1 + ... ... + s9 17 stim parietal 1 + 18 cue frontal 1 + parietal 1 + stim frontal 1 + parietal 1 + + [1064 rows x 1 columns] + [1] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 76-82 + +We have exactly one value per condition. + +Lets try to build a model, that uses parietal and frontal signal to predicts +whether the event was a *cue* or a *stim*. + +First we define our X and y variables. + +.. GENERATED FROM PYTHON SOURCE LINES 82-85 + +.. code-block:: default + + X = ["parietal", "frontal"] + y = "event" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 86-91 + +In order for this to work, both *parietal* and *frontal* must be columns. +We need to *pivot* the table. + +The values of *region* will be the columns. The column *signal* will be the +values. And the columns *subject*, *timepoint* and *event* will be the index + +.. GENERATED FROM PYTHON SOURCE LINES 91-97 + +.. code-block:: default + + df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" + ) + + df_fmri = df_fmri.reset_index() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 98-100 + +Here we want to zscore all the features and then train a Support Vector +Machine classifier. + +.. GENERATED FROM PYTHON SOURCE LINES 100-112 + +.. code-block:: default + + + scores = run_cross_validation( + X=X, + y=y, + data=df_fmri, + preprocess="zscore", + model="svm", + problem_type="classification", + ) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:33,225 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:33,225 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:33,225 - julearn - INFO - Features: ['parietal', 'frontal'] + 2024-10-17 13:53:33,225 - julearn - INFO - Target: event + 2024-10-17 13:53:33,225 - julearn - INFO - Expanded features: ['parietal', 'frontal'] + 2024-10-17 13:53:33,225 - julearn - INFO - X_types:{} + 2024-10-17 13:53:33,226 - julearn - WARNING - The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:33,226 - julearn - INFO - ==================== + 2024-10-17 13:53:33,226 - julearn - INFO - + 2024-10-17 13:53:33,226 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:33,226 - julearn - INFO - Step added + 2024-10-17 13:53:33,226 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:33,227 - julearn - INFO - Step added + 2024-10-17 13:53:33,227 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:33,227 - julearn - INFO - ==================== + 2024-10-17 13:53:33,227 - julearn - INFO - + 2024-10-17 13:53:33,227 - julearn - INFO - = Data Information = + 2024-10-17 13:53:33,227 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:33,227 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:33,227 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:33,227 - julearn - INFO - ==================== + 2024-10-17 13:53:33,227 - julearn - INFO - + 2024-10-17 13:53:33,228 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:33,228 - julearn - INFO - Target type: object + 2024-10-17 13:53:33,228 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:33,228 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:33,229 - julearn - INFO - Binary classification problem detected. + 0.7218303650149884 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 113-134 + +This results indicate that we can decode the kind of event by looking at +the *parietal* and *frontal* signal. However, that claim is true only if we +have some data from the same subject already acquired. + +The problem is that we split the data randomly into 5 folds (default, see +:func:`.run_cross_validation`). This means that data from one subject could +be both in the training and the testing set. If this is the case, then the +model can learn the subjects' specific characteristics and apply it to the +testing set. Thus, it is not true that we can decode it for an unseen +subject, but for an unseen timepoint for a subject that for whom we already +have data. + +To test for unseen subject, we need to make sure that all the data from each +subject is either on the training or the testing set, but not in both. + +We can use ``scikit-learn``'s +:class:`sklearn.model_selection.GroupShuffleSplit` and specify which is the +grouping column using the ``group`` parameter. +By setting ``return_estimator="final"``, the :func:`.run_cross_validation` +function returns the estimator fitted with all the data. We will use this +later to do some analyses. + +.. GENERATED FROM PYTHON SOURCE LINES 134-150 + +.. code-block:: default + + cv = GroupShuffleSplit(n_splits=5, test_size=0.5, random_state=42) + + scores, model = run_cross_validation( + X=X, + y=y, + data=df_fmri, + preprocess="zscore", + model="svm", + cv=cv, + groups="subject", + problem_type="classification", + return_estimator="final", + ) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:33,285 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:33,285 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:33,285 - julearn - INFO - Features: ['parietal', 'frontal'] + 2024-10-17 13:53:33,285 - julearn - INFO - Target: event + 2024-10-17 13:53:33,285 - julearn - INFO - Expanded features: ['parietal', 'frontal'] + 2024-10-17 13:53:33,285 - julearn - INFO - X_types:{} + 2024-10-17 13:53:33,285 - julearn - WARNING - The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['parietal', 'frontal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:33,285 - julearn - INFO - Using subject as groups + 2024-10-17 13:53:33,286 - julearn - INFO - ==================== + 2024-10-17 13:53:33,286 - julearn - INFO - + 2024-10-17 13:53:33,286 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:33,286 - julearn - INFO - Step added + 2024-10-17 13:53:33,286 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:33,286 - julearn - INFO - Step added + 2024-10-17 13:53:33,286 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:33,287 - julearn - INFO - ==================== + 2024-10-17 13:53:33,287 - julearn - INFO - + 2024-10-17 13:53:33,287 - julearn - INFO - = Data Information = + 2024-10-17 13:53:33,287 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:33,287 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:33,287 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:33,287 - julearn - INFO - ==================== + 2024-10-17 13:53:33,287 - julearn - INFO - + 2024-10-17 13:53:33,287 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:33,287 - julearn - INFO - Target type: object + 2024-10-17 13:53:33,288 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:33,288 - julearn - INFO - Using outer CV scheme GroupShuffleSplit(n_splits=5, random_state=42, test_size=0.5, train_size=None) (incl. final model) + 2024-10-17 13:53:33,288 - julearn - INFO - Binary classification problem detected. + 0.7210526315789474 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 151-156 + +After testing on independent subjects, we can now claim that given a new +subject, we can predict the kind of event. + +Let's do some visualization on how these two features interact and what +the preprocessing part of the model does. + +.. GENERATED FROM PYTHON SOURCE LINES 156-182 + +.. code-block:: default + + + # Plot the raw features + fig, axes = plt.subplots(1, 2, figsize=(8, 4)) + sns.scatterplot( + x="parietal", y="frontal", hue="event", data=df_fmri, ax=axes[0], s=5 + ) + axes[0].set_title("Raw data") + + # Plot the preprocessed features + pre_X = preprocess( + model, X=X, data=df_fmri, until="zscore", with_column_types=True + ) + + pre_df = pre_X.join(df_fmri[y]) + + sns.scatterplot( + x="parietal__:type:__continuous", + y="frontal__:type:__continuous", + hue="event", + data=pre_df, + ax=axes[1], + s=5, + ) + + axes[1].set_title("Preprocessed data") + + + + +.. image-sg:: /auto_examples/02_inspection/images/sphx_glr_plot_groupcv_inspect_svm_001.png + :alt: Raw data, Preprocessed data + :srcset: /auto_examples/02_inspection/images/sphx_glr_plot_groupcv_inspect_svm_001.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Text(0.5, 1.0, 'Preprocessed data') + + + +.. GENERATED FROM PYTHON SOURCE LINES 183-188 + +In this case, the preprocessing is nothing more than a +:class:`sklearn.preprocessing.StandardScaler`. + +It seems that the data is not quite linearly separable. Let's now visualize +how the SVM does this complex task. + +.. GENERATED FROM PYTHON SOURCE LINES 188-218 + +.. code-block:: default + + + # Get the model from the pipeline + clf = model[2] + fig = plt.figure() + ax = sns.scatterplot( + x="parietal__:type:__continuous", + y="frontal__:type:__continuous", + hue="event", + data=pre_df, + s=5, + ) + + xlim = ax.get_xlim() + ylim = ax.get_ylim() + + # Create grid to evaluate model + xx = np.linspace(xlim[0], xlim[1], 30) + yy = np.linspace(ylim[0], ylim[1], 30) + YY, XX = np.meshgrid(yy, xx) + xy = np.vstack([XX.ravel(), YY.ravel()]).T + + # Create pandas.DataFrame + xy_df = pd.DataFrame( + data=xy, + columns=["parietal__:type:__continuous", "frontal__:type:__continuous"], + ) + + Z = clf.decision_function(xy_df).reshape(XX.shape) + a = ax.contour(XX, YY, Z, colors="k", levels=[0], alpha=0.5, linestyles=["-"]) + ax.set_title("Preprocessed data with SVM decision function boundaries") + + + +.. image-sg:: /auto_examples/02_inspection/images/sphx_glr_plot_groupcv_inspect_svm_002.png + :alt: Preprocessed data with SVM decision function boundaries + :srcset: /auto_examples/02_inspection/images/sphx_glr_plot_groupcv_inspect_svm_002.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Text(0.5, 1.0, 'Preprocessed data with SVM decision function boundaries') + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.495 seconds) + + +.. _sphx_glr_download_auto_examples_02_inspection_plot_groupcv_inspect_svm.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_groupcv_inspect_svm.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_groupcv_inspect_svm.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_inspect_random_forest.rst.txt b/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_inspect_random_forest.rst.txt new file mode 100644 index 000000000..d669f2ac0 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_inspect_random_forest.rst.txt @@ -0,0 +1,390 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/02_inspection/plot_inspect_random_forest.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_02_inspection_plot_inspect_random_forest.py: + + +Inspecting Random Forest models +=============================== + +This example uses the ``iris`` dataset, performs simple binary classification +using a Random Forest classifier and analyse the model. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 10-22 + +.. code-block:: default + + # Authors: Federico Raimondo + # License: AGPL + + import pandas as pd + + import matplotlib.pyplot as plt + import seaborn as sns + from seaborn import load_dataset + + from julearn import run_cross_validation + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 23-24 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 24-26 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:31,629 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:31,629 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:31,629 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:31,629 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:31,629 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:31,629 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:31,629 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 27-29 + +Random Forest variable importance +--------------------------------- + +.. GENERATED FROM PYTHON SOURCE LINES 29-32 + +.. code-block:: default + + + df_iris = load_dataset("iris") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-35 + +The dataset has three kind of species. We will keep two to perform a binary +classification. + +.. GENERATED FROM PYTHON SOURCE LINES 35-41 + +.. code-block:: default + + df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + + X = ["sepal_length", "sepal_width", "petal_length"] + y = "species" + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 42-45 + +We will use a Random Forest classifier. By setting +`return_estimator='final'`, the :func:`.run_cross_validation` function +returns the estimator fitted with all the data. + +.. GENERATED FROM PYTHON SOURCE LINES 45-56 + +.. code-block:: default + + + scores, model_iris = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="rf", + preprocess="zscore", + problem_type="classification", + return_estimator="final", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:31,631 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:31,632 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:31,632 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:31,632 - julearn - INFO - Target: species + 2024-10-17 13:53:31,632 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:31,632 - julearn - INFO - X_types:{} + 2024-10-17 13:53:31,632 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:31,633 - julearn - INFO - ==================== + 2024-10-17 13:53:31,633 - julearn - INFO - + 2024-10-17 13:53:31,633 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:31,633 - julearn - INFO - Step added + 2024-10-17 13:53:31,633 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:31,633 - julearn - INFO - Step added + 2024-10-17 13:53:31,634 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:31,634 - julearn - INFO - ==================== + 2024-10-17 13:53:31,634 - julearn - INFO - + 2024-10-17 13:53:31,634 - julearn - INFO - = Data Information = + 2024-10-17 13:53:31,634 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:31,634 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:31,634 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:31,634 - julearn - INFO - ==================== + 2024-10-17 13:53:31,634 - julearn - INFO - + 2024-10-17 13:53:31,634 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:31,634 - julearn - INFO - Target type: object + 2024-10-17 13:53:31,635 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:31,635 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:31,635 - julearn - INFO - Binary classification problem detected. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 57-61 + +This type of classifier has an internal variable that can inform us on how +*important* is each of the features. Caution: read the proper ``scikit-learn`` +documentation :class:`~sklearn.ensemble.RandomForestClassifier` to understand +how this learning algorithm works. + +.. GENERATED FROM PYTHON SOURCE LINES 61-75 + +.. code-block:: default + + rf = model_iris["rf"] + + to_plot = pd.DataFrame( + { + "variable": [x.replace("_", " ") for x in X], + "importance": rf.feature_importances_, + } + ) + + fig, ax = plt.subplots(1, 1, figsize=(6, 4)) + sns.barplot(x="importance", y="variable", data=to_plot, ax=ax) + ax.set_title("Variable Importances for Random Forest Classifier") + fig.tight_layout() + + + + +.. image-sg:: /auto_examples/02_inspection/images/sphx_glr_plot_inspect_random_forest_001.png + :alt: Variable Importances for Random Forest Classifier + :srcset: /auto_examples/02_inspection/images/sphx_glr_plot_inspect_random_forest_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 76-83 + +However, some reviewers (including us), might wander about the +variability of the importance of these features. In the previous example +all the feature importances were obtained by fitting on the entire dataset, +while the performance was estimated using cross validation. + +By specifying `return_estimator='cv'`, we can get, for each fold, the fitted +estimator. + +.. GENERATED FROM PYTHON SOURCE LINES 83-94 + +.. code-block:: default + + + scores = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="rf", + preprocess="zscore", + problem_type="classification", + return_estimator="cv", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:32,347 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:32,348 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:32,348 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:32,348 - julearn - INFO - Target: species + 2024-10-17 13:53:32,348 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:32,348 - julearn - INFO - X_types:{} + 2024-10-17 13:53:32,348 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:32,349 - julearn - INFO - ==================== + 2024-10-17 13:53:32,349 - julearn - INFO - + 2024-10-17 13:53:32,349 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:32,349 - julearn - INFO - Step added + 2024-10-17 13:53:32,350 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:32,350 - julearn - INFO - Step added + 2024-10-17 13:53:32,351 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:32,351 - julearn - INFO - ==================== + 2024-10-17 13:53:32,351 - julearn - INFO - + 2024-10-17 13:53:32,351 - julearn - INFO - = Data Information = + 2024-10-17 13:53:32,351 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:32,351 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:32,351 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:32,351 - julearn - INFO - ==================== + 2024-10-17 13:53:32,351 - julearn - INFO - + 2024-10-17 13:53:32,351 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:32,351 - julearn - INFO - Target type: object + 2024-10-17 13:53:32,352 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:32,352 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:32,353 - julearn - INFO - Binary classification problem detected. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 95-96 + +Now we can obtain the feature importance for each estimator (CV fold). + +.. GENERATED FROM PYTHON SOURCE LINES 96-109 + +.. code-block:: default + + to_plot = [] + for i_fold, estimator in enumerate(scores["estimator"]): + this_importances = pd.DataFrame( + { + "variable": [x.replace("_", " ") for x in X], + "importance": estimator["rf"].feature_importances_, + "fold": i_fold, + } + ) + to_plot.append(this_importances) + + to_plot = pd.concat(to_plot) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 110-111 + +Finally, we can plot the variable importances for each fold. + +.. GENERATED FROM PYTHON SOURCE LINES 111-119 + +.. code-block:: default + + + fig, ax = plt.subplots(1, 1, figsize=(6, 4)) + sns.swarmplot(x="importance", y="variable", data=to_plot, ax=ax) + ax.set_title( + "Distribution of variable Importances for Random Forest " + "Classifier across folds" + ) + fig.tight_layout() + + + +.. image-sg:: /auto_examples/02_inspection/images/sphx_glr_plot_inspect_random_forest_002.png + :alt: Distribution of variable Importances for Random Forest Classifier across folds + :srcset: /auto_examples/02_inspection/images/sphx_glr_plot_inspect_random_forest_002.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1075: FutureWarning: When grouping with a length-1 list-like, you will need to pass a length-1 tuple to get_group in a future version of pandas. Pass `(name,)` instead of `name` to silence this warning. + data_subset = grouped_data.get_group(pd_key) + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 1.431 seconds) + + +.. _sphx_glr_download_auto_examples_02_inspection_plot_inspect_random_forest.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_inspect_random_forest.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_inspect_random_forest.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_preprocess.rst.txt b/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_preprocess.rst.txt new file mode 100644 index 000000000..13e87caf6 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/02_inspection/plot_preprocess.rst.txt @@ -0,0 +1,378 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/02_inspection/plot_preprocess.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_02_inspection_plot_preprocess.py: + + +Preprocessing with variance threshold, zscore and PCA +===================================================== + +This example uses the ``make_regression`` function to create a simple dataset, +performs a simple regression after the preprocessing of the features +including removal of low variance features, feature normalization for only +two features using zscore and feature reduction using PCA. +We will check the features after each preprocessing step. + +.. GENERATED FROM PYTHON SOURCE LINES 11-25 + +.. code-block:: default + + # Authors: Shammi More + # Leonard Sasse + # License: AGPL + + import matplotlib.pyplot as plt + import pandas as pd + import seaborn as sns + from sklearn.datasets import make_regression + + from julearn import run_cross_validation + from julearn.inspect import preprocess + from julearn.pipeline import PipelineCreator + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 26-27 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 27-29 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:33,850 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:33,850 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:33,850 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:33,850 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:33,850 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:33,850 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:33,850 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 30-31 + +Create a dataset using ``sklearn`` ``make_regression``. + +.. GENERATED FROM PYTHON SOURCE LINES 31-45 + +.. code-block:: default + + df = pd.DataFrame() + X, y = [f"Feature {x}" for x in range(1, 5)], "y" + df[X], df[y] = make_regression( + n_samples=100, n_features=4, n_informative=3, noise=0.3, random_state=0 + ) + + # We only want to zscore the first two features, so let's get their names. + first_two = X[:2] + + # We can define a dictionary, in which the 'key' defines the names of our + # different 'types' of 'X'. The 'value' determine, which features belong to + # this type. + X_types = {"X_to_zscore": first_two} + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 46-47 + +Let's look at the summary statistics of the raw features. + +.. GENERATED FROM PYTHON SOURCE LINES 47-49 + +.. code-block:: default + + print("Summary Statistics of the raw features : \n", df.describe()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Summary Statistics of the raw features : + Feature 1 Feature 2 Feature 3 Feature 4 y + count 100.000000 100.000000 100.000000 100.000000 100.000000 + mean -0.111790 0.141618 0.023682 -0.167718 -14.686170 + std 0.977322 0.972988 1.087946 0.895011 79.741431 + min -2.069985 -2.772593 -2.552990 -2.659172 -204.293317 + 25% -0.751257 -0.484100 -0.759419 -0.688891 -64.724008 + 50% -0.206729 0.194442 -0.028152 -0.174160 -16.789944 + 75% 0.421885 0.724302 0.772156 0.408321 30.392804 + max 1.943621 2.256723 2.383145 2.259309 216.221085 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 50-62 + +We will preprocess all features using variance thresholding. +We will only zscore the first two features, and then perform PCA using all +features. We will zscore the target and then train a random forest model. +Since we use the PipelineCreator object we have to explicitly declare which +`X_types` each preprocessing step should be applied to. If we do not declare +the type in the ``add`` method using the ``apply_to`` keyword argument, +the step will default to ``"continuous"`` or to another type that can be +declared in the constructor of the ``PipelineCreator``. +To transform the target we could set ``apply_to="target"``, which is a special +type, that cannot be user-defined. Please note also that if a step is added +to transform the target, you also have to explicitly add the model that is +to be used in the regression to the ``PipelineCreator``. + +.. GENERATED FROM PYTHON SOURCE LINES 62-127 + +.. code-block:: default + + + # Define model parameters and preprocessing steps first + # The hyperparameters for each step can be added as a keyword argument and + # should be either one parameter or an iterable of multiple parameters for a + # search. + + # Setting the threshold for variance to 0.15, number of PCA components to 2 + # and number of trees for random forest to 200. + + # By setting "apply_to=*", we can apply the preprocessing step to all features. + pipeline_creator = PipelineCreator(problem_type="regression") + + pipeline_creator.add("select_variance", apply_to="*", threshold=0.15) + pipeline_creator.add("zscore", apply_to="X_to_zscore") + pipeline_creator.add("pca", apply_to="*", n_components=2) + pipeline_creator.add("rf", apply_to="*", n_estimators=200) + + # Because we have already added the model to the pipeline creator, we can + # simply drop in the ``pipeline_creator`` as a model. If we did not add a model + # here, we could add the ``pipeline_creator`` using the keyword argument + # ``preprocess`` and hand over a model separately. + + scores, model = run_cross_validation( + X=X, + y=y, + X_types=X_types, + data=df, + model=pipeline_creator, + scoring=["r2", "neg_mean_absolute_error"], + return_estimator="final", + seed=200, + ) + + # We can use the final estimator to inspect the transformed features at a + # specific step of the pipeline. Since the PCA was the last step added to the + # pipeline, we can simply get the model up to this step by indexing as follows: + + X_after_pca = model[:-1].transform(df[X]) + + print("X after PCA:") + print("=" * 79) + print(X_after_pca) + + # We can select pipelines up to earlier steps by indexing previous elements + # in the final estimator. For example, to inspect features after the zscoring + # step: + + X_after_zscore = model[:-2].transform(df[X]) + print("X after zscore:") + print("=" * 79) + print(X_after_zscore) + + # However, to make this less confusing you can also simply use the high-level + # function ``preprocess`` to explicitly refer to a pipeline step by name: + + X_after_pca = preprocess(model, X=X, data=df, until="pca") + X_after_zscore = preprocess(model, X=X, data=df, until="zscore") + + # Let's plot scatter plots for raw features and the PCA components. + fig, axes = plt.subplots(1, 2, figsize=(12, 6)) + sns.scatterplot(x=X[0], y=X[1], data=df, ax=axes[0]) + axes[0].set_title("Raw features") + sns.scatterplot(x="pca0", y="pca1", data=X_after_pca, ax=axes[1]) + axes[1].set_title("PCA components") + + + + +.. image-sg:: /auto_examples/02_inspection/images/sphx_glr_plot_preprocess_001.png + :alt: Raw features, PCA components + :srcset: /auto_examples/02_inspection/images/sphx_glr_plot_preprocess_001.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:33,865 - julearn - INFO - Adding step select_variance that applies to ColumnTypes + 2024-10-17 13:53:33,865 - julearn - INFO - Setting hyperparameter threshold = 0.15 + 2024-10-17 13:53:33,866 - julearn - INFO - Step added + 2024-10-17 13:53:33,866 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:33,866 - julearn - INFO - Step added + 2024-10-17 13:53:33,866 - julearn - INFO - Adding step pca that applies to ColumnTypes + 2024-10-17 13:53:33,866 - julearn - INFO - Setting hyperparameter n_components = 2 + 2024-10-17 13:53:33,866 - julearn - INFO - Step added + 2024-10-17 13:53:33,866 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:33,866 - julearn - INFO - Setting hyperparameter n_estimators = 200 + 2024-10-17 13:53:33,866 - julearn - INFO - Step added + 2024-10-17 13:53:33,866 - julearn - INFO - Setting random seed to 200 + 2024-10-17 13:53:33,866 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:33,866 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:33,866 - julearn - INFO - Features: ['Feature 1', 'Feature 2', 'Feature 3', 'Feature 4'] + 2024-10-17 13:53:33,866 - julearn - INFO - Target: y + 2024-10-17 13:53:33,867 - julearn - INFO - Expanded features: ['Feature 1', 'Feature 2', 'Feature 3', 'Feature 4'] + 2024-10-17 13:53:33,867 - julearn - INFO - X_types:{'X_to_zscore': ['Feature 1', 'Feature 2']} + 2024-10-17 13:53:33,867 - julearn - WARNING - The following columns are not defined in X_types: ['Feature 3', 'Feature 4']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['Feature 3', 'Feature 4']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:33,868 - julearn - INFO - ==================== + 2024-10-17 13:53:33,868 - julearn - INFO - + 2024-10-17 13:53:33,869 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:33,869 - julearn - INFO - ==================== + 2024-10-17 13:53:33,869 - julearn - INFO - + 2024-10-17 13:53:33,869 - julearn - INFO - = Data Information = + 2024-10-17 13:53:33,869 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:33,869 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:33,869 - julearn - INFO - Number of features: 4 + 2024-10-17 13:53:33,869 - julearn - INFO - ==================== + 2024-10-17 13:53:33,869 - julearn - INFO - + 2024-10-17 13:53:33,869 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:33,869 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + X after PCA: + =============================================================================== + pca0 pca1 + 0 -0.078319 2.336101 + 1 -0.756861 0.596518 + 2 1.191895 0.451629 + 3 -0.750820 0.958497 + 4 0.121426 -1.599419 + .. ... ... + 95 0.175061 -0.081051 + 96 -0.586593 0.325382 + 97 -1.011121 0.024891 + 98 0.478725 0.788625 + 99 0.893666 0.435353 + + [100 rows x 2 columns] + X after zscore: + =============================================================================== + Feature 1__:type:__X_to_zscore ... Feature 4__:type:__continuous + 0 -2.013728 ... 0.666996 + 1 -0.364686 ... -0.872214 + 2 0.118838 ... 1.234740 + 3 -0.004437 ... -2.091986 + 4 0.309092 ... 0.776631 + .. ... ... ... + 95 -0.206189 ... 0.011287 + 96 -1.099154 ... 0.156690 + 97 -0.335538 ... -1.218469 + 98 -0.049139 ... 0.038198 + 99 0.240085 ... 0.686765 + + [100 rows x 4 columns] + + Text(0.5, 1.0, 'PCA components') + + + +.. GENERATED FROM PYTHON SOURCE LINES 128-130 + +Let's look at the summary statistics of the zscored features. We see here +that the mean of all the features is zero and standard deviation is one. + +.. GENERATED FROM PYTHON SOURCE LINES 130-134 + +.. code-block:: default + + print( + "Summary Statistics of the zscored features : \n", + X_after_zscore.describe(), + ) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Summary Statistics of the zscored features : + Feature 1 Feature 2 Feature 3 Feature 4 + count 1.000000e+02 1.000000e+02 1.000000e+02 1.000000e+02 + mean -5.689893e-17 1.387779e-17 4.440892e-18 3.330669e-18 + std 1.005038e+00 1.005038e+00 1.005038e+00 1.005038e+00 + min -2.013728e+00 -3.010202e+00 -2.380315e+00 -2.797736e+00 + 25% -6.576015e-01 -6.463286e-01 -7.234239e-01 -5.852417e-01 + 50% -9.763131e-02 5.456486e-02 -4.788377e-02 -7.233997e-03 + 75% 5.488094e-01 6.018781e-01 6.914360e-01 6.468533e-01 + max 2.113700e+00 2.184776e+00 2.179658e+00 2.725389e+00 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 1.255 seconds) + + +.. _sphx_glr_download_auto_examples_02_inspection_plot_preprocess.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_preprocess.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_preprocess.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/02_inspection/run_binary_inspect_folds.rst.txt b/pr-preview/pr-276/_sources/auto_examples/02_inspection/run_binary_inspect_folds.rst.txt new file mode 100644 index 000000000..21be83479 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/02_inspection/run_binary_inspect_folds.rst.txt @@ -0,0 +1,298 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/02_inspection/run_binary_inspect_folds.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_02_inspection_run_binary_inspect_folds.py: + + +Inspecting the fold-wise predictions +==================================== + +This example uses the ``iris`` dataset and performs a simple binary +classification using a Support Vector Machine classifier. + +We later inspect the predictions of the model for each fold. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 12-23 + +.. code-block:: default + + # Authors: Federico Raimondo + # License: AGPL + + from seaborn import load_dataset + + from sklearn.model_selection import RepeatedStratifiedKFold, ShuffleSplit + + from julearn import run_cross_validation + from julearn.pipeline import PipelineCreator + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 24-25 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 25-27 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:31,267 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:31,267 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:31,267 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:31,267 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:31,267 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:31,267 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:31,267 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 28-30 + +.. code-block:: default + + df_iris = load_dataset("iris") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 31-33 + +The dataset has three kind of species. We will keep two to perform a binary +classification. + +.. GENERATED FROM PYTHON SOURCE LINES 33-35 + +.. code-block:: default + + df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 36-38 + +As features, we will use the sepal length, width and petal length. +We will try to predict the species. + +.. GENERATED FROM PYTHON SOURCE LINES 38-60 + +.. code-block:: default + + + X = ["sepal_length", "sepal_width", "petal_length"] + y = "species" + X_types = {"continuous": X} + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm") + + cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=4, random_state=200) + + scores, model, inspector = run_cross_validation( + X=X, + y=y, + data=df_iris, + model=creator, + return_inspector=True, + cv=cv, + ) + + print(scores) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:31,270 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:31,270 - julearn - INFO - Step added + 2024-10-17 13:53:31,270 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:31,270 - julearn - INFO - Step added + 2024-10-17 13:53:31,270 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:31,270 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:31,270 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:31,270 - julearn - INFO - Target: species + 2024-10-17 13:53:31,271 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:31,271 - julearn - INFO - X_types:{} + 2024-10-17 13:53:31,271 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:31,271 - julearn - INFO - ==================== + 2024-10-17 13:53:31,271 - julearn - INFO - + 2024-10-17 13:53:31,272 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:31,272 - julearn - INFO - ==================== + 2024-10-17 13:53:31,272 - julearn - INFO - + 2024-10-17 13:53:31,272 - julearn - INFO - = Data Information = + 2024-10-17 13:53:31,272 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:31,272 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:31,272 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:31,272 - julearn - INFO - ==================== + 2024-10-17 13:53:31,272 - julearn - INFO - + 2024-10-17 13:53:31,272 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:31,272 - julearn - INFO - Target type: object + 2024-10-17 13:53:31,273 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:31,273 - julearn - INFO - Using outer CV scheme RepeatedStratifiedKFold(n_repeats=4, n_splits=5, random_state=200) (incl. final model) + 2024-10-17 13:53:31,273 - julearn - INFO - Binary classification problem detected. + fit_time score_time ... fold cv_mdsum + 0 0.004670 0.002430 ... 0 42489ff0163b2f12752440a6b7ef74c7 + 1 0.004345 0.002430 ... 1 42489ff0163b2f12752440a6b7ef74c7 + 2 0.004302 0.002423 ... 2 42489ff0163b2f12752440a6b7ef74c7 + 3 0.004303 0.002407 ... 3 42489ff0163b2f12752440a6b7ef74c7 + 4 0.004287 0.002420 ... 4 42489ff0163b2f12752440a6b7ef74c7 + 5 0.004317 0.002411 ... 0 42489ff0163b2f12752440a6b7ef74c7 + 6 0.004313 0.002491 ... 1 42489ff0163b2f12752440a6b7ef74c7 + 7 0.004341 0.002408 ... 2 42489ff0163b2f12752440a6b7ef74c7 + 8 0.004254 0.002415 ... 3 42489ff0163b2f12752440a6b7ef74c7 + 9 0.004286 0.002385 ... 4 42489ff0163b2f12752440a6b7ef74c7 + 10 0.004308 0.002391 ... 0 42489ff0163b2f12752440a6b7ef74c7 + 11 0.004325 0.002448 ... 1 42489ff0163b2f12752440a6b7ef74c7 + 12 0.004293 0.002457 ... 2 42489ff0163b2f12752440a6b7ef74c7 + 13 0.004297 0.002409 ... 3 42489ff0163b2f12752440a6b7ef74c7 + 14 0.004302 0.002368 ... 4 42489ff0163b2f12752440a6b7ef74c7 + 15 0.004325 0.002393 ... 0 42489ff0163b2f12752440a6b7ef74c7 + 16 0.004235 0.002387 ... 1 42489ff0163b2f12752440a6b7ef74c7 + 17 0.004372 0.002430 ... 2 42489ff0163b2f12752440a6b7ef74c7 + 18 0.004253 0.002423 ... 3 42489ff0163b2f12752440a6b7ef74c7 + 19 0.004312 0.002387 ... 4 42489ff0163b2f12752440a6b7ef74c7 + + [20 rows x 9 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 61-62 + +We can now inspect the predictions of the model for each fold. + +.. GENERATED FROM PYTHON SOURCE LINES 62-67 + +.. code-block:: default + + + cv_predictions = inspector.folds.predict() + + print(cv_predictions) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + repeat0_p0 repeat1_p0 repeat2_p0 repeat3_p0 target + 0 versicolor versicolor versicolor versicolor versicolor + 1 versicolor versicolor versicolor versicolor versicolor + 2 versicolor versicolor versicolor versicolor versicolor + 3 versicolor versicolor versicolor versicolor versicolor + 4 versicolor versicolor versicolor versicolor versicolor + .. ... ... ... ... ... + 95 virginica virginica virginica virginica virginica + 96 virginica virginica virginica virginica virginica + 97 virginica virginica virginica virginica virginica + 98 virginica virginica virginica virginica virginica + 99 virginica virginica virginica virginica virginica + + [100 rows x 5 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 68-69 + +.. code-block:: default + + inspector.folds[0].model + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.235 seconds) + + +.. _sphx_glr_download_auto_examples_02_inspection_run_binary_inspect_folds.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_binary_inspect_folds.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_binary_inspect_folds.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/02_inspection/sg_execution_times.rst.txt b/pr-preview/pr-276/_sources/auto_examples/02_inspection/sg_execution_times.rst.txt new file mode 100644 index 000000000..e2eb50a48 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/02_inspection/sg_execution_times.rst.txt @@ -0,0 +1,19 @@ + +:orphan: + +.. _sphx_glr_auto_examples_02_inspection_sg_execution_times: + + +Computation times +================= +**00:03.415** total execution time for **auto_examples_02_inspection** files: + ++---------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_02_inspection_plot_inspect_random_forest.py` (``plot_inspect_random_forest.py``) | 00:01.431 | 0.0 MB | ++---------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_02_inspection_plot_preprocess.py` (``plot_preprocess.py``) | 00:01.255 | 0.0 MB | ++---------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_02_inspection_plot_groupcv_inspect_svm.py` (``plot_groupcv_inspect_svm.py``) | 00:00.495 | 0.0 MB | ++---------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_02_inspection_run_binary_inspect_folds.py` (``run_binary_inspect_folds.py``) | 00:00.235 | 0.0 MB | ++---------------------------------------------------------------------------------------------------------------+-----------+--------+ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/index.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/index.rst.txt new file mode 100644 index 000000000..07f0ff478 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/index.rst.txt @@ -0,0 +1,138 @@ +:orphan: + +Complex Models +============== + +Examples that show how to build complex models. + + + +.. raw:: html + +
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/03_complex_models/images/thumb/sphx_glr_run_apply_to_target_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_03_complex_models_run_apply_to_target.py` + +.. raw:: html + +
Transforming target variable with z-score
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/03_complex_models/images/thumb/sphx_glr_run_hyperparameter_tuning_bayessearch_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_03_complex_models_run_hyperparameter_tuning_bayessearch.py` + +.. raw:: html + +
Tuning Hyperparameters using Bayesian Search
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/03_complex_models/images/thumb/sphx_glr_run_hyperparameter_multiple_grids_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_03_complex_models_run_hyperparameter_multiple_grids.py` + +.. raw:: html + +
Tuning Multiple Hyperparameters Grids
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/03_complex_models/images/thumb/sphx_glr_run_stacked_models_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_03_complex_models_run_stacked_models.py` + +.. raw:: html + +
Stacking Classification
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/03_complex_models/images/thumb/sphx_glr_run_hyperparameter_tuning_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_03_complex_models_run_hyperparameter_tuning.py` + +.. raw:: html + +
Tuning Hyperparameters
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/03_complex_models/images/thumb/sphx_glr_run_example_pca_featsets_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_03_complex_models_run_example_pca_featsets.py` + +.. raw:: html + +
Regression Analysis
+
+ + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /auto_examples/03_complex_models/run_apply_to_target + /auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch + /auto_examples/03_complex_models/run_hyperparameter_multiple_grids + /auto_examples/03_complex_models/run_stacked_models + /auto_examples/03_complex_models/run_hyperparameter_tuning + /auto_examples/03_complex_models/run_example_pca_featsets + + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_apply_to_target.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_apply_to_target.rst.txt new file mode 100644 index 000000000..7d548e2b5 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_apply_to_target.rst.txt @@ -0,0 +1,329 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/03_complex_models/run_apply_to_target.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_03_complex_models_run_apply_to_target.py: + + +Transforming target variable with z-score +========================================= + +This example uses the sklearn ``diabetes`` regression dataset, and transforms the +target variable, in this case, using z-score. Then, we perform a regression +analysis using Ridge Regression model. + +.. GENERATED FROM PYTHON SOURCE LINES 10-24 + +.. code-block:: default + + # Authors: Lya K. Paas Oliveros + # Sami Hamdan + # + # License: AGPL + + import pandas as pd + from sklearn.datasets import load_diabetes + from sklearn.model_selection import train_test_split + + from julearn import run_cross_validation + from julearn.utils import configure_logging + + from julearn.pipeline import PipelineCreator, TargetPipelineCreator + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 25-26 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 26-28 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:35,273 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:35,273 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:35,273 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:35,274 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:35,274 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:35,274 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:35,274 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 29-30 + +Load the diabetes dataset from ``sklearn`` as a ``pandas.DataFrame``. + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +.. code-block:: default + + features, target = load_diabetes(return_X_y=True, as_frame=True) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-37 + +Dataset contains ten variables age, sex, body mass index, average blood +pressure, and six blood serum measurements (s1-s6) diabetes patients and +a quantitative measure of disease progression one year after baseline which +is the target we are interested in predicting. + +.. GENERATED FROM PYTHON SOURCE LINES 37-40 + +.. code-block:: default + + print("Features: \n", features.head()) + print("Target: \n", target.describe()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Features: + age sex bmi ... s4 s5 s6 + 0 0.038076 0.050680 0.061696 ... -0.002592 0.019907 -0.017646 + 1 -0.001882 -0.044642 -0.051474 ... -0.039493 -0.068332 -0.092204 + 2 0.085299 0.050680 0.044451 ... -0.002592 0.002861 -0.025930 + 3 -0.089063 -0.044642 -0.011595 ... 0.034309 0.022688 -0.009362 + 4 0.005383 -0.044642 -0.036385 ... -0.002592 -0.031988 -0.046641 + + [5 rows x 10 columns] + Target: + count 442.000000 + mean 152.133484 + std 77.093005 + min 25.000000 + 25% 87.000000 + 50% 140.500000 + 75% 211.500000 + max 346.000000 + Name: target, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 41-43 + +Let's combine features and target together in one dataframe and define X +and y. + +.. GENERATED FROM PYTHON SOURCE LINES 43-48 + +.. code-block:: default + + data_diabetes = pd.concat([features, target], axis=1) + + X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] + y = "target" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 49-50 + +Split the dataset into train and test. + +.. GENERATED FROM PYTHON SOURCE LINES 50-52 + +.. code-block:: default + + train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 53-55 + +Let's create the model. Since we will be transforming the target variable +we will first need to create a TargetPipelineCreator for this. + +.. GENERATED FROM PYTHON SOURCE LINES 55-59 + +.. code-block:: default + + + target_creator = TargetPipelineCreator() + target_creator.add("zscore") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 60-61 + +Now we can create the pipeline using a PipelineCreator. + +.. GENERATED FROM PYTHON SOURCE LINES 61-76 + +.. code-block:: default + + creator = PipelineCreator(problem_type="regression") + creator.add(target_creator, apply_to="target") + creator.add("ridge") + + scores, model = run_cross_validation( + X=X, + y=y, + data=train_diabetes, + model=creator, + return_estimator="final", + scoring="neg_mean_absolute_error", + ) + + print(scores.head(5)) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:35,289 - julearn - INFO - Adding step jutargetpipeline that applies to ColumnTypes + 2024-10-17 13:53:35,290 - julearn - INFO - Step added + 2024-10-17 13:53:35,290 - julearn - INFO - Adding step ridge that applies to ColumnTypes + 2024-10-17 13:53:35,290 - julearn - INFO - Step added + 2024-10-17 13:53:35,290 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:35,290 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:35,290 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:35,290 - julearn - INFO - Target: target + 2024-10-17 13:53:35,290 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:35,290 - julearn - INFO - X_types:{} + 2024-10-17 13:53:35,290 - julearn - WARNING - The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:35,291 - julearn - INFO - ==================== + 2024-10-17 13:53:35,291 - julearn - INFO - + 2024-10-17 13:53:35,291 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:35,291 - julearn - INFO - ==================== + 2024-10-17 13:53:35,291 - julearn - INFO - + 2024-10-17 13:53:35,291 - julearn - INFO - = Data Information = + 2024-10-17 13:53:35,291 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:35,292 - julearn - INFO - Number of samples: 309 + 2024-10-17 13:53:35,292 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:35,292 - julearn - INFO - ==================== + 2024-10-17 13:53:35,292 - julearn - INFO - + 2024-10-17 13:53:35,292 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:35,292 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + fit_time score_time ... fold cv_mdsum + 0 0.003451 0.001575 ... 0 b10eef89b4192178d482d7a1587a248a + 1 0.003157 0.001528 ... 1 b10eef89b4192178d482d7a1587a248a + 2 0.003109 0.001582 ... 2 b10eef89b4192178d482d7a1587a248a + 3 0.003158 0.001519 ... 3 b10eef89b4192178d482d7a1587a248a + 4 0.003211 0.001525 ... 4 b10eef89b4192178d482d7a1587a248a + + [5 rows x 8 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 77-78 + +Mean value of mean absolute error across CV + +.. GENERATED FROM PYTHON SOURCE LINES 78-79 + +.. code-block:: default + + print(scores["test_score"].mean() * -1) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 51.51357151914367 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.063 seconds) + + +.. _sphx_glr_download_auto_examples_03_complex_models_run_apply_to_target.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_apply_to_target.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_apply_to_target.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_example_pca_featsets.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_example_pca_featsets.rst.txt new file mode 100644 index 000000000..4f4ebca1a --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_example_pca_featsets.rst.txt @@ -0,0 +1,698 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/03_complex_models/run_example_pca_featsets.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_03_complex_models_run_example_pca_featsets.py: + + +Regression Analysis +=================== + +This example uses the ``diabetes`` data from ``sklearn datasets`` and performs +a regression analysis using a Ridge Regression model. We'll use the +``julearn.PipelineCreator`` to create a pipeline with two different PCA steps and +reduce the dimensionality of the data, each one computed on a different +subset of features. + +.. GENERATED FROM PYTHON SOURCE LINES 12-31 + +.. code-block:: default + + # Authors: Georgios Antonopoulos + # Kaustubh R. Patil + # Shammi More + # Federico Raimondo + # License: AGPL + + import pandas as pd + import seaborn as sns + import numpy as np + import matplotlib.pyplot as plt + from sklearn.datasets import load_diabetes + from sklearn.metrics import mean_absolute_error + from sklearn.model_selection import train_test_split + + from julearn import run_cross_validation + from julearn.utils import configure_logging + from julearn.pipeline import PipelineCreator + from julearn.inspect import preprocess + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 32-33 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 33-35 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:47,899 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:47,899 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:47,899 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:47,899 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:47,899 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:47,899 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:47,899 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 36-37 + +Load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. + +.. GENERATED FROM PYTHON SOURCE LINES 37-39 + +.. code-block:: default + + features, target = load_diabetes(return_X_y=True, as_frame=True) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 40-44 + +Dataset contains ten variables age, sex, body mass index, average blood +pressure, and six blood serum measurements (s1-s6) diabetes patients and +a quantitative measure of disease progression one year after baseline which +is the target we are interested in predicting. + +.. GENERATED FROM PYTHON SOURCE LINES 44-48 + +.. code-block:: default + + + print("Features: \n", features.head()) + print("Target: \n", target.describe()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Features: + age sex bmi ... s4 s5 s6 + 0 0.038076 0.050680 0.061696 ... -0.002592 0.019907 -0.017646 + 1 -0.001882 -0.044642 -0.051474 ... -0.039493 -0.068332 -0.092204 + 2 0.085299 0.050680 0.044451 ... -0.002592 0.002861 -0.025930 + 3 -0.089063 -0.044642 -0.011595 ... 0.034309 0.022688 -0.009362 + 4 0.005383 -0.044642 -0.036385 ... -0.002592 -0.031988 -0.046641 + + [5 rows x 10 columns] + Target: + count 442.000000 + mean 152.133484 + std 77.093005 + min 25.000000 + 25% 87.000000 + 50% 140.500000 + 75% 211.500000 + max 346.000000 + Name: target, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 49-51 + +Let's combine features and target together in one dataframe and define X +and y + +.. GENERATED FROM PYTHON SOURCE LINES 51-56 + +.. code-block:: default + + data_diabetes = pd.concat([features, target], axis=1) + + X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] + y = "target" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 57-59 + +Assign types to the features and create feature groups for PCA. +We will keep 1 component per PCA group. + +.. GENERATED FROM PYTHON SOURCE LINES 59-65 + +.. code-block:: default + + X_types = { + "pca1": ["age", "bmi", "bp"], + "pca2": ["s1", "s2", "s3", "s4", "s5", "s6"], + "categorical": ["sex"], + } + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 66-70 + +Create a pipeline to process the data and the fit a model. We must specify +how each ``X_type`` will be used. For example if in the last step we do not +specify ``apply_to=["continuous", "categorical"]``, then the pipeline will not +know what to do with the categorical features. + +.. GENERATED FROM PYTHON SOURCE LINES 70-75 + +.. code-block:: default + + creator = PipelineCreator(problem_type="regression") + creator.add("pca", apply_to="pca1", n_components=1, name="pca_feats1") + creator.add("pca", apply_to="pca2", n_components=1, name="pca_feats2") + creator.add("ridge", apply_to=["continuous", "categorical"]) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:47,914 - julearn - INFO - Adding step pca_feats1 that applies to ColumnTypes + 2024-10-17 13:53:47,914 - julearn - INFO - Setting hyperparameter n_components = 1 + 2024-10-17 13:53:47,914 - julearn - INFO - Step added + 2024-10-17 13:53:47,914 - julearn - INFO - Adding step pca_feats2 that applies to ColumnTypes + 2024-10-17 13:53:47,914 - julearn - INFO - Setting hyperparameter n_components = 1 + 2024-10-17 13:53:47,914 - julearn - INFO - Step added + 2024-10-17 13:53:47,914 - julearn - INFO - Adding step ridge that applies to ColumnTypes + 2024-10-17 13:53:47,914 - julearn - INFO - Step added + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 76-77 + +Split the dataset into train and test. + +.. GENERATED FROM PYTHON SOURCE LINES 77-79 + +.. code-block:: default + + train_diabetes, test_diabetes = train_test_split(data_diabetes, test_size=0.3) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 80-82 + +Train a ridge regression model on train dataset and use mean absolute error +for scoring. + +.. GENERATED FROM PYTHON SOURCE LINES 82-92 + +.. code-block:: default + + scores, model = run_cross_validation( + X=X, + y=y, + X_types=X_types, + data=train_diabetes, + model=creator, + scoring="r2", + return_estimator="final", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:47,915 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:47,915 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:47,916 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:47,916 - julearn - INFO - Target: target + 2024-10-17 13:53:47,916 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:47,916 - julearn - INFO - X_types:{'pca1': ['age', 'bmi', 'bp'], 'pca2': ['s1', 's2', 's3', 's4', 's5', 's6'], 'categorical': ['sex']} + 2024-10-17 13:53:47,916 - julearn - INFO - ==================== + 2024-10-17 13:53:47,916 - julearn - INFO - + 2024-10-17 13:53:47,918 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:47,918 - julearn - INFO - ==================== + 2024-10-17 13:53:47,918 - julearn - INFO - + 2024-10-17 13:53:47,918 - julearn - INFO - = Data Information = + 2024-10-17 13:53:47,918 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:47,918 - julearn - INFO - Number of samples: 309 + 2024-10-17 13:53:47,918 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:47,918 - julearn - INFO - ==================== + 2024-10-17 13:53:47,918 - julearn - INFO - + 2024-10-17 13:53:47,918 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:47,918 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 93-94 + +The scores dataframe has all the values for each CV split. + +.. GENERATED FROM PYTHON SOURCE LINES 94-96 + +.. code-block:: default + + print(scores.head()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + fit_time score_time ... fold cv_mdsum + 0 0.013706 0.006040 ... 0 b10eef89b4192178d482d7a1587a248a + 1 0.012865 0.005929 ... 1 b10eef89b4192178d482d7a1587a248a + 2 0.013188 0.005961 ... 2 b10eef89b4192178d482d7a1587a248a + 3 0.012801 0.005950 ... 3 b10eef89b4192178d482d7a1587a248a + 4 0.013081 0.006739 ... 4 b10eef89b4192178d482d7a1587a248a + + [5 rows x 8 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 97-98 + +Mean value of mean absolute error across CV. + +.. GENERATED FROM PYTHON SOURCE LINES 98-100 + +.. code-block:: default + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 0.3107976743678922 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 101-104 + +Let's see how the data looks like after preprocessing. We will process the +data until the first PCA step. We should get the first PCA component for +["age", "bmi", "bp"] and leave other features untouched. + +.. GENERATED FROM PYTHON SOURCE LINES 104-108 + +.. code-block:: default + + data_processed1 = preprocess(model, X, data=train_diabetes, until="pca_feats1") + print("Data after preprocessing until PCA step 1") + data_processed1.head() + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Data after preprocessing until PCA step 1 + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
pca_feats1__pca0sexs1s2s3s4s5s6
1610.0631750.0506800.1332740.131461-0.0397190.1081110.0757410.085907
1400.0547790.050680-0.030464-0.001314-0.043401-0.002592-0.0332460.015491
1450.098172-0.044642-0.033216-0.0326290.011824-0.039493-0.015999-0.050783
9-0.032289-0.044642-0.012577-0.034508-0.024993-0.0025920.067737-0.013504
315-0.045025-0.0446420.0314540.0206070.056003-0.039493-0.010903-0.001078
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 109-111 + +We will process the data until the second PCA step. We should now also get +one PCA component for ["s1", "s2", "s3", "s4", "s5", "s6"]. + +.. GENERATED FROM PYTHON SOURCE LINES 111-115 + +.. code-block:: default + + data_processed2 = preprocess(model, X, data=train_diabetes, until="pca_feats2") + print("Data after preprocessing until PCA step 2") + data_processed2.head() + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Data after preprocessing until PCA step 2 + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
pca_feats2__pca0pca_feats1__pca0sex
1610.2347160.0631750.050680
140-0.0121410.0547790.050680
145-0.0787840.098172-0.044642
90.006290-0.032289-0.044642
315-0.026190-0.045025-0.044642
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 116-117 + +Now we can get the MAE fold and repetition: + +.. GENERATED FROM PYTHON SOURCE LINES 117-123 + +.. code-block:: default + + df_mae = scores.set_index(["repeat", "fold"])["test_score"].unstack() * -1 + df_mae.index.name = "Repeats" + df_mae.columns.name = "K-fold splits" + + print(df_mae) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + K-fold splits 0 1 2 3 4 + Repeats + 0 -0.341472 -0.348168 -0.269257 -0.286067 -0.309025 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 124-125 + +Plot heatmap of mean absolute error (MAE) over all repeats and CV splits. + +.. GENERATED FROM PYTHON SOURCE LINES 125-129 + +.. code-block:: default + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.heatmap(df_mae, cmap="YlGnBu") + plt.title("Cross-validation MAE") + + + + +.. image-sg:: /auto_examples/03_complex_models/images/sphx_glr_run_example_pca_featsets_001.png + :alt: Cross-validation MAE + :srcset: /auto_examples/03_complex_models/images/sphx_glr_run_example_pca_featsets_001.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + Text(0.5, 1.0, 'Cross-validation MAE') + + + +.. GENERATED FROM PYTHON SOURCE LINES 130-132 + +Use the final model to make predictions on test data and plot scatterplot +of true values vs predicted values. + +.. GENERATED FROM PYTHON SOURCE LINES 132-155 + +.. code-block:: default + + y_true = test_diabetes[y] + y_pred = model.predict(test_diabetes[X]) + mae = format(mean_absolute_error(y_true, y_pred), ".2f") + corr = format(np.corrcoef(y_pred, y_true)[1, 0], ".2f") + + fig, ax = plt.subplots(1, 1, figsize=(10, 7)) + sns.set_style("darkgrid") + plt.scatter(y_true, y_pred) + plt.plot(y_true, y_true) + xmin, xmax = ax.get_xlim() + ymin, ymax = ax.get_ylim() + text = "MAE: " + str(mae) + " CORR: " + str(corr) + ax.set(xlabel="True values", ylabel="Predicted values") + plt.title("Actual vs Predicted") + plt.text( + xmax - 0.01 * xmax, + ymax - 0.01 * ymax, + text, + verticalalignment="top", + horizontalalignment="right", + fontsize=12, + ) + plt.axis("scaled") + + + +.. image-sg:: /auto_examples/03_complex_models/images/sphx_glr_run_example_pca_featsets_002.png + :alt: Actual vs Predicted + :srcset: /auto_examples/03_complex_models/images/sphx_glr_run_example_pca_featsets_002.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + + (8.95, 362.05, 8.95, 362.05) + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.427 seconds) + + +.. _sphx_glr_download_auto_examples_03_complex_models_run_example_pca_featsets.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_example_pca_featsets.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_example_pca_featsets.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_multiple_grids.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_multiple_grids.rst.txt new file mode 100644 index 000000000..6b6d19be2 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_multiple_grids.rst.txt @@ -0,0 +1,538 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/03_complex_models/run_hyperparameter_multiple_grids.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_03_complex_models_run_hyperparameter_multiple_grids.py: + + +Tuning Multiple Hyperparameters Grids +===================================== + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier while tuning multiple hyperparameters +grids at the same time. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 17-27 + +.. code-block:: default + + # Authors: Federico Raimondo + # License: AGPL + + import numpy as np + from seaborn import load_dataset + + from julearn import run_cross_validation + from julearn.utils import configure_logging + from julearn.pipeline import PipelineCreator + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 28-29 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 29-31 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:40,096 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:40,096 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:40,096 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:40,096 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:40,096 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:40,096 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:40,096 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 32-33 + +Set the random seed to always have the same example. + +.. GENERATED FROM PYTHON SOURCE LINES 33-35 + +.. code-block:: default + + np.random.seed(42) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 36-37 + +Load the dataset. + +.. GENERATED FROM PYTHON SOURCE LINES 37-40 + +.. code-block:: default + + df_fmri = load_dataset("fmri") + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
subjecttimepointeventregionsignal
0s1318stimparietal-0.017552
1s514stimparietal-0.080883
2s1218stimparietal-0.081033
3s1118stimparietal-0.046134
4s1018stimparietal-0.037970
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 41-42 + +Set the dataframe in the right format. + +.. GENERATED FROM PYTHON SOURCE LINES 42-49 + +.. code-block:: default + + df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" + ) + + df_fmri = df_fmri.reset_index() + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
regionsubjecttimepointeventfrontalparietal
0s00cue0.007766-0.006899
1s00stim-0.021452-0.039327
2s01cue0.0164400.000300
3s01stim-0.021054-0.035735
4s02cue0.0242960.033220
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 50-51 + +Lets do a first attempt and use a linear SVM with the default parameters. + +.. GENERATED FROM PYTHON SOURCE LINES 51-63 + +.. code-block:: default + + + X = ["frontal", "parietal"] + y = "event" + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm", kernel="linear") + + scores = run_cross_validation(X=X, y=y, data=df_fmri, model=creator) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:40,105 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:40,105 - julearn - INFO - Step added + 2024-10-17 13:53:40,105 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:40,105 - julearn - INFO - Setting hyperparameter kernel = linear + 2024-10-17 13:53:40,105 - julearn - INFO - Step added + 2024-10-17 13:53:40,105 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:40,105 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:40,106 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:40,106 - julearn - INFO - Target: event + 2024-10-17 13:53:40,106 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:40,106 - julearn - INFO - X_types:{} + 2024-10-17 13:53:40,106 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:40,106 - julearn - INFO - ==================== + 2024-10-17 13:53:40,106 - julearn - INFO - + 2024-10-17 13:53:40,107 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:40,107 - julearn - INFO - ==================== + 2024-10-17 13:53:40,107 - julearn - INFO - + 2024-10-17 13:53:40,107 - julearn - INFO - = Data Information = + 2024-10-17 13:53:40,107 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:40,107 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:40,107 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:40,107 - julearn - INFO - ==================== + 2024-10-17 13:53:40,107 - julearn - INFO - + 2024-10-17 13:53:40,108 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:40,108 - julearn - INFO - Target type: object + 2024-10-17 13:53:40,108 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:40,108 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:40,109 - julearn - INFO - Binary classification problem detected. + 0.5939164168576971 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 64-72 + +Now let's tune a bit this SVM. We will use a grid search to tune the +regularization parameter ``C`` and the kernel. We will also tune the ``gamma``. +But since the ``gamma`` is only used for the rbf kernel, we will use a +different grid for the ``"rbf"`` kernel. + +To specify two different sets of parameters for the same step, we can +explicitly specify the name of the step. This is done by passing the +``name`` parameter to the ``add`` method. + +.. GENERATED FROM PYTHON SOURCE LINES 72-99 + +.. code-block:: default + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm", kernel="linear", C=[0.01, 0.1], name="svm") + creator.add( + "svm", + kernel="rbf", + C=[0.01, 0.1], + gamma=["scale", "auto", 1e-2, 1e-3], + name="svm", + ) + + search_params = { + "kind": "grid", + "cv": 2, # to speed up the example + } + + scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", + ) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:40,164 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:40,164 - julearn - INFO - Step added + 2024-10-17 13:53:40,164 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:40,164 - julearn - INFO - Setting hyperparameter kernel = linear + 2024-10-17 13:53:40,164 - julearn - INFO - Tuning hyperparameter C = [0.01, 0.1] + 2024-10-17 13:53:40,164 - julearn - INFO - Step added + 2024-10-17 13:53:40,164 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:40,164 - julearn - INFO - Setting hyperparameter kernel = rbf + 2024-10-17 13:53:40,164 - julearn - INFO - Tuning hyperparameter C = [0.01, 0.1] + 2024-10-17 13:53:40,164 - julearn - INFO - Tuning hyperparameter gamma = ['scale', 'auto', 0.01, 0.001] + 2024-10-17 13:53:40,165 - julearn - INFO - Step added + 2024-10-17 13:53:40,165 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:40,165 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:40,165 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:40,165 - julearn - INFO - Target: event + 2024-10-17 13:53:40,165 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:40,165 - julearn - INFO - X_types:{} + 2024-10-17 13:53:40,165 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:40,165 - julearn - INFO - ==================== + 2024-10-17 13:53:40,166 - julearn - INFO - + 2024-10-17 13:53:40,166 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:40,166 - julearn - INFO - Tuning hyperparameters using grid + 2024-10-17 13:53:40,166 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:40,166 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:40,166 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:40,166 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:40,166 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:40,167 - julearn - INFO - ==================== + 2024-10-17 13:53:40,167 - julearn - INFO - + 2024-10-17 13:53:40,167 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:40,167 - julearn - INFO - Tuning hyperparameters using grid + 2024-10-17 13:53:40,167 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:40,167 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:40,167 - julearn - INFO - svm__gamma: ['scale', 'auto', 0.01, 0.001] + 2024-10-17 13:53:40,167 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:40,167 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:40,167 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:40,168 - julearn - INFO - ==================== + 2024-10-17 13:53:40,168 - julearn - INFO - + 2024-10-17 13:53:40,168 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:40,168 - julearn - INFO - Tuning hyperparameters using grid + 2024-10-17 13:53:40,168 - julearn - INFO - Hyperparameters list: + 2024-10-17 13:53:40,168 - julearn - INFO - Set 0 + 2024-10-17 13:53:40,168 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:40,168 - julearn - INFO - set_column_types: [SetColumnTypes(X_types={})] + 2024-10-17 13:53:40,168 - julearn - INFO - svm: [SVC(kernel='linear')] + 2024-10-17 13:53:40,168 - julearn - INFO - Set 1 + 2024-10-17 13:53:40,168 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:40,168 - julearn - INFO - svm__gamma: ['scale', 'auto', 0.01, 0.001] + 2024-10-17 13:53:40,168 - julearn - INFO - set_column_types: [SetColumnTypes(X_types={})] + 2024-10-17 13:53:40,169 - julearn - INFO - svm: [SVC()] + 2024-10-17 13:53:40,169 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:40,169 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:40,169 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:40,169 - julearn - INFO - ==================== + 2024-10-17 13:53:40,169 - julearn - INFO - + 2024-10-17 13:53:40,169 - julearn - INFO - = Data Information = + 2024-10-17 13:53:40,169 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:40,169 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:40,169 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:40,169 - julearn - INFO - ==================== + 2024-10-17 13:53:40,169 - julearn - INFO - + 2024-10-17 13:53:40,169 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:40,169 - julearn - INFO - Target type: object + 2024-10-17 13:53:40,170 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:40,170 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:40,170 - julearn - INFO - Binary classification problem detected. + 0.7087109857168048 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 100-101 + +It seems that we might have found a better model, but which one is it? + +.. GENERATED FROM PYTHON SOURCE LINES 101-103 + +.. code-block:: default + + print(estimator.best_params_) + print(estimator.best_estimator_["svm"]._gamma) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + {'set_column_types': SetColumnTypes(X_types={}), 'svm': SVC(), 'svm__C': 0.1, 'svm__gamma': 'scale'} + 0.5 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 1.317 seconds) + + +.. _sphx_glr_download_auto_examples_03_complex_models_run_hyperparameter_multiple_grids.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_hyperparameter_multiple_grids.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_hyperparameter_multiple_grids.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_tuning.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_tuning.rst.txt new file mode 100644 index 000000000..513783a54 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_tuning.rst.txt @@ -0,0 +1,755 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/03_complex_models/run_hyperparameter_tuning.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_03_complex_models_run_hyperparameter_tuning.py: + + +Tuning Hyperparameters +======================= + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier and analyze the model. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 16-26 + +.. code-block:: default + + # Authors: Federico Raimondo + # License: AGPL + + import numpy as np + from seaborn import load_dataset + + from julearn import run_cross_validation + from julearn.utils import configure_logging + from julearn.pipeline import PipelineCreator + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 27-28 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 28-30 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:45,335 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:45,335 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:45,335 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:45,335 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:45,335 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:45,335 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:45,335 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 31-32 + +Set the random seed to always have the same example. + +.. GENERATED FROM PYTHON SOURCE LINES 32-34 + +.. code-block:: default + + np.random.seed(42) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 35-36 + +Load the dataset. + +.. GENERATED FROM PYTHON SOURCE LINES 36-39 + +.. code-block:: default + + df_fmri = load_dataset("fmri") + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
subjecttimepointeventregionsignal
0s1318stimparietal-0.017552
1s514stimparietal-0.080883
2s1218stimparietal-0.081033
3s1118stimparietal-0.046134
4s1018stimparietal-0.037970
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 40-41 + +Set the dataframe in the right format. + +.. GENERATED FROM PYTHON SOURCE LINES 41-48 + +.. code-block:: default + + df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" + ) + + df_fmri = df_fmri.reset_index() + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
regionsubjecttimepointeventfrontalparietal
0s00cue0.007766-0.006899
1s00stim-0.021452-0.039327
2s01cue0.0164400.000300
3s01stim-0.021054-0.035735
4s02cue0.0242960.033220
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 49-50 + +Let's do a first attempt and use a linear SVM with the default parameters. + +.. GENERATED FROM PYTHON SOURCE LINES 50-61 + +.. code-block:: default + + X = ["frontal", "parietal"] + y = "event" + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm", kernel="linear") + + scores = run_cross_validation(X=X, y=y, data=df_fmri, model=creator) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:45,343 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:45,343 - julearn - INFO - Step added + 2024-10-17 13:53:45,344 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:45,344 - julearn - INFO - Setting hyperparameter kernel = linear + 2024-10-17 13:53:45,344 - julearn - INFO - Step added + 2024-10-17 13:53:45,344 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:45,344 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:45,344 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:45,344 - julearn - INFO - Target: event + 2024-10-17 13:53:45,344 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:45,344 - julearn - INFO - X_types:{} + 2024-10-17 13:53:45,344 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:45,345 - julearn - INFO - ==================== + 2024-10-17 13:53:45,345 - julearn - INFO - + 2024-10-17 13:53:45,345 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:45,345 - julearn - INFO - ==================== + 2024-10-17 13:53:45,345 - julearn - INFO - + 2024-10-17 13:53:45,345 - julearn - INFO - = Data Information = + 2024-10-17 13:53:45,345 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:45,346 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:45,346 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:45,346 - julearn - INFO - ==================== + 2024-10-17 13:53:45,346 - julearn - INFO - + 2024-10-17 13:53:45,346 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:45,346 - julearn - INFO - Target type: object + 2024-10-17 13:53:45,346 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:45,347 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:45,347 - julearn - INFO - Binary classification problem detected. + 0.5939164168576971 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 62-65 + +The score is not so good. Let's try to see if there is an optimal +regularization parameter (C) for the linear SVM. +We will use a grid search to find the best ``C``. + +.. GENERATED FROM PYTHON SOURCE LINES 65-86 + +.. code-block:: default + + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm", kernel="linear", C=[0.01, 0.1]) + + search_params = { + "kind": "grid", + "cv": 2, # to speed up the example + } + + scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", + ) + + print(scores["test_score"].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:45,402 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:45,402 - julearn - INFO - Step added + 2024-10-17 13:53:45,402 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:45,402 - julearn - INFO - Setting hyperparameter kernel = linear + 2024-10-17 13:53:45,402 - julearn - INFO - Tuning hyperparameter C = [0.01, 0.1] + 2024-10-17 13:53:45,402 - julearn - INFO - Step added + 2024-10-17 13:53:45,403 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:45,403 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:45,403 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:45,403 - julearn - INFO - Target: event + 2024-10-17 13:53:45,403 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:45,403 - julearn - INFO - X_types:{} + 2024-10-17 13:53:45,403 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:45,403 - julearn - INFO - ==================== + 2024-10-17 13:53:45,403 - julearn - INFO - + 2024-10-17 13:53:45,404 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:45,404 - julearn - INFO - Tuning hyperparameters using grid + 2024-10-17 13:53:45,404 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:45,404 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:45,404 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:45,404 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:45,404 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:45,404 - julearn - INFO - ==================== + 2024-10-17 13:53:45,405 - julearn - INFO - + 2024-10-17 13:53:45,405 - julearn - INFO - = Data Information = + 2024-10-17 13:53:45,405 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:45,405 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:45,405 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:45,405 - julearn - INFO - ==================== + 2024-10-17 13:53:45,405 - julearn - INFO - + 2024-10-17 13:53:45,405 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:45,405 - julearn - INFO - Target type: object + 2024-10-17 13:53:45,405 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:45,406 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:45,406 - julearn - INFO - Binary classification problem detected. + 0.588308940222183 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 87-88 + +This did not change much, lets explore other kernels too. + +.. GENERATED FROM PYTHON SOURCE LINES 88-103 + +.. code-block:: default + + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm", kernel=["linear", "rbf", "poly"], C=[0.01, 0.1]) + + scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", + ) + + print(scores["test_score"].mean()) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:45,688 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:45,688 - julearn - INFO - Step added + 2024-10-17 13:53:45,688 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:45,688 - julearn - INFO - Tuning hyperparameter kernel = ['linear', 'rbf', 'poly'] + 2024-10-17 13:53:45,688 - julearn - INFO - Tuning hyperparameter C = [0.01, 0.1] + 2024-10-17 13:53:45,688 - julearn - INFO - Step added + 2024-10-17 13:53:45,688 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:45,688 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:45,688 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:45,688 - julearn - INFO - Target: event + 2024-10-17 13:53:45,688 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:45,689 - julearn - INFO - X_types:{} + 2024-10-17 13:53:45,689 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:45,689 - julearn - INFO - ==================== + 2024-10-17 13:53:45,689 - julearn - INFO - + 2024-10-17 13:53:45,690 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:45,690 - julearn - INFO - Tuning hyperparameters using grid + 2024-10-17 13:53:45,690 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:45,690 - julearn - INFO - svm__kernel: ['linear', 'rbf', 'poly'] + 2024-10-17 13:53:45,690 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:45,690 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:45,690 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:45,690 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:45,690 - julearn - INFO - ==================== + 2024-10-17 13:53:45,690 - julearn - INFO - + 2024-10-17 13:53:45,690 - julearn - INFO - = Data Information = + 2024-10-17 13:53:45,690 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:45,691 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:45,691 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:45,691 - julearn - INFO - ==================== + 2024-10-17 13:53:45,691 - julearn - INFO - + 2024-10-17 13:53:45,691 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:45,691 - julearn - INFO - Target type: object + 2024-10-17 13:53:45,691 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:45,692 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:45,692 - julearn - INFO - Binary classification problem detected. + 0.7087109857168048 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 104-105 + +It seems that we might have found a better model, but which one is it? + +.. GENERATED FROM PYTHON SOURCE LINES 105-107 + +.. code-block:: default + + print(estimator.best_params_) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + {'svm__C': 0.1, 'svm__kernel': 'rbf'} + + + + +.. GENERATED FROM PYTHON SOURCE LINES 108-110 + +Now that we know that a RBF kernel is better, lest test different *gamma* +parameters. + +.. GENERATED FROM PYTHON SOURCE LINES 110-127 + +.. code-block:: default + + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm", kernel="rbf", C=[0.01, 0.1], gamma=[1e-2, 1e-3]) + + scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + search_params=search_params, + return_estimator="final", + ) + + print(scores["test_score"].mean()) + print(estimator.best_params_) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:46,416 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:46,416 - julearn - INFO - Step added + 2024-10-17 13:53:46,416 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:46,416 - julearn - INFO - Setting hyperparameter kernel = rbf + 2024-10-17 13:53:46,416 - julearn - INFO - Tuning hyperparameter C = [0.01, 0.1] + 2024-10-17 13:53:46,417 - julearn - INFO - Tuning hyperparameter gamma = [0.01, 0.001] + 2024-10-17 13:53:46,417 - julearn - INFO - Step added + 2024-10-17 13:53:46,417 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:46,417 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:46,417 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:46,417 - julearn - INFO - Target: event + 2024-10-17 13:53:46,417 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:46,417 - julearn - INFO - X_types:{} + 2024-10-17 13:53:46,417 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:46,418 - julearn - INFO - ==================== + 2024-10-17 13:53:46,418 - julearn - INFO - + 2024-10-17 13:53:46,418 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:46,418 - julearn - INFO - Tuning hyperparameters using grid + 2024-10-17 13:53:46,418 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:46,418 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:46,418 - julearn - INFO - svm__gamma: [0.01, 0.001] + 2024-10-17 13:53:46,418 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:46,419 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:46,419 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:46,419 - julearn - INFO - ==================== + 2024-10-17 13:53:46,419 - julearn - INFO - + 2024-10-17 13:53:46,419 - julearn - INFO - = Data Information = + 2024-10-17 13:53:46,419 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:46,419 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:46,419 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:46,419 - julearn - INFO - ==================== + 2024-10-17 13:53:46,419 - julearn - INFO - + 2024-10-17 13:53:46,419 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:46,419 - julearn - INFO - Target type: object + 2024-10-17 13:53:46,420 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:46,420 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:46,420 - julearn - INFO - Binary classification problem detected. + 0.5188855581026275 + {'svm__C': 0.01, 'svm__gamma': 0.001} + + + + +.. GENERATED FROM PYTHON SOURCE LINES 128-130 + +It seems that without tuning the gamma parameter we had a better accuracy. +Let's add the default value and see what happens. + +.. GENERATED FROM PYTHON SOURCE LINES 130-151 + +.. code-block:: default + + + creator = PipelineCreator(problem_type="classification") + creator.add("zscore") + creator.add("svm", kernel="rbf", C=[0.01, 0.1], gamma=[1e-2, 1e-3, "scale"]) + X = ["frontal", "parietal"] + y = "event" + + search_params = {"cv": 2} + + scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=creator, + return_estimator="final", + search_params=search_params, + ) + + print(scores["test_score"].mean()) + print(estimator.best_params_) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:46,972 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:46,973 - julearn - INFO - Step added + 2024-10-17 13:53:46,973 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:46,973 - julearn - INFO - Setting hyperparameter kernel = rbf + 2024-10-17 13:53:46,973 - julearn - INFO - Tuning hyperparameter C = [0.01, 0.1] + 2024-10-17 13:53:46,973 - julearn - INFO - Tuning hyperparameter gamma = [0.01, 0.001, 'scale'] + 2024-10-17 13:53:46,973 - julearn - INFO - Step added + 2024-10-17 13:53:46,973 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:46,973 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:46,973 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:46,973 - julearn - INFO - Target: event + 2024-10-17 13:53:46,973 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:46,973 - julearn - INFO - X_types:{} + 2024-10-17 13:53:46,973 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:46,974 - julearn - INFO - ==================== + 2024-10-17 13:53:46,974 - julearn - INFO - + 2024-10-17 13:53:46,974 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:46,975 - julearn - INFO - Tuning hyperparameters using grid + 2024-10-17 13:53:46,975 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:46,975 - julearn - INFO - svm__C: [0.01, 0.1] + 2024-10-17 13:53:46,975 - julearn - INFO - svm__gamma: [0.01, 0.001, 'scale'] + 2024-10-17 13:53:46,975 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:46,975 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:46,975 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:46,975 - julearn - INFO - ==================== + 2024-10-17 13:53:46,975 - julearn - INFO - + 2024-10-17 13:53:46,975 - julearn - INFO - = Data Information = + 2024-10-17 13:53:46,975 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:46,975 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:46,975 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:46,975 - julearn - INFO - ==================== + 2024-10-17 13:53:46,975 - julearn - INFO - + 2024-10-17 13:53:46,976 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:46,976 - julearn - INFO - Target type: object + 2024-10-17 13:53:46,976 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:46,976 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:46,977 - julearn - INFO - Binary classification problem detected. + 0.7087109857168048 + {'svm__C': 0.1, 'svm__gamma': 'scale'} + + + + +.. GENERATED FROM PYTHON SOURCE LINES 152-153 + +.. code-block:: default + + print(estimator.best_estimator_["svm"]._gamma) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 0.5 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 2.442 seconds) + + +.. _sphx_glr_download_auto_examples_03_complex_models_run_hyperparameter_tuning.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_hyperparameter_tuning.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_hyperparameter_tuning.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch.rst.txt new file mode 100644 index 000000000..4a905e6fb --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch.rst.txt @@ -0,0 +1,497 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/03_complex_models/run_hyperparameter_tuning_bayessearch.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_03_complex_models_run_hyperparameter_tuning_bayessearch.py: + + +Tuning Hyperparameters using Bayesian Search +============================================ + +This example uses the ``fmri`` dataset, performs simple binary classification +using a Support Vector Machine classifier and analyzes the model. + +References +---------- + + Waskom, M.L., Frank, M.C., Wagner, A.D. (2016). Adaptive engagement of + cognitive control in context-dependent decision-making. Cerebral Cortex. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 16-28 + +.. code-block:: default + + + # Authors: Federico Raimondo + # License: AGPL + + import numpy as np + from seaborn import load_dataset + + from julearn import run_cross_validation + from julearn.utils import configure_logging, logger + from julearn.pipeline import PipelineCreator + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 29-30 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:35,465 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:35,466 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:35,466 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:35,466 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:35,466 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:35,466 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:35,466 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-34 + +Set the random seed to always have the same example. + +.. GENERATED FROM PYTHON SOURCE LINES 34-36 + +.. code-block:: default + + np.random.seed(42) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 37-38 + +Load the dataset. + +.. GENERATED FROM PYTHON SOURCE LINES 38-41 + +.. code-block:: default + + df_fmri = load_dataset("fmri") + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
subjecttimepointeventregionsignal
0s1318stimparietal-0.017552
1s514stimparietal-0.080883
2s1218stimparietal-0.081033
3s1118stimparietal-0.046134
4s1018stimparietal-0.037970
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 42-43 + +Set the dataframe in the right format. + +.. GENERATED FROM PYTHON SOURCE LINES 43-50 + +.. code-block:: default + + df_fmri = df_fmri.pivot( + index=["subject", "timepoint", "event"], columns="region", values="signal" + ) + + df_fmri = df_fmri.reset_index() + df_fmri.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
regionsubjecttimepointeventfrontalparietal
0s00cue0.007766-0.006899
1s00stim-0.021452-0.039327
2s01cue0.0164400.000300
3s01stim-0.021054-0.035735
4s02cue0.0242960.033220
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 51-53 + +Following the hyperparamter tuning example, we will now use a Bayesian +search to find the best hyperparameters for the SVM model. + +.. GENERATED FROM PYTHON SOURCE LINES 53-93 + +.. code-block:: default + + X = ["frontal", "parietal"] + y = "event" + + creator1 = PipelineCreator(problem_type="classification") + creator1.add("zscore") + creator1.add( + "svm", + kernel=["linear"], + C=(1e-6, 1e3, "log-uniform"), + ) + + creator2 = PipelineCreator(problem_type="classification") + creator2.add("zscore") + creator2.add( + "svm", + kernel=["rbf"], + C=(1e-6, 1e3, "log-uniform"), + gamma=(1e-6, 1e1, "log-uniform"), + ) + + search_params = { + "kind": "bayes", + "cv": 2, # to speed up the example + "n_iter": 10, # 10 iterations of bayesian search to speed up example + } + + + scores, estimator = run_cross_validation( + X=X, + y=y, + data=df_fmri, + model=[creator1, creator2], + cv=2, # to speed up the example + search_params=search_params, + return_estimator="final", + ) + + print(scores["test_score"].mean()) + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:35,475 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:35,475 - julearn - INFO - Step added + 2024-10-17 13:53:35,475 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:35,475 - julearn - INFO - Setting hyperparameter kernel = linear + 2024-10-17 13:53:35,475 - julearn - INFO - Tuning hyperparameter C = (1e-06, 1000.0, 'log-uniform') + 2024-10-17 13:53:35,475 - julearn - INFO - Step added + 2024-10-17 13:53:35,475 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:35,476 - julearn - INFO - Step added + 2024-10-17 13:53:35,476 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:35,476 - julearn - INFO - Setting hyperparameter kernel = rbf + 2024-10-17 13:53:35,476 - julearn - INFO - Tuning hyperparameter C = (1e-06, 1000.0, 'log-uniform') + 2024-10-17 13:53:35,476 - julearn - INFO - Tuning hyperparameter gamma = (1e-06, 10.0, 'log-uniform') + 2024-10-17 13:53:35,476 - julearn - INFO - Step added + 2024-10-17 13:53:35,476 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:35,476 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:35,476 - julearn - INFO - Features: ['frontal', 'parietal'] + 2024-10-17 13:53:35,476 - julearn - INFO - Target: event + 2024-10-17 13:53:35,476 - julearn - INFO - Expanded features: ['frontal', 'parietal'] + 2024-10-17 13:53:35,476 - julearn - INFO - X_types:{} + 2024-10-17 13:53:35,476 - julearn - WARNING - The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['frontal', 'parietal']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:35,477 - julearn - INFO - ==================== + 2024-10-17 13:53:35,477 - julearn - INFO - + 2024-10-17 13:53:35,478 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:35,478 - julearn - INFO - Tuning hyperparameters using bayes + 2024-10-17 13:53:35,478 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:35,478 - julearn - INFO - svm__C: (1e-06, 1000.0, 'log-uniform') + 2024-10-17 13:53:35,478 - julearn - INFO - Hyperparameter svm__C is log-uniform float [1e-06, 1000.0] + 2024-10-17 13:53:35,479 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:35,479 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:35,479 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:35,479 - julearn - INFO - n_iter: 10 + 2024-10-17 13:53:35,479 - julearn - INFO - ==================== + 2024-10-17 13:53:35,479 - julearn - INFO - + 2024-10-17 13:53:35,480 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:35,480 - julearn - INFO - Tuning hyperparameters using bayes + 2024-10-17 13:53:35,480 - julearn - INFO - Hyperparameters: + 2024-10-17 13:53:35,480 - julearn - INFO - svm__C: (1e-06, 1000.0, 'log-uniform') + 2024-10-17 13:53:35,480 - julearn - INFO - svm__gamma: (1e-06, 10.0, 'log-uniform') + 2024-10-17 13:53:35,480 - julearn - INFO - Hyperparameter svm__C is log-uniform float [1e-06, 1000.0] + 2024-10-17 13:53:35,481 - julearn - INFO - Hyperparameter svm__gamma is log-uniform float [1e-06, 10.0] + 2024-10-17 13:53:35,481 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:35,481 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:35,481 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:35,481 - julearn - INFO - n_iter: 10 + 2024-10-17 13:53:35,482 - julearn - INFO - ==================== + 2024-10-17 13:53:35,482 - julearn - INFO - + 2024-10-17 13:53:35,482 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:35,482 - julearn - INFO - Tuning hyperparameters using bayes + 2024-10-17 13:53:35,482 - julearn - INFO - Hyperparameters list: + 2024-10-17 13:53:35,482 - julearn - INFO - Set 0 + 2024-10-17 13:53:35,482 - julearn - INFO - svm__C: Real(low=1e-06, high=1000.0, prior='log-uniform', transform='identity') + 2024-10-17 13:53:35,482 - julearn - INFO - set_column_types: [SetColumnTypes(X_types={})] + 2024-10-17 13:53:35,482 - julearn - INFO - zscore: [StandardScaler()] + 2024-10-17 13:53:35,483 - julearn - INFO - svm: [SVC(kernel='linear')] + 2024-10-17 13:53:35,483 - julearn - INFO - Set 1 + 2024-10-17 13:53:35,483 - julearn - INFO - svm__C: Real(low=1e-06, high=1000.0, prior='log-uniform', transform='identity') + 2024-10-17 13:53:35,483 - julearn - INFO - svm__gamma: Real(low=1e-06, high=10.0, prior='log-uniform', transform='identity') + 2024-10-17 13:53:35,483 - julearn - INFO - set_column_types: [SetColumnTypes(X_types={})] + 2024-10-17 13:53:35,483 - julearn - INFO - zscore: [StandardScaler()] + 2024-10-17 13:53:35,483 - julearn - INFO - svm: [SVC()] + 2024-10-17 13:53:35,483 - julearn - INFO - Hyperparameter svm__C as is Real(low=1e-06, high=1000.0, prior='log-uniform', transform='identity') + 2024-10-17 13:53:35,483 - julearn - INFO - Hyperparameter set_column_types as is [SetColumnTypes(X_types={})] + 2024-10-17 13:53:35,483 - julearn - INFO - Hyperparameter zscore as is [StandardScaler()] + 2024-10-17 13:53:35,484 - julearn - INFO - Hyperparameter svm as is [SVC(kernel='linear')] + 2024-10-17 13:53:35,484 - julearn - INFO - Hyperparameter svm__C as is Real(low=1e-06, high=1000.0, prior='log-uniform', transform='identity') + 2024-10-17 13:53:35,484 - julearn - INFO - Hyperparameter svm__gamma as is Real(low=1e-06, high=10.0, prior='log-uniform', transform='identity') + 2024-10-17 13:53:35,484 - julearn - INFO - Hyperparameter set_column_types as is [SetColumnTypes(X_types={})] + 2024-10-17 13:53:35,484 - julearn - INFO - Hyperparameter zscore as is [StandardScaler()] + 2024-10-17 13:53:35,484 - julearn - INFO - Hyperparameter svm as is [SVC()] + 2024-10-17 13:53:35,484 - julearn - INFO - Using inner CV scheme KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:35,484 - julearn - INFO - Search Parameters: + 2024-10-17 13:53:35,484 - julearn - INFO - cv: KFold(n_splits=2, random_state=None, shuffle=False) + 2024-10-17 13:53:35,485 - julearn - INFO - n_iter: 10 + 2024-10-17 13:53:35,493 - julearn - INFO - ==================== + 2024-10-17 13:53:35,493 - julearn - INFO - + 2024-10-17 13:53:35,493 - julearn - INFO - = Data Information = + 2024-10-17 13:53:35,494 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:35,494 - julearn - INFO - Number of samples: 532 + 2024-10-17 13:53:35,494 - julearn - INFO - Number of features: 2 + 2024-10-17 13:53:35,494 - julearn - INFO - ==================== + 2024-10-17 13:53:35,494 - julearn - INFO - + 2024-10-17 13:53:35,494 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:35,494 - julearn - INFO - Target type: object + 2024-10-17 13:53:35,495 - julearn - INFO - Class distributions: event + cue 266 + stim 266 + Name: count, dtype: int64 + 2024-10-17 13:53:35,495 - julearn - INFO - Using outer CV scheme KFold(n_splits=2, random_state=None, shuffle=False) (incl. final model) + 2024-10-17 13:53:35,495 - julearn - INFO - Binary classification problem detected. + 0.6203007518796992 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 94-95 + +It seems that we might have found a better model, but which one is it? + +.. GENERATED FROM PYTHON SOURCE LINES 95-96 + +.. code-block:: default + + print(estimator.best_params_) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + OrderedDict([('set_column_types', SetColumnTypes(X_types={})), ('svm', SVC()), ('svm__C', 193.62585277239563), ('svm__gamma', 4.909675645518994), ('zscore', StandardScaler())]) + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 4.504 seconds) + + +.. _sphx_glr_download_auto_examples_03_complex_models_run_hyperparameter_tuning_bayessearch.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_hyperparameter_tuning_bayessearch.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_hyperparameter_tuning_bayessearch.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_stacked_models.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_stacked_models.rst.txt new file mode 100644 index 000000000..c8aca239c --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/run_stacked_models.rst.txt @@ -0,0 +1,250 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/03_complex_models/run_stacked_models.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_03_complex_models_run_stacked_models.py: + + +Stacking Classification +======================= + +This example uses the ``iris`` dataset and performs a complex stacking +classification. We will use two different classifiers, one applied to petal +features and one applied to sepal features. A final logistic regression +classifier will be applied on the predictions of the two classifiers. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 12-20 + +.. code-block:: default + + # Authors: Federico Raimondo + # License: AGPL + + from seaborn import load_dataset + from julearn import run_cross_validation + from julearn.pipeline import PipelineCreator + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 21-22 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 22-24 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:41,534 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:41,534 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:41,534 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:41,534 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:41,534 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:41,534 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:41,534 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 25-27 + +.. code-block:: default + + df_iris = load_dataset("iris") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 28-30 + +The dataset has three kind of species. We will keep two to perform a binary +classification. + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +.. code-block:: default + + df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-35 + +As features, we will use the sepal length, width and petal length. +We will try to predict the species. + +.. GENERATED FROM PYTHON SOURCE LINES 35-70 + +.. code-block:: default + + + X = ["sepal_length", "sepal_width", "petal_length", "petal_width"] + y = "species" + + # Define our feature types + X_types = { + "sepal": ["sepal_length", "sepal_width"], + "petal": ["petal_length", "petal_width"], + } + + # Create the pipeline for the sepal features, by default will apply to "sepal" + model_sepal = PipelineCreator(problem_type="classification", apply_to="sepal") + model_sepal.add("filter_columns", apply_to="*", keep="sepal") + model_sepal.add("zscore") + model_sepal.add("svm") + + # Create the pipeline for the petal features, by default will apply to "petal" + model_petal = PipelineCreator(problem_type="classification", apply_to="petal") + model_petal.add("filter_columns", apply_to="*", keep="petal") + model_petal.add("zscore") + model_petal.add("rf") + + # Create the stacking model + model = PipelineCreator(problem_type="classification") + model.add( + "stacking", + estimators=[[("model_sepal", model_sepal), ("model_petal", model_petal)]], + apply_to="*", + ) + + scores = run_cross_validation( + X=X, y=y, X_types=X_types, data=df_iris, model=model + ) + + print(scores["test_score"]) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:41,537 - julearn - INFO - Adding step filter_columns that applies to ColumnTypes + 2024-10-17 13:53:41,537 - julearn - INFO - Setting hyperparameter keep = sepal + 2024-10-17 13:53:41,537 - julearn - INFO - Step added + 2024-10-17 13:53:41,537 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:41,537 - julearn - INFO - Step added + 2024-10-17 13:53:41,537 - julearn - INFO - Adding step svm that applies to ColumnTypes + 2024-10-17 13:53:41,537 - julearn - INFO - Step added + 2024-10-17 13:53:41,537 - julearn - INFO - Adding step filter_columns that applies to ColumnTypes + 2024-10-17 13:53:41,537 - julearn - INFO - Setting hyperparameter keep = petal + 2024-10-17 13:53:41,538 - julearn - INFO - Step added + 2024-10-17 13:53:41,538 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:41,538 - julearn - INFO - Step added + 2024-10-17 13:53:41,538 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:41,538 - julearn - INFO - Step added + 2024-10-17 13:53:41,538 - julearn - INFO - Adding step stacking that applies to ColumnTypes + 2024-10-17 13:53:41,538 - julearn - INFO - Setting hyperparameter estimators = [('model_sepal', ), ('model_petal', )] + 2024-10-17 13:53:41,538 - julearn - INFO - Step added + 2024-10-17 13:53:41,538 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:41,538 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:41,538 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width'] + 2024-10-17 13:53:41,538 - julearn - INFO - Target: species + 2024-10-17 13:53:41,538 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width'] + 2024-10-17 13:53:41,539 - julearn - INFO - X_types:{'sepal': ['sepal_length', 'sepal_width'], 'petal': ['petal_length', 'petal_width']} + 2024-10-17 13:53:41,539 - julearn - INFO - ==================== + 2024-10-17 13:53:41,539 - julearn - INFO - + 2024-10-17 13:53:41,541 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:41,541 - julearn - INFO - ==================== + 2024-10-17 13:53:41,541 - julearn - INFO - + 2024-10-17 13:53:41,542 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:41,542 - julearn - INFO - ==================== + 2024-10-17 13:53:41,542 - julearn - INFO - + 2024-10-17 13:53:41,576 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:41,576 - julearn - INFO - ==================== + 2024-10-17 13:53:41,576 - julearn - INFO - + 2024-10-17 13:53:41,576 - julearn - INFO - = Data Information = + 2024-10-17 13:53:41,577 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:41,577 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:41,577 - julearn - INFO - Number of features: 4 + 2024-10-17 13:53:41,577 - julearn - INFO - ==================== + 2024-10-17 13:53:41,577 - julearn - INFO - + 2024-10-17 13:53:41,577 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:41,577 - julearn - INFO - Target type: object + 2024-10-17 13:53:41,577 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:41,578 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) + 2024-10-17 13:53:41,578 - julearn - INFO - Binary classification problem detected. + 0 1.00 + 1 0.85 + 2 0.95 + 3 0.95 + 4 0.95 + Name: test_score, dtype: float64 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 3.682 seconds) + + +.. _sphx_glr_download_auto_examples_03_complex_models_run_stacked_models.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_stacked_models.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_stacked_models.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/03_complex_models/sg_execution_times.rst.txt b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/sg_execution_times.rst.txt new file mode 100644 index 000000000..f7d891bb4 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/03_complex_models/sg_execution_times.rst.txt @@ -0,0 +1,23 @@ + +:orphan: + +.. _sphx_glr_auto_examples_03_complex_models_sg_execution_times: + + +Computation times +================= +**00:12.435** total execution time for **auto_examples_03_complex_models** files: + ++-----------------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_03_complex_models_run_hyperparameter_tuning_bayessearch.py` (``run_hyperparameter_tuning_bayessearch.py``) | 00:04.504 | 0.0 MB | ++-----------------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_03_complex_models_run_stacked_models.py` (``run_stacked_models.py``) | 00:03.682 | 0.0 MB | ++-----------------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_03_complex_models_run_hyperparameter_tuning.py` (``run_hyperparameter_tuning.py``) | 00:02.442 | 0.0 MB | ++-----------------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_03_complex_models_run_hyperparameter_multiple_grids.py` (``run_hyperparameter_multiple_grids.py``) | 00:01.317 | 0.0 MB | ++-----------------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_03_complex_models_run_example_pca_featsets.py` (``run_example_pca_featsets.py``) | 00:00.427 | 0.0 MB | ++-----------------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_03_complex_models_run_apply_to_target.py` (``run_apply_to_target.py``) | 00:00.063 | 0.0 MB | ++-----------------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ diff --git a/pr-preview/pr-276/_sources/auto_examples/04_confounds/index.rst.txt b/pr-preview/pr-276/_sources/auto_examples/04_confounds/index.rst.txt new file mode 100644 index 000000000..36a5a021a --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/04_confounds/index.rst.txt @@ -0,0 +1,66 @@ +:orphan: + +Confounds +========= + +Examples that show the confound-related functionality of ``julearn``. + + + +.. raw:: html + +
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/04_confounds/images/thumb/sphx_glr_run_return_confounds_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_04_confounds_run_return_confounds.py` + +.. raw:: html + +
Return Confounds in Confound Removal
+
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/04_confounds/images/thumb/sphx_glr_plot_confound_removal_classification_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_04_confounds_plot_confound_removal_classification.py` + +.. raw:: html + +
Confound Removal (model comparison)
+
+ + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /auto_examples/04_confounds/run_return_confounds + /auto_examples/04_confounds/plot_confound_removal_classification + + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/04_confounds/plot_confound_removal_classification.rst.txt b/pr-preview/pr-276/_sources/auto_examples/04_confounds/plot_confound_removal_classification.rst.txt new file mode 100644 index 000000000..5b1536ce0 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/04_confounds/plot_confound_removal_classification.rst.txt @@ -0,0 +1,704 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/04_confounds/plot_confound_removal_classification.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_04_confounds_plot_confound_removal_classification.py: + + +Confound Removal (model comparison) +=================================== + +This example uses the ``iris`` dataset, performs simple binary classification +with and without confound removal using a Random Forest classifier. + +.. GENERATED FROM PYTHON SOURCE LINES 9-24 + +.. code-block:: default + + # Authors: Shammi More + # Federico Raimondo + # Leonard Sasse + # License: AGPL + + import matplotlib.pyplot as plt + import pandas as pd + import seaborn as sns + from seaborn import load_dataset + + from julearn import run_cross_validation + from julearn.model_selection import StratifiedBootstrap + from julearn.pipeline import PipelineCreator + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 25-26 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 26-28 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:49,256 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:49,256 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:49,256 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:49,256 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:49,256 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:49,256 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:49,256 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 29-30 + +Load the iris data from seaborn. + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +.. code-block:: default + + df_iris = load_dataset("iris") + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-35 + +The dataset has three kind of species. We will keep two to perform a binary +classification. + +.. GENERATED FROM PYTHON SOURCE LINES 35-37 + +.. code-block:: default + + df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 38-40 + +As features, we will use the sepal length, width and petal length and use +petal width as confound. + +.. GENERATED FROM PYTHON SOURCE LINES 40-45 + +.. code-block:: default + + + X = ["sepal_length", "sepal_width", "petal_length"] + y = "species" + confounds = ["petal_width"] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 46-59 + +Doing hypothesis testing in ML is not that simple. If we were to use +classical frequentist statistics, we have the problem that using cross +validation, the samples are not independent and the population (train + test) +is always the same. + +If we want to compare two models, an alternative is to contrast, for each +fold, the performance gap between the models. If we combine that approach +with bootstrapping, we can then compare the confidence intervals of the +difference. If the 95% CI is above 0 (or below), we can claim that the models +are different with p < 0.05. + +Let's use a bootstrap CV. In the interest of time we do 20 iterations, +change the number of bootstrap iterations to at least 2000 for a valid test. + +.. GENERATED FROM PYTHON SOURCE LINES 59-63 + +.. code-block:: default + + n_bootstrap = 20 + n_elements = len(df_iris) + cv = StratifiedBootstrap(n_splits=n_bootstrap, test_size=0.3, random_state=42) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 64-66 + +First, we will train a model without performing confound removal on features. +Note: confounds by default. + +.. GENERATED FROM PYTHON SOURCE LINES 66-79 + +.. code-block:: default + + scores_ncr = run_cross_validation( + X=X, + y=y, + data=df_iris, + model="rf", + cv=cv, + problem_type="classification", + preprocess="zscore", + scoring=["accuracy", "roc_auc"], + return_estimator="cv", + seed=200, + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:49,259 - julearn - INFO - Setting random seed to 200 + 2024-10-17 13:53:49,259 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:49,259 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:49,259 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:49,259 - julearn - INFO - Target: species + 2024-10-17 13:53:49,259 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length'] + 2024-10-17 13:53:49,259 - julearn - INFO - X_types:{} + 2024-10-17 13:53:49,259 - julearn - WARNING - The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['sepal_length', 'sepal_width', 'petal_length']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:49,260 - julearn - INFO - ==================== + 2024-10-17 13:53:49,260 - julearn - INFO - + 2024-10-17 13:53:49,260 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:49,260 - julearn - INFO - Step added + 2024-10-17 13:53:49,260 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:49,261 - julearn - INFO - Step added + 2024-10-17 13:53:49,261 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:49,261 - julearn - INFO - ==================== + 2024-10-17 13:53:49,261 - julearn - INFO - + 2024-10-17 13:53:49,261 - julearn - INFO - = Data Information = + 2024-10-17 13:53:49,261 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:49,261 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:49,261 - julearn - INFO - Number of features: 3 + 2024-10-17 13:53:49,261 - julearn - INFO - ==================== + 2024-10-17 13:53:49,261 - julearn - INFO - + 2024-10-17 13:53:49,262 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:49,262 - julearn - INFO - Target type: object + 2024-10-17 13:53:49,262 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:49,262 - julearn - INFO - Using outer CV scheme StratifiedBootstrap(n_splits=20, random_state=42, test_size=0.3, + train_size=None) + 2024-10-17 13:53:49,263 - julearn - INFO - Binary classification problem detected. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 80-82 + +Next, we train a model after performing confound removal on the features. +Note: we initialize the CV again to use the same folds as before. + +.. GENERATED FROM PYTHON SOURCE LINES 82-88 + +.. code-block:: default + + cv = StratifiedBootstrap(n_splits=n_bootstrap, test_size=0.3, random_state=42) + + # In order to tell ``run_cross_validation`` which columns are confounds, + # and which columns are features, we have to define the X_types: + X_types = {"features": X, "confound": confounds} + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 89-101 + +We can now define a pipeline creator and add a confound removal step. +The pipeline creator should apply all the steps, by default, to the +features type. + +The first step will zscore both features and confounds. + +The second step will remove the confounds (type "confound") from the +"features". + +Finally, a random forest will be trained. +Given the default ``apply_to`` in the pipeline creator, +the random forest will only be trained using "features". + +.. GENERATED FROM PYTHON SOURCE LINES 101-118 + +.. code-block:: default + + creator = PipelineCreator(problem_type="classification", apply_to="features") + creator.add("zscore", apply_to=["features", "confound"]) + creator.add("confound_removal", apply_to="features", confounds="confound") + creator.add("rf") + + scores_cr = run_cross_validation( + X=X + confounds, + y=y, + data=df_iris, + model=creator, + cv=cv, + X_types=X_types, + scoring=["accuracy", "roc_auc"], + return_estimator="cv", + seed=200, + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:51,369 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:51,369 - julearn - INFO - Step added + 2024-10-17 13:53:51,369 - julearn - INFO - Adding step confound_removal that applies to ColumnTypes + 2024-10-17 13:53:51,369 - julearn - INFO - Setting hyperparameter confounds = confound + 2024-10-17 13:53:51,369 - julearn - INFO - Step added + 2024-10-17 13:53:51,369 - julearn - INFO - Adding step rf that applies to ColumnTypes + 2024-10-17 13:53:51,369 - julearn - INFO - Step added + 2024-10-17 13:53:51,370 - julearn - INFO - Setting random seed to 200 + 2024-10-17 13:53:51,370 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:51,370 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:51,370 - julearn - INFO - Features: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width'] + 2024-10-17 13:53:51,370 - julearn - INFO - Target: species + 2024-10-17 13:53:51,370 - julearn - INFO - Expanded features: ['sepal_length', 'sepal_width', 'petal_length', 'petal_width'] + 2024-10-17 13:53:51,370 - julearn - INFO - X_types:{'features': ['sepal_length', 'sepal_width', 'petal_length'], 'confound': ['petal_width']} + 2024-10-17 13:53:51,370 - julearn - INFO - ==================== + 2024-10-17 13:53:51,371 - julearn - INFO - + 2024-10-17 13:53:51,372 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:51,372 - julearn - INFO - ==================== + 2024-10-17 13:53:51,372 - julearn - INFO - + 2024-10-17 13:53:51,372 - julearn - INFO - = Data Information = + 2024-10-17 13:53:51,372 - julearn - INFO - Problem type: classification + 2024-10-17 13:53:51,372 - julearn - INFO - Number of samples: 100 + 2024-10-17 13:53:51,372 - julearn - INFO - Number of features: 4 + 2024-10-17 13:53:51,372 - julearn - INFO - ==================== + 2024-10-17 13:53:51,372 - julearn - INFO - + 2024-10-17 13:53:51,373 - julearn - INFO - Number of classes: 2 + 2024-10-17 13:53:51,373 - julearn - INFO - Target type: object + 2024-10-17 13:53:51,373 - julearn - INFO - Class distributions: species + versicolor 50 + virginica 50 + Name: count, dtype: int64 + 2024-10-17 13:53:51,373 - julearn - INFO - Using outer CV scheme StratifiedBootstrap(n_splits=20, random_state=42, test_size=0.3, + train_size=None) + 2024-10-17 13:53:51,373 - julearn - INFO - Binary classification problem detected. + + + + +.. GENERATED FROM PYTHON SOURCE LINES 119-121 + +Now we can compare the accuracies. We can combine the two outputs as +``pandas.DataFrame``. + +.. GENERATED FROM PYTHON SOURCE LINES 121-124 + +.. code-block:: default + + scores_ncr["confounds"] = "Not Removed" + scores_cr["confounds"] = "Removed" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 125-127 + +Now we convert the metrics to a column for easier seaborn plotting (convert +to long format). + +.. GENERATED FROM PYTHON SOURCE LINES 127-144 + +.. code-block:: default + + + index = ["fold", "confounds"] + scorings = ["test_accuracy", "test_roc_auc"] + + df_ncr_metrics = scores_ncr.set_index(index)[scorings].stack() + df_ncr_metrics.index.names = ["fold", "confounds", "metric"] + df_ncr_metrics.name = "value" + + df_cr_metrics = scores_cr.set_index(index)[scorings].stack() + df_cr_metrics.index.names = ["fold", "confounds", "metric"] + df_cr_metrics.name = "value" + + df_metrics = pd.concat((df_ncr_metrics, df_cr_metrics)) + + df_metrics = df_metrics.reset_index() + df_metrics.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
foldconfoundsmetricvalue
00Not Removedtest_accuracy0.933333
10Not Removedtest_roc_auc0.968889
21Not Removedtest_accuracy0.933333
31Not Removedtest_roc_auc0.948889
42Not Removedtest_accuracy1.000000
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 145-146 + +And finally plot the results. + +.. GENERATED FROM PYTHON SOURCE LINES 146-151 + +.. code-block:: default + + sns.catplot( + x="confounds", y="value", col="metric", data=df_metrics, kind="swarm" + ) + plt.tight_layout() + + + + +.. image-sg:: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_001.png + :alt: metric = test_accuracy, metric = test_roc_auc + :srcset: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_001.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + + + + +.. GENERATED FROM PYTHON SOURCE LINES 152-159 + +While this plot allows us to see the mean performance values and compare +them, these samples are paired. In order to see if there is a systematic +difference, we need to check the distribution of differences between the +the models. + +First, we remove the column "confounds" from the index and make the difference +between the metrics. + +.. GENERATED FROM PYTHON SOURCE LINES 159-165 + +.. code-block:: default + + df_cr_metrics = df_cr_metrics.reset_index().set_index(["fold", "metric"]) + df_ncr_metrics = df_ncr_metrics.reset_index().set_index(["fold", "metric"]) + + df_diff_metrics = df_ncr_metrics["value"] - df_cr_metrics["value"] + df_diff_metrics = df_diff_metrics.reset_index() + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 166-168 + +Now we can finally plot the difference, setting the whiskers of the box plot +at 2.5 and 97.5 to see the 95% CI. + +.. GENERATED FROM PYTHON SOURCE LINES 168-174 + +.. code-block:: default + + sns.boxplot( + x="metric", y="value", data=df_diff_metrics.reset_index(), whis=[2.5, 97.5] + ) + plt.axhline(0, color="k", ls=":") + plt.tight_layout() + + + + +.. image-sg:: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_002.png + :alt: plot confound removal classification + :srcset: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_002.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 175-187 + +We can see that while it seems that the accuracy and ROC AUC scores are +higher when confounds are not removed. We can not really claim (using this +test), that the models are different in terms of these metrics. + +Maybe the percentiles will be more accuracy with the proper amount of +bootstrap iterations? + +But the main point of confound removal is for interpretability. Let's see +if there is a change in the feature importances. + +First, we need to collect the feature importances for each model, for each +fold. + +.. GENERATED FROM PYTHON SOURCE LINES 187-216 + +.. code-block:: default + + + ncr_fi = [] + for i_fold, estimator in enumerate(scores_ncr["estimator"]): + this_importances = pd.DataFrame( + { + "feature": [x.replace("_", " ") for x in X], + "importance": estimator["rf"].feature_importances_, + "confounds": "Not Removed", + "fold": i_fold, + } + ) + ncr_fi.append(this_importances) + ncr_fi = pd.concat(ncr_fi) + + cr_fi = [] + for i_fold, estimator in enumerate(scores_cr["estimator"]): + this_importances = pd.DataFrame( + { + "feature": [x.replace("_", " ") for x in X], + "importance": estimator["rf"].model.feature_importances_, + "confounds": "Removed", + "fold": i_fold, + } + ) + cr_fi.append(this_importances) + cr_fi = pd.concat(cr_fi) + + feature_importance = pd.concat([cr_fi, ncr_fi]) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 217-218 + +We can now plot the importances. + +.. GENERATED FROM PYTHON SOURCE LINES 218-229 + +.. code-block:: default + + sns.catplot( + x="feature", + y="importance", + hue="confounds", + dodge=True, + data=feature_importance, + kind="swarm", + s=3, + ) + plt.tight_layout() + + + + +.. image-sg:: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_003.png + :alt: plot confound removal classification + :srcset: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_003.png + :class: sphx-glr-single-img + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead. + with pd.option_context('mode.use_inf_as_na', True): + + + + +.. GENERATED FROM PYTHON SOURCE LINES 230-232 + +And check the differences in importances. We can now see that there is +a difference in importances. + +.. GENERATED FROM PYTHON SOURCE LINES 232-241 + +.. code-block:: default + + diff_fi = ( + cr_fi.set_index(["feature", "fold"])["importance"] + - ncr_fi.set_index(["feature", "fold"])["importance"] + ) + sns.boxplot( + x="importance", y="feature", data=diff_fi.reset_index(), whis=[2.5, 97.5] + ) + plt.axvline(0, color="k", ls=":") + plt.tight_layout() + + + +.. image-sg:: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_004.png + :alt: plot confound removal classification + :srcset: /auto_examples/04_confounds/images/sphx_glr_plot_confound_removal_classification_004.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 5.618 seconds) + + +.. _sphx_glr_download_auto_examples_04_confounds_plot_confound_removal_classification.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: plot_confound_removal_classification.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: plot_confound_removal_classification.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/04_confounds/run_return_confounds.rst.txt b/pr-preview/pr-276/_sources/auto_examples/04_confounds/run_return_confounds.rst.txt new file mode 100644 index 000000000..f97d97d5d --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/04_confounds/run_return_confounds.rst.txt @@ -0,0 +1,958 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/04_confounds/run_return_confounds.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_04_confounds_run_return_confounds.py: + + +Return Confounds in Confound Removal +==================================== + +In most cases confound removal is a simple operation. +You regress out the confound from the features and only continue working with +these new confound removed features. This is also the default setting for +``julearn``'s ``remove_confound`` step. But sometimes you want to work with the +confound even after removing it from the features. In this example, we +will discuss the options you have. + +.. include:: ../../links.inc + +.. GENERATED FROM PYTHON SOURCE LINES 14-25 + +.. code-block:: default + + # Authors: Sami Hamdan + # License: AGPL + + from sklearn.datasets import load_diabetes # to load data + from julearn.pipeline import PipelineCreator + from julearn import run_cross_validation + from julearn.inspect import preprocess + + # Load in the data + df_features, target = load_diabetes(return_X_y=True, as_frame=True) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 26-30 + +First, we can have a look at our features. +You can see it includes Age, BMI, average blood pressure (bp) and 6 other +measures from s1 to s6. Furthermore, it includes sex which will be considered +as a confound in this example. + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +.. code-block:: default + + print("Features: ", df_features.head()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Features: age sex bmi ... s4 s5 s6 + 0 0.038076 0.050680 0.061696 ... -0.002592 0.019907 -0.017646 + 1 -0.001882 -0.044642 -0.051474 ... -0.039493 -0.068332 -0.092204 + 2 0.085299 0.050680 0.044451 ... -0.002592 0.002861 -0.025930 + 3 -0.089063 -0.044642 -0.011595 ... 0.034309 0.022688 -0.009362 + 4 0.005383 -0.044642 -0.036385 ... -0.002592 -0.031988 -0.046641 + + [5 rows x 10 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-34 + +Second, we can have a look at the target. + +.. GENERATED FROM PYTHON SOURCE LINES 34-36 + +.. code-block:: default + + print("Target: ", target.describe()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Target: count 442.000000 + mean 152.133484 + std 77.093005 + min 25.000000 + 25% 87.000000 + 50% 140.500000 + 75% 211.500000 + max 346.000000 + Name: target, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 37-38 + +Now, we can put both into one DataFrame: + +.. GENERATED FROM PYTHON SOURCE LINES 38-41 + +.. code-block:: default + + data = df_features.copy() + data["target"] = target + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 42-51 + +In the following we will explore different settings of confound removal +using ``julearn``'s pipeline functionalities. + +Confound Removal Typical Use Case +--------------------------------- +Here, we want to deconfound the features and not include the confound as a +feature into our last model. We will use the ``remove_confound`` step for this. +Then we will use the ``pca`` step to reduce the dimensionality of the features. +Finally, we will fit a linear regression model. + +.. GENERATED FROM PYTHON SOURCE LINES 51-57 + +.. code-block:: default + + + creator = PipelineCreator(problem_type="regression", apply_to="continuous") + creator.add("confound_removal") + creator.add("pca") + creator.add("linreg") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:48,497 - julearn - INFO - Adding step confound_removal that applies to ColumnTypes + 2024-10-17 13:53:48,497 - julearn - INFO - Step added + 2024-10-17 13:53:48,497 - julearn - INFO - Adding step pca that applies to ColumnTypes + 2024-10-17 13:53:48,497 - julearn - INFO - Step added + 2024-10-17 13:53:48,497 - julearn - INFO - Adding step linreg that applies to ColumnTypes + 2024-10-17 13:53:48,498 - julearn - INFO - Step added + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 58-64 + +Now we need to set the ``X_types`` argument of the ``run_cross_validation`` +function. This argument is a dictionary that maps the names of the different +types of X to the features that belong to this type. In this example, we +have two types of features: `continuous` and `confound`. The `continuous` +features are the features that we want to deconfound and the `confound` +features are the features that we want to remove from the `continuous`. + +.. GENERATED FROM PYTHON SOURCE LINES 64-70 + +.. code-block:: default + + + feature_names = list(df_features.drop(columns="sex").columns) + X_types = {"continuous": feature_names, "confound": "sex"} + + X = feature_names + ["sex"] + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 71-72 + +Now we can run the cross validation and get the scores. + +.. GENERATED FROM PYTHON SOURCE LINES 72-81 + +.. code-block:: default + + scores, model = run_cross_validation( + X=X, + y="target", + X_types=X_types, + data=data, + model=creator, + return_estimator="final", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:48,498 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:48,498 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:48,498 - julearn - INFO - Features: ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6', 'sex'] + 2024-10-17 13:53:48,498 - julearn - INFO - Target: target + 2024-10-17 13:53:48,499 - julearn - INFO - Expanded features: ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6', 'sex'] + 2024-10-17 13:53:48,499 - julearn - INFO - X_types:{'continuous': ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'], 'confound': ['sex']} + 2024-10-17 13:53:48,499 - julearn - INFO - ==================== + 2024-10-17 13:53:48,499 - julearn - INFO - + 2024-10-17 13:53:48,501 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:48,501 - julearn - INFO - ==================== + 2024-10-17 13:53:48,501 - julearn - INFO - + 2024-10-17 13:53:48,501 - julearn - INFO - = Data Information = + 2024-10-17 13:53:48,501 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:48,501 - julearn - INFO - Number of samples: 442 + 2024-10-17 13:53:48,501 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:48,501 - julearn - INFO - ==================== + 2024-10-17 13:53:48,501 - julearn - INFO - + 2024-10-17 13:53:48,501 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:48,501 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 82-87 + +We can use the ``preprocess`` method of the ``inspect`` module to inspect the +transformations steps of the returned estimator. +By providing a step name to the ``until`` argument of the +``preprocess`` method we return the transformed X and y up to +the provided step (inclusive). + +.. GENERATED FROM PYTHON SOURCE LINES 87-90 + +.. code-block:: default + + df_deconfounded = preprocess(model, X=X, data=data, until="confound_removal") + df_deconfounded.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
agebmibps1s2s3s4s5s6
00.0292710.0572280.009658-0.046011-0.042050-0.024189-0.0194240.012310-0.028194
10.005874-0.047538-0.015568-0.006874-0.0127960.057488-0.024667-0.061639-0.082913
20.0764940.039983-0.017885-0.047387-0.041423-0.013144-0.019424-0.004736-0.036479
3-0.081307-0.007659-0.0258970.0137650.031358-0.0529610.0491350.029380-0.000071
40.013139-0.0324490.0326310.0055100.021964-0.0087810.012234-0.025295-0.037349
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 91-107 + +As you can see the confound ``sex`` was dropped and only the confound removed +features are used in the following PCA. + +But what if you want to keep the confound after removal for +other transformations? + +For example, let's assume that you want to do a PCA on the confound removed +feature, but want to keep the confound for the actual modelling step. +Let us have a closer look to the confound remover in order to understand +how we could achieve such a task: + +.. autoclass:: julearn.transformers.confound_remover.ConfoundRemover + :noindex: + :exclude-members: transform, get_support, get_feature_names_out, + filter_columns, fit, fit_transform, get_apply_to, + get_needed_types, get_params, set_output, set_params + +.. GENERATED FROM PYTHON SOURCE LINES 109-111 + +In this example, we will set the ``keep_confounds`` argument to True. +This will keep the confounds after confound removal. + +.. GENERATED FROM PYTHON SOURCE LINES 111-117 + +.. code-block:: default + + + creator = PipelineCreator(problem_type="regression", apply_to="continuous") + creator.add("confound_removal", keep_confounds=True) + creator.add("pca") + creator.add("linreg") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:48,707 - julearn - INFO - Adding step confound_removal that applies to ColumnTypes + 2024-10-17 13:53:48,707 - julearn - INFO - Setting hyperparameter keep_confounds = True + 2024-10-17 13:53:48,707 - julearn - INFO - Step added + 2024-10-17 13:53:48,707 - julearn - INFO - Adding step pca that applies to ColumnTypes + 2024-10-17 13:53:48,707 - julearn - INFO - Step added + 2024-10-17 13:53:48,707 - julearn - INFO - Adding step linreg that applies to ColumnTypes + 2024-10-17 13:53:48,707 - julearn - INFO - Step added + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 118-119 + +Now we can run the cross validation and get the scores. + +.. GENERATED FROM PYTHON SOURCE LINES 119-128 + +.. code-block:: default + + scores, model = run_cross_validation( + X=X, + y="target", + X_types=X_types, + data=data, + model=creator, + return_estimator="final", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:48,708 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:48,708 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:48,708 - julearn - INFO - Features: ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6', 'sex'] + 2024-10-17 13:53:48,708 - julearn - INFO - Target: target + 2024-10-17 13:53:48,708 - julearn - INFO - Expanded features: ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6', 'sex'] + 2024-10-17 13:53:48,708 - julearn - INFO - X_types:{'continuous': ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'], 'confound': ['sex']} + 2024-10-17 13:53:48,709 - julearn - INFO - ==================== + 2024-10-17 13:53:48,709 - julearn - INFO - + 2024-10-17 13:53:48,710 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:48,710 - julearn - INFO - ==================== + 2024-10-17 13:53:48,710 - julearn - INFO - + 2024-10-17 13:53:48,710 - julearn - INFO - = Data Information = + 2024-10-17 13:53:48,710 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:48,710 - julearn - INFO - Number of samples: 442 + 2024-10-17 13:53:48,710 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:48,710 - julearn - INFO - ==================== + 2024-10-17 13:53:48,710 - julearn - INFO - + 2024-10-17 13:53:48,710 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:48,710 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 129-130 + +As you can see this kept the confound variable ``sex`` in the data. + +.. GENERATED FROM PYTHON SOURCE LINES 130-133 + +.. code-block:: default + + df_deconfounded = preprocess(model, X=X, data=data, until="confound_removal") + df_deconfounded.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
agebmibps1s2s3s4s5s6sex
00.0292710.0572280.009658-0.046011-0.042050-0.024189-0.0194240.012310-0.0281940.050680
10.005874-0.047538-0.015568-0.006874-0.0127960.057488-0.024667-0.061639-0.082913-0.044642
20.0764940.039983-0.017885-0.047387-0.041423-0.013144-0.019424-0.004736-0.0364790.050680
3-0.081307-0.007659-0.0258970.0137650.031358-0.0529610.0491350.029380-0.000071-0.044642
40.013139-0.0324490.0326310.0055100.021964-0.0087810.012234-0.025295-0.037349-0.044642
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 134-138 + +Even after the PCA, the confound will still be present. +This is the case because by default transformers only transform continuous +features (including features without a specified type) and ignore confounds +and categorical variables. + +.. GENERATED FROM PYTHON SOURCE LINES 138-141 + +.. code-block:: default + + df_transformed = preprocess(model, X=X, data=data) + df_transformed.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
pca__pca0pca__pca1pca__pca2pca__pca3pca__pca4pca__pca5pca__pca6pca__pca7pca__pca8sex
0-0.014051-0.0757150.0173950.012591-0.0466760.0134080.034497-0.008604-0.0023300.050680
1-0.0998830.0628290.0145160.013673-0.048058-0.010254-0.0041240.0240220.002075-0.044642
2-0.029015-0.0532530.0324770.061933-0.0491670.0295650.042031-0.001197-0.0025790.050680
30.0351620.001324-0.106807-0.0289810.020850-0.023413-0.008421-0.006566-0.003545-0.044642
4-0.0039510.0254450.0004210.018411-0.039692-0.025022-0.0430860.002095-0.000517-0.044642
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 142-147 + +This means that the resulting Linear Regression can use the deconfounded +features together with the confound to predict the target. However, in the +pipeline creator, the model is only applied to the continuous features. +This means that the confound is not used in the model. +Here we can see that the model is using 9 features. + +.. GENERATED FROM PYTHON SOURCE LINES 147-150 + +.. code-block:: default + + + print(len(model.steps[-1][1].model.coef_)) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 9 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 151-153 + +Lastly, you can also use the confound as a normal feature after confound +removal. + +.. GENERATED FROM PYTHON SOURCE LINES 153-168 + +.. code-block:: default + + creator = PipelineCreator(problem_type="regression", apply_to="continuous") + creator.add("confound_removal", keep_confounds=True) + creator.add("pca") + creator.add("linreg", apply_to="*") + + scores, model = run_cross_validation( + X=X, + y="target", + X_types=X_types, + data=data, + model=creator, + return_estimator="final", + ) + scores + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:48,931 - julearn - INFO - Adding step confound_removal that applies to ColumnTypes + 2024-10-17 13:53:48,931 - julearn - INFO - Setting hyperparameter keep_confounds = True + 2024-10-17 13:53:48,932 - julearn - INFO - Step added + 2024-10-17 13:53:48,932 - julearn - INFO - Adding step pca that applies to ColumnTypes + 2024-10-17 13:53:48,932 - julearn - INFO - Step added + 2024-10-17 13:53:48,932 - julearn - INFO - Adding step linreg that applies to ColumnTypes + 2024-10-17 13:53:48,932 - julearn - INFO - Step added + 2024-10-17 13:53:48,932 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:48,932 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:48,932 - julearn - INFO - Features: ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6', 'sex'] + 2024-10-17 13:53:48,932 - julearn - INFO - Target: target + 2024-10-17 13:53:48,932 - julearn - INFO - Expanded features: ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6', 'sex'] + 2024-10-17 13:53:48,932 - julearn - INFO - X_types:{'continuous': ['age', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'], 'confound': ['sex']} + 2024-10-17 13:53:48,933 - julearn - INFO - ==================== + 2024-10-17 13:53:48,933 - julearn - INFO - + 2024-10-17 13:53:48,934 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:48,934 - julearn - INFO - ==================== + 2024-10-17 13:53:48,934 - julearn - INFO - + 2024-10-17 13:53:48,934 - julearn - INFO - = Data Information = + 2024-10-17 13:53:48,934 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:48,934 - julearn - INFO - Number of samples: 442 + 2024-10-17 13:53:48,934 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:48,934 - julearn - INFO - ==================== + 2024-10-17 13:53:48,934 - julearn - INFO - + 2024-10-17 13:53:48,934 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:48,934 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
fit_timescore_timetest_scoren_trainn_testrepeatfoldcv_mdsum
00.0221440.0077660.4295563538900b10eef89b4192178d482d7a1587a248a
10.0220780.0077740.5225993538901b10eef89b4192178d482d7a1587a248a
20.0219530.0077960.4826813548802b10eef89b4192178d482d7a1587a248a
30.0222590.0077710.4264983548803b10eef89b4192178d482d7a1587a248a
40.0220410.0078090.5502483548804b10eef89b4192178d482d7a1587a248a
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 169-175 + +As you can see the confound is now used in the linear regression model. +This is the case because we set the ``apply_to`` argument of the ``linreg`` +step to ``*``. This means that the step will be applied to all features +(including confounds and categorical variables). +Here we can see that the model is using 10 features (9 deconfounded features +and the confound). + +.. GENERATED FROM PYTHON SOURCE LINES 175-176 + +.. code-block:: default + + print(len(model.steps[-1][1].coef_)) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 10 + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.642 seconds) + + +.. _sphx_glr_download_auto_examples_04_confounds_run_return_confounds.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_return_confounds.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_return_confounds.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/04_confounds/sg_execution_times.rst.txt b/pr-preview/pr-276/_sources/auto_examples/04_confounds/sg_execution_times.rst.txt new file mode 100644 index 000000000..3ff3319f4 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/04_confounds/sg_execution_times.rst.txt @@ -0,0 +1,15 @@ + +:orphan: + +.. _sphx_glr_auto_examples_04_confounds_sg_execution_times: + + +Computation times +================= +**00:06.260** total execution time for **auto_examples_04_confounds** files: + ++----------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_04_confounds_plot_confound_removal_classification.py` (``plot_confound_removal_classification.py``) | 00:05.618 | 0.0 MB | ++----------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_04_confounds_run_return_confounds.py` (``run_return_confounds.py``) | 00:00.642 | 0.0 MB | ++----------------------------------------------------------------------------------------------------------------------------------+-----------+--------+ diff --git a/pr-preview/pr-276/_sources/auto_examples/05_customization/index.rst.txt b/pr-preview/pr-276/_sources/auto_examples/05_customization/index.rst.txt new file mode 100644 index 000000000..8a12713f0 --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/05_customization/index.rst.txt @@ -0,0 +1,48 @@ +:orphan: + +Customization +============= + +Examples that show to extend and control various aspects of ``julearn``. + + + +.. raw:: html + +
+ + +.. raw:: html + +
+ +.. only:: html + + .. image:: /auto_examples/05_customization/images/thumb/sphx_glr_run_custom_scorers_regression_thumb.png + :alt: + + :ref:`sphx_glr_auto_examples_05_customization_run_custom_scorers_regression.py` + +.. raw:: html + +
Custom Scoring Function for Regression
+
+ + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /auto_examples/05_customization/run_custom_scorers_regression + + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/05_customization/run_custom_scorers_regression.rst.txt b/pr-preview/pr-276/_sources/auto_examples/05_customization/run_custom_scorers_regression.rst.txt new file mode 100644 index 000000000..b434c08cc --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/05_customization/run_custom_scorers_regression.rst.txt @@ -0,0 +1,574 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "auto_examples/05_customization/run_custom_scorers_regression.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + :ref:`Go to the end ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_auto_examples_05_customization_run_custom_scorers_regression.py: + + +Custom Scoring Function for Regression +====================================== + +This example uses the ``diabetes`` data from ``sklearn datasets`` and performs +a regression analysis using a Ridge Regression model. As scorers, it uses +``scikit-learn``, ``julearn`` and a custom metric defined by the user. + +.. GENERATED FROM PYTHON SOURCE LINES 10-24 + +.. code-block:: default + + # Authors: Shammi More + # Federico Raimondo + # License: AGPL + + import pandas as pd + import scipy + from sklearn.datasets import load_diabetes + + from sklearn.metrics import make_scorer + from julearn.scoring import register_scorer + + from julearn import run_cross_validation + from julearn.utils import configure_logging + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 25-26 + +Set the logging level to info to see extra information. + +.. GENERATED FROM PYTHON SOURCE LINES 26-28 + +.. code-block:: default + + configure_logging(level="INFO") + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + /home/runner/work/julearn/julearn/julearn/utils/logging.py:66: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead. + vstring = str(getattr(module, "__version__", None)) + 2024-10-17 13:53:55,037 - julearn - INFO - ===== Lib Versions ===== + 2024-10-17 13:53:55,037 - julearn - INFO - numpy: 1.26.4 + 2024-10-17 13:53:55,037 - julearn - INFO - scipy: 1.14.1 + 2024-10-17 13:53:55,037 - julearn - INFO - sklearn: 1.5.2 + 2024-10-17 13:53:55,037 - julearn - INFO - pandas: 2.2.3 + 2024-10-17 13:53:55,037 - julearn - INFO - julearn: 0.3.4.dev37 + 2024-10-17 13:53:55,037 - julearn - INFO - ======================== + + + + +.. GENERATED FROM PYTHON SOURCE LINES 29-30 + +load the diabetes data from ``sklearn`` as a ``pandas.DataFrame``. + +.. GENERATED FROM PYTHON SOURCE LINES 30-32 + +.. code-block:: default + + features, target = load_diabetes(return_X_y=True, as_frame=True) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 33-37 + +Dataset contains ten variables age, sex, body mass index, average blood +pressure, and six blood serum measurements (s1-s6) diabetes patients and +a quantitative measure of disease progression one year after baseline which +is the target we are interested in predicting. + +.. GENERATED FROM PYTHON SOURCE LINES 37-40 + +.. code-block:: default + + print("Features: \n", features.head()) + print("Target: \n", target.describe()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Features: + age sex bmi ... s4 s5 s6 + 0 0.038076 0.050680 0.061696 ... -0.002592 0.019907 -0.017646 + 1 -0.001882 -0.044642 -0.051474 ... -0.039493 -0.068332 -0.092204 + 2 0.085299 0.050680 0.044451 ... -0.002592 0.002861 -0.025930 + 3 -0.089063 -0.044642 -0.011595 ... 0.034309 0.022688 -0.009362 + 4 0.005383 -0.044642 -0.036385 ... -0.002592 -0.031988 -0.046641 + + [5 rows x 10 columns] + Target: + count 442.000000 + mean 152.133484 + std 77.093005 + min 25.000000 + 25% 87.000000 + 50% 140.500000 + 75% 211.500000 + max 346.000000 + Name: target, dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 41-43 + +Let's combine features and target together in one dataframe and define X +and y. + +.. GENERATED FROM PYTHON SOURCE LINES 43-48 + +.. code-block:: default + + data_diabetes = pd.concat([features, target], axis=1) # type: ignore + + X = ["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"] + y = "target" + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 49-51 + +Train a ridge regression model on train dataset and use mean absolute error +for scoring. + +.. GENERATED FROM PYTHON SOURCE LINES 51-62 + +.. code-block:: default + + scores, model = run_cross_validation( + X=X, + y=y, + data=data_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring="neg_mean_absolute_error", + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:55,051 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:55,051 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:55,051 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:55,051 - julearn - INFO - Target: target + 2024-10-17 13:53:55,051 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:55,051 - julearn - INFO - X_types:{} + 2024-10-17 13:53:55,052 - julearn - WARNING - The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:55,052 - julearn - INFO - ==================== + 2024-10-17 13:53:55,052 - julearn - INFO - + 2024-10-17 13:53:55,052 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:55,052 - julearn - INFO - Step added + 2024-10-17 13:53:55,052 - julearn - INFO - Adding step ridge that applies to ColumnTypes + 2024-10-17 13:53:55,053 - julearn - INFO - Step added + 2024-10-17 13:53:55,053 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:55,053 - julearn - INFO - ==================== + 2024-10-17 13:53:55,053 - julearn - INFO - + 2024-10-17 13:53:55,053 - julearn - INFO - = Data Information = + 2024-10-17 13:53:55,053 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:55,053 - julearn - INFO - Number of samples: 442 + 2024-10-17 13:53:55,053 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:55,053 - julearn - INFO - ==================== + 2024-10-17 13:53:55,053 - julearn - INFO - + 2024-10-17 13:53:55,053 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:55,054 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 63-64 + +The scores dataframe has all the values for each CV split. + +.. GENERATED FROM PYTHON SOURCE LINES 64-66 + +.. code-block:: default + + scores.head() + + + + + + +.. raw:: html + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
fit_timescore_timetest_scoren_trainn_testrepeatfoldcv_mdsum
00.0046900.002261-43.1043593538900b10eef89b4192178d482d7a1587a248a
10.0044640.002251-44.8613643538901b10eef89b4192178d482d7a1587a248a
20.0044320.002243-47.9814073548802b10eef89b4192178d482d7a1587a248a
30.0054000.002886-42.9562543548803b10eef89b4192178d482d7a1587a248a
40.0049390.002305-42.4198863548804b10eef89b4192178d482d7a1587a248a
+
+
+
+
+ +.. GENERATED FROM PYTHON SOURCE LINES 67-68 + +Mean value of mean absolute error across CV. + +.. GENERATED FROM PYTHON SOURCE LINES 68-70 + +.. code-block:: default + + print(scores["test_score"].mean() * -1) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 44.264653948271885 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 71-73 + +Now do the same thing, but use mean absolute error and Pearson product-moment +correlation coefficient (squared) as scoring functions. + +.. GENERATED FROM PYTHON SOURCE LINES 73-84 + +.. code-block:: default + + scores, model = run_cross_validation( + X=X, + y=y, + data=data_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring=["neg_mean_absolute_error", "r2_corr"], + ) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:55,103 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:55,103 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:55,103 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:55,103 - julearn - INFO - Target: target + 2024-10-17 13:53:55,103 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:55,103 - julearn - INFO - X_types:{} + 2024-10-17 13:53:55,103 - julearn - WARNING - The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:55,104 - julearn - INFO - ==================== + 2024-10-17 13:53:55,104 - julearn - INFO - + 2024-10-17 13:53:55,104 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:55,104 - julearn - INFO - Step added + 2024-10-17 13:53:55,104 - julearn - INFO - Adding step ridge that applies to ColumnTypes + 2024-10-17 13:53:55,104 - julearn - INFO - Step added + 2024-10-17 13:53:55,104 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:55,105 - julearn - INFO - ==================== + 2024-10-17 13:53:55,105 - julearn - INFO - + 2024-10-17 13:53:55,105 - julearn - INFO - = Data Information = + 2024-10-17 13:53:55,105 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:55,105 - julearn - INFO - Number of samples: 442 + 2024-10-17 13:53:55,105 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:55,105 - julearn - INFO - ==================== + 2024-10-17 13:53:55,105 - julearn - INFO - + 2024-10-17 13:53:55,105 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:55,105 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + +.. GENERATED FROM PYTHON SOURCE LINES 85-88 + +Now the scores dataframe has all the values for each CV split, but two scores +unders the column names ``"test_neg_mean_absolute_error"`` and +``"test_r2_corr"``. + +.. GENERATED FROM PYTHON SOURCE LINES 88-90 + +.. code-block:: default + + print(scores[["test_neg_mean_absolute_error", "test_r2_corr"]].mean()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + test_neg_mean_absolute_error -44.264654 + test_r2_corr 0.486498 + dtype: float64 + + + + +.. GENERATED FROM PYTHON SOURCE LINES 91-94 + +If we want to define a custom scoring metric, we need to define a function +that takes the predicted and the actual values as input and returns a value. +In this case, we want to compute Pearson correlation coefficient (r). + +.. GENERATED FROM PYTHON SOURCE LINES 94-100 + +.. code-block:: default + + + + def pearson_scorer(y_true, y_pred): + return scipy.stats.pearsonr(y_true.squeeze(), y_pred.squeeze())[0] + + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 101-103 + +Before using it, we need to convert it to a ``sklearn scorer`` and register it +with ``julearn``. + +.. GENERATED FROM PYTHON SOURCE LINES 103-106 + +.. code-block:: default + + + register_scorer(scorer_name="pearsonr", scorer=make_scorer(pearson_scorer)) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:55,151 - julearn - INFO - registering scorer named pearsonr + + + + +.. GENERATED FROM PYTHON SOURCE LINES 107-108 + +Now we can use it as another scoring metric. + +.. GENERATED FROM PYTHON SOURCE LINES 108-118 + +.. code-block:: default + + scores, model = run_cross_validation( + X=X, + y=y, + data=data_diabetes, + preprocess="zscore", + problem_type="regression", + model="ridge", + return_estimator="final", + scoring=["neg_mean_absolute_error", "r2_corr", "pearsonr"], + ) + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + 2024-10-17 13:53:55,152 - julearn - INFO - ==== Input Data ==== + 2024-10-17 13:53:55,152 - julearn - INFO - Using dataframe as input + 2024-10-17 13:53:55,152 - julearn - INFO - Features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:55,152 - julearn - INFO - Target: target + 2024-10-17 13:53:55,152 - julearn - INFO - Expanded features: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6'] + 2024-10-17 13:53:55,152 - julearn - INFO - X_types:{} + 2024-10-17 13:53:55,152 - julearn - WARNING - The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + /home/runner/work/julearn/julearn/julearn/prepare.py:509: RuntimeWarning: The following columns are not defined in X_types: ['age', 'sex', 'bmi', 'bp', 's1', 's2', 's3', 's4', 's5', 's6']. They will be treated as continuous. + warn_with_log( + 2024-10-17 13:53:55,153 - julearn - INFO - ==================== + 2024-10-17 13:53:55,153 - julearn - INFO - + 2024-10-17 13:53:55,153 - julearn - INFO - Adding step zscore that applies to ColumnTypes + 2024-10-17 13:53:55,153 - julearn - INFO - Step added + 2024-10-17 13:53:55,153 - julearn - INFO - Adding step ridge that applies to ColumnTypes + 2024-10-17 13:53:55,153 - julearn - INFO - Step added + 2024-10-17 13:53:55,153 - julearn - INFO - = Model Parameters = + 2024-10-17 13:53:55,154 - julearn - INFO - ==================== + 2024-10-17 13:53:55,154 - julearn - INFO - + 2024-10-17 13:53:55,154 - julearn - INFO - = Data Information = + 2024-10-17 13:53:55,154 - julearn - INFO - Problem type: regression + 2024-10-17 13:53:55,154 - julearn - INFO - Number of samples: 442 + 2024-10-17 13:53:55,154 - julearn - INFO - Number of features: 10 + 2024-10-17 13:53:55,154 - julearn - INFO - ==================== + 2024-10-17 13:53:55,154 - julearn - INFO - + 2024-10-17 13:53:55,154 - julearn - INFO - Target type: float64 + 2024-10-17 13:53:55,154 - julearn - INFO - Using outer CV scheme KFold(n_splits=5, random_state=None, shuffle=False) (incl. final model) + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** (0 minutes 0.173 seconds) + + +.. _sphx_glr_download_auto_examples_05_customization_run_custom_scorers_regression.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: run_custom_scorers_regression.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: run_custom_scorers_regression.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/pr-preview/pr-276/_sources/auto_examples/05_customization/sg_execution_times.rst.txt b/pr-preview/pr-276/_sources/auto_examples/05_customization/sg_execution_times.rst.txt new file mode 100644 index 000000000..d5604d5bc --- /dev/null +++ b/pr-preview/pr-276/_sources/auto_examples/05_customization/sg_execution_times.rst.txt @@ -0,0 +1,13 @@ + +:orphan: + +.. _sphx_glr_auto_examples_05_customization_sg_execution_times: + + +Computation times +================= +**00:00.173** total execution time for **auto_examples_05_customization** files: + ++------------------------------------------------------------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_auto_examples_05_customization_run_custom_scorers_regression.py` (``run_custom_scorers_regression.py``) | 00:00.173 | 0.0 MB | ++------------------------------------------------------------------------------------------------------------------------+-----------+--------+ diff --git a/pr-preview/pr-276/_sources/available_pipeline_steps.rst.txt b/pr-preview/pr-276/_sources/available_pipeline_steps.rst.txt new file mode 100644 index 000000000..2370764e6 --- /dev/null +++ b/pr-preview/pr-276/_sources/available_pipeline_steps.rst.txt @@ -0,0 +1,396 @@ +.. include:: links.inc + +.. _available_pipeline_steps: + +Overview of available Pipeline Steps +==================================== + +The following is a list of all available steps that can be used to create +a pipeline by name. The overview is sorted based on the type of the step: +:ref:`available_transformers` or :ref:`available_models`. + +* The column ``Name`` refers to the string-name of + the respective step, i.e. how it should be specified when passed to e.g., the + :class:`.PipelineCreator`. + +* The column ``Description`` gives a short + description of what the step is doing. + +* The column ``Class`` either indicates the underlying `scikit-learn`_ class of + the respective pipeline step together with a link to the class in the + `scikit-learn`_ documentation (follow the link to see the valid parameters) or + indicates the class in ``julearn``, so one can have a closer look at it in + ``julearn``'s :ref:`api`. + +For feature transformations, the :ref:`available_transformers` are to be used +with the :class:`.PipelineCreator` and for target transformations, the +:ref:`available_transformers` are to be used with the +:class:`.TargetPipelineCreator`. + +.. _available_transformers: + +Transformers +------------ + +.. _available_scalers: + +Scalers +~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + * - ``zscore`` + - Removing mean and scale to unit variance + - :class:`~sklearn.preprocessing.StandardScaler` + * - ``scaler_robust`` + - Removing median and scale to IQR + - :class:`~sklearn.preprocessing.RobustScaler` + * - ``scaler_minmax`` + - Scale to a given range + - :class:`~sklearn.preprocessing.MinMaxScaler` + * - ``scaler_maxabs`` + - Scale by max absolute value + - :class:`~sklearn.preprocessing.MaxAbsScaler` + * - ``scaler_normalizer`` + - Normalize to unit norm + - :class:`~sklearn.preprocessing.Normalizer` + * - ``scaler_quantile`` + - Transform to uniform or normal distribution (robust) + - :class:`~sklearn.preprocessing.QuantileTransformer` + * - ``scaler_power`` + - *Gaussianise* data + - :class:`~sklearn.preprocessing.PowerTransformer` + +Feature Selection +~~~~~~~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + * - ``select_univariate`` + - Removing mean and scale to unit variance + - :class:`~sklearn.feature_selection.GenericUnivariateSelect` + * - ``select_percentile`` + - Rank and select percentile + - :class:`~sklearn.feature_selection.SelectPercentile` + * - ``select_k`` + - Rank and select K + - :class:`~sklearn.feature_selection.SelectKBest` + * - ``select_fdr`` + - Select based on estimated FDR + - :class:`~sklearn.feature_selection.SelectFdr` + * - ``select_fpr`` + - Select based on FPR threshold + - :class:`~sklearn.feature_selection.SelectFpr` + * - ``select_fwe`` + - Select based on FWE threshold + - :class:`~sklearn.feature_selection.SelectFwe` + * - ``select_variance`` + - Remove low variance features + - :class:`~sklearn.feature_selection.VarianceThreshold` + +DataFrame operations +~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + * - ``confound_removal`` + - | Removing confounds from features, + | by subtracting the prediction of each feature given all confounds. + | By default this is equal to "independently regressing out + | the confounds from the features" + - :class:`.ConfoundRemover` + * - ``drop_columns`` + - Drop columns from the DataFrame + - :class:`.DropColumns` + * - ``change_column_types`` + - Change the type of a column in a DataFrame + - :class:`.ChangeColumnTypes` + * - ``filter_columns`` + - Filter columns in a DataFrame + - :class:`.FilterColumns` + +.. _available_decompositions: + +Decomposition +~~~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + * - ``pca`` + - Principal Component Analysis + - :class:`~sklearn.decomposition.PCA` + +Custom +~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + * - ``cbpm`` + - Connectome-based Predictive Modeling (CBPM) + - :class:`.CBPM` + +.. _available_models: + +Models (Estimators) +------------------- + +Support Vector Machines +~~~~~~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + - Binary + - Multiclass + - Regression + * - ``svm`` + - Support Vector Machine + - | :class:`~sklearn.svm.SVC` and + | :class:`~sklearn.svm.SVR` + - Y + - Y + - Y + +Ensemble +~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + - Binary + - Multiclass + - Regression + * - ``rf`` + - Random Forest + - | :class:`~sklearn.ensemble.RandomForestClassifier` and + | :class:`~sklearn.ensemble.RandomForestRegressor` + - Y + - Y + - Y + * - ``et`` + - Extra-Trees + - | :class:`~sklearn.ensemble.ExtraTreesClassifier` and + | :class:`~sklearn.ensemble.ExtraTreesRegressor` + - Y + - Y + - Y + * - ``adaboost`` + - AdaBoost + - | :class:`~sklearn.ensemble.AdaBoostClassifier` and + | :class:`~sklearn.ensemble.AdaBoostRegressor` + - Y + - Y + - Y + * - ``bagging`` + - Bagging + - | :class:`~sklearn.ensemble.BaggingClassifier` and + | :class:`~sklearn.ensemble.BaggingRegressor` + - Y + - Y + - Y + * - ``gradientboost`` + - Gradient Boosting + - | :class:`~sklearn.ensemble.GradientBoostingClassifier` and + | :class:`~sklearn.ensemble.GradientBoostingRegressor` + - Y + - Y + - Y + * - ``stacking`` + - Stacking + - | :class:`~sklearn.ensemble.StackingClassifier` and + | :class:`~sklearn.ensemble.StackingRegressor` + - Y + - Y + - Y + +Gaussian Processes +~~~~~~~~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + - Binary + - Multiclass + - Regression + * - ``gauss`` + - Gaussian Process + - | :class:`~sklearn.gaussian_process.GaussianProcessClassifier` and + | :class:`~sklearn.gaussian_process.GaussianProcessRegressor` + - Y + - Y + - Y + +Linear Models +~~~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + - Binary + - Multiclass + - Regression + * - ``logit`` + - Logistic Regression (aka logit, MaxEnt). + - :class:`~sklearn.linear_model.LogisticRegression` + - Y + - Y + - N + * - ``logitcv`` + - Logistic Regression CV (aka logit, MaxEnt). + - :class:`~sklearn.linear_model.LogisticRegressionCV` + - Y + - Y + - N + * - ``linreg`` + - Least Squares regression. + - :class:`~sklearn.linear_model.LinearRegression` + - N + - N + - Y + * - ``ridge`` + - Linear least squares with l2 regularization. + - | :class:`~sklearn.linear_model.RidgeClassifier` and + | :class:`~sklearn.linear_model.Ridge` + - Y + - Y + - Y + * - ``ridgecv`` + - Ridge regression with built-in cross-validation. + - | :class:`~sklearn.linear_model.RidgeClassifierCV` and + | :class:`~sklearn.linear_model.RidgeCV` + - Y + - Y + - Y + * - ``sgd`` + - Linear model fitted by minimizing a regularized empirical loss with SGD + - | :class:`~sklearn.linear_model.SGDClassifier` and + | :class:`~sklearn.linear_model.SGDRegressor` + - Y + - Y + - Y + +Naive Bayes +~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + - Binary + - Multiclass + - Regression + * - ``nb_bernoulli`` + - Multivariate Bernoulli models. + - :class:`~sklearn.naive_bayes.BernoulliNB` + - Y + - Y + - N + * - ``nb_categorical`` + - Categorical features. + - :class:`~sklearn.naive_bayes.CategoricalNB` + - Y + - Y + - N + * - ``nb_complement`` + - Complement Naive Bayes + - :class:`~sklearn.naive_bayes.ComplementNB` + - Y + - Y + - N + * - ``nb_gaussian`` + - Gaussian Naive Bayes + - :class:`~sklearn.naive_bayes.GaussianNB` + - Y + - Y + - N + * - ``nb_multinomial`` + - Multinomial models + - :class:`~sklearn.naive_bayes.MultinomialNB` + - Y + - Y + - N + +Dynamic Selection +~~~~~~~~~~~~~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + - Binary + - Multiclass + - Regression + * - ``ds`` + - Support for `DESlib`_ models + - :class:`.DynamicSelection` + - Y + - Y + - Y + +Dummy +~~~~~ + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Name + - Description + - Class + - Binary + - Multiclass + - Regression + * - ``dummy`` + - Use simple rules (without features). + - | :class:`~sklearn.dummy.DummyClassifier` and + | :class:`~sklearn.dummy.DummyRegressor` + - Y + - Y + - Y diff --git a/pr-preview/pr-276/_sources/configuration.rst.txt b/pr-preview/pr-276/_sources/configuration.rst.txt new file mode 100644 index 000000000..f2959e288 --- /dev/null +++ b/pr-preview/pr-276/_sources/configuration.rst.txt @@ -0,0 +1,56 @@ +.. include:: links.inc + +.. _configuration: + +Configuring ``julearn`` +======================= + +While ``julearn`` is meant to be a user-friendly tool, this also comes with a +cost. For example, in order to provide the user with information as well as to +be able to detect potential errors, we have implemented several checks. These +checks, however, might yield high computational costs. Therefore, we have +implemented a global configuration module in ``julearn`` that allows to set +flags to enable or disable certain extra functionality. This module is called +``julearn.config`` and it has a single function called ``set_config`` +that given a configuration flag name and a value, it sets the flag to the given +value. + +Here you can find the comprehensive list of flags that can be set: + +.. list-table:: + :widths: auto + :header-rows: 1 + + * - Flag + - Description + - Potential problem(s) + * - ``disable_x_check`` + - | Disable checking for unmatched column names in ``X``. + | If set to ``True``, any element in ``X`` that is not present in the + | dataframe will not result in an error. + - | The user might think that a certain feature is used in the model when + | it is not. + * - ``disable_xtypes_check`` + - | Disable checking for missing/present ``X_types`` in the ``X`` parameter + | of the :func:`.run_cross_validation` method. + | If set to ``True``, the ``X_types`` parameter will not be checked for + | consistency with the ``X`` parameter, including undefined columns in + | ``X``, missing types in ``X_types`` or duplicated columns in + | ``X_types``. + - | The user might think that a certain feature is considered in the model + | when it is not. + * - ``disable_x_verbose`` + - | Disable printing the list of expanded column names in ``X``. + | If set to ``True``, the list of column names will not be printed. + - The user will not see the expanded column names in ``X``. + * - ``disable_xtypes_verbose`` + - | Disable printing the list of expanded column names in ``X_types``. + | If set to ``True``, the list of types of X will not be printed. + - The user will not see the expanded ``X_types`` column names. + * - ``enable_parallel_column_transformers`` + - | This flag enables parallel execution of column transformers by + | reverting to the default behaviour of scikit-learn + | (instead of using ``n_jobs=1``) + | If set to ``True``, the parameter will be set back to None. + - | Column transformers will be applied in parallel, using more resources. + | than expected. \ No newline at end of file diff --git a/pr-preview/pr-276/_sources/contributing.rst.txt b/pr-preview/pr-276/_sources/contributing.rst.txt new file mode 100644 index 000000000..b42558f1e --- /dev/null +++ b/pr-preview/pr-276/_sources/contributing.rst.txt @@ -0,0 +1,219 @@ +.. include:: links.inc + +.. _contribution_guidelines: + +Contributing +============ + +Setting up the local development environment +-------------------------------------------- + +#. Fork the https://github.com/juaml/julearn repository on GitHub. If you + have never done this before, `follow the official guide + `_. +#. Clone your fork locally as described in the same guide. +#. Install your local copy into a Python virtual environment. You can `read + this guide to learn more + `_ about them + and how to create one. + + .. code-block:: bash + + pip install -e ".[dev]" + +#. Create a branch for local development using the ``main`` branch as a + starting point. Use ``fix``, ``refactor``, or ``feat`` as a prefix. + + .. code-block:: bash + + git checkout main + git checkout -b / + + Now you can make your changes locally. + +#. Make sure you install git pre-commit hooks like so: + + .. code-block:: bash + + pre-commit install + +#. When making changes locally, it is helpful to ``git commit`` your work + regularly. On one hand to save your work and on the other hand, the smaller + the steps, the easier it is to review your work later. Please use `semantic + commit messages + `_. + + .. code-block:: bash + + git add . + git commit -m ": " + + In case, you want to commit some WIP (work-in-progress) code, please indicate + that in the commit message and use the flag ``--no-verify`` with + ``git commit`` like so: + + .. code-block:: bash + + git commit --no-verify -m "WIP: " + +#. When you're done making changes, check that your changes pass our test suite. + This is all included with ``tox``. + + .. code-block:: bash + + tox + + You can also run all ``tox`` tests in parallel. As of ``tox 3.7``, you can run + + .. code-block:: bash + + tox --parallel + + +#. Push your branch to GitHub. + + .. code-block:: bash + + git push origin / + +#. Open the link displayed in the message when pushing your new branch in order + to submit a pull request. Please follow the template presented to you in the + web interface to complete your pull request. + + +GitHub Pull Request guidelines +------------------------------ + +Before you submit a pull request, check that it meets these guidelines: + +#. The pull request should include tests in the respective ``tests`` directory. + Except in rare circumstances, code coverage must not decrease (as reported + by codecov which runs automatically when you submit your pull request). +#. If the pull request adds functionality, the docs should be + updated. Consider creating a Python file that demonstrates the usage in + ``examples/`` directory. +#. Make sure to create a Draft Pull Request. If you are not sure how to do it, + check + `here `_. +#. Note the pull request ID assigned after completing the previous step and + create a short one-liner file of your contribution named as + ``.`` in ``docs/changes/newsfragments/``, ```` + being as per the following convention: + + * API change : ``change`` + * Bug fix : ``bugfix`` + * Enhancement : ``enh`` + * Feature : ``feature`` + * Documentation improvement : ``doc`` + * Miscellaneous : ``misc`` + * Deprecation and API removal : ``removal`` + + For example, a basic documentation improvement can be recorded in a file + ``101.doc`` with the content: + + .. code-block:: + + Fixed a typo in intro by `julearn's biggest fan`_ + +#. If it's your first contribution, also add yourself to + ``docs/changes/contributors.inc``. +#. The pull request will be tested against several Python versions. +#. Someone from the core team will review your work and guide you to a successful + contribution. + + +Running unit tests +------------------ + +julearn uses `pytest `_ for its +unit-tests and new features should in general always come with new +tests that make sure that the code runs as intended. + +To run all tests + +.. code-block:: bash + + tox -e test + + +Adding and building documentation +--------------------------------- + +Building the documentation requires some extra packages and can be installed by + +.. code-block:: bash + + pip install -e ".[docs]" + +To build the docs + +.. code-block:: bash + + cd docs + make local + +To view the documentation, open ``docs/_build/html/index.html``. + +In case you remove some files or change their filenames, you can run into +errors when using ``make local``. In this situation you can use ``make clean`` +to clean up the already build files and then re-run ``make local``. + + +Writing Examples +---------------- + +The format used for text is reST. Check the `sphinx RST reference`_ for more +details. The examples are run and displayed in HTML format using +`sphinx gallery`_. To add an example, just create a ``.py`` file that starts +either with ``plot_`` or ``run_``, depending on whether the example generates +a figure or not. + +The first lines of the example should be a Python block comment with a title, +a description of the example, authors and license name. + +The following is an example of how to start an example + +.. code-block:: python + + """ + Simple Binary Classification + ============================ + + This example uses the 'iris' dataset and performs a simple binary + classification using a Support Vector Machine classifier. + + """ + + +The rest of the script will be executed as normal Python code. In order to +render the output and embed formatted text within the code, you need to add +a 79 ``#`` (a full line) at the point in which you want to render and add text. +Each line of text shall be preceded with ``#``. The code that is not +commented will be executed. + +The following example will create texts and render the output between the +texts. + +.. code-block:: python + + from julearn import run_cross_validation + from julearn.utils import configure_logging + from seaborn import load_dataset + + + ############################################################################### + # Set the logging level to info to see extra information + configure_logging(level="INFO") + + ############################################################################### + # Load the iris dataset + df_iris = load_dataset("iris") + + ############################################################################### + # The dataset has three species. We will keep two to perform a binary + # classification. + df_iris = df_iris[df_iris["species"].isin(["versicolor", "virginica"])] + + +Finally, when the example is done, you can run as a normal Python script. +To generate the HTML, just build the docs. diff --git a/pr-preview/pr-276/_sources/examples.rst.txt b/pr-preview/pr-276/_sources/examples.rst.txt new file mode 100644 index 000000000..f285cc67e --- /dev/null +++ b/pr-preview/pr-276/_sources/examples.rst.txt @@ -0,0 +1,18 @@ +Examples +======== + +The following are a set of examples that use ``julearn``. + +.. this needs to be done manually to avoid TOC issues +.. see https://github.com/sphinx-gallery/sphinx-gallery/pull/944/ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + auto_examples/00_starting/index.rst + auto_examples/01_model_comparison/index.rst + auto_examples/02_inspection/index.rst + auto_examples/03_complex_models/index.rst + auto_examples/04_confounds/index.rst + auto_examples/05_customization/index.rst diff --git a/pr-preview/pr-276/_sources/faq.rst.txt b/pr-preview/pr-276/_sources/faq.rst.txt new file mode 100644 index 000000000..38d945e3c --- /dev/null +++ b/pr-preview/pr-276/_sources/faq.rst.txt @@ -0,0 +1,75 @@ +.. include:: links.inc + +FAQs +==== + +I have issues with the dependencies for the :mod:`.viz` module. +--------------------------------------------------------------- + +The :mod:`.viz` module uses `bokeh`_ and `panel`_ to create interactive +plots. These packages are not installed by default when you install +``julearn``. This libraries are also under development and they might not +be as robust as we want. + +Usually, installing ``julearn`` with the ``[viz]`` option will install the +necessary dependencies using ``pip``. However, if you have issues with the +installation or you want to install them through other package managers, +you can install them manually. + +Using ``pip``: + +.. code-block:: bash + + pip install panel + pip install bokeh + +Using ``conda``: + +.. code-block:: bash + + conda install -c conda-forge panel + conda install -c bokeh bokeh + + +How do I use the :mod:`.viz` interactive plots? +----------------------------------------------- + +The interactive plots are based on `bokeh`_ and `panel`_. You can use them +in different ways: + +#. As a standalone application, in a browser: + + To do so, you need to call the function ``show`` on the plot object. For + example: + + .. code-block:: python + + panel = plot_scores(scores1, scores2, scores3) + panel.show() + + +#. As part of a Jupyter notebook: + + You will need to install the ``jupyter_bokeh`` package. + + Using ``pip``: + + .. code-block:: bash + + pip install jupyter_bokeh + + Using ``conda``: + + .. code-block:: bash + + conda install -c bokeh jupyter_bokeh + + This will allow you to see the plots interactively in the notebook. To do so, + you need to call the function ``servable`` on the plot object. For example: + + .. code-block:: python + + panel = plot_scores(scores1, scores2, scores3) + panel.servable() + +.. TODO: As part of a Binder notebook to share with colleagues. diff --git a/pr-preview/pr-276/_sources/getting_started.rst.txt b/pr-preview/pr-276/_sources/getting_started.rst.txt new file mode 100644 index 000000000..47bd86601 --- /dev/null +++ b/pr-preview/pr-276/_sources/getting_started.rst.txt @@ -0,0 +1,100 @@ +.. include:: links.inc + + +Getting started +=============== + +Requirements +------------ + +``julearn`` is compatible with `Python`_ >= 3.8 and requires the following +packages: + +* ``numpy>=1.24,<1.27`` +* ``pandas>=1.5.0,<2.2`` +* ``scikit-learn>=1.2.0,<1.5.0`` +* ``statsmodels>=0.13,<0.15`` + +Running the examples require: + +* ``seaborn>=0.12.2,<0.13`` +* ``bokeh>=3.0.0`` +* ``panel>=1.3.0`` +* ``param>=2.0.0`` + +Depending on the installation method (e.g. the ``pip install`` option below), +these packages might be installed automatically. It is nevertheless good to be +aware of these dependencies as installing ``julearn`` might lead to changes in +these packages. + +Setup suggestion +================ + +Although not required, we strongly recommend using **virtual environments** and +installing ``julearn`` into a virtual environment. This helps to keep the setup +clean. The most prominent options are: + +* pip: `venv`_ +* conda: `conda env`_ + +Installing +========== + +.. note:: + ``julearn`` keeps on being updated and improved. The latest stable release + and the developer version therefore often differ quite a bit. + If you want the newest updates, it might make more sense for you to use the + developer version until we release the next stable ``julearn`` version. + + +Depending on your aimed usage of ``julearn`` you have different options +how to install ``julearn``: + +#. Install the *latest release*: Likely most suitable for most + **end users**. This is done by installing the latest stable release from + PyPI: + + .. code-block:: bash + + pip install -U julearn + + or via ``conda`` like so: + + .. code-block:: bash + + conda install -c conda-forge julearn + +#. Install the *latest pre-relase*: This version will have the + **latest updates**. However, it is still under development and not yet + officially released. Some features might still change before the next stable + release. + + .. code-block:: bash + + pip install -U julearn --pre + + +.. _install_optional_dependencies: + +Optional Dependencies +===================== + +Some functionality of ``julearn`` requires additional packages. These are not +installed by default. If you want to use these features, you need to specify +them during installation. For example, if you want to use the :mod:`.viz` +module, you need to install the ``viz`` optional dependencies as follows: + +.. code-block:: bash + + pip install -U julearn[viz] + +The following optional dependencies are available: + +* ``viz``: Visualization tools for ``julearn``. This includes the + :mod:`.viz` module. +* ``deslib``: The :mod:`.dynamic` module requires the `deslib`_ package. This + module is not compatible with newer Python versions and it is unmaintained. +* ``skopt``: Using the ``"bayes"`` searcher (:class:`~skopt.BayesSearchCV`) + requires the `scikit-optimize`_ package. +* ``optuna``: Using the ``"optuna"`` searcher (:class:`~optuna_integration.OptunaSearchCV`) requires the `Optuna`_ and `optuna_integration`_ packages. +* ``all``: Install all optional functional dependencies (except ``deslib``). diff --git a/pr-preview/pr-276/_sources/index.rst.txt b/pr-preview/pr-276/_sources/index.rst.txt new file mode 100644 index 000000000..1a52ebf55 --- /dev/null +++ b/pr-preview/pr-276/_sources/index.rst.txt @@ -0,0 +1,99 @@ +.. julearn documentation master file, created by + sphinx-quickstart on Thu Oct 29 14:29:33 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +.. include:: links.inc + +Welcome to julearn's documentation! +=================================== + +.. image:: images/julearn_logo_it.png + :width: 300px + :alt: julearn logo + +... a user-oriented machine-learning library. + +What is ``julearn``? +-------------------- + +At the Applied Machine Learning (`AML`_) group, as part of the Institute of +Neuroscience and Medicine - Brain and Behaviour (`INM-7`_), we thought that +using ML in research could be simpler. + +In the same way as `seaborn`_ provides an abstraction of `matplotlib`_'s +functionality aiming for powerful data visualization with minor coding, we +built ``julearn`` on top of `scikit-learn`_. + +``julearn`` is a library that provides users with the possibility of easy +testing ML models directly from `pandas`_ DataFrames, while keeping the +flexibility of using `scikit-learn`_'s models. + +To get started with ``julearn`` just keep reading here. Additionally you can +check out our `video tutorial`_. + +Why ``julearn``? +---------------- + +Why not just use ``scikit-learn``? ``julearn`` offers **three essential benefits**: + +#. You can do machine learning with **less amount of code** than in + ``scikit-learn``. +#. ``julearn`` helps you build and evaluate pipelines in an easy way and thereby + helps you **avoid data leakage**! +#. It offers you nice **additional functionality**: + + * Easy to implement **confound removal**: ``julearn`` offers you a simple way + to remove confounds from your data in a cross-validated way. + * Data **typing**: ``julearn`` provides a system to specify **data types** + for your features, and then provides you with the possibility to filter and + transform your data according to these types. + * Model **inspection**: ``julearn`` provides you with a simple way to + **inspect** your models and pipelines, and thereby helps you to understand + what is going on in your pipeline. + * Model **comparison**: ``julearn`` provides out-of-the-box interactive + **visualizations** and **statistics** to compare your models. + + +Table of Contents +================= + +.. toctree:: + :maxdepth: 2 + :numbered: 2 + + getting_started + what_really_need_know/index.rst + selected_deeper_topics/index.rst + available_pipeline_steps.rst + examples.rst + api/index.rst + configuration + contributing + maintaining + faq + whats_new + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + + +Indices and tables +================== + +If you use julearn in a scientific publication, please use the following +reference + + Hamdan, Sami, Shammi More, Leonard Sasse, Vera Komeyer, + Kaustubh R. Patil, and Federico Raimondo. ‘Julearn: + An Easy-to-Use Library for Leakage-Free Evaluation and Inspection of + ML Models’. arXiv, 19 October 2023. + https://doi.org/10.48550/arXiv.2310.12568. + +Since julearn is also heavily reliant on scikit-learn, please also cite +them: https://scikit-learn.org/stable/about.html#citing-scikit-learn diff --git a/pr-preview/pr-276/_sources/maintaining.rst.txt b/pr-preview/pr-276/_sources/maintaining.rst.txt new file mode 100644 index 000000000..2f0122b05 --- /dev/null +++ b/pr-preview/pr-276/_sources/maintaining.rst.txt @@ -0,0 +1,87 @@ +.. include:: links.inc + +Maintaining +=========== + +Versioning +---------- + +Version numbers are as follows: *MAJOR.MINOR.MICRO*. Additionally, +development version append *devN* where N is the distance (in commits) to +the last release. + +This is done automatically by `setuptools_scm`_. + +This plugin reads the latest tagged version from git and automatically +increments the *MICRO* segment and appends *devN*. This is considered a +pre-release. + +The CI scripts will publish every tag with the format *v.X.Y.Z* to PyPI as +version "X.Y.Z". Additionally, for every push to main, it will be published +as pre-release to PyPI. + +Releasing a new version +----------------------- + +Once the milestone is reached (all issues closed), it is time to do a new +release. Make sure you have +`towncrier `_ installed +before proceeding. + +#. Make sure you are in sync with the main branch. + + .. code-block:: bash + + git checkout main + git pull --rebase origin main + +#. Run the following to check changelog is properly generated: + + .. code-block:: bash + + towncrier build --draft + +#. Then, run: + + .. code-block:: bash + + towncrier + + to generate the proper changelog that should be reflected in + ``docs/whats_new.rst``. + +#. Commit the changes, make a PR and merge via a merge commit. + +#. Make sure you are in sync with the main branch. + + .. code-block:: bash + + git checkout main + git pull --rebase origin main + +#. Create tag (replace ``X.Y.Z`` with the proper version) on the merged PR's + merge commit. + + .. code-block:: bash + + git tag -a vX.Y.Z -m "Release X.Y.Z" + +#. Check that the build system is creating the proper version + + .. code-block:: bash + + SETUPTOOLS_SCM_DEBUG=1 python -m build --outdir dist/ . + +#. Push the tag + + .. code-block:: bash + + git push origin --follow-tags + +#. Optional: bump the *MAJOR* or *MINOR* segment of next release (replace + ``D.E.0`` with the proper version). + + .. code-block:: bash + + git tag -a vD.E.0.dev -m "Set next release to D.E.0" + git push origin --follow-tags diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/CBPM.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/CBPM.rst.txt new file mode 100644 index 000000000..509dea1ce --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/CBPM.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_cbpm_docs.py + +.. _cbpm: + +.. include:: ../auto_examples/99_docs/run_cbpm_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/confound_removal.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/confound_removal.rst.txt new file mode 100644 index 000000000..b85b598b9 --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/confound_removal.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_confound_removal_docs.py + +.. _confound_removal: + +.. include:: ../auto_examples/99_docs/run_confound_removal_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/cross_validation_splitter.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/cross_validation_splitter.rst.txt new file mode 100644 index 000000000..bd83ccc8e --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/cross_validation_splitter.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_cv_splitters_docs.py + +.. _cv_splitter: + +.. include:: ../auto_examples/99_docs/run_cv_splitters_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/hyperparameter_tuning.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/hyperparameter_tuning.rst.txt new file mode 100644 index 000000000..05830a31f --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/hyperparameter_tuning.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_hyperparameters_docs.py + +.. _hp_tuning: + +.. include:: ../auto_examples/99_docs/run_hyperparameters_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/index.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/index.rst.txt new file mode 100644 index 000000000..41ea2c3dd --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/index.rst.txt @@ -0,0 +1,18 @@ +.. include:: ../links.inc + +.. _selected_deeper_topics: + +Selected deeper topics +====================== + +.. toctree:: + :maxdepth: 2 + :caption: Table of Contents + + target_transformers.rst + confound_removal.rst + hyperparameter_tuning.rst + model_inspect.rst + cross_validation_splitter.rst + stacked_models.rst + CBPM.rst diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/model_inspect.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/model_inspect.rst.txt new file mode 100644 index 000000000..5d835dc9d --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/model_inspect.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_model_inspection_docs.py + +.. _model_inspection: + +.. include:: ../auto_examples/99_docs/run_model_inspection_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/stacked_models.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/stacked_models.rst.txt new file mode 100644 index 000000000..0617a2c85 --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/stacked_models.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_stacked_models_docs.py + +.. _stacked_models: + +.. include:: ../auto_examples/99_docs/run_stacked_models_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/selected_deeper_topics/target_transformers.rst.txt b/pr-preview/pr-276/_sources/selected_deeper_topics/target_transformers.rst.txt new file mode 100644 index 000000000..dfa7bd9f8 --- /dev/null +++ b/pr-preview/pr-276/_sources/selected_deeper_topics/target_transformers.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_target_transformer_docs.py + +.. _target_transformers: + +.. include:: ../auto_examples/99_docs/run_target_transformer_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/what_really_need_know/cross_validation.rst.txt b/pr-preview/pr-276/_sources/what_really_need_know/cross_validation.rst.txt new file mode 100644 index 000000000..8d1ae36e0 --- /dev/null +++ b/pr-preview/pr-276/_sources/what_really_need_know/cross_validation.rst.txt @@ -0,0 +1,89 @@ +.. _why_cv: + +Why cross validation? +===================== + +Cross-validation - The fundamentals +----------------------------------- + + "The fundamental goal of machine learning is to generalize beyond the + examples in the training set. This is because, no matter how much data we + have, it is very unlikely that we will see those exact examples again at + test time.” + + -- Domingos, 2012, A Few Useful Things to Know about Machine Learning + +This means that in order to evaluate if a model is *successful* in learning, +we need to evaluate if it is able to predict new data. Thus, we need to have +separate data for learning and testing. At the same time, data is a valuable +resource in machine learning and one wants to use it as efficiently as possible. + +To solve this, we use *cross validation*. The core idea is that we want to +train (also named *fit*) a model on a subset of our data and evaluate it on a +different subset of our data to see how well the trained model generalizes to +unseen data. The training set is used to fit a model +(see :ref:`pipeline_usage`), while the validation set is used to predict the +data labels. The predictions are then compared to the true labels of the +validation dataset, obtaining an estimation of the prediction performance of +the model. + +There are several ways to split the data into training and validation sets. +The most common way is to split the data into two parts, the training set and +the validation set. However, this approach has the disadvantage that the +validation set is only used once and thus, the estimation of the prediction +performance is based on a small number of data points. This can lead to +unstable results. To overcome this problem, cross validation is used. In +cross validation, the data is split into *k* *folds* (splits). Then, *k* +models are trained, each time using a different fold as the validation set and +the remaining folds as the training set. This procedure can be repeated several +times, each time with a different split of the data into folds. + +To read more about cross validation, its functionality and usage and why it is +such an important concept in machine learning, you can have a look at these +[#1]_ [#2]_ [#3]_ [#4]_ resources. + + +The essence of :func:`.run_cross_validation` +-------------------------------------------- + +Building pipelines (see :ref:`pipeline_usage`) within a (nested) +cross-validation scheme, without accidentally leaking some information between +steps can quickly become complicated and errors are often not-obvious to +detect. ``julearn``'s :func:`.run_cross_validation` provides a simple and +straightforward way to do cross-validation less prone to such accidental +mistakes and more transparent for debugging. The user only needs to specify +the model to be used, the data to be used and the evaluation scheme to be used. +``julearn`` then builds the pipeline, splits the data into training and +validation sets accordingly, and most importantly, does all specified steps in a +cross-validation consistent manner. + +The main parameters needed for :func:`.run_cross_validation` include the +specification of: + +#. ``data``: the data, including features, labels and feature types + (see :ref:`data_usage`) +#. ``model``: the model to evaluate, including the data transformation steps + and the learning algorithm to use (see :ref:`pipeline_usage`). +#. ``model evaluation``: how the model performance should be estimated, + like the cross validation scheme or the metrics to be computed + (see :ref:`model_evaluation_usage`) + +The :func:`.run_cross_validation` function will then output the DataFrame with +the fold-wise metrics, which can then be used to visualize and evaluate the +estimation of the models' performance. + +Additional parameters can be used to control the output of the function, in +order to provide mechanisms for model inspection and debugging. + +See :ref:`model_evaluation_usage` for further details on the model evaluation. + + +.. topic:: References: + + .. [#1] https://www.sciencedirect.com/science/article/pii/S105381191630595X + + .. [#2] https://www.nature.com/articles/s41746-022-00592-y + + .. [#3] https://www.sciencedirect.com/science/article/pii/S1053811917305311 + + .. [#4] https://scikit-learn.org/stable/modules/cross_validation.html diff --git a/pr-preview/pr-276/_sources/what_really_need_know/data.rst.txt b/pr-preview/pr-276/_sources/what_really_need_know/data.rst.txt new file mode 100644 index 000000000..2b1817dc6 --- /dev/null +++ b/pr-preview/pr-276/_sources/what_really_need_know/data.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_data_docs.py + +.. _data_usage: + +.. include:: ../auto_examples/99_docs/run_data_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/what_really_need_know/index.rst.txt b/pr-preview/pr-276/_sources/what_really_need_know/index.rst.txt new file mode 100644 index 000000000..171bf399d --- /dev/null +++ b/pr-preview/pr-276/_sources/what_really_need_know/index.rst.txt @@ -0,0 +1,40 @@ +.. include:: ../links.inc + +.. _need_to_know: + +What you really need to know +============================ + +The backbone of ``julearn`` is the function :func:`.run_cross_validation`, which +allows you to do all the *magic*. All important information needed to estimate +your machine learning workflow's performance goes into this function, specified +via its parameters. + +But why is basically everything based on one *cross-validation* function? Well, +because doing proper cross-validation is of utmost importance in machine +learning and it is not as easy as it might seem at first glance. If you want to +understand why, reading the sub-chapter :ref:`cross_validation` is a good +starting point. + +Once you are familiar with the basics of *cross-validation*, you can follow +along the other sub-chapters to learn how to setup a basic workflow using +``julearn``'s :func:`.run_cross_validation`. There you can find out more about +the required data, building a basic pipeline and how to evaluate your model's +performance. + +.. toctree:: + :maxdepth: 2 + + cross_validation.rst + data.rst + pipeline.rst + model_evaluation.rst + model_comparison.rst + +If you are just interested in seeing all parameters of +:func:`.run_cross_validation`, click on the function link to have a look at all +its parameters in the :ref:`api`. + +If you are already familiar with how to set up a basic workflow using +``julearn`` and want to do more fancy stuff, go to +:ref:`selected_deeper_topics`. diff --git a/pr-preview/pr-276/_sources/what_really_need_know/model_comparison.rst.txt b/pr-preview/pr-276/_sources/what_really_need_know/model_comparison.rst.txt new file mode 100644 index 000000000..c4457949d --- /dev/null +++ b/pr-preview/pr-276/_sources/what_really_need_know/model_comparison.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_model_comparison_docs.py + +.. _model_comparison_usage: + +.. include:: ../auto_examples/99_docs/run_model_comparison_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/what_really_need_know/model_evaluation.rst.txt b/pr-preview/pr-276/_sources/what_really_need_know/model_evaluation.rst.txt new file mode 100644 index 000000000..5a6c67fa0 --- /dev/null +++ b/pr-preview/pr-276/_sources/what_really_need_know/model_evaluation.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_model_evaluation_docs.py + +.. _model_evaluation_usage: + +.. include:: ../auto_examples/99_docs/run_model_evaluation_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/what_really_need_know/pipeline.rst.txt b/pr-preview/pr-276/_sources/what_really_need_know/pipeline.rst.txt new file mode 100644 index 000000000..cfa6d56c4 --- /dev/null +++ b/pr-preview/pr-276/_sources/what_really_need_know/pipeline.rst.txt @@ -0,0 +1,10 @@ +.. include:: ../links.inc + +.. to edit the contents of this file, edit + examples/99_docs/run_pipeline_docs.py + +.. _pipeline_usage: + +.. include:: ../auto_examples/99_docs/run_pipeline_docs.rst + :start-after: sphx-glr-example-title + :end-before: _sphx_glr_download_ diff --git a/pr-preview/pr-276/_sources/whats_new.rst.txt b/pr-preview/pr-276/_sources/whats_new.rst.txt new file mode 100644 index 000000000..7a99c53dc --- /dev/null +++ b/pr-preview/pr-276/_sources/whats_new.rst.txt @@ -0,0 +1,232 @@ +.. include:: links.inc +.. include:: changes/contributors.inc + +.. _whats_new: + +What's new +========== + +.. towncrier release notes start + +Julearn 0.3.4.dev37 (2024-10-17) +-------------------------------- + +No significant changes. + + +Julearn 0.3.4 (2024-10-17) +-------------------------- + +Bugfixes +^^^^^^^^ + +- Fix usage of optuna distributions as hyperparameters in older versions of + scikit-learn by `Fede Raimondo`_. (:gh:`268`) + + +Enhancements +^^^^^^^^^^^^ + +- Remove final model fit requirement for inspector to be returned by + :func:`.run_cross_validation` by `Fede Raimondo`_. (:gh:`270`) +- Add :func:`.run_fit` that implements a model fitting procedure with the same + API as :func:`.run_cross_validation` by `Fede Raimondo`_. (:gh:`271`) +- Optimise wrapping of steps and models in the pipeline only when a subset of + features is being used, by `Fede Raimondo`_ (:gh:`274`) +- Change the internal logic of :func:`.run_cross_validation` to optimise joblib + calls by `Fede Raimondo`_ (:gh:`293`) + + +Julearn 0.3.3 (2024-05-16) +-------------------------- + +Bugfixes +^^^^^^^^ + +- Fix ``OptunaSearchCV`` issue + (https://github.com/optuna/optuna-integration/issues/118) with an internal + implementation until the issue is fixed in ``optuna-integration`` by `Fede + Raimondo`_ (:gh:`265`) + + +Improved Documentation +^^^^^^^^^^^^^^^^^^^^^^ + +- Fixed a typo in cross validation by `Dante Culaciati`_ (:gh:`258`) + + +Julearn 0.3.2 (2024-05-03) +-------------------------- + +Bugfixes +^^^^^^^^ + +- Fix a bug in which tuning hyperparemters while using a target transformer + will not work as a result of target transformation detection by `Fede + Raimondo`_ (:gh:`249`) +- Update bokeh api calls to remove warnings by `Fede Raimondo`_ (:gh:`255`) + + +Improved Documentation +^^^^^^^^^^^^^^^^^^^^^^ + +- Update documentation on Hyperparameter Tuning by `Fede Raimondo`_ (:gh:`262`) + + +Enhancements +^^^^^^^^^^^^ + +- Add :class:`~skopt.BayesSearchCV` to the list of available searchers as + 'bayes' by `Fede Raimondo`_ (:gh:`260`) +- Refactor how hyperparmeters' distributions are specified by `Fede Raimondo`_ + (:gh:`262`) + + +Features +^^^^^^^^ + +- Add :class:`~optuna_integration.OptunaSearchCV` to the list of + available searchers as ``optuna`` by `Fede Raimondo`_ (:gh:`262`) + + +Misc +^^^^ + +- Add a new GitHub Action to mark stale issues by `Synchon Mandal`_ (:gh:`224`) +- Add support for ``scikit-learn 1.4.x`` by `Synchon Mandal`_ (:gh:`244`) +- Remove ``pytest-lazy-fixture`` and enable support for ``pytest >= 8.0.0`` by + `Synchon Mandal`_ (:gh:`251`) +- Add ``all`` as optional dependencies to install all functional dependencies + by `Fede Raimondo`_ (:gh:`260`) + + +Julearn 0.3.1 (2024-01-23) +-------------------------- + +Bugfixes +^^^^^^^^ + +- Add ``BaseEstimator`` to ``ConfoundRemoval`` for Target by `Sami Hamdan`_ + (:gh:`151`) +- Skip wrapping scorers when target transformers can inverse transform and + avoid logging filtered warnings by `Fede Raimondo`_ (:gh:`236`) +- Fix ``alternative`` parameter not being used in the corrected t-test `Fede + Raimondo`_ (:gh:`245`) +- Fix hyperparameter not being set in ``PipelineCreator.add`` when an object is + used as model, by `Fede Raimondo`_ (:gh:`247`) + + +Improved Documentation +^^^^^^^^^^^^^^^^^^^^^^ + +- Fix cross-referencing links in documentation by `Synchon Mandal`_ (:gh:`168`) +- Standardize ``Sphinx`` section and header ordering format by `Synchon + Mandal`_ (:gh:`234`) +- Improve documentation language, fix typos and code snippets by `Synchon + Mandal`_ (:gh:`235`) + + +Enhancements +^^^^^^^^^^^^ + +- Make CBPM use sum by default by `Sami Hamdan`_ (:gh:`170`) + + +Features +^^^^^^^^ + +- Add support for multiple grids in hyperparameter tuning by `Fede Raimondo`_ + (:gh:`47`) + + +Misc +^^^^ + +- Adopt ``ruff`` as the only linter for the codebase by `Synchon Mandal`_ + (:gh:`231`) +- Improve ``codespell`` support and fix typos in documentation by `Synchon + Mandal`_ (:gh:`232`) +- Adopt ``pre-commit`` for adding and managing git pre-commit hooks by `Synchon + Mandal`_ (:gh:`233`) + + +Julearn 0.3.0 (2023-07-19) +-------------------------- + +The general API and behavior of Julearn has been changed to make it easier to +define and use pipelines. The documentation has been updated to reflect these +changes. Basic functionality from the previous API is still +present. However, it might require setting a few more parameters in the +:func:`.run_cross_validation` function. + + +Julearn 0.2.5 (2022-07-21) +-------------------------- + +API Changes +^^^^^^^^^^^ + +- Make API surrounding registering consistently use overwrite by `Sami Hamdan`_ +- Inner ``cv`` needs to be provided using ``search_params``. Deprecating ``cv`` in + ``model_params`` by `Sami Hamdan`_ (:gh:`146`) +- Add ``n_jobs`` and ``verbose`` to ``run_cross_validation`` by `Sami Hamdan`_ + + +Bugfixes +^^^^^^^^ + +- Fix a hyperparameters setting issue where the parameter had an iterable of + only one element by `Sami Hamdan`_ (:gh:`96`) +- Fix installations instruction for latest development version (add ``--pre``) + by `Fede Raimondo`_ +- Fix target transformers that only normal transformers are wrapped by + `Sami Hamdan`_ (:gh:`94`) +- Fix compatibility with new scikit-learn release 0.24 by `Sami Hamdan`_ + (:gh:`108`) +- Fix compatibility with multiprocessing in scikit-learn by `Sami Hamdan`_ +- Raise error message when columns in the dataframe are nos strings by + `Fede Raimondo`_ (:gh:`77`) +- Fix not implemented bug for decision_function in ExtendedDataFramePipeline by + `Sami Hamdan`_ (:gh:`135`) +- Fix Bug in the transformer wrapper implementation by `Sami Hamdan`_ + (:gh:`122`) +- Fix Bug Target Transformer missing BaseEstimator by `Sami Hamdan`_ (:gh:`151`) +- Fix Bug of showing Warnings when using confound removal by `Sami Hamdan`_ + (:gh:`152`) +- Fix Bug registered scorer not working in dictionary for scoring by `Sami Hamdan`_ + + +Improved Documentation +^^^^^^^^^^^^^^^^^^^^^^ + +- Add *What's new* section in DOC to document changes by `Fede Raimondo`_ +- Add information on updating the *What's new* section before releasing by + `Fede Raimondo`_ +- Update docs to make it more uniform by `Kaustubh Patil`_ +- Add example for ``tranform_until`` by `Shammi More`_ (:gh:`63`) +- Add documentation/example for parallelization by `Sami Hamdan`_ + + +Enhancements +^^^^^^^^^^^^ + +- Bump minimum python version to 3.7 by `Fede Raimondo`_ +- Refactor scoring to allow for registering and callable scorers by + `Sami Hamdan`_ +- Update :mod:`.model_selection` and add capabilities to register searchers by + `Sami Hamdan`_ +- Update default behavior of setting inner cv according to scikit-learn instead + of using outer cv as default by `Sami Hamdan`_ +- Add tests and more algorithms to ``DynamicSelection`` by `Sami Hamdan`_ and + `Shammi More`_ + +Features +^^^^^^^^ + +- Add user facing ``create_pipeline`` function by `Sami Hamdan`_ +- Add CV schemes for stratifying continuous variables, useful for + regression problems. Check :class:`.ContinuousStratifiedKFold` and + :class:`.RepeatedContinuousStratifiedKFold` by + `Fede Raimondo`_ and `Shammi More`_ +- Add ``CBPM`` transformer by `Sami Hamdan`_ +- Add ``register_model`` by `Sami Hamdan`_ (:gh:`105`) diff --git a/pr-preview/pr-276/_static/basic.css b/pr-preview/pr-276/_static/basic.css new file mode 100644 index 000000000..30fee9d0f --- /dev/null +++ b/pr-preview/pr-276/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/pr-preview/pr-276/_static/binder_badge_logo.svg b/pr-preview/pr-276/_static/binder_badge_logo.svg new file mode 100644 index 000000000..327f6b639 --- /dev/null +++ b/pr-preview/pr-276/_static/binder_badge_logo.svg @@ -0,0 +1 @@ + launchlaunchbinderbinder \ No newline at end of file diff --git a/pr-preview/pr-276/_static/broken_example.png b/pr-preview/pr-276/_static/broken_example.png new file mode 100644 index 000000000..4fea24e7d Binary files /dev/null and b/pr-preview/pr-276/_static/broken_example.png differ diff --git a/pr-preview/pr-276/_static/check-solid.svg b/pr-preview/pr-276/_static/check-solid.svg new file mode 100644 index 000000000..92fad4b5c --- /dev/null +++ b/pr-preview/pr-276/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/pr-preview/pr-276/_static/clipboard.min.js b/pr-preview/pr-276/_static/clipboard.min.js new file mode 100644 index 000000000..54b3c4638 --- /dev/null +++ b/pr-preview/pr-276/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/pr-preview/pr-276/_static/copybutton.css b/pr-preview/pr-276/_static/copybutton.css new file mode 100644 index 000000000..f1916ec7d --- /dev/null +++ b/pr-preview/pr-276/_static/copybutton.css @@ -0,0 +1,94 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +/* Show the copybutton */ +.highlight:hover button.copybtn, button.copybtn.success { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/pr-preview/pr-276/_static/copybutton.js b/pr-preview/pr-276/_static/copybutton.js new file mode 100644 index 000000000..2ea7ff3e2 --- /dev/null +++ b/pr-preview/pr-276/_static/copybutton.js @@ -0,0 +1,248 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copier dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for a moment, then changes it back +// We want the timeout of our `success` class to be a bit shorter than the +// tooltip and icon change, so that we can hide the icon before changing back. +var timeoutIcon = 2000; +var timeoutSuccessClass = 1500; + +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + // Remove success a little bit sooner than we change the tooltip + // So that we can use CSS to hide the copybutton first + setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) + setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/pr-preview/pr-276/_static/copybutton_funcs.js b/pr-preview/pr-276/_static/copybutton_funcs.js new file mode 100644 index 000000000..dbe1aaad7 --- /dev/null +++ b/pr-preview/pr-276/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/pr-preview/pr-276/_static/css/custom.css b/pr-preview/pr-276/_static/css/custom.css new file mode 100644 index 000000000..6ba3a5c91 --- /dev/null +++ b/pr-preview/pr-276/_static/css/custom.css @@ -0,0 +1,21 @@ +.table-wrapper.colwidths-auto table td, .table-wrapper.colwidths-auto table th { + white-space: nowrap; +} +.table-wrapper.colwidths-auto table td a.reference.internal { + overflow-wrap: normal; +} + +/* Style the button that is used to open and close the collapsible content */ +.version-select { + cursor: pointer; +} + +/* Style the collapsible content. Note: hidden by default */ +#versions { + display: none; + overflow: hidden; +} + +.sphx-glr-download { + visibility: hidden; +} diff --git a/pr-preview/pr-276/_static/debug.css b/pr-preview/pr-276/_static/debug.css new file mode 100644 index 000000000..74d4aec33 --- /dev/null +++ b/pr-preview/pr-276/_static/debug.css @@ -0,0 +1,69 @@ +/* + This CSS file should be overridden by the theme authors. It's + meant for debugging and developing the skeleton that this theme provides. +*/ +body { + font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji"; + background: lavender; +} +.sb-announcement { + background: rgb(131, 131, 131); +} +.sb-announcement__inner { + background: black; + color: white; +} +.sb-header { + background: lightskyblue; +} +.sb-header__inner { + background: royalblue; + color: white; +} +.sb-header-secondary { + background: lightcyan; +} +.sb-header-secondary__inner { + background: cornflowerblue; + color: white; +} +.sb-sidebar-primary { + background: lightgreen; +} +.sb-main { + background: blanchedalmond; +} +.sb-main__inner { + background: antiquewhite; +} +.sb-header-article { + background: lightsteelblue; +} +.sb-article-container { + background: snow; +} +.sb-article-main { + background: white; +} +.sb-footer-article { + background: lightpink; +} +.sb-sidebar-secondary { + background: lightgoldenrodyellow; +} +.sb-footer-content { + background: plum; +} +.sb-footer-content__inner { + background: palevioletred; +} +.sb-footer { + background: pink; +} +.sb-footer__inner { + background: salmon; +} +.sb-article { + background: white; +} diff --git a/pr-preview/pr-276/_static/doctools.js b/pr-preview/pr-276/_static/doctools.js new file mode 100644 index 000000000..d06a71d75 --- /dev/null +++ b/pr-preview/pr-276/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/pr-preview/pr-276/_static/documentation_options.js b/pr-preview/pr-276/_static/documentation_options.js new file mode 100644 index 000000000..7e4c114f2 --- /dev/null +++ b/pr-preview/pr-276/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/pr-preview/pr-276/_static/file.png b/pr-preview/pr-276/_static/file.png new file mode 100644 index 000000000..a858a410e Binary files /dev/null and b/pr-preview/pr-276/_static/file.png differ diff --git a/pr-preview/pr-276/_static/js/custom.js b/pr-preview/pr-276/_static/js/custom.js new file mode 100644 index 000000000..fa8ecefd6 --- /dev/null +++ b/pr-preview/pr-276/_static/js/custom.js @@ -0,0 +1,14 @@ +var coll = document.getElementsByClassName("version-select"); +var i; + +for (i = 0; i < coll.length; i++) { + coll[i].addEventListener("click", function() { + this.classList.toggle("active"); + var content = this.nextElementSibling; + if (content.style.display === "block") { + content.style.display = "none"; + } else { + content.style.display = "block"; + } + }); +} diff --git a/pr-preview/pr-276/_static/julearn_logo_it.png b/pr-preview/pr-276/_static/julearn_logo_it.png new file mode 100644 index 000000000..e02e6ee88 Binary files /dev/null and b/pr-preview/pr-276/_static/julearn_logo_it.png differ diff --git a/pr-preview/pr-276/_static/jupyterlite_badge_logo.svg b/pr-preview/pr-276/_static/jupyterlite_badge_logo.svg new file mode 100644 index 000000000..5de36d7fd --- /dev/null +++ b/pr-preview/pr-276/_static/jupyterlite_badge_logo.svg @@ -0,0 +1,3 @@ + + +launchlaunchlitelite \ No newline at end of file diff --git a/pr-preview/pr-276/_static/language_data.js b/pr-preview/pr-276/_static/language_data.js new file mode 100644 index 000000000..250f5665f --- /dev/null +++ b/pr-preview/pr-276/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, is available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/pr-preview/pr-276/_static/minus.png b/pr-preview/pr-276/_static/minus.png new file mode 100644 index 000000000..d96755fda Binary files /dev/null and b/pr-preview/pr-276/_static/minus.png differ diff --git a/pr-preview/pr-276/_static/no_image.png b/pr-preview/pr-276/_static/no_image.png new file mode 100644 index 000000000..8c2d48d5d Binary files /dev/null and b/pr-preview/pr-276/_static/no_image.png differ diff --git a/pr-preview/pr-276/_static/plus.png b/pr-preview/pr-276/_static/plus.png new file mode 100644 index 000000000..7107cec93 Binary files /dev/null and b/pr-preview/pr-276/_static/plus.png differ diff --git a/pr-preview/pr-276/_static/pygments.css b/pr-preview/pr-276/_static/pygments.css new file mode 100644 index 000000000..02b4b1281 --- /dev/null +++ b/pr-preview/pr-276/_static/pygments.css @@ -0,0 +1,258 @@ +.highlight pre { line-height: 125%; } +.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #204a87; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #ce5c00; font-weight: bold } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000000; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #000000; font-style: italic } /* Generic.Output */ +.highlight .gp { color: #8f5902 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #204a87 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8 } /* Text.Whitespace */ +.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */ +.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ +.highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ +.highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ +.highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ +@media not print { +body[data-theme="dark"] .highlight pre { line-height: 125%; } +body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight .hll { background-color: #404040 } +body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 } +body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */ +body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */ +body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */ +body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */ +body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */ +body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */ +body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */ +body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body[data-theme="dark"] .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body[data-theme="dark"] .highlight .gd { color: #ff3a3a } /* Generic.Deleted */ +body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body[data-theme="dark"] .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body[data-theme="dark"] .highlight .gr { color: #ff3a3a } /* Generic.Error */ +body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */ +body[data-theme="dark"] .highlight .go { color: #cccccc } /* Generic.Output */ +body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body[data-theme="dark"] .highlight .gt { color: #ff3a3a } /* Generic.Traceback */ +body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */ +body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */ +body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */ +body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */ +body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */ +body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */ +body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */ +body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body[data-theme="dark"] .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body[data-theme="dark"] .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body[data-theme="dark"] .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body[data-theme="dark"] .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body[data-theme="dark"] .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body[data-theme="dark"] .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +@media (prefers-color-scheme: dark) { +body:not([data-theme="light"]) .highlight pre { line-height: 125%; } +body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight .hll { background-color: #404040 } +body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 } +body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */ +body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */ +body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */ +body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */ +body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */ +body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */ +body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */ +body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body:not([data-theme="light"]) .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body:not([data-theme="light"]) .highlight .gd { color: #ff3a3a } /* Generic.Deleted */ +body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body:not([data-theme="light"]) .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body:not([data-theme="light"]) .highlight .gr { color: #ff3a3a } /* Generic.Error */ +body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */ +body:not([data-theme="light"]) .highlight .go { color: #cccccc } /* Generic.Output */ +body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body:not([data-theme="light"]) .highlight .gt { color: #ff3a3a } /* Generic.Traceback */ +body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */ +body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */ +body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */ +body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */ +body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */ +body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */ +body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */ +body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body:not([data-theme="light"]) .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body:not([data-theme="light"]) .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body:not([data-theme="light"]) .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body:not([data-theme="light"]) .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body:not([data-theme="light"]) .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body:not([data-theme="light"]) .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +} +} \ No newline at end of file diff --git a/pr-preview/pr-276/_static/scripts/furo-extensions.js b/pr-preview/pr-276/_static/scripts/furo-extensions.js new file mode 100644 index 000000000..e69de29bb diff --git a/pr-preview/pr-276/_static/scripts/furo.js b/pr-preview/pr-276/_static/scripts/furo.js new file mode 100644 index 000000000..32e7c05be --- /dev/null +++ b/pr-preview/pr-276/_static/scripts/furo.js @@ -0,0 +1,3 @@ +/*! For license information please see furo.js.LICENSE.txt */ +(()=>{var t={212:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort((function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,(function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})})),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame((function(){r(a),v.detect()}))};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,(function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}})),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(212),e=n.n(t),o=null,r=null,c=window.pageYOffset||document.documentElement.scrollTop;const s=64;function l(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function a(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",l)}))}(),function(){let t=0,e=!1;window.addEventListener("scroll",(function(n){t=window.scrollY,e||(window.requestAnimationFrame((function(){var n;n=t,0==Math.floor(r.getBoundingClientRect().top)?r.classList.add("scrolled"):r.classList.remove("scrolled"),function(t){tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1})),e=!0)})),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);return r.getBoundingClientRect().height+.5*t+1}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),a()}))})()})(); +//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/pr-preview/pr-276/_static/scripts/furo.js.LICENSE.txt b/pr-preview/pr-276/_static/scripts/furo.js.LICENSE.txt new file mode 100644 index 000000000..1632189c7 --- /dev/null +++ b/pr-preview/pr-276/_static/scripts/furo.js.LICENSE.txt @@ -0,0 +1,7 @@ +/*! + * gumshoejs v5.1.2 (patched by @pradyunsg) + * A simple, framework-agnostic scrollspy script. + * (c) 2019 Chris Ferdinandi + * MIT License + * http://github.com/cferdinandi/gumshoe + */ diff --git a/pr-preview/pr-276/_static/scripts/furo.js.map b/pr-preview/pr-276/_static/scripts/furo.js.map new file mode 100644 index 000000000..7b7ddb113 --- /dev/null +++ b/pr-preview/pr-276/_static/scripts/furo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/furo.js","mappings":"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","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n return header.getBoundingClientRect().height + 0.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","floor","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","height"],"sourceRoot":""} \ No newline at end of file diff --git a/pr-preview/pr-276/_static/searchtools.js b/pr-preview/pr-276/_static/searchtools.js new file mode 100644 index 000000000..7918c3fab --- /dev/null +++ b/pr-preview/pr-276/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/pr-preview/pr-276/_static/sg_gallery-binder.css b/pr-preview/pr-276/_static/sg_gallery-binder.css new file mode 100644 index 000000000..420005d22 --- /dev/null +++ b/pr-preview/pr-276/_static/sg_gallery-binder.css @@ -0,0 +1,11 @@ +/* CSS for binder integration */ + +div.binder-badge { + margin: 1em auto; + vertical-align: middle; +} + +div.lite-badge { + margin: 1em auto; + vertical-align: middle; +} diff --git a/pr-preview/pr-276/_static/sg_gallery-dataframe.css b/pr-preview/pr-276/_static/sg_gallery-dataframe.css new file mode 100644 index 000000000..fac74c43b --- /dev/null +++ b/pr-preview/pr-276/_static/sg_gallery-dataframe.css @@ -0,0 +1,47 @@ +/* Pandas dataframe css */ +/* Taken from: https://github.com/spatialaudio/nbsphinx/blob/fb3ba670fc1ba5f54d4c487573dbc1b4ecf7e9ff/src/nbsphinx.py#L587-L619 */ +html[data-theme="light"] { + --sg-text-color: #000; + --sg-tr-odd-color: #f5f5f5; + --sg-tr-hover-color: rgba(66, 165, 245, 0.2); +} +html[data-theme="dark"] { + --sg-text-color: #fff; + --sg-tr-odd-color: #373737; + --sg-tr-hover-color: rgba(30, 81, 122, 0.2); +} + +table.dataframe { + border: none !important; + border-collapse: collapse; + border-spacing: 0; + border-color: transparent; + color: var(--sg-text-color); + font-size: 12px; + table-layout: fixed; + width: auto; +} +table.dataframe thead { + border-bottom: 1px solid var(--sg-text-color); + vertical-align: bottom; +} +table.dataframe tr, +table.dataframe th, +table.dataframe td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} +table.dataframe th { + font-weight: bold; +} +table.dataframe tbody tr:nth-child(odd) { + background: var(--sg-tr-odd-color); +} +table.dataframe tbody tr:hover { + background: var(--sg-tr-hover-color); +} diff --git a/pr-preview/pr-276/_static/sg_gallery-rendered-html.css b/pr-preview/pr-276/_static/sg_gallery-rendered-html.css new file mode 100644 index 000000000..93dc2ffb0 --- /dev/null +++ b/pr-preview/pr-276/_static/sg_gallery-rendered-html.css @@ -0,0 +1,224 @@ +/* Adapted from notebook/static/style/style.min.css */ +html[data-theme="light"] { + --sg-text-color: #000; + --sg-background-color: #ffffff; + --sg-code-background-color: #eff0f1; + --sg-tr-hover-color: rgba(66, 165, 245, 0.2); + --sg-tr-odd-color: #f5f5f5; +} +html[data-theme="dark"] { + --sg-text-color: #fff; + --sg-background-color: #121212; + --sg-code-background-color: #2f2f30; + --sg-tr-hover-color: rgba(66, 165, 245, 0.2); + --sg-tr-odd-color: #1f1f1f; +} + +.rendered_html { + color: var(--sg-text-color); + /* any extras will just be numbers: */ +} +.rendered_html em { + font-style: italic; +} +.rendered_html strong { + font-weight: bold; +} +.rendered_html u { + text-decoration: underline; +} +.rendered_html :link { + text-decoration: underline; +} +.rendered_html :visited { + text-decoration: underline; +} +.rendered_html h1 { + font-size: 185.7%; + margin: 1.08em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h2 { + font-size: 157.1%; + margin: 1.27em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h3 { + font-size: 128.6%; + margin: 1.55em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h4 { + font-size: 100%; + margin: 2em 0 0 0; + font-weight: bold; + line-height: 1.0; +} +.rendered_html h5 { + font-size: 100%; + margin: 2em 0 0 0; + font-weight: bold; + line-height: 1.0; + font-style: italic; +} +.rendered_html h6 { + font-size: 100%; + margin: 2em 0 0 0; + font-weight: bold; + line-height: 1.0; + font-style: italic; +} +.rendered_html h1:first-child { + margin-top: 0.538em; +} +.rendered_html h2:first-child { + margin-top: 0.636em; +} +.rendered_html h3:first-child { + margin-top: 0.777em; +} +.rendered_html h4:first-child { + margin-top: 1em; +} +.rendered_html h5:first-child { + margin-top: 1em; +} +.rendered_html h6:first-child { + margin-top: 1em; +} +.rendered_html ul:not(.list-inline), +.rendered_html ol:not(.list-inline) { + padding-left: 2em; +} +.rendered_html ul { + list-style: disc; +} +.rendered_html ul ul { + list-style: square; + margin-top: 0; +} +.rendered_html ul ul ul { + list-style: circle; +} +.rendered_html ol { + list-style: decimal; +} +.rendered_html ol ol { + list-style: upper-alpha; + margin-top: 0; +} +.rendered_html ol ol ol { + list-style: lower-alpha; +} +.rendered_html ol ol ol ol { + list-style: lower-roman; +} +.rendered_html ol ol ol ol ol { + list-style: decimal; +} +.rendered_html * + ul { + margin-top: 1em; +} +.rendered_html * + ol { + margin-top: 1em; +} +.rendered_html hr { + color: var(--sg-text-color); + background-color: var(--sg-text-color); +} +.rendered_html pre { + margin: 1em 2em; + padding: 0px; + background-color: var(--sg-background-color); +} +.rendered_html code { + background-color: var(--sg-code-background-color); +} +.rendered_html p code { + padding: 1px 5px; +} +.rendered_html pre code { + background-color: var(--sg-background-color); +} +.rendered_html pre, +.rendered_html code { + border: 0; + color: var(--sg-text-color); + font-size: 100%; +} +.rendered_html blockquote { + margin: 1em 2em; +} +.rendered_html table { + margin-left: auto; + margin-right: auto; + border: none; + border-collapse: collapse; + border-spacing: 0; + color: var(--sg-text-color); + font-size: 12px; + table-layout: fixed; +} +.rendered_html thead { + border-bottom: 1px solid var(--sg-text-color); + vertical-align: bottom; +} +.rendered_html tr, +.rendered_html th, +.rendered_html td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} +.rendered_html th { + font-weight: bold; +} +.rendered_html tbody tr:nth-child(odd) { + background: var(--sg-tr-odd-color); +} +.rendered_html tbody tr:hover { + color: var(--sg-text-color); + background: var(--sg-tr-hover-color); +} +.rendered_html * + table { + margin-top: 1em; +} +.rendered_html p { + text-align: left; +} +.rendered_html * + p { + margin-top: 1em; +} +.rendered_html img { + display: block; + margin-left: auto; + margin-right: auto; +} +.rendered_html * + img { + margin-top: 1em; +} +.rendered_html img, +.rendered_html svg { + max-width: 100%; + height: auto; +} +.rendered_html img.unconfined, +.rendered_html svg.unconfined { + max-width: none; +} +.rendered_html .alert { + margin-bottom: initial; +} +.rendered_html * + .alert { + margin-top: 1em; +} +[dir="rtl"] .rendered_html p { + text-align: right; +} diff --git a/pr-preview/pr-276/_static/sg_gallery.css b/pr-preview/pr-276/_static/sg_gallery.css new file mode 100644 index 000000000..72227837d --- /dev/null +++ b/pr-preview/pr-276/_static/sg_gallery.css @@ -0,0 +1,342 @@ +/* +Sphinx-Gallery has compatible CSS to fix default sphinx themes +Tested for Sphinx 1.3.1 for all themes: default, alabaster, sphinxdoc, +scrolls, agogo, traditional, nature, haiku, pyramid +Tested for Read the Docs theme 0.1.7 */ + +/* Define light colors */ +:root, html[data-theme="light"], body[data-theme="light"]{ + --sg-tooltip-foreground: black; + --sg-tooltip-background: rgba(250, 250, 250, 0.9); + --sg-tooltip-border: #ccc transparent; + --sg-thumb-box-shadow-color: #6c757d40; + --sg-thumb-hover-border: #0069d9; + --sg-script-out: #888; + --sg-script-pre: #fafae2; + --sg-pytb-foreground: #000; + --sg-pytb-background: #ffe4e4; + --sg-pytb-border-color: #f66; + --sg-download-a-background-color: #ffc; + --sg-download-a-background-image: linear-gradient(to bottom, #ffc, #d5d57e); + --sg-download-a-border-color: 1px solid #c2c22d; + --sg-download-a-color: #000; + --sg-download-a-hover-background-color: #d5d57e; + --sg-download-a-hover-box-shadow-1: rgba(255, 255, 255, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(0, 0, 0, 0.25); +} +@media(prefers-color-scheme: light) { + :root[data-theme="auto"], html[data-theme="auto"], body[data-theme="auto"] { + --sg-tooltip-foreground: black; + --sg-tooltip-background: rgba(250, 250, 250, 0.9); + --sg-tooltip-border: #ccc transparent; + --sg-thumb-box-shadow-color: #6c757d40; + --sg-thumb-hover-border: #0069d9; + --sg-script-out: #888; + --sg-script-pre: #fafae2; + --sg-pytb-foreground: #000; + --sg-pytb-background: #ffe4e4; + --sg-pytb-border-color: #f66; + --sg-download-a-background-color: #ffc; + --sg-download-a-background-image: linear-gradient(to bottom, #ffc, #d5d57e); + --sg-download-a-border-color: 1px solid #c2c22d; + --sg-download-a-color: #000; + --sg-download-a-hover-background-color: #d5d57e; + --sg-download-a-hover-box-shadow-1: rgba(255, 255, 255, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(0, 0, 0, 0.25); + } +} + +html[data-theme="dark"], body[data-theme="dark"] { + --sg-tooltip-foreground: white; + --sg-tooltip-background: rgba(10, 10, 10, 0.9); + --sg-tooltip-border: #333 transparent; + --sg-thumb-box-shadow-color: #79848d40; + --sg-thumb-hover-border: #003975; + --sg-script-out: rgb(179, 179, 179); + --sg-script-pre: #2e2e22; + --sg-pytb-foreground: #fff; + --sg-pytb-background: #1b1717; + --sg-pytb-border-color: #622; + --sg-download-a-background-color: #443; + --sg-download-a-background-image: linear-gradient(to bottom, #443, #221); + --sg-download-a-border-color: 1px solid #3a3a0d; + --sg-download-a-color: #fff; + --sg-download-a-hover-background-color: #616135; + --sg-download-a-hover-box-shadow-1: rgba(0, 0, 0, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(255, 255, 255, 0.25); +} +@media(prefers-color-scheme: dark){ + html[data-theme="auto"], body[data-theme="auto"] { + --sg-tooltip-foreground: white; + --sg-tooltip-background: rgba(10, 10, 10, 0.9); + --sg-tooltip-border: #333 transparent; + --sg-thumb-box-shadow-color: #79848d40; + --sg-thumb-hover-border: #003975; + --sg-script-out: rgb(179, 179, 179); + --sg-script-pre: #2e2e22; + --sg-pytb-foreground: #fff; + --sg-pytb-background: #1b1717; + --sg-pytb-border-color: #622; + --sg-download-a-background-color: #443; + --sg-download-a-background-image: linear-gradient(to bottom, #443, #221); + --sg-download-a-border-color: 1px solid #3a3a0d; + --sg-download-a-color: #fff; + --sg-download-a-hover-background-color: #616135; + --sg-download-a-hover-box-shadow-1: rgba(0, 0, 0, 0.1); + --sg-download-a-hover-box-shadow-2: rgba(255, 255, 255, 0.25); + } +} + +.sphx-glr-thumbnails { + width: 100%; + margin: 0px 0px 20px 0px; + + /* align thumbnails on a grid */ + justify-content: space-between; + display: grid; + /* each grid column should be at least 160px (this will determine + the actual number of columns) and then take as much of the + remaining width as possible */ + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 15px; +} +.sphx-glr-thumbnails .toctree-wrapper { + /* hide empty toctree divs added to the DOM + by sphinx even though the toctree is hidden + (they would fill grid places with empty divs) */ + display: none; +} +.sphx-glr-thumbcontainer { + background: transparent; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + box-shadow: 0 0 10px var(--sg-thumb-box-shadow-color); + + /* useful to absolutely position link in div */ + position: relative; + + /* thumbnail width should include padding and borders + and take all available space */ + box-sizing: border-box; + width: 100%; + padding: 10px; + border: 1px solid transparent; + + /* align content in thumbnail */ + display: flex; + flex-direction: column; + align-items: center; + gap: 7px; +} +.sphx-glr-thumbcontainer p { + position: absolute; + top: 0; + left: 0; +} +.sphx-glr-thumbcontainer p, +.sphx-glr-thumbcontainer p a { + /* link should cover the whole thumbnail div */ + width: 100%; + height: 100%; +} +.sphx-glr-thumbcontainer p a span { + /* text within link should be masked + (we are just interested in the href) */ + display: none; +} +.sphx-glr-thumbcontainer:hover { + border: 1px solid; + border-color: var(--sg-thumb-hover-border); + cursor: pointer; +} +.sphx-glr-thumbcontainer a.internal { + bottom: 0; + display: block; + left: 0; + box-sizing: border-box; + padding: 150px 10px 0; + position: absolute; + right: 0; + top: 0; +} +/* Next one is to avoid Sphinx traditional theme to cover all the +thumbnail with its default link Background color */ +.sphx-glr-thumbcontainer a.internal:hover { + background-color: transparent; +} + +.sphx-glr-thumbcontainer p { + margin: 0 0 0.1em 0; +} +.sphx-glr-thumbcontainer .figure { + margin: 10px; + width: 160px; +} +.sphx-glr-thumbcontainer img { + display: inline; + max-height: 112px; + max-width: 160px; +} +.sphx-glr-thumbcontainer[tooltip]:hover:after { + background: var(--sg-tooltip-background); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + color: var(--sg-tooltip-foreground); + content: attr(tooltip); + padding: 10px; + z-index: 98; + width: 100%; + height: 100%; + position: absolute; + pointer-events: none; + top: 0; + box-sizing: border-box; + overflow: hidden; + backdrop-filter: blur(3px); +} + +.sphx-glr-script-out { + color: var(--sg-script-out); + display: flex; + gap: 0.5em; +} +.sphx-glr-script-out::before { + content: "Out:"; + /* These numbers come from the pre style in the pydata sphinx theme. This + * turns out to match perfectly on the rtd theme, but be a bit too low for + * the pydata sphinx theme. As I could not find a dimension to use that was + * scaled the same way, I just picked one option that worked pretty close for + * both. */ + line-height: 1.4; + padding-top: 10px; +} +.sphx-glr-script-out .highlight { + background-color: transparent; + /* These options make the div expand... */ + flex-grow: 1; + /* ... but also keep it from overflowing its flex container. */ + overflow: auto; +} +.sphx-glr-script-out .highlight pre { + background-color: var(--sg-script-pre); + border: 0; + max-height: 30em; + overflow: auto; + padding-left: 1ex; + /* This margin is necessary in the pydata sphinx theme because pre has a box + * shadow which would be clipped by the overflow:auto in the parent div + * above. */ + margin: 2px; + word-break: break-word; +} +.sphx-glr-script-out + p { + margin-top: 1.8em; +} +blockquote.sphx-glr-script-out { + margin-left: 0pt; +} +.sphx-glr-script-out.highlight-pytb .highlight pre { + color: var(--sg-pytb-foreground); + background-color: var(--sg-pytb-background); + border: 1px solid var(--sg-pytb-border-color); + margin-top: 10px; + padding: 7px; +} + +div.sphx-glr-footer { + text-align: center; +} + +div.sphx-glr-download { + margin: 1em auto; + vertical-align: middle; +} + +div.sphx-glr-download a { + background-color: var(--sg-download-a-background-color); + background-image: var(--sg-download-a-background-image); + border-radius: 4px; + border: 1px solid var(--sg-download-a-border-color); + color: var(--sg-download-a-color); + display: inline-block; + font-weight: bold; + padding: 1ex; + text-align: center; +} + +div.sphx-glr-download code.download { + display: inline-block; + white-space: normal; + word-break: normal; + overflow-wrap: break-word; + /* border and background are given by the enclosing 'a' */ + border: none; + background: none; +} + +div.sphx-glr-download a:hover { + box-shadow: inset 0 1px 0 var(--sg-download-a-hover-box-shadow-1), 0 1px 5px var(--sg-download-a-hover-box-shadow-2); + text-decoration: none; + background-image: none; + background-color: var(--sg-download-a-hover-background-color); +} + +.sphx-glr-example-title:target::before { + display: block; + content: ""; + margin-top: -50px; + height: 50px; + visibility: hidden; +} + +ul.sphx-glr-horizontal { + list-style: none; + padding: 0; +} +ul.sphx-glr-horizontal li { + display: inline; +} +ul.sphx-glr-horizontal img { + height: auto !important; +} + +.sphx-glr-single-img { + margin: auto; + display: block; + max-width: 100%; +} + +.sphx-glr-multi-img { + max-width: 42%; + height: auto; +} + +div.sphx-glr-animation { + margin: auto; + display: block; + max-width: 100%; +} +div.sphx-glr-animation .animation { + display: block; +} + +p.sphx-glr-signature a.reference.external { + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 3px; + font-size: 75%; + text-align: right; + margin-left: auto; + display: table; +} + +.sphx-glr-clear { + clear: both; +} + +a.sphx-glr-backref-instance { + text-decoration: none; +} diff --git a/pr-preview/pr-276/_static/skeleton.css b/pr-preview/pr-276/_static/skeleton.css new file mode 100644 index 000000000..467c878c6 --- /dev/null +++ b/pr-preview/pr-276/_static/skeleton.css @@ -0,0 +1,296 @@ +/* Some sane resets. */ +html { + height: 100%; +} + +body { + margin: 0; + min-height: 100%; +} + +/* All the flexbox magic! */ +body, +.sb-announcement, +.sb-content, +.sb-main, +.sb-container, +.sb-container__inner, +.sb-article-container, +.sb-footer-content, +.sb-header, +.sb-header-secondary, +.sb-footer { + display: flex; +} + +/* These order things vertically */ +body, +.sb-main, +.sb-article-container { + flex-direction: column; +} + +/* Put elements in the center */ +.sb-header, +.sb-header-secondary, +.sb-container, +.sb-content, +.sb-footer, +.sb-footer-content { + justify-content: center; +} +/* Put elements at the ends */ +.sb-article-container { + justify-content: space-between; +} + +/* These elements grow. */ +.sb-main, +.sb-content, +.sb-container, +article { + flex-grow: 1; +} + +/* Because padding making this wider is not fun */ +article { + box-sizing: border-box; +} + +/* The announcements element should never be wider than the page. */ +.sb-announcement { + max-width: 100%; +} + +.sb-sidebar-primary, +.sb-sidebar-secondary { + flex-shrink: 0; + width: 17rem; +} + +.sb-announcement__inner { + justify-content: center; + + box-sizing: border-box; + height: 3rem; + + overflow-x: auto; + white-space: nowrap; +} + +/* Sidebars, with checkbox-based toggle */ +.sb-sidebar-primary, +.sb-sidebar-secondary { + position: fixed; + height: 100%; + top: 0; +} + +.sb-sidebar-primary { + left: -17rem; + transition: left 250ms ease-in-out; +} +.sb-sidebar-secondary { + right: -17rem; + transition: right 250ms ease-in-out; +} + +.sb-sidebar-toggle { + display: none; +} +.sb-sidebar-overlay { + position: fixed; + top: 0; + width: 0; + height: 0; + + transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease; + + opacity: 0; + background-color: rgba(0, 0, 0, 0.54); +} + +#sb-sidebar-toggle--primary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"], +#sb-sidebar-toggle--secondary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] { + width: 100%; + height: 100%; + opacity: 1; + transition: width 0ms ease, height 0ms ease, opacity 250ms ease; +} + +#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary { + left: 0; +} +#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary { + right: 0; +} + +/* Full-width mode */ +.drop-secondary-sidebar-for-full-width-content + .hide-when-secondary-sidebar-shown { + display: none !important; +} +.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary { + display: none !important; +} + +/* Mobile views */ +.sb-page-width { + width: 100%; +} + +.sb-article-container, +.sb-footer-content__inner, +.drop-secondary-sidebar-for-full-width-content .sb-article, +.drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 100vw; +} + +.sb-article, +.match-content-width { + padding: 0 1rem; + box-sizing: border-box; +} + +@media (min-width: 32rem) { + .sb-article, + .match-content-width { + padding: 0 2rem; + } +} + +/* Tablet views */ +@media (min-width: 42rem) { + .sb-article-container { + width: auto; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 42rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 46rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 46rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 50rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 50rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Tablet views */ +@media (min-width: 59rem) { + .sb-sidebar-secondary { + position: static; + } + .hide-when-secondary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 63rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 67rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Desktop views */ +@media (min-width: 76rem) { + .sb-sidebar-primary { + position: static; + } + .hide-when-primary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} + +/* Full desktop views */ +@media (min-width: 80rem) { + .sb-article, + .match-content-width { + width: 46rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } +} + +@media (min-width: 84rem) { + .sb-article, + .match-content-width { + width: 50rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } +} + +@media (min-width: 88rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-page-width { + width: 88rem; + } +} diff --git a/pr-preview/pr-276/_static/sphinx_highlight.js b/pr-preview/pr-276/_static/sphinx_highlight.js new file mode 100644 index 000000000..8a96c69a1 --- /dev/null +++ b/pr-preview/pr-276/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/pr-preview/pr-276/_static/styles/furo-extensions.css b/pr-preview/pr-276/_static/styles/furo-extensions.css new file mode 100644 index 000000000..bc447f228 --- /dev/null +++ b/pr-preview/pr-276/_static/styles/furo-extensions.css @@ -0,0 +1,2 @@ +#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)} +/*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/pr-preview/pr-276/_static/styles/furo-extensions.css.map b/pr-preview/pr-276/_static/styles/furo-extensions.css.map new file mode 100644 index 000000000..9ba5637f9 --- /dev/null +++ b/pr-preview/pr-276/_static/styles/furo-extensions.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/pr-preview/pr-276/_static/styles/furo.css b/pr-preview/pr-276/_static/styles/furo.css new file mode 100644 index 000000000..3d29a218f --- /dev/null +++ b/pr-preview/pr-276/_static/styles/furo.css @@ -0,0 +1,2 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#646776;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2962ff;--color-brand-content:#2a5adf;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link--hover:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link-underline--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto,body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link);text-decoration-color:var(--color-link-underline--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}.sidebar-scroll::-webkit-scrollbar,.toc-scroll::-webkit-scrollbar,article[role=main] ::-webkit-scrollbar{height:.25rem;width:.25rem}.sidebar-scroll::-webkit-scrollbar-thumb,.toc-scroll::-webkit-scrollbar-thumb,article[role=main] ::-webkit-scrollbar-thumb{background-color:var(--color-foreground-border);border-radius:.125rem}body,html{background:var(--color-background-primary);color:var(--color-foreground-primary);height:100%}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{vertical-align:middle}.theme-toggle{background:transparent;border:none;cursor:pointer;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1rem;vertical-align:middle;width:1rem}.theme-toggle-header{float:left;padding:1rem .5rem}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1rem;width:1rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg{color:inherit;height:1rem;width:1rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms,height 0ms,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{fill:currentColor;display:inline-block;height:1rem;width:1rem}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.theme-toggle-header{display:block}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.25rem;width:1.25rem}:target{scroll-margin-top:var(--header-height)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}.content{margin-left:auto;margin-right:auto}}@media(max-width:52em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){.content{padding:0 1em}article aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:3.5rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}.versionmodified{font-style:italic}div.deprecated p,div.versionadded p,div.versionchanged p{margin-bottom:.125rem;margin-top:.125rem}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class] pre{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>p,div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}ol.arabic{list-style:decimal}ol.loweralpha{list-style:lower-alpha}ol.upperalpha{list-style:upper-alpha}ol.lowerroman{list-style:lower-roman}ol.upperroman{list-style:upper-roman}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}.table-wrapper{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23607D8B' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree .reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling.Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right} +/*# sourceMappingURL=furo.css.map*/ \ No newline at end of file diff --git a/pr-preview/pr-276/_static/styles/furo.css.map b/pr-preview/pr-276/_static/styles/furo.css.map new file mode 100644 index 000000000..d1dfb109d --- /dev/null +++ b/pr-preview/pr-276/_static/styles/furo.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo.css","mappings":"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0CACE,CAFF,kBACA,CACE,wEACA,CARA,YACA,CAKF,mBAFF,OAII,eACA,CAJF,iCAJE,cAGJ,CANI,oBAEA,CAKF,SAIE,2BADA,UACA,kBAGF,sCACA,CAFF,WACE,WACA,qCACE,gCACA,2EACA,sDAKJ,aACE,mDAII,CAJJ,6CAII,kEACA,iBACE,iDACA,+CACE,aACA,WADA,+BACA,uEANN,YACE,mDAEE,mBADF,0CACE,CADF,qBACE,0DACA,YACE,4DACA,sEANN,YACE,8CACA,kBADA,UACA,2CACE,2EACA,cACE,kEACA,mEANN,yBACE,4DACA,sBACE,+EAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCrEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAKA,oCAEA,yDAEE,gEAKF,+CC5FA,0EAGE,CACA,qDCLJ,+DAIE,sCAIA,kEACE,yBACA,2FAMA,gBACA,yGCbF,mBAOA,2MAIA,4HAYA,0DACE,8GAYF,8HAQE,mBAEA,6HAOF,YAGA,mIAME,eACA,CAFF,YAEE,4FAMJ,8BAEE,uBAYA,sCAEE,CAJF,oBAEA,CARA,wCAEA,CAHA,8BACA,CAFA,eACA,CAGA,wCAEA,CAEA,mDAIE,kCACE,6BACA,4CAKJ,kDAIA,eACE,aAGF,8BACE,uDACA,sCACA,cAEA,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","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto\n display: block\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace: \"SFMono-Regular\", Menlo, Consolas, Monaco,\n Liberation Mono, Lucida Console, monospace;\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 * #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #646776; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2962ff;\n --color-brand-content: #2a5adf;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link-underline--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #ffffffcc; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2b8cee;\n --color-brand-content: #368ce2;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n // Override Firefox scrollbar style\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n // Override Chrome scrollbar styles\n &::-webkit-scrollbar\n width: 0.25rem\n height: 0.25rem\n &::-webkit-scrollbar-thumb\n background-color: var(--color-foreground-border)\n border-radius: 0.125rem\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n vertical-align: middle\n\n.theme-toggle\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n vertical-align: middle\n height: 1rem\n width: 1rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n float: left\n padding: 1rem 0.5rem\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1rem\n width: 1rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page svg\n color: inherit\n height: 1rem\n width: 1rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $full-width - $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n .theme-toggle-header\n display: block\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: var(--header-height)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Center the page, and accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n .content\n margin-left: auto\n margin-right: auto\n\n@media (max-width: $content-width + 2* $content-padding)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n .content\n padding: 0 $content-padding--small\n // Don't float sidebars to the right.\n article aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","//\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 3.5rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\n.versionmodified\n font-style: italic\ndiv.versionadded, div.versionchanged, div.deprecated\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n pre\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > p,\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n",".table-wrapper\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n",":target\n scroll-margin-top: 0.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(0.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(0.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the