Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 84 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@
A convenience wrapper for using [astropy](https://www.astropy.org) with
[xarray](https://xarray.pydata.org).

## Usage
## Simple Usage

To convert the variables of a `Dataset` to quantities:
To convert the variables of a `Dataset` to quantities, use accessor `.astropy.quantify()`:

```python
In [1]: import astropy_xarray
...: import xarray as xr
```{code-block} python
import astropy_xarray
import xarray as xr

ds = xr.Dataset({"a": ("x", [0, 1, 2]), "b": ("y", [-3, 5, 1], {"units": "m"})})
ds
```

output:

In [2]: ds = xr.Dataset({"a": ("x", [0, 1, 2]), "b": ("y", [-3, 5, 1], {"units": "m"})})
...: ds
Out[2]:
```
<xarray.Dataset> Size: 48B
Dimensions: (x: 3, y: 3)
Dimensions without coordinates: x, y
Expand All @@ -39,12 +43,16 @@ Data variables:
b (y) float64 24B [m] -3.0 5.0 1.0
```

to convert to different units:
to convert to different units, use accessor `.astropy.to()`:

```python
In [4]: c = q.astropy.to({"a": "ms", "b": "km"})
...: c
Out[4]:
```{code-block} python
c = q.astropy.to({"a": "ms", "b": "km"})
c
```

output:

```
<xarray.Dataset> Size: 48B
Dimensions: (x: 3, y: 3)
Dimensions without coordinates: x, y
Expand All @@ -53,7 +61,7 @@ Data variables:
b (y) float64 24B [km] -0.003 0.005 0.001
```

to convert back to non-quantities:
to convert back to non-quantities for portability, use accessor `.astropy.dequantify()`:

```python
In [5]: d = c.astropy.dequantify()
Expand All @@ -67,4 +75,66 @@ Data variables:
b (y) float64 24B -0.003 0.005 0.001
```

## SkyCoord Usage

To convert a `astropy.skyCoord` to a `Dataset`, use `skycoord_to_dataset()`.

```{code-block} python
import astropy.units as u
from astropy.coordinates import ICRS, SkyCoord
from astropy.time import Time

from astropy_xarray.coordinates.sky_coord import (
skycoord_to_dataset,
)

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"]),
}
)
sc
```

output:

```{code-block} python
<xarray.Dataset> Size: 152B
Dimensions: (time: 1, field: 4)
Coordinates:
timestamp (time) float64 8B [utc unix] 2023-11-14T22:13:20.000000000
field_label (field) <U1 16B 'a' 'b' 'c' 'd'
Dimensions without coordinates: time, field
Data variables:
ra (time, field) float64 32B [°] 2.0 6.0 7.0 4.0
dec (time, field) float64 32B [°] 4.0 7.0 4.0 3.0
pm_ra_cosdec (time, field) float64 32B [mas yr⁻¹] 1.0 1.0 1.0 1.0
pm_dec (time, field) float64 32B [mas yr⁻¹] 1.0 1.0 1.0 1.0
Attributes:
frame: {'name': 'icrs', 'representation_type': 'spherical', 'different...
```

To convert SkyCoord-like datasets back to astropy types, use `.astropy.to_skycoord()`:

```python
sc.astropy.to_skycoord()
```

output:

```
<SkyCoord (ICRS): (ra, dec) in deg
[[(2., 4.), (6., 7.), (7., 4.), (4., 3.)]]
(pm_ra_cosdec, pm_dec) in mas / yr
[[(1., 1.), (1., 1.), (1., 1.), (1., 1.)]]>
```

For more, see the [documentation](https://astropy-xarray.readthedocs.io/en/latest/)
4 changes: 2 additions & 2 deletions astropy_xarray/accessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1726,8 +1726,8 @@ def interpolate_na(

return conversion.attach_units(interpolated, units)

def to_sky_coord(self):
"""TODO"""
def to_skycoord(self):
"""Convert a SkyCoord-based Dataset with metadata attributes to a SkyCoord."""
return dataset_to_skycoord(self.ds)


Expand Down
22 changes: 18 additions & 4 deletions astropy_xarray/coordinates/frame.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from importlib.metadata import version
from types import NoneType

import astropy.units as u
Expand Down Expand Up @@ -36,6 +37,7 @@
Supergalactic,
)
from astropy.time import Time
from packaging.version import Version

from astropy_xarray.coordinates.core import (
dump_quantity,
Expand All @@ -53,17 +55,29 @@
load_optional_earthlocation,
)

if Version(version("astropy")) < Version("7.0.0"):

def get_name_compat(representation):
return representation.get_name()

else:
# 7.0.0 and above raises deprecation warnings when using get_name()
def get_name_compat(representation):
return representation.name


def dump_frame(frame: BaseCoordinateFrame, with_data: bool = False) -> dict:
ser = {
"name": frame.name,
"representation_type": frame.representation_type.get_name(),
"differential_type": frame.differential_type.get_name(),
"representation_type": get_name_compat(frame.representation_type),
"differential_type": get_name_compat(frame.differential_type),
}
if frame.has_data:
ser["data"] = {"representation_type": frame.data.get_name()}
ser["data"] = {"representation_type": get_name_compat(frame.data)}
if "s" in frame.data.differentials:
ser["data"]["differential_type"] = frame.data.differentials["s"].get_name()
ser["data"]["differential_type"] = get_name_compat(
frame.data.differentials["s"]
)
if with_data:
for component in frame.data.components:
quantity: u.Quantity = getattr(frame.data, component)
Expand Down
23 changes: 16 additions & 7 deletions astropy_xarray/coordinates/sky_coord.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import numpy as np
import xarray as xr
from astropy.coordinates import SkyCoord
from astropy.utils import ShapedLikeNDArray

from astropy_xarray.coordinates.frame import dump_frame, load_frame, load_representation

_ArrayLike = list | np.ndarray | ShapedLikeNDArray


def _skycoord_representation_component_names(
skycoord: SkyCoord,
Expand Down Expand Up @@ -68,19 +71,26 @@ def _skycoord_components(skycoord: SkyCoord) -> dict[str, u.Quantity]:

def _skycoord_to_dataarrays(
skycoord: SkyCoord,
coords: list[tuple[str, list]] | None = None,
coords: dict[str, tuple[str, _ArrayLike] | _ArrayLike] | None = None,
) -> dict[str, xr.DataArray]:

dims = (
[
coords[key][0] if isinstance(coords[key], tuple) else key
for key in coords.keys()
]
if coords is not None
else None
)
return {
name: xr.DataArray(
data=component, coords=dict(coords) if coords is not None else None
)
name: xr.DataArray(data=component, coords=coords, dims=dims)
for name, component in _skycoord_components(skycoord).items()
}


def skycoord_to_dataset(
skycoord: SkyCoord,
coords: list[tuple[str, np.ndarray]] | None = None,
coords: dict[str, tuple[str, _ArrayLike] | _ArrayLike] | None = None,
) -> xr.Dataset:
"""Convert a SkyCoord object to an xarray Dataset.

Expand All @@ -92,7 +102,7 @@ def skycoord_to_dataset(
quantified dataset.
"""
return xr.Dataset(
coords=dict(coords) if coords is not None else None,
coords=coords if coords is not None else None,
data_vars=_skycoord_to_dataarrays(skycoord, coords),
attrs=dict(
frame=dump_frame(skycoord.frame),
Expand All @@ -105,7 +115,6 @@ def dataset_to_skycoord(ds: xr.Dataset) -> SkyCoord:

Args:
ds: skycoord dataset.
use_frame_names: True if frame component names are used a data_vars. Defaults to True.

Returns:
SkyCoord object.
Expand Down
8 changes: 4 additions & 4 deletions astropy_xarray/tests/test_sky_coord.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,10 @@ def test_skycoord_roundtrip(
== expected_frame_data_diff_name
)

coords = [
("calibrator_name", ["a1", "a2"]),
("time_polynomial", [0]),
]
coords = {
"calibrator_name": ["a1", "a2"],
"time_polynomial": [0],
}
ds = skycoord_to_dataset(expected, coords=coords)

# data_var keys
Expand Down
10 changes: 5 additions & 5 deletions astropy_xarray/tests/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,19 @@ def test_array_ufunc(self, time, dtype):
np.array([1.0, 2.0, 3.0, 4.0]),
"unix",
"utc",
"[unix utc] 1970-01-01T00:00:01.000000000 ... 1970-01-01T0...",
"[utc unix] 1970-01-01T00:00:01.000000000 ... 1970-01-01T0...",
),
(
np.array([1.0, 2.0, 3.0, 4.0]),
"unix_tai",
"utc",
"[unix_tai utc] 1969-12-31T23:59:52.999918210 ... 1969-12-...",
"[utc unix_tai] 1969-12-31T23:59:52.999918210 ... 1969-12-...",
),
(
np.array([1.0, 2.0, 3.0, 4.0]),
"mjd",
"utc",
"[mjd utc] 1858-11-18T00:00:00.000000000 ... 1858-11-21T00...",
"[utc mjd] 1858-11-18T00:00:00.000000000 ... 1858-11-21T00...",
),
(
np.array(
Expand All @@ -97,7 +97,7 @@ def test_array_ufunc(self, time, dtype):
),
"datetime64",
"utc",
"[datetime64 utc] 1970-01-01T00:00:09.000082030 ... 1970-0...",
"[utc datetime64] 1970-01-01T00:00:09.000082030 ... 1970-0...",
),
(
np.array(
Expand All @@ -109,7 +109,7 @@ def test_array_ufunc(self, time, dtype):
),
"iso",
"utc",
"[iso utc] 1970-01-01T00:00:09.000082030 ... 1970-01-01T00...",
"[utc iso] 1970-01-01T00:00:09.000082030 ... 1970-01-01T00...",
),
],
)
Expand Down
30 changes: 15 additions & 15 deletions astropy_xarray/tests/test_visibility_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ def test_simple_visibility_dataset():
field_phase_center=xr.DataTree(
skycoord_to_dataset(
SkyCoord(ra=[0.1] * u.deg, dec=[0.5] * u.deg, frame=ICRS()),
coords=[
("time_poly", [0]),
],
coords={
"time_poly": [0],
},
)
),
# extensions
Expand All @@ -203,10 +203,10 @@ def test_simple_visibility_dataset():
ra=[[0.5], [1.5], [0.2], [0.9], [1.1]] * u.deg,
dec=[[1.2], [0.8], [0.6], [1.0], [1.5]] * u.deg,
),
coords=[
("calibrator_label", ["A", "B", "C", "D", "E"]),
("time_poly", [0]),
],
coords={
"calibrator_label": ["A", "B", "C", "D", "E"],
"time_poly": [0],
},
)
),
),
Expand Down Expand Up @@ -310,10 +310,10 @@ def test_visibility_dataset(skycoords: list[SkyCoord], frame, unit):
radial_velocity=[[1, 1]] * u.pc / u.yr,
frame=ICRS(),
),
coords=[
("phase_center_label", np.array([0])),
("time_poly", np.array([0, 1])),
],
coords={
"phase_center_label": np.array([0]),
"time_poly": np.array([0, 1]),
},
)
),
# extensions
Expand All @@ -323,10 +323,10 @@ def test_visibility_dataset(skycoords: list[SkyCoord], frame, unit):
ra=[[0.5], [1.5], [0.2], [0.9], [1.1]] * u.deg,
dec=[[1.2], [0.8], [0.6], [1.0], [1.5]] * u.deg,
),
coords=[
("calibrator_label", ["A", "B", "C", "D", "E"]),
("time_poly", np.array([0])),
],
coords={
"calibrator_label": ["A", "B", "C", "D", "E"],
"time_poly": np.array([0]),
},
)
),
),
Expand Down
4 changes: 2 additions & 2 deletions astropy_xarray/time_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

def time_inline_repr(time: Time, max_width: int):
value = time.datetime64
scale_repr = f"{time.format} {time.scale}"
scale_repr = f"{time.scale} {time.format}"
if isinstance(value, np.ndarray):
data_repr = astropy_xarray.formatting.format_array_flat(
value, max_width - len(scale_repr) - 3
Expand All @@ -22,7 +22,7 @@ def time_inline_repr(time: Time, max_width: int):

def time_delta_inline_repr(time: TimeDelta, max_width: int):
value = time.value
scale_repr = f"{time.format} {time.scale}"
scale_repr = f"{time.scale} {time.format}"
if isinstance(value, np.ndarray):
data_repr = astropy_xarray.formatting.format_array_flat(
value, max_width - len(scale_repr) - 3
Expand Down
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Dataset
xarray.Dataset.astropy.ffill
xarray.Dataset.astropy.bfill
xarray.Dataset.astropy.interpolate_na
xarray.Dataset.astropy.to_skycoord

DataArray
---------
Expand Down
Loading
Loading