diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 2ca7de037cb..5291eba93fd 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -25,6 +25,9 @@ New Features - Add static typing to plot accessors (:issue:`6949`, :pull:`7052`). By `Michael Niklas `_. +- Display the indexes in a new section of the text and HTML reprs + (:pull:`6795`, :pull:`7183`, :pull:`7185`) + By `Justus Magin `_ and `BenoƮt Bovy `_. Breaking changes ~~~~~~~~~~~~~~~~ diff --git a/xarray/core/formatting_html.py b/xarray/core/formatting_html.py index ed36547e3b5..ee1805f6d2b 100644 --- a/xarray/core/formatting_html.py +++ b/xarray/core/formatting_html.py @@ -6,7 +6,7 @@ from html import escape from importlib.resources import read_binary -from .formatting import inline_variable_array_repr, short_data_repr +from .formatting import inline_index_repr, inline_variable_array_repr, short_data_repr from .options import _get_boolean_with_default STATIC_FILES = ( @@ -125,6 +125,40 @@ def summarize_vars(variables): return f"
    {vars_li}
" +def short_index_repr_html(index): + if hasattr(index, "_repr_html_"): + return index._repr_html_() + + return f"
{escape(repr(index))}
" + + +def summarize_index(coord_names, index): + name = "
".join([escape(str(n)) for n in coord_names]) + + index_id = f"index-{uuid.uuid4()}" + preview = escape(inline_index_repr(index)) + details = short_index_repr_html(index) + + data_icon = _icon("icon-database") + + return ( + f"
{name}
" + f"
{preview}
" + f"
" + f"" + f"" + f"
{details}
" + ) + + +def summarize_indexes(indexes): + indexes_li = "".join( + f"
  • {summarize_index(v, i)}
  • " + for v, i in indexes.items() + ) + return f"
      {indexes_li}
    " + + def collapsible_section( name, inline_details="", details="", n_items=None, enabled=True, collapsed=False ): @@ -213,6 +247,13 @@ def array_section(obj): expand_option_name="display_expand_data_vars", ) +index_section = partial( + _mapping_section, + name="Indexes", + details_func=summarize_indexes, + max_items_collapse=0, + expand_option_name="display_expand_indexes", +) attr_section = partial( _mapping_section, @@ -223,6 +264,12 @@ def array_section(obj): ) +def _get_indexes_dict(indexes): + return { + tuple(index_vars.keys()): idx for idx, index_vars in indexes.group_by_index() + } + + def _obj_repr(obj, header_components, sections): """Return HTML repr of an xarray object. @@ -266,6 +313,10 @@ def array_repr(arr): if hasattr(arr, "coords"): sections.append(coord_section(arr.coords)) + if hasattr(arr, "xindexes"): + indexes = _get_indexes_dict(arr.xindexes) + sections.append(index_section(indexes)) + sections.append(attr_section(arr.attrs)) return _obj_repr(arr, header_components, sections) @@ -280,6 +331,7 @@ def dataset_repr(ds): dim_section(ds), coord_section(ds.coords), datavar_section(ds.data_vars), + index_section(_get_indexes_dict(ds.xindexes)), attr_section(ds.attrs), ] diff --git a/xarray/core/options.py b/xarray/core/options.py index 865d0755a01..f7f5b9eddcd 100644 --- a/xarray/core/options.py +++ b/xarray/core/options.py @@ -169,34 +169,41 @@ class set_options: Colormap to use for nondivergent data plots. If string, must be matplotlib built-in colormap. Can also be a Colormap object (e.g. mpl.cm.magma) - display_expand_attrs : {"default", True, False}: + display_expand_attrs : {"default", True, False} Whether to expand the attributes section for display of ``DataArray`` or ``Dataset`` objects. Can be * ``True`` : to always expand attrs * ``False`` : to always collapse attrs * ``default`` : to expand unless over a pre-defined limit - display_expand_coords : {"default", True, False}: + display_expand_coords : {"default", True, False} Whether to expand the coordinates section for display of ``DataArray`` or ``Dataset`` objects. Can be * ``True`` : to always expand coordinates * ``False`` : to always collapse coordinates * ``default`` : to expand unless over a pre-defined limit - display_expand_data : {"default", True, False}: + display_expand_data : {"default", True, False} Whether to expand the data section for display of ``DataArray`` objects. Can be * ``True`` : to always expand data * ``False`` : to always collapse data * ``default`` : to expand unless over a pre-defined limit - display_expand_data_vars : {"default", True, False}: + display_expand_data_vars : {"default", True, False} Whether to expand the data variables section for display of ``Dataset`` objects. Can be * ``True`` : to always expand data variables * ``False`` : to always collapse data variables * ``default`` : to expand unless over a pre-defined limit + display_expand_indexes : {"default", True, False} + Whether to expand the indexes section for display of + ``DataArray`` or ``Dataset``. Can be + + * ``True`` : to always expand indexes + * ``False`` : to always collapse indexes + * ``default`` : to expand unless over a pre-defined limit (always collapse for html style) display_max_rows : int, default: 12 Maximum display rows. display_values_threshold : int, default: 200 diff --git a/xarray/static/css/style.css b/xarray/static/css/style.css index 9fa27c03359..e0a51312b10 100644 --- a/xarray/static/css/style.css +++ b/xarray/static/css/style.css @@ -244,6 +244,11 @@ body.vscode-dark { grid-column: 4; } +.xr-index-preview { + grid-column: 2 / 5; + color: var(--xr-font-color2); +} + .xr-var-name, .xr-var-dims, .xr-var-dtype, @@ -265,14 +270,16 @@ body.vscode-dark { } .xr-var-attrs, -.xr-var-data { +.xr-var-data, +.xr-index-data { display: none; background-color: var(--xr-background-color) !important; padding-bottom: 5px !important; } .xr-var-attrs-in:checked ~ .xr-var-attrs, -.xr-var-data-in:checked ~ .xr-var-data { +.xr-var-data-in:checked ~ .xr-var-data, +.xr-index-data-in:checked ~ .xr-index-data { display: block; } @@ -282,13 +289,16 @@ body.vscode-dark { .xr-var-name span, .xr-var-data, +.xr-index-name div, +.xr-index-data, .xr-attrs { padding-left: 25px !important; } .xr-attrs, .xr-var-attrs, -.xr-var-data { +.xr-var-data, +.xr-index-data { grid-column: 1 / -1; } @@ -326,7 +336,8 @@ dl.xr-attrs { } .xr-icon-database, -.xr-icon-file-text2 { +.xr-icon-file-text2, +.xr-no-icon { display: inline-block; vertical-align: middle; width: 1em; diff --git a/xarray/tests/test_formatting_html.py b/xarray/tests/test_formatting_html.py index 039c6269350..7ea5c19019b 100644 --- a/xarray/tests/test_formatting_html.py +++ b/xarray/tests/test_formatting_html.py @@ -102,20 +102,20 @@ def test_repr_of_dataarray(dataarray) -> None: assert "dim_0" in formatted # has an expanded data section assert formatted.count("class='xr-array-in' type='checkbox' checked>") == 1 - # coords and attrs don't have an items so they'll be be disabled and collapsed + # coords, indexes and attrs don't have an items so they'll be be disabled and collapsed assert ( - formatted.count("class='xr-section-summary-in' type='checkbox' disabled >") == 2 + formatted.count("class='xr-section-summary-in' type='checkbox' disabled >") == 3 ) with xr.set_options(display_expand_data=False): formatted = fh.array_repr(dataarray) assert "dim_0" in formatted - # has an expanded data section + # has a collapsed data section assert formatted.count("class='xr-array-in' type='checkbox' checked>") == 0 - # coords and attrs don't have an items so they'll be be disabled and collapsed + # coords, indexes and attrs don't have an items so they'll be be disabled and collapsed assert ( formatted.count("class='xr-section-summary-in' type='checkbox' disabled >") - == 2 + == 3 ) @@ -130,6 +130,8 @@ def test_repr_of_dataset(dataset) -> None: assert ( formatted.count("class='xr-section-summary-in' type='checkbox' checked>") == 3 ) + # indexes is collapsed + assert formatted.count("class='xr-section-summary-in' type='checkbox' >") == 1 assert "<U4" in formatted or ">U4" in formatted assert "<IA>" in formatted @@ -137,12 +139,13 @@ def test_repr_of_dataset(dataset) -> None: display_expand_coords=False, display_expand_data_vars=False, display_expand_attrs=False, + display_expand_indexes=True, ): formatted = fh.dataset_repr(dataset) - # coords, attrs, and data_vars are collapsed + # coords, attrs, and data_vars are collapsed, indexes is expanded assert ( formatted.count("class='xr-section-summary-in' type='checkbox' checked>") - == 0 + == 1 ) assert "<U4" in formatted or ">U4" in formatted assert "<IA>" in formatted