From 5d6743a1a477a0a29f1f9dee4942d4768c01bd55 Mon Sep 17 00:00:00 2001 From: Callan Gray Date: Mon, 4 Aug 2025 21:59:54 +0800 Subject: [PATCH 1/3] Notebook corrections Signed-off-by: Callan Gray --- docs/examples/plotting.ipynb | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/docs/examples/plotting.ipynb b/docs/examples/plotting.ipynb index 84578c3d..cefa08ca 100644 --- a/docs/examples/plotting.ipynb +++ b/docs/examples/plotting.ipynb @@ -66,7 +66,7 @@ "metadata": {}, "source": [ "
\n", - "Note: this example uses the data provided by the xarray.tutorial functions. As such, the units attributes follow the CF conventions, which pint does not understand by default. To still be able to read them we are using the registry provided by cf-xarray.\n", + "Note: this example uses the data provided by the xarray.tutorial functions. As such, the units attributes follow the CF conventions, which astropy does not understand by default. To still be able to read them, registry be aliases can be used. For more information, see cf-xarray.\n", "
" ] }, @@ -110,7 +110,7 @@ "id": "9", "metadata": {}, "source": [ - "Most operations will preserve the units but there are some which will drop them (see the [duck array integration status](https://xarray.pydata.org/en/stable/user-guide/duckarrays.html#missing-features) page). To work around that there are unit-aware versions on the `.pint` accessor. For example, to select data use `.astropy.sel` instead of `.sel`:" + "Most operations will preserve the units but there are some which will drop them (see the [duck array integration status](https://xarray.pydata.org/en/stable/user-guide/duckarrays.html#missing-features) page). To work around that there are unit-aware versions on the `.astropy` accessor. For example, to select data use `.astropy.sel` instead of `.sel`:" ] }, { @@ -145,14 +145,6 @@ "source": [ "monthly_means.astropy.dequantify(format=\"unicode\").plot.imshow(col=\"month\", col_wrap=4)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "13", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 574bfc91a7b6fb0e2faec2c27d864f887dd1497d Mon Sep 17 00:00:00 2001 From: Callan Gray Date: Mon, 30 Mar 2026 15:24:03 +0800 Subject: [PATCH 2/3] cleanup docs and notebooks Signed-off-by: Callan Gray --- README.md | 22 +- docs/examples/plotting.ipynb | 121 --------- docs/examples/skycoord.ipynb | 30 --- docs/examples/specialized_quantities.ipynb | 284 +++++++++++++++++++++ docs/requirements.txt | 2 +- docs/whats-new.rst | 4 +- 6 files changed, 298 insertions(+), 165 deletions(-) create mode 100644 docs/examples/specialized_quantities.ipynb diff --git a/README.md b/README.md index 4e9b97c5..248e48d1 100644 --- a/README.md +++ b/README.md @@ -99,17 +99,17 @@ from astropy_xarray.coordinates.sky_coord import ( ) sc = skycoord_to_dataset( - SkyCoord( - ra=[[2, 6, 7, 4]] * u.deg, - dec=[[4, 7, 4, 3]] * u.deg, - pm_ra_cosdec=[[1, 1, 1, 1]] * u.mas / u.yr, - pm_dec=[[1, 1, 1, 1]] * u.mas / u.yr, - frame="icrs", - ), - coords={ - "timestamp": ("time", Time([1.7e9], format='unix')), - "field_label": ("field", ["a", "b", "c", "d"]), - } + SkyCoord( + ra=[[2, 6, 7, 4]] * u.deg, + dec=[[4, 7, 4, 3]] * u.deg, + pm_ra_cosdec=[[1, 1, 1, 1]] * u.mas / u.yr, + pm_dec=[[1, 1, 1, 1]] * u.mas / u.yr, + frame="icrs", + ), + coords={ + "timestamp": ("time", Time([1.7e9], format="unix")), + "field_label": ("field", ["a", "b", "c", "d"]), + }, ) sc ``` diff --git a/docs/examples/plotting.ipynb b/docs/examples/plotting.ipynb index 57601e6b..09797a88 100644 --- a/docs/examples/plotting.ipynb +++ b/docs/examples/plotting.ipynb @@ -142,127 +142,6 @@ "source": [ "monthly_means.astropy.dequantify(format=\"unicode\").plot.imshow(col=\"month\", col_wrap=4)" ] - }, - { - "cell_type": "markdown", - "id": "13", - "metadata": {}, - "source": [ - "## Time\n", - "\n", - "astropy-xarray patches Time and DeltaTime to be recognized xarray, capturing:\n", - "* format\n", - "* timescale\n", - "* precision" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "14", - "metadata": {}, - "outputs": [], - "source": [ - "from astropy.time import Time, TimeDelta\n", - "\n", - "ds = xr.Dataset(\n", - " data_vars={\n", - " \"f_time\": (\"time_idx\", Time([0, 1, 2], format=\"unix\", scale=\"utc\")),\n", - " \"i_time\": (\n", - " \"time_idx\",\n", - " Time([0, 1, 2], format=\"unix\", scale=\"utc\").copy(\"datetime64\"),\n", - " ),\n", - " \"s_time\": (\n", - " \"time_idx\",\n", - " Time([0, 1, 2], format=\"unix\", scale=\"utc\").copy(\"isot\"),\n", - " ),\n", - " \"interval\": (\"time_idx\", TimeDelta([0.9, 0.9, 0.9], format=\"sec\", scale=\"tai\")),\n", - " \"temp\": (\"time_idx\", u.Quantity([0, 1, 2], \"K\")),\n", - " },\n", - " coords=xr.Coordinates(\n", - " {\n", - " \"time_idx\": [0, 1, 2],\n", - " }\n", - " ),\n", - ")\n", - "ds" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15", - "metadata": {}, - "outputs": [], - "source": [ - "ds = ds.astropy.dequantify()\n", - "ds.i_time" - ] - }, - { - "cell_type": "markdown", - "id": "16", - "metadata": {}, - "source": [ - "## SkyCoord, Frame and Angle\n", - "\n", - "SkyCoord and Frame fully support conversion to datasets preserving:\n", - "* Representation\n", - "* Differential\n", - "* Frame\n", - "* Angle, Longitude, Latitude containers\n", - "\n", - "Conversion optionally requires coords argument for applying named coordinates to data variable axes.\n", - "\n", - "Frame-specific mapped names for data variables coming soon." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "17", - "metadata": {}, - "outputs": [], - "source": [ - "from astropy.coordinates import SkyCoord\n", - "\n", - "from astropy_xarray.coordinates.sky_coord import (\n", - " dataset_to_skycoord,\n", - " skycoord_to_dataset,\n", - ")\n", - "\n", - "sc = SkyCoord(\n", - " ra=[2, 6, 7, 4] * u.deg,\n", - " dec=[4, 7, 4, 3] * u.deg,\n", - " pm_ra_cosdec=[1, 1, 1, 1] * u.mas / u.yr,\n", - " pm_dec=[1, 1, 1, 1] * u.mas / u.yr,\n", - " frame=\"icrs\",\n", - ")\n", - "\n", - "\n", - "s = skycoord_to_dataset(sc, coords=[(\"field\", [0, 1, 2, 3])])\n", - "\n", - "display(s)\n", - "s.astropy.dequantify()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18", - "metadata": {}, - "outputs": [], - "source": [ - "display(dataset_to_skycoord(s))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "19", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/docs/examples/skycoord.ipynb b/docs/examples/skycoord.ipynb index 668ec671..2e38510e 100644 --- a/docs/examples/skycoord.ipynb +++ b/docs/examples/skycoord.ipynb @@ -304,36 +304,6 @@ "print(\"Dataset:\", ds.ra.data[1].value)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import astropy.units as u\n", - "from astropy.coordinates import SkyCoord\n", - "from astropy.time import Time\n", - "\n", - "from astropy_xarray.coordinates.sky_coord import (\n", - " skycoord_to_dataset,\n", - ")\n", - "\n", - "sc = skycoord_to_dataset(\n", - " SkyCoord(\n", - " ra=[[2, 6, 7, 4]] * u.deg,\n", - " dec=[[4, 7, 4, 3]] * u.deg,\n", - " pm_ra_cosdec=[[1, 1, 1, 1]] * u.mas / u.yr,\n", - " pm_dec=[[1, 1, 1, 1]] * u.mas / u.yr,\n", - " frame=\"icrs\",\n", - " ),\n", - " coords={\n", - " \"timestamp\": (\"time\", Time([1.7e9], format=\"unix\")),\n", - " \"field_label\": (\"field\", [\"a\", \"b\", \"c\", \"d\"]),\n", - " },\n", - ")\n", - "sc" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/docs/examples/specialized_quantities.ipynb b/docs/examples/specialized_quantities.ipynb new file mode 100644 index 00000000..e2733a13 --- /dev/null +++ b/docs/examples/specialized_quantities.ipynb @@ -0,0 +1,284 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Specialized Quantities\n", + "\n", + "Beyond `astropy.units.Quantity`, astropy provides further specialized quantities. `astropy-xarray` captures this using the `units.class` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import xarray as xr\n", + "from astropy import units as u" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Time and TimeDelta\n", + "\n", + "`astropy_xarray` patches `astropy.time.Time` and `astropy.time.TimeDelta` to be recognized xarray, capturing:\n", + "* format\n", + "* timescale\n", + "* precision" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "from astropy.time import Time, TimeDelta\n", + "\n", + "import astropy_xarray # noqa: F401\n", + "\n", + "ds = xr.Dataset(\n", + " data_vars={\n", + " \"d_time\": (\n", + " \"time\",\n", + " Time([0, 1, 2], format=\"unix\", scale=\"utc\").copy(\"datetime64\"),\n", + " ),\n", + " \"s_time\": (\n", + " \"time\",\n", + " Time([0, 1, 2], format=\"unix\", scale=\"utc\").copy(\"isot\"),\n", + " ),\n", + " \"interval\": (\"time\", TimeDelta([0.9, 0.9, 0.9], format=\"sec\", scale=\"tai\")),\n", + " },\n", + " coords=xr.Coordinates(\n", + " {\n", + " \"f_time\": (\"time\", Time([0, 1, 2], format=\"unix\", scale=\"utc\")),\n", + " }\n", + " ),\n", + ")\n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# dequantified attributes\n", + "ds.astropy.dequantify()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## SkyCoord, Frame and Angle\n", + "\n", + "SkyCoord and Frame fully support conversion to datasets preserving:\n", + "* Representation\n", + "* Differential\n", + "* Frame\n", + "* Angle, Longitude, Latitude containers\n", + "\n", + "Conversion optionally requires coords argument for applying named coordinates to data variable axes.\n", + "\n", + "Frame-specific mapped names for data variables coming soon." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "from astropy.coordinates import SkyCoord\n", + "\n", + "from astropy_xarray.coordinates.sky_coord import (\n", + " skycoord_to_dataset,\n", + ")\n", + "\n", + "sc = SkyCoord(\n", + " ra=[2, 6, 7, 4] * u.deg,\n", + " dec=[4, 7, 4, 3] * u.deg,\n", + ")\n", + "\n", + "\n", + "s = skycoord_to_dataset(sc, coords={\"field\": [\"a\", \"b\", \"c\", \"d\"]})\n", + "display(s)" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "## Logarithmic Units and Quantities\n", + "\n", + "Astropy provides a generic `LogQuantity` and other various log scale specialized classes and units:\n", + "\n", + "$$scalar = A * log_{B}(\\frac{V}{V_0})$$\n", + "\n", + "\n", + "| Name | Equiv SI Unit | Scale Factor | Log Base | 0 Reference |\n", + "| --------------------| ------------- | -------------- | --------- | ---------------------------------------|\n", + "| Magnitude (absolute)| Unitless | -2.5 | 10 | Apparent brightness at 10pc away |\n", + "| Dex | Unitless | 1 | 10 | 1 |\n", + "| Decibels (power) | Unitless | 10 | 10 | Variable |\n", + "| Decibels (amplitude)| Unitless | 20 | 10 | Variable |\n", + "| Magnitude (apparent)| Unitless | -2.5 | 10 | Usually Vega or AB from Earth |\n", + "| Magnitude (AB) | Jy | -2.5 | 10 | 3631 Jy |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "import types\n", + "\n", + "import astropy.units\n", + "import astropy.units as u\n", + "import numpy as np\n", + "from astropy.units import Dex, Equivalency, Magnitude, Quantity\n", + "\n", + "import astropy_xarray # noqa: F401\n", + "\n", + "\n", + "# extend registry\n", + "def asplund():\n", + " asplund = types.ModuleType(\"asplund\")\n", + " astropy.units.def_unit(\n", + " [\"solH\", \"Xsun\"],\n", + " format={\"latex\": \"X_{\\\\odot}\", \"unicode\": \"X⊙\"},\n", + " namespace=asplund.__dict__,\n", + " )\n", + " astropy.units.def_unit(\n", + " [\"solHe\", \"Ysun\"],\n", + " format={\"latex\": \"Y_{\\\\odot}\", \"unicode\": \"Y⊙\"},\n", + " namespace=asplund.__dict__,\n", + " )\n", + " astropy.units.def_unit(\n", + " [\"solMetal\", \"Zsun\"],\n", + " format={\"latex\": \"Z_{\\\\odot}\", \"unicode\": \"Z⊙\"},\n", + " namespace=asplund.__dict__,\n", + " )\n", + "\n", + " return asplund\n", + "\n", + "\n", + "if not hasattr(u, \"asplund\"):\n", + " u.asplund = asplund()\n", + " u.add_enabled_units(u.asplund)\n", + " u.__dict__.update(u.get_current_unit_registry().registry)\n", + "\n", + "\n", + "def asplund_solar_relative_mass() -> Equivalency:\n", + " \"\"\"measurements from https://www.aanda.org/articles/aa/full_html/2021/09/aa40445-21/aa40445-21.html\"\"\"\n", + " return Equivalency(\n", + " [\n", + " (u.Xsun, u.Msun, lambda x: x * 0.7438),\n", + " (u.Ysun, u.Msun, lambda x: x * 0.2423),\n", + " (u.Zsun, u.Msun, lambda x: x * 0.0139),\n", + " ]\n", + " )\n", + "\n", + "\n", + "if not hasattr(u, \"asplund_solar_relative_mass\"):\n", + " u.asplund_solar_relative_mass = asplund_solar_relative_mass\n", + " # uncomment for implicit conversions\n", + " # u.add_enabled_equivalencies(u.asplund_solar_relative_mass())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "ds = xr.Dataset(\n", + " coords=xr.Coordinates(\n", + " {\n", + " \"timestamp\": (\"time\", Time([1.7e9], format=\"unix\")),\n", + " \"body_label\": (\"body\", [\"sun\", \"proxima centauri\"]),\n", + " \"wavelength\": (\"frequency\", [565, 535, 445] * u.nm),\n", + " }\n", + " ),\n", + " data_vars={\n", + " \"mass\": ([\"time\", \"body\"], Dex([[0.0, 0.02]], unit=u.dex(u.Msun))),\n", + " \"distance\": ([\"time\", \"body\"], [[1.007, 12950]] * u.AU),\n", + " \"luminosity\": (\n", + " [\"time\", \"body\"],\n", + " Quantity([[1, 0.001]], unit=u.Lsun).to(u.dex(u.Lsun)),\n", + " ), # radiant flux\n", + " \"metallicity\": ([\"time\", \"body\"], Dex([[0.0, np.nan]], unit=u.dex(u.Zsun))),\n", + " \"apparent_magnitude\": (\n", + " [\"time\", \"body\"],\n", + " Magnitude([[-26.74, 10.67]]),\n", + " ), # to Vega\n", + " \"absolute_magnitude\": ([\"time\", \"body\"], Magnitude([[4.83, 15.60]])),\n", + " # \"irradiance\": ([\"time\", \"body\"], [[1361, 0]] * u.W / u.m**2),\n", + " # \"radiant_flux_density\": ([\"time\", \"body\"], [[1.0, 0.0]] * u.W / u.m**2),\n", + " # \"spectral_flux_density\": ([\"time\", \"body\", \"frequency\"], [[[68000, 0, 0], [0, 0, 0]]] * u.Jy),\n", + " },\n", + ")\n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "ds.astropy.sel(body=[0]).astropy.to(\n", + " {\n", + " \"mass\": u.kg,\n", + " \"distance\": u.km,\n", + " \"illuminance\": u.cd * u.sr / u.m**2,\n", + " \"luminosity\": u.kW,\n", + " \"metallicity\": u.kg,\n", + " },\n", + " equivalencies=asplund_solar_relative_mass(),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/requirements.txt b/docs/requirements.txt index 2aa1e0ef..67f127a4 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,7 +3,7 @@ xarray>=2022.06.0 pooch netCDF4 cf-xarray>=0.6 -sphinx +sphinx>=8.0.0,<9.0.0 sphinx_rtd_theme>=1.0 ipython ipykernel diff --git a/docs/whats-new.rst b/docs/whats-new.rst index 26a33bee..fb2b9e90 100644 --- a/docs/whats-new.rst +++ b/docs/whats-new.rst @@ -3,13 +3,13 @@ What's new ========== -0.2.0 (30 Mar 2026) +0.2.0 (31 Mar 2026) ------------------- - Added `coordinates` submodule with `dataset_to_skycoord` and `skycoord_to_dataset` explicit conversion functions for working with :py:class:`astropy.coordinates.SkyCoord`. - Added support for specialized quantities: - :py:class:`astropy.time.Time` - - :py:class:`astropy.time.DeltaTime` + - :py:class:`astropy.time.TimeDelta` - :py:class:`astropy.coordinates.Angle` - :py:class:`astropy.coordinates.Longitude` - :py:class:`astropy.coordinates.Latitude` From ab31a36dd803af231552fcd0e5b90360605142bc Mon Sep 17 00:00:00 2001 From: Callan Gray Date: Tue, 31 Mar 2026 20:41:50 +0800 Subject: [PATCH 3/3] lock to sphinx 8 Signed-off-by: Callan Gray --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 08c9a738..281dc117 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ test = [ "bottleneck", ] docs = [ - "sphinx>=8.0", + "sphinx==8.2.3", "sphinx_auto_summary_accessors", "nbsphinx", "sphinx_rtd_theme",