UXarray for Basic HEALPix Statistics & Visualization¶
In this section, you’ll learn:¶
Utilizing
intaketo open a HEALPix data catalogUsing the
uxarraypackage to look at basic statistics over HEALPix dataUsing UXarray plotting functionality on HEALPix data
Related Documentation¶
UXarray overview - Unstructured Grids Visualization Cookbook
Data visualization with UXarray - Unstructured Grids Visualization Cookbook
Prerequisites¶
| Concepts | Importance | Notes |
|---|---|---|
| UXarray | Necessary | |
| HEALPix overview | Necessary |
Time to learn: 30 minutes
import cartopy.crs as ccrs
import uxarray as uxOpen data catalog¶
Let us use the online data catalog from the WCRP’s Digital Earths Global Hackathon 2025’s catalog repository using intake and read the output of the ICON simulation run ngc4008, which is stored in the HEALPix format:
import intake
# Hackathon data catalogs
cat_url = "https://digital-earths-global-hackathon.github.io/catalog/catalog.yaml"
cat = intake.open_catalog(cat_url).online
model_run = cat.icon_ngc4008/home/runner/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/intake/catalog/utils.py:173: UserWarning: Shell command not executed due to getshell=False
warnings.warn("Shell command not executed due to getshell=False")
/home/runner/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/intake/catalog/utils.py:182: UserWarning: Shell command not executed due to getshell=False
warnings.warn("Shell command not executed due to getshell=False")
We will be looking at two resolution levels, one is the coarsest zoom level of 0, which is the default in this model run, and the other is a finer one at the zoom level of 7:
ds_coarsest = model_run().to_dask()
ds_fine = model_run(zoom=7).to_dask()Create UXarray Datasets from HEALPix¶
Now let us use those xarray.Datasets from the model run to open unstructured grid-aware uxarray.UxDataset:
%%time
uxds_coarsest = ux.UxDataset.from_healpix(ds_coarsest)
uxds_fine = ux.UxDataset.from_healpix(ds_fine)
uxds_fineCPU times: user 167 ms, sys: 9.17 ms, total: 176 ms
Wall time: 176 ms
HEALPix basic stats using UXarray¶
Let us look at the global and Boulder, CO, USA air temperature averages for the dataset. Data spans from 2020 to 2050, so let us also consider slicing it to have a 3-year interval between 2020 and 2023, which would also give us similar results to that with easy.gems in the previous section.
import matplotlib.pylab as plt
boulder_lon = -105.2747
boulder_lat = 40.0190
time_slice = slice("2020-01-02T00:00:00.000000000", "2023-01-01T00:00:00.000000000")Mesh face containing Boulder’s coords¶
We can find face(s) containing a given point with uxarray conveniently as follows:
%%time
boulder_face = uxds_fine.uxgrid.get_faces_containing_point(
points=[boulder_lon, boulder_lat],
return_counts=False
)[0]
boulder_faceCPU times: user 9.38 s, sys: 127 ms, total: 9.51 s
Wall time: 9.48 s
[2]Data variables of interest¶
In order to use in the rest of the analyses, we can grab data variables, in theuxarray.UxDataArray type, from the dataset as follows:
uxda_fine = uxds_fine.tas
uxda_coarsest = uxds_coarsest.tasGlobal and Boulder’s temperature averages¶
In order to get a line plot of our UXarray.UxDataset objects’ 1-dimensional temperature variables, we will convert them to xarray and call the default plot function because UXarray’s default plotting functions are all dedicated to grid-topology aware visualizations:
uxda_fine.isel(n_face=boulder_face)%%time
uxda_fine.isel(n_face=boulder_face).sel(time=time_slice).to_xarray().plot(
label="Boulder"
)
uxda_coarsest.sel(time=time_slice).mean("n_face").to_xarray().plot(label="Global mean")
plt.legend()CPU times: user 39.9 ms, sys: 4.15 ms, total: 44.1 ms
Wall time: 1.34 s

Data plotting with UXarray¶
UXarray provides several built-in plotting functions to visualize unstructured grids, which can also be applied to HEALPix grids in the same interface:
Let us first look into interactive plots with the bokeh backend (i.e. UXarray’s plotting functions have a backend parameter that defaults to “bokeh”, and it can also accept “matplotlib”)
Global plots¶
Let us first plot the global temperature (at the first timestep for simplicity), using the default backend, bokeh, of UXarray’s visualization API to create an interactive plot:
%%time
projection = ccrs.Robinson(central_longitude=-135.5808361)
uxda_fine.isel(time=0).plot(
projection=projection,
cmap="inferno",
features=["borders", "coastline"],
title="Global temperature",
width=700,
)WARNING:param.Parameterized: Use method 'warning' via param namespace
WARNING:param.main: pandas could not register all extension types imports failed with the following error: cannot import name 'ABCIndexClass' from 'pandas.core.dtypes.generic' (/home/runner/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/pandas/core/dtypes/generic.py)
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
WARNING:param.Dimension: Use method 'get_param_values' via param namespace
CPU times: user 77.5 ms, sys: 9.14 ms, total: 86.7 ms
Wall time: 85.4 ms
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
Cell In[10], line 1
----> 1 get_ipython().run_cell_magic('time', '', '\nprojection = ccrs.Robinson(central_longitude=-135.5808361)\n\nuxda_fine.isel(time=0).plot(\n projection=projection,\n cmap="inferno",\n features=["borders", "coastline"],\n title="Global temperature",\n width=700,\n)\n')
File <timed exec>:3
1 'Could not get source, probably due dynamically evaluated source code.'
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/uxarray/core/dataarray.py:2236, in UxDataArray.__getattribute__(self, name)
2233 return method
2235 # For all other attributes, use the default behavior
-> 2236 return super().__getattribute__(name)
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/xarray/core/utils.py:1178, in UncachedAccessor.__get__(self, obj, cls)
1175 if obj is None:
1176 return self._accessor
-> 1178 return self._accessor(obj)
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/uxarray/plot/accessor.py:375, in UxDataArrayPlotAccessor.__init__(self, uxda)
374 def __init__(self, uxda: UxDataArray) -> None:
--> 375 _ensure_hvplot_imported()
376 self._uxda = uxda
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/uxarray/plot/accessor.py:40, in _ensure_hvplot_imported()
31 global _IMPORTED_HVPLOT
32 if not _IMPORTED_HVPLOT:
33 # workaround for hvplot issue #1735;
34 # import hvplot.pandas and hvplot.xarray always adjust the hvplot.extension().
(...) 38 # Store.current_backend defaults to "matplotlib" while Store.registry is empty,
39 # and restoring that unloaded backend would break rendering.
---> 40 from holoviews import Store as _store
42 _backend_orig = _store.current_backend
43 import hvplot.pandas
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/holoviews/__init__.py:12
6 import param
9 __version__ = str(param.version.Version(fpath=__file__, archive_commit="$Format:%h$",
10 reponame="holoviews"))
---> 12 from . import util # noqa (API import)
13 from .core import archive, config # noqa (API import)
14 from .core.boundingregion import BoundingBox # noqa (API import)
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/holoviews/util/__init__.py:14
11 import param
12 from pyviz_comms import extension as _pyviz_extension
---> 14 from ..core import DynamicMap, HoloMap, Dimensioned, ViewableElement, StoreOptions, Store
15 from ..core.options import options_policy, Keywords, Options
16 from ..core.operation import Operation
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/holoviews/core/__init__.py:4
1 from datetime import date, datetime
3 from .boundingregion import * # noqa (API import)
----> 4 from .data import * # noqa (API import)
5 from .dimension import * # noqa (API import)
6 from .element import * # noqa (API import)
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/holoviews/core/data/__init__.py:20
18 from .array import ArrayInterface
19 from .dictionary import DictInterface
---> 20 from .grid import GridInterface
21 from .multipath import MultiInterface # noqa (API import)
22 from .image import ImageInterface # noqa (API import)
File ~/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/site-packages/holoviews/core/data/grid.py:5
3 import sys
4 import datetime as dt
----> 5 from collections import OrderedDict, defaultdict, Iterable
7 try:
8 import itertools.izip as zip
ImportError: cannot import name 'Iterable' from 'collections' (/home/runner/micromamba/envs/healpix-cookbook-dev2/lib/python3.14/collections/__init__.py)Now, let us create the same plot, using matplotlib as the backend:
%%time
uxda_fine.isel(time=0).plot(
backend="matplotlib",
projection=projection,
cmap="inferno",
features=["borders", "coastline"],
title="Global temperature",
width=1100,
)Regional subsets (Not only for plotting but also for analysis)¶
When a region on the globe is of interest, UXarray provides subsetting functions, which return new regional grids that can then be used in the same way a global grid is plotted.
Let us look into the USA map using the Boulder, CO, USA coords we had used before for simplicity:
Subsetting uxds_fine into a new UxDataset using a “bounding box” around Boulder, CO first:
%%time
lon_bounds = (boulder_lon - 20, boulder_lon + 40)
lat_bounds = (boulder_lat - 20, boulder_lat + 12)
uxda_fine_subset = uxda_fine.isel(time=0).subset.bounding_box(lon_bounds, lat_bounds)If we check the global and regional subset’s average temperature at the first timestep, we can see the difference:
print(
"Global temperature average: ", uxda_fine.isel(time=0).mean("n_face").values, " K"
)
print(
"Regional subset's temperature average: ", uxda_fine_subset.mean("n_face").values, " K"
)Now, let us plot the regional subset UxDataset:
%%time
projection = ccrs.Robinson(central_longitude=boulder_lon)
uxda_fine_subset.plot(
projection=projection,
cmap="inferno",
features=["borders", "coastline"],
title="Boulder temperature",
width=1100,
)Grid topology exploration¶
Exploring the grid topology may be needed sometimes, and UXarray provides functionality to do so, both numerically and visually. Each UxDataset or UxDataArray has their associated Grid object that has all the information such as spherical and cartesian coordinates, connectives, dimensions, etc. about the topology this data belongs to. This Grid object can be explored as follows:
# uxds_fine.uxgrid # this would give the same as the below
uxda_fine.uxgridThere might be times that the user wants to open a standalone Grid object for a HEALPix grid (or any other unstructured grids supported by UXarray) without accessing the data yet. Let’s create the coarsest HEALPix grid as follows:
uxgrid = ux.Grid.from_healpix(zoom=0, pixels_only=False)
uxgridLet’s investigate how a HEALPix grid looks like over the poles. We can do things by selecting an Orthographic projection and setting a Geodetic source projection. This allows to better approximates the true HEALPix structured compared to the defaut PlateCaree projection.
projection = ccrs.Orthographic(central_latitude=90)
projection.threshold /= 100 # Smoothes out great circle arcs
uxgrid.plot(
periodic_elements="ignore", # Allow Cartopy to handle periodic elements
crs=ccrs.Geodetic(), # Enables edges to be plotted as GCAs
project=True,
projection=projection,
width=500,
title="HEALPix (Orthographic Proj), zoom=0",
)Let’s chose another projection.
projection = ccrs.Mollweide()
projection.threshold /= 100 # Smoothes out great circle arcs
uxgrid.plot(
periodic_elements="ignore", # Allow Cartopy to handle periodic elements
crs=ccrs.Geodetic(), # Enables edges to be plotted as GCAs
project=True,
projection=projection,
width=500,
title="HEALPix (Mollweide Proj), zoom=0",
)The grid structure here is approximated. While the boundary between each pixel is easily known in HEALPix workflows, UXarray represents the boundaries as a Great Circle Arc (GCA) due to the requirement of having explicit connectivity information, which is different than HEALPix boundaries and leads to minor differences in the computed plots and computations.
Now that we’ve looked at the grid structure, we can also apply the same principles to our data plotting.
uxda_coarsest.isel(time=0).plot(
periodic_elements="ignore", # Allow Cartopy to handle periodic elements
crs=ccrs.Geodetic(), # Enables edges to be plotted as GCAs
project=True,
projection=projection,
features=["borders", "coastline"],
cmap="inferno",
title="Temperature (Mollweide Proj), zoom=0",
width=500,
)What is next?¶
The next section will provide an UXarray workflow that loads in and analyzes & visualizes HEALPix data.