-
Notifications
You must be signed in to change notification settings - Fork 9
Feature: fxplot Plotting Accessor #548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: fxplot Plotting Accessor #548
Conversation
…ation, reducing code duplication while maintaining the same functionality (data preparation, color resolution from components, PlotResult wrapping).
… area(), and duration_curve() methods in both DatasetPlotAccessor and DataArrayPlotAccessor
2. scatter() method - Plots two variables against each other with x and y parameters
3. pie() method - Creates pie charts from aggregated (scalar) dataset values, e.g. ds.sum('time').fxplot.pie()
4. duration_curve() method - Sorts values along the time dimension in descending order, with optional normalize parameter for percentage x-axis
5. CONFIG.Plotting.default_line_shape - New config option (default 'hv') that controls the default line shape for line(), area(), and duration_curve() methods
1. X-axis is now determined first using CONFIG.Plotting.x_dim_priority
2. Facets are resolved from remaining dimensions (x-axis excluded)
x_dim_priority expanded:
x_dim_priority = ('time', 'duration', 'duration_pct', 'period', 'scenario', 'cluster')
- Time-like dims first, then common grouping dims as fallback
- variable stays excluded (it's used for color, not x-axis)
_get_x_dim() refactored:
- Now takes dims: list[str] instead of a DataFrame
- More versatile - works with any list of dimension names
- Add `x` parameter to bar/stacked_bar/line/area for explicit x-axis control - Add CONFIG.Plotting.x_dim_priority for auto x-axis selection order - X-axis determined first, facets from remaining dimensions - Refactor _get_x_column -> _get_x_dim (takes dim list, not DataFrame) - Support scalar data (no dims) by using 'variable' as x-axis
- Add `x` parameter to bar/stacked_bar/line/area for explicit x-axis control
- Add CONFIG.Plotting.x_dim_priority for auto x-axis selection
Default: ('time', 'duration', 'duration_pct', 'period', 'scenario', 'cluster')
- X-axis determined first, facets resolved from remaining dimensions
- Refactor _get_x_column -> _get_x_dim (takes dim list, more versatile)
- Support scalar data (no dims) by using 'variable' as x-axis
- Skip color='variable' when x='variable' to avoid double encoding
- Fix _dataset_to_long_df to use dims (not just coords) as id_vars
- Add `x` parameter to bar/stacked_bar/line/area for explicit x-axis control
- Add CONFIG.Plotting.x_dim_priority for auto x-axis selection
Default: ('time', 'duration', 'duration_pct', 'period', 'scenario', 'cluster')
- X-axis determined first, facets resolved from remaining dimensions
- Refactor _get_x_column -> _get_x_dim (takes dim list, more versatile)
- Support scalar data (no dims) by using 'variable' as x-axis
- Skip color='variable' when x='variable' to avoid double encoding
- Fix _dataset_to_long_df to use dims (not just coords) as id_vars
- Ensure px_kwargs properly overrides all defaults (color, facets, etc.)
…wargs} so user can override 2. scatter unused colors - Removed the unused parameter 3. to_duration_curve sorting - Changed [::-1] to np.flip(..., axis=time_axis) for correct multi-dimensional handling 4. DataArrayPlotAccessor.heatmap - Same kwarg merge fix
📝 WalkthroughWalkthroughThis pull request introduces a comprehensive XArray accessor-based plotting API ( Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Accessor as fxplot Accessor
participant Helpers as Internal Helpers
participant PX as Plotly Express
participant Figure as go.Figure
User->>Accessor: ds.fxplot.line(x='auto', colors=...)
Accessor->>Helpers: _get_x_dim(dims, 'auto')
Helpers-->>Accessor: x_dim (from priority list)
rect rgba(200, 220, 255, 0.3)
note over Accessor,Helpers: Dimension Resolution
Accessor->>Helpers: _resolve_auto_facets(facet_col='auto', ...)
Helpers-->>Accessor: facet_col, facet_row, animation_frame
end
Accessor->>Helpers: _dataset_to_long_df(ds, value_name, var_name)
Helpers-->>Accessor: long_form_df
rect rgba(200, 240, 200, 0.3)
note over Accessor: Color & Layout Processing
Accessor->>Accessor: process_colors(colors, ...)
Accessor->>Accessor: apply CONFIG defaults
end
Accessor->>PX: px.line(data=df, x=x_dim, ..., facet_col, animation_frame)
PX-->>Figure: Figure (with facets/animation)
Figure-->>User: Figure
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
docs/notebooks/fxplot_accessor_demo.ipynb (1)
29-37: Document or configure the Plotly renderer for offline environments.The
'notebook_connected'renderer hardcoded in this cell requires internet access for CDN-based Plotly.js loading. While the docs build succeeds in CI because it setsPLOTLY_RENDERER=jsonas an environment variable (which overridespio.renderers.default), this creates a fragile, implicit dependency. Local documentation builds or other CI contexts without this environment variable would attempt CDN access.Consider either:
- Configuring
pio.renderers.defaultbased on the execution environment (e.g., usingPLOTLY_RENDERERor another environment variable)- Using
'notebook'(self-contained) instead for better portability- Documenting that
PLOTLY_RENDERER=jsonmust be set for offline buildsflixopt/dataset_plot_accessor.py (1)
30-68: Consider consolidating_resolve_auto_facetsto reduce duplication.This function is nearly identical to the one in
statistics_accessor.py(lines 183-234), with the addition ofexclude_dims. Consider:
- Moving the more complete version (this one with
exclude_dims) to a shared module- Having
statistics_accessor.pyimport from here or the shared moduleThis would reduce maintenance burden and ensure consistent behavior.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
docs/notebooks/fxplot_accessor_demo.ipynbdocs/user-guide/recipes/plotting-custom-data.mdflixopt/__init__.pyflixopt/config.pyflixopt/dataset_plot_accessor.pyflixopt/statistics_accessor.pymkdocs.yml
🧰 Additional context used
🧬 Code graph analysis (2)
flixopt/statistics_accessor.py (2)
flixopt/dataset_plot_accessor.py (6)
stacked_bar(192-268)stacked_bar(754-780)heatmap(423-486)heatmap(842-891)line(270-346)line(782-810)flixopt/clustering/base.py (1)
heatmap(660-794)
flixopt/dataset_plot_accessor.py (3)
flixopt/color_processing.py (1)
process_colors(165-233)flixopt/config.py (2)
CONFIG(185-916)Plotting(551-595)flixopt/statistics_accessor.py (6)
_resolve_auto_facets(183-234)sizes(464-468)sizes(1035-1078)sizes(1766-1830)_dataset_to_long_df(250-260)heatmap(1539-1669)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: test (3.14)
- GitHub Check: test (3.13)
- GitHub Check: Build documentation
🔇 Additional comments (31)
mkdocs.yml (1)
76-76: LGTM!The navigation entry for the new fxplot accessor demo notebook is correctly placed and follows the existing navigation structure.
flixopt/__init__.py (1)
17-19: LGTM!The side-effect import correctly registers the xarray accessors (.fxplot and .fxstats) at package initialization time. The comment clearly explains the purpose, and the noqa directive appropriately suppresses the unused import warning.
flixopt/config.py (4)
166-166: LGTM!The default line shape of
'hv'(horizontal-then-vertical step) is appropriate for time-series energy data where values typically represent constant rates or states over each time period.
169-169: LGTM!The x_dim_priority default provides a sensible hierarchy for automatic x-axis selection, prioritizing temporal dimensions (time, duration) followed by organizational dimensions (period, scenario, cluster).
592-595: LGTM!The new plotting configuration attributes are correctly defined with appropriate type hints and default values from the _DEFAULTS mapping.
696-699: LGTM!Both new plotting configuration parameters are correctly included in the to_dict() serialization method, maintaining consistency with the class attributes.
docs/notebooks/fxplot_accessor_demo.ipynb (3)
1-27: LGTM!Clear introduction and setup that imports necessary dependencies and displays the flixopt version for reproducibility.
39-68: LGTM!Clear section introducing sample data creation with a simple time-series dataset. The use of
np.random.seed(42)ensures reproducibility.
70-541: LGTM!Excellent comprehensive demonstration of the fxplot accessor functionality. The examples progress logically from basic plots to advanced features including:
- All major plot types (line, bar, stacked_bar, area, heatmap, scatter, pie, duration_curve)
- Automatic faceting and animation with clear explanations
- Customization options (colors, axis labels, facet control)
- Integration with xarray operations (filtering, selection)
- DataArray accessor usage
- Chaining with Plotly methods
The narrative structure and comments make it easy for users to understand and adapt the examples.
docs/user-guide/recipes/plotting-custom-data.md (3)
3-3: LGTM!The introduction correctly describes the purpose of the .fxplot accessor and its relationship to the optimization results plot accessor.
5-19: LGTM!The quick example is concise and clearly demonstrates the key advantage of the fxplot accessor - direct plotting without DataFrame conversion. The progression from creating a dataset to plotting it is intuitive.
21-29: Notebook link is correct and accessible.The relative path
../../notebooks/fxplot_accessor_demo.ipynbresolves correctly to the existing notebook filedocs/notebooks/fxplot_accessor_demo.ipynb.flixopt/statistics_accessor.py (6)
1399-1406: LGTM - Clean delegation to fxplot API.The
balancemethod correctly delegates tods.fxplot.stacked_bar()with pre-resolved facet dimensions. The title, colors, and facet parameters are properly passed through.
1523-1530: LGTM - Consistent pattern for carrier_balance.Same clean delegation pattern as
balance(). Pre-resolved facets and colors are correctly forwarded.
1656-1661: LGTM - Heatmap delegation via DataArray accessor.Correctly uses
da.fxplot.heatmap()on the DataArray with pre-resolved facet and animation dimensions.
1750-1757: LGTM - flows() method delegation.Correctly delegates to
ds.fxplot.line()for flow visualization.
1926-1933: LGTM - duration_curve() delegation.Correctly uses
result_ds.fxplot.line()for the duration curve visualization.
2145-2152: LGTM - charge_states() delegation.Correctly delegates to
ds.fxplot.line()for charge state visualization.flixopt/dataset_plot_accessor.py (13)
16-28: LGTM - Clean x-dimension selection helper.The
_get_x_dimfunction correctly implements priority-based dimension selection with a sensible fallback to'variable'for scalar data.
71-81: Slight duplication withstatistics_accessor.py, but implementation differs appropriately.This version correctly uses
ds.dimsforid_vars(line 80), whilestatistics_accessor.pyusesds.coords.keys()(line 259). The comment explains the reasoning well. If consolidating, this version's approach is more robust for handling dimensions without explicit coordinates.
119-191: LGTM - Grouped bar chart implementation.The
barmethod correctly implements dimension resolution, color processing, and faceting. The check on line 173 to avoid coloring by variable when it's on the x-axis is a good edge case handling.
192-268: LGTM - Stacked bar with relative barmode.The
stacked_barmethod correctly setsbarmode='relative'for proper positive/negative stacking. Layout updates for gap removal are appropriate for continuous stacked bars.
270-346: LGTM - Line chart with configurable shape.The
linemethod correctly appliesCONFIG.Plotting.default_line_shapewhen no explicit shape is provided.
348-421: LGTM - Area chart implementation.Consistent with line chart pattern, correctly uses
px.areafor stacked areas.
423-486: LGTM - Heatmap with auto variable selection.Good UX: auto-selects the variable if only one exists, provides clear error message otherwise. Using
px.imshowdirectly with xarray DataArray is the correct approach.
488-554: LGTM - Scatter plot between two variables.Clean implementation requiring explicit x and y variable names. Good validation with informative error messages.
556-636: LGTM - Pie chart with scalar and multi-dimensional support.Good handling of both scalar (single pie) and multi-dimensional (faceted pies) cases. The scalar case detection via
max_ndim == 0is elegant.
639-696: LGTM - Duration curve transformation accessor.The
to_duration_curvemethod correctly:
- Sorts values descending along the time axis
- Handles multi-dimensional arrays properly via
axis=time_axis- Renames the time dimension to
duration_pctordurationbased on normalization
698-752: LGTM - DataArray accessor with delegation pattern.Clean implementation that converts DataArray to Dataset for plotting. The
_to_datasethelper correctly handles unnamed DataArrays by defaulting to'value'.
754-840: LGTM - Delegating methods for DataArray.The
stacked_bar,line, andareamethods correctly forward all parameters to the Dataset accessor.
842-891: LGTM - DataArray-native heatmap implementation.Good design choice to implement heatmap directly rather than delegating, since
px.imshowworks optimally with DataArray. The implementation is consistent withDatasetPlotAccessor.heatmap.
flixopt/config.py
Outdated
| x_dim_priority: Order of dimensions to prefer for x-axis when 'auto'. | ||
| Default: ('time', 'duration', 'duration_pct'). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct the docstring to match the actual default value.
The docstring states the default is ('time', 'duration', 'duration_pct'), but the actual default at line 169 includes six dimensions: ('time', 'duration', 'duration_pct', 'period', 'scenario', 'cluster').
🔎 Proposed fix
- x_dim_priority: Order of dimensions to prefer for x-axis when 'auto'.
- Default: ('time', 'duration', 'duration_pct').
+ x_dim_priority: Order of dimensions to prefer for x-axis when 'auto'.
+ Default: ('time', 'duration', 'duration_pct', 'period', 'scenario', 'cluster').📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| x_dim_priority: Order of dimensions to prefer for x-axis when 'auto'. | |
| Default: ('time', 'duration', 'duration_pct'). | |
| x_dim_priority: Order of dimensions to prefer for x-axis when 'auto'. | |
| Default: ('time', 'duration', 'duration_pct', 'period', 'scenario', 'cluster'). |
🤖 Prompt for AI Agents
In flixopt/config.py around lines 569-570 (actual default tuple defined at line
169), the docstring incorrectly lists x_dim_priority default as ('time',
'duration', 'duration_pct') while the true default is ('time', 'duration',
'duration_pct', 'period', 'scenario', 'cluster'); update the docstring to
exactly match the actual default tuple at line 169 by replacing the shorter
tuple with the full six-element tuple so the docs and code stay consistent.
…ork-v2+plotting # Conflicts: # docs/notebooks/08a-aggregation.ipynb # docs/notebooks/08b-rolling-horizon.ipynb # docs/notebooks/08c-clustering.ipynb # docs/notebooks/08c2-clustering-storage-modes.ipynb # docs/notebooks/08d-clustering-multiperiod.ipynb # docs/notebooks/08e-clustering-internals.ipynb
… it, causing the NetCDF write failure in CI
Add new dataset plotting accessor for enhanced visualization: - fxplot accessor for xarray datasets - Improved plotting defaults and styling - Animation frame handling improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add new dataset plotting accessor for enhanced visualization: - fxplot accessor for xarray datasets - Improved plotting defaults and styling - Animation frame handling improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…nges: Changes Made 1. CHANGELOG.md (line 145) - Before: #### FXPlot Accessor (#548) - After: #### Plotly Accessor (#548) 2. CHANGELOG.md (lines 169-170) - Before: Two duplicate .plotly.bar() entries (grouped/stacked) - After: Single entry: .plotly.bar() | Bar charts (use barmode='group' or 'relative' for stacked) 3. flixopt/stats_accessor.py - to_duration_curve() (lines 43-66) - Before: Used da.values (breaks dask laziness) and tuple assignment (loses attrs) - After: Uses xr.apply_ufunc() with dask='parallelized' to preserve: - Dask lazy evaluation - DataArray attributes - Dataset attributes - Variables without time dimension (unchanged) 4. docs/user-guide/recipes/plotting-custom-data.md - Added note explaining that .plotly and .fxstats accessors are automatically registered on import
* Remove fxplot accessor in favor of external ploty accessor
* Moved stats accessor to new module name
* Reuse fxstats.duration_curve
* Update notebooks
* Update notebooks
* Remove auto
* Set x='time' for most plots
* Remove slot params
* Improve color handling
* 1. statistics_accessor.py
- Removed explicit slot parameters (facet_col, facet_row, animation_frame) from all plotting methods
- Added _build_color_kwargs() helper to consistently handle dict, list, and string colors
- Refactored all methods to use _build_color_kwargs for consistent color handling
- Updated _prepare_for_heatmap() to support reshape='auto' (default)
- Slot parameters are now passed through via **plotly_kwargs
2. clustering/base.py
- Removed explicit slot parameters from compare() and heatmap() methods
- Parameters passed through via **plotly_kwargs
3. comparison.py
- Completely refactored ComparisonStatisticsPlot:
- Removed the complex generic _plot() method
- Each method now has explicit parameters with proper docstrings
- Uses _build_color_kwargs consistently
- Changed ds.fxplot to ds.plotly
- Changed stacked_bar to bar with barmode='relative'
Key Design Patterns
1. Color handling: All methods use _build_color_kwargs(colors, labels) which handles:
- dict → color_discrete_map
- list → color_discrete_sequence
- str (colorscale name) → converted to color_discrete_map via process_colors()
2. Dimension slots: Let xarray-plotly handle assignments by default, only set explicitly when needed (e.g., x='time' for time series)
3. Consistent API: All methods now have explicit parameters instead of **kwargs everywhere
* ⏺ All done! Here's a summary of the changes:
Changes to flixopt/comparison.py:
1. Added import: from xarray_plotly import SLOT_ORDERS
2. Added helper function:
_CASE_SLOTS = frozenset(slot for slots in SLOT_ORDERS.values() for slot in slots)
def _set_case_default(kwargs: dict, slot: str) -> None:
"""Set default slot for 'case' dimension if not already assigned elsewhere."""
if not any(kwargs.get(s) == 'case' for s in _CASE_SLOTS):
kwargs.setdefault(slot, 'case')
3. Updated all 9 plotting methods with sensible defaults:
| Method | Default Slot |
|-------------------|--------------------------------|
| balance() | facet_col='case' |
| carrier_balance() | facet_col='case' |
| storage() | facet_col='case' |
| flows() | line_dash='case' |
| charge_states() | line_dash='case' |
| duration_curve() | line_dash='case' |
| sizes() | color='case' + barmode='group' |
| effects() | color='case' + barmode='group' |
| heatmap() | facet_col='case' |
The defaults are "safe" - if the user explicitly assigns 'case' to any slot, the default won't override it.
* ⏺ Add sensible slot defaults for comparison plots and block unused slots
Comparison plots now automatically assign the 'case' dimension to
appropriate slots based on plot type:
- Stacked bars (balance, carrier_balance, storage): facet_col='case'
- Line charts (flows, charge_states, duration_curve): line_dash='case'
- Grouped bars (sizes, effects): color='case' with barmode='group'
- Heatmaps: facet_col='case'
Uses _set_case_default() helper that checks if 'case' is already
assigned elsewhere before setting the default, preventing conflicts.
Also blocks unused slots in StatisticsPlotAccessor to prevent
xarray_plotly from auto-assigning dimensions to visually unhelpful
slots:
- Time-series bars: pattern_shape=None
- Line charts: symbol=None
All defaults use setdefault() so users can override when needed.
* Add sensible slot defaults for plotting methods
Use setdefault() for all dimension-to-slot assignments, allowing user
overrides while providing sensible defaults.
Comparison plots (ComparisonStatisticsPlot):
- Stacked bars: x='time', facet_col='case'
- Line charts: x='time', line_dash='case'
- Grouped bars: x='variable', color='case', barmode='group'
- Duration curve: x='duration_pct'/'duration'
- Heatmaps: facet_col='case'
Statistics plots (StatisticsPlotAccessor):
- Time-series bars: x='time', pattern_shape=None (blocked)
- Line charts: x='time', symbol=None (blocked)
- Duration curve: x='duration_pct'/'duration'
- Sizes: x='variable'
- Effects: x computed from 'by' parameter
Uses SLOT_ORDERS from xarray_plotly to dynamically detect all valid
slots when checking for 'case' dimension conflicts.
All defaults use setdefault() so users can override when needed.
* Updated _SLOT_DEFAULTS dict with proper plotly kwargs (no custom keys):
_SLOT_DEFAULTS: dict[str, dict[str, str | None]] = {
'balance': {'x': 'time', 'color': 'variable', 'pattern_shape': None, 'facet_col': 'case'},
'carrier_balance': {'x': 'time', 'color': 'variable', 'pattern_shape': None, 'facet_col': 'case'},
'flows': {'x': 'time', 'color': 'variable', 'symbol': None, 'line_dash': 'case'},
'charge_states': {'x': 'time', 'color': 'variable', 'symbol': None, 'line_dash': 'case'},
'storage': {'x': 'time', 'color': 'variable', 'pattern_shape': None, 'facet_col': 'case'},
'sizes': {'x': 'variable', 'color': 'case'},
'duration_curve': {'symbol': None, 'line_dash': 'case'},
'effects': {'color': 'case'},
'heatmap': {'facet_col': 'case'},
}
Updated _apply_slot_defaults() helper to detect if 'case' is already assigned:
- If user has already set 'case' to any slot, skips the default case assignment
- Otherwise applies all defaults via setdefault() (user overridable)
Updated all 9 methods (balance, carrier_balance, flows, charge_states, storage, duration_curve, sizes, effects, heatmap) to use:
_apply_slot_defaults(plotly_kwargs, 'method_name')
* Move slot defaults inline into each comparison plot method
Improves code locality by defining defaults dict directly in each method
rather than in a module-level lookup table.
* Build job (PRs & pushes):
- Only runs fast notebooks (~1 min total)
- Slow notebooks are skipped
Deploy job (releases only):
- Runs fast notebooks first
- Then runs slow notebooks (~10 min extra)
- Full documentation with all notebooks executed
Files changed:
- .github/workflows/docs.yaml - updated both execution steps
- docs/notebooks/slow_notebooks.txt - created with the 2 slow notebooks
To add/remove slow notebooks in the future, just edit slow_notebooks.txt.
* Fix storage plot passing invalid kwargs to add_line_overlay
Filter plotly_kwargs to only pass faceting-related kwargs (x, facet_col,
facet_row, animation_frame) to add_line_overlay, avoiding TypeError from
bar-chart-specific kwargs like pattern_shape.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix storage plot passing invalid kwargs to add_line_overlay
Filter plotly_kwargs to only pass faceting-related kwargs (x, facet_col,
facet_row, animation_frame) to add_line_overlay, avoiding TypeError from
bar-chart-specific kwargs like pattern_shape.
Applied to both statistics_accessor.py and comparison.py.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* 1. Identified slow notebooks - 08b-rolling-horizon (5 min) and 08c2-clustering-storage-modes (5 min)
2. Created slow_notebooks.txt - config file to exclude them from regular CI
3. Updated CI - fast notebooks run on every build, slow ones only on release
4. Switched to Plotly CDN - notebooks now ~98% smaller (4.8MB → 79KB each)
* - Type: tuple[str, str] | Literal['auto'] | None
- Docstring: "Use 'auto' to reshape to ('D', 'h') if data has a time index"
* Apply slot defaults pattern to clustering and fix color handling
- Add _apply_slot_defaults helper for clustering plots
- Use inline defaults dict in compare() and clusters() methods
- Remove explicit slot parameters (color, line_dash, facet_col) from signatures
- Pass all slot assignments through **plotly_kwargs with sensible defaults
- Fix color handling: use _build_color_kwargs for proper ColorType support
- Apply default colorscale for heatmap when colors=None
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Add other plot to notebook
* Add plotly template
* Add plotly template
* Fix notebook
* Add docs about ploty express
* More about plotly templates
* Fixed. Now n is calculated explicitly from the first entry in sorted_vars rather than relying on the implicit loop variable from the last iteration.
* Template System
- 'flixopt' template registered on import (no side effects)
- CONFIG.use_theme() to explicitly activate
- Fixed .title() for colorscale names + fallback handling
Color Handling Simplification
- process_colors() reads CONFIG internally
- Removed 8+ explicit default_colorscale=CONFIG... calls
- Heatmaps use template default when colors not specified
Heatmap Default
- Changed reshape=None → reshape=('D', 'h')
- More useful out-of-the-box daily pattern visualization
Documentation
- Added "Plotly Express Internals" section covering slots, colorscales, templates
- Fixed markdown code block language specifiers
* All fixes have been applied and verified. Here's a summary of the changes:
Changes Made
1. CHANGELOG.md (line 145)
- Before: #### FXPlot Accessor (#548)
- After: #### Plotly Accessor (#548)
2. CHANGELOG.md (lines 169-170)
- Before: Two duplicate .plotly.bar() entries (grouped/stacked)
- After: Single entry: .plotly.bar() | Bar charts (use barmode='group' or 'relative' for stacked)
3. flixopt/stats_accessor.py - to_duration_curve() (lines 43-66)
- Before: Used da.values (breaks dask laziness) and tuple assignment (loses attrs)
- After: Uses xr.apply_ufunc() with dask='parallelized' to preserve:
- Dask lazy evaluation
- DataArray attributes
- Dataset attributes
- Variables without time dimension (unchanged)
4. docs/user-guide/recipes/plotting-custom-data.md
- Added note explaining that .plotly and .fxstats accessors are automatically registered on import
* 1. flixopt/stats_accessor.py (line 67-69) - Preserve non-time coordinates
# Before
sorted_ds = xr.Dataset(sorted_vars, attrs=self._ds.attrs)
# After
non_time_coords = {k: v for k, v in self._ds.coords.items() if k != 'time'}
sorted_ds = xr.Dataset(sorted_vars, coords=non_time_coords, attrs=self._ds.attrs)
2. flixopt/config.py (line 820-821) - Re-register template on use_theme()
# Re-register template to pick up any config changes made after import
_register_flixopt_template()
pio.templates.default = 'plotly_white+flixopt'
Now if a user modifies CONFIG.Plotting.default_qualitative_colorscale after import but before calling use_theme(), the template will use the updated value.
3. flixopt/config.py (line 958-963) - Log warning on colorscale fallback
if colorway is None:
logging.getLogger(__name__).warning(
f"Colorscale '{CONFIG.Plotting.default_qualitative_colorscale}' not found in "
f"plotly.express.colors.qualitative, falling back to 'Plotly'. "
f"Available: {[n for n in dir(colors.qualitative) if not n.startswith('_')]}"
)
colorway = colors.qualitative.Plotly
* ⏺ Much cleaner. The simplified implementation:
- Preserves DataArray attrs via attrs=da.attrs
- Preserves variable-level coords via coords={k: v for k, v in da.coords.items() if k != 'time'}
- Preserves dataset-level coords and attrs separately
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Summary
New .fxplot accessor for convenient plotting of any xr.Dataset with automatic dimension handling, faceting, and animation support.
✨ Added
New DatasetPlotAccessor (ds.fxplot.*)
Smart Dimension Handling
New Config Options
♻️ Changed
🐛 Fixed
📚 Documentation