Skip to content

feat: Extend .frame() API with format and query parameters#2322

Open
Premkumar-2004 wants to merge 6 commits intoprobabl-ai:mainfrom
Premkumar-2004:issue-2161
Open

feat: Extend .frame() API with format and query parameters#2322
Premkumar-2004 wants to merge 6 commits intoprobabl-ai:mainfrom
Premkumar-2004:issue-2161

Conversation

@Premkumar-2004
Copy link
Contributor

Extended CoefficientsDisplay.frame() with three new parameters:

format ("long" | "wide" | "auto") - Controls output shape

long (default): Original format, one row per coefficient
wide: Features as rows, labels/splits as columns
auto: Wide for EstimatorReport/CV, long for ComparisonReport
aggregate (bool) - For CV reports, shows mean ± std instead of individual splits

query (dict) - Filter by column values, e.g., {"label": "setosa"}

@github-actions
Copy link
Contributor

github-actions bot commented Jan 23, 2026

Documentation preview @ ccec1da

@github-actions
Copy link
Contributor

github-actions bot commented Jan 23, 2026

Coverage

Coverage Report for skore/
FileStmtsMissCoverMissing
skore/src/skore
   __init__.py260100% 
   _config.py310100% 
   _login.py140100% 
   exceptions.py440%4, 15, 19, 23
skore/src/skore/_sklearn
   __init__.py60100% 
   _base.py73889%272–279
   feature_names.py260100% 
   find_ml_task.py610100% 
   types.py28196%30
skore/src/skore/_sklearn/_comparison
   __init__.py70100% 
   inspection_accessor.py27196%114
   metrics_accessor.py166298%179, 1138
   report.py111397%488, 491, 497
   utils.py570100% 
skore/src/skore/_sklearn/_cross_validation
   __init__.py90100% 
   data_accessor.py400100% 
   inspection_accessor.py20195%113
   metrics_accessor.py169597%1054, 1115, 1118, 1144–1145
   report.py125496%492, 502, 505, 511
skore/src/skore/_sklearn/_estimator
   __init__.py90100% 
   data_accessor.py61296%84, 188
   inspection_accessor.py70297%316, 330
   metrics_accessor.py367698%427, 431, 446, 476, 1661, 1744
   report.py158497%443–444, 463, 466
skore/src/skore/_sklearn/_plot
   __init__.py30100% 
   base.py57296%59–60
   utils.py121298%273–274
skore/src/skore/_sklearn/_plot/data
   __init__.py20100% 
   table_report.py175199%657
skore/src/skore/_sklearn/_plot/inspection
   __init__.py00100% 
   coefficients.py219199%294
   impurity_decrease.py61198%185
   permutation_importance.py1490100% 
   utils.py90100% 
skore/src/skore/_sklearn/_plot/metrics
   __init__.py60100% 
   confusion_matrix.py1560100% 
   metrics_summary_display.py80100% 
   precision_recall_curve.py1080100% 
   prediction_error.py1510100% 
   roc_curve.py1120100% 
skore/src/skore/_sklearn/train_test_split
   __init__.py00100% 
   train_test_split.py580100% 
skore/src/skore/_sklearn/train_test_split/warning
   __init__.py80100% 
   high_class_imbalance_too_few_examples_warning.py19194%83
   high_class_imbalance_warning.py200100% 
   random_state_unset_warning.py100100% 
   shuffle_true_warning.py90100% 
   stratify_is_set_warning.py100100% 
   time_based_column_warning.py210100% 
   train_test_split_warning.py30100% 
skore/src/skore/_utils
   __init__.py6266%8, 13
   _accessor.py112397%38, 214, 268
   _cache.py370100% 
   _environment.py32293%41, 44
   _fixes.py80100% 
   _index.py50100% 
   _logger.py22481%15–17, 19
   _measure_time.py100100% 
   _parallel.py38392%23, 33, 124
   _patch.py211242%30, 35–39, 42–43, 46–47, 58, 60
   _progress_bar.py42392%67–68, 80
   _show_versions.py380100% 
   _testing.py1031189%20, 29, 157, 166, 177–182, 184
skore/src/skore/_utils/repr
   __init__.py20100% 
   base.py540100% 
   data.py1630100% 
   html_repr.py380100% 
   rich_repr.py810100% 
skore/src/skore/project
   __init__.py20100% 
   _summary.py75198%119
   _widget.py1870100% 
   project.py560100% 
TOTAL42929297% 

Tests Skipped Failures Errors Time
1629 5 💤 0 ❌ 0 🔥 6m 9s ⏱️

@Premkumar-2004 Premkumar-2004 changed the title Extended the API of the .frame of display feat: Extend .frame() API with format and query parameters Jan 23, 2026
GaetandeCast

This comment was marked as outdated.

@Premkumar-2004

This comment was marked as outdated.

GaetandeCast

This comment was marked as outdated.

@GaetandeCast

This comment was marked as outdated.

@Premkumar-2004

This comment was marked as outdated.

@Premkumar-2004

This comment was marked as outdated.

GaetandeCast

This comment was marked as outdated.

@Premkumar-2004

This comment was marked as outdated.

GaetandeCast

This comment was marked as outdated.

@Premkumar-2004

This comment was marked as outdated.

GaetandeCast

This comment was marked as outdated.

@glemaitre
Copy link
Member

I would prefer that we exclude the aggregate and query feature for the moment. I would like to tackle them in a separate PR.

Regarding the format, I would expect "wide" to not work in the case of comparing cross-validation report with different split numbers.

If we support this case (I would need to test to be sure), then I would prefer to have a format="auto" that would choose format="wide" in priority and when not possible fallback on the "long" format.

@Premkumar-2004

This comment was marked as outdated.

@glemaitre

This comment was marked as outdated.

@glemaitre glemaitre self-requested a review January 28, 2026 17:08
@Premkumar-2004

This comment was marked as outdated.

Copy link
Member

@glemaitre glemaitre left a comment

Choose a reason for hiding this comment

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

So a couple of thoughts:

  • we will strive to not have multi-index in the return frame because it is complex to index
  • if we want to have be able to distinguish split/label/output/estimator, we need to prepend some names with what data it is.
  • for the comparison, we should further check if the features for each model have the same name (we should have a _has_same_features method) and not allow to go further if it is not the case.

So now, my question would be if we really allow wide format when we have more than a single column in the pivot_table because we will end-up with complex name.

@GaetandeCast What do you think.

@GaetandeCast
Copy link
Collaborator

I agree with the points you make @glemaitre and the solutions you propose.

To answer your question, the solution might be to use an "auto" option for the format parameter that uses wide when the number of columns is limited (I would say <= 2, it's still readable in that situation imo) and that switches to long otherwise. I would still want to let the user override that to force wide even if it's hard to read.

One could also make a point that in every case, the wide format is easier (but not necessarily easy) to read than long and therefore should be used by default.

Let me know what you think.

@Premkumar-2004

This comment was marked as outdated.

@Premkumar-2004

This comment was marked as outdated.

@Premkumar-2004

This comment was marked as outdated.

@Premkumar-2004 Premkumar-2004 marked this pull request as draft January 30, 2026 19:24
@Premkumar-2004 Premkumar-2004 marked this pull request as ready for review January 30, 2026 19:24
@Premkumar-2004
Copy link
Contributor Author

Premkumar-2004 commented Jan 30, 2026

I've completed the changes you requested (removing query and aggregate parameters, keeping only the format parameter with "wide"/"long" options). However, when I tried to rebase with the latest main branch, I encountered significant conflicts due to recent upstream changes:

PR #2346 renamed feature_importance to inspection
PR #2305 added select_k and sorting_order parameters to the frame() method
These changes conflict with my implementation since the file path changed and the frame() method signature is now different.

Could you please advise on how to proceed?
@GaetandeCast

@GaetandeCast
Copy link
Collaborator

GaetandeCast commented Feb 2, 2026

@Premkumar-2004 I've solved merge conflicts for you. However there still are some unfinished discussions here. Please refrain from making too many changes and asking for reviews until we are settled on those.

Let's tackle:

  • the two comments from @glemaitre that are not resolved.
  • if and how we should limit the wide format when there is too much information to display.
  • how to interact with the newly added sorting_order parameter. For now I put the changes of this PR after the sorting and/or selecting of the features but it's probably not the best thing to do. For the long format, the current sorting makes sense but in wide format does sorting even make sense ? Maybe if we sort by the average coefficient value across columns. Also the select_k feature will break the pivot table when the selection is different across groups.

@Premkumar-2004
Copy link
Contributor Author

Premkumar-2004 commented Feb 2, 2026

For the wide format issues:
If there are too many columns(like if columns > 20), we can throw an error telling users to switch to long format.
For sorting in wide format, sorting by mean coefficient value across columns makes sense to me.
For select_k and wide, we should error out if different groups end up with different features since the pivot won't work

@auguste-probabl auguste-probabl linked an issue Feb 5, 2026 that may be closed by this pull request
@auguste-probabl
Copy link
Collaborator

From a discussion with @GaetandeCast :

The most complex case we want to support is a ComparisonReport of CrossValidationReport:

from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from skore import train_test_split
from skore import ComparisonReport, CrossValidationReport

X, y = load_iris(return_X_y=True)
split_data = train_test_split(X=X, y=y, random_state=42, as_dict=True)
estimator_1 = LogisticRegression(max_iter=10000, random_state=42)
estimator_2 = LogisticRegression(max_iter=10000, random_state=43)
comparison_report = ComparisonReport(
    [CrossValidationReport(estimator_1, X, y), CrossValidationReport(estimator_2, X, y)]
)
comparison_report.inspection.coefficients().frame()

with the output as of right now:

estimator  LogisticRegression_1                                         LogisticRegression_2  ...
split                         0         1         2         3         4                    0  ...
label                         0         0         0         0         0                    0  ...
feature                                                                                       ...
Intercept              9.862255  9.490178  9.203867  9.097505  9.657548             9.862255  ...
Feature #2            -2.350558 -2.357779 -2.395413 -2.364760 -2.364974            -2.350558  ...
Feature #3            -0.994196 -1.014208 -1.012199 -1.023142 -0.975954            -0.994196  ...
Feature #1             0.847198  0.858916  0.907798  0.941640  0.849863             0.847198  ...
Feature #0            -0.485465 -0.425253 -0.385390 -0.395168 -0.430971            -0.485465  ...

That table is hard to read because it's so wide, and also hard to query because the columns are a MultiIndex.

Some thoughts:

  • When format="wide", aggregate by split by default, like we do in .metrics.summarize().
    • In the first iteration, force aggregation.
  • Flatten MultiIndex, as already mentioned by @glemaitre.

More generally, we need to decide what wide is for:

  • Prioritize readability (force more stuff, forbid more cases)? or
  • Prioritize query-ability (more configurable, less readable by default)?

I think, along with @GaetandeCast, that we should go for readability. The customizability need is already addressed by format="long".

So specifically for the Comparison[CV] case, it could look something like

estimator  LogisticRegression_1_label_0 LogisticRegression_2_label_0 LogisticRegression_1_label_1 ...
feature                                                                                           ...
Intercept              9.862255 ± 0.445             9.862255 ± 0.445             9.862255 ± 0.445 ...
Feature #2             9.862255 ± 0.445             9.862255 ± 0.445             9.862255 ± 0.445 ...
Feature #3             9.862255 ± 0.445             9.862255 ± 0.445             9.862255 ± 0.445 ...
Feature #1             9.862255 ± 0.445             9.862255 ± 0.445             9.862255 ± 0.445 ...
Feature #0             9.862255 ± 0.445             9.862255 ± 0.445             9.862255 ± 0.445 ...

There is a case to be made that the ± thing goes a step too far, but it's much more readable this way.

As for the default value for format, I'm aligned with @GaetandeCast in thinking it should be "wide". Displays are for displaying something meaningful and readable. For the most complex case, when the estimators have different features, it's a bit tough but I think we should make "wide" output the same thing as "long". The default parameters should not lead to an error if we can avoid it.

Later, the query and aggregate parameters can also help us get both: sane defaults, e.g. aggregate=("mean", "std"), but also customization for the user. This will be relevant e.g. when there are many classes.

@Premkumar-2004 Thanks for your work on this; I think it would be a bit faster to push on this PR to bring it to the finish line. Hope that's okay with you. Again your work is very much appreciated.

@Premkumar-2004
Copy link
Contributor Author

Sure, go ahead! Happy to help and thanks for the guidance on this. Let me know if you need anything else from my side.

@auguste-probabl auguste-probabl self-assigned this Feb 9, 2026
Premkumar-2004 and others added 5 commits February 10, 2026 17:15
Fix linting issues

feat(api): add format, aggregate, and query parameters to CoefficientsDisplay.frame()

Merge branch 'main' into issue-2161
feat: Extend .frame() API with format and query parameters

feat: Extend .frame() API with format and query parameters

Merge branch 'main' into issue-2161
feat: Extend .frame() API with format and query parameters

Merge branch 'main' into issue-2161
feat: Extend .frame() API with format and query parameters

feat: Extend .frame() API with format and query parameters

Merge branch 'main' into issue-2161
Merge branch 'main' into issue-2161
feat: Extend .frame() API with format and query parameters

feat: Extend .frame() API with format and query parameters

Merge branch 'main' into issue-2161
feat: Extend .frame() API with format and query parameters

Merge branch 'main' into issue-2161
feat: Extend .frame() API with format and query parameters

Merge branch 'main' into HEAD

fix merge

fix doctest

merge
@github-actions
Copy link
Contributor

Caution

Some commits in the pull request are not signed, or GitHub is not able to verify the signature.
Please sign all your commits; you can find more information here.
Please note that when you activate commit signing, you'll need to retroactively sign your previous commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

api(skore): Extend the API of the .frame of display

4 participants