Skip to content

WIP: convert to/from cdms2 variables #236

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

Merged
merged 2 commits into from
Dec 19, 2014
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
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ matrix:
include:
- python: 2.6
env: UPDATE_ENV="conda install unittest2 pandas==0.15.0"
# Test on Python 2.7 with and without netCDF4/scipy
# Test on Python 2.7 with and without netCDF4/scipy/cdat-lite
- python: 2.7
env: UPDATE_ENV="pip install cyordereddict"
env: UPDATE_ENV="pip install cyordereddict && conda install -c ajdawson cdat-lite"
- python: 2.7
# nb. we have to remove scipy because conda install pandas brings it in:
# https://github.com/ContinuumIO/anaconda-issues/issues/145
Expand Down Expand Up @@ -42,7 +42,7 @@ before_install:
install:
- conda create --yes -n test_env python=$TRAVIS_PYTHON_VERSION pip nose numpy pandas scipy netCDF4
- source activate test_env
- echo $UPDATE_ENV; $UPDATE_ENV
- echo $UPDATE_ENV; eval $UPDATE_ENV
- python setup.py install

script:
Expand Down
2 changes: 2 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -272,5 +272,7 @@ IO / Conversion
DataArray.to_series
DataArray.to_dataframe
DataArray.to_index
DataArray.to_cdms2
DataArray.from_series
DataArray.from_cdms2
DataArray.load_data
4 changes: 2 additions & 2 deletions xray/conventions.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def infer_datetime_units(dates):
'hours', 'minutes' or 'seconds' (the first one that can evenly divide all
unique time deltas in `dates`)
"""
dates = pd.to_datetime(dates, box=False)
dates = pd.to_datetime(np.asarray(dates), box=False)
unique_timedeltas = np.unique(np.diff(dates[pd.notnull(dates)]))
units = _infer_time_units_from_diff(unique_timedeltas)
return '%s since %s' % (units, pd.Timestamp(dates[0]))
Expand All @@ -185,7 +185,7 @@ def infer_timedelta_units(deltas):
{'days', 'hours', 'minutes' 'seconds'} (the first one that can evenly
divide all unique time deltas in `deltas`)
"""
deltas = pd.to_timedelta(deltas, box=False)
deltas = pd.to_timedelta(np.asarray(deltas), box=False)
unique_timedeltas = np.unique(deltas[pd.notnull(deltas)])
units = _infer_time_units_from_diff(unique_timedeltas)
return units
Expand Down
51 changes: 51 additions & 0 deletions xray/convert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Functions for converting to and from xray objects
"""
import numpy as np

from .core.dataarray import DataArray
from .conventions import (
maybe_encode_timedelta, maybe_encode_datetime, decode_cf)

ignored_attrs = set(['name', 'tileIndex'])


def from_cdms2(variable):
"""Convert a cdms2 variable into an DataArray
"""
def get_cdms2_attrs(var):
return dict((k, v) for k, v in var.attributes.items()
if k not in ignored_attrs)

values = np.asarray(variable)
name = variable.id
coords = [(v.id, np.asarray(v), get_cdms2_attrs(v))
for v in variable.getAxisList()]
attrs = get_cdms2_attrs(variable)
dataarray = DataArray(values, coords=coords, name=name, attrs=attrs)
return decode_cf(dataarray.to_dataset())[dataarray.name]


def to_cdms2(dataarray):
"""Convert a DataArray into a cdms2 variable
"""
# we don't want cdms2 to be a hard dependency
import cdms2

def encode(var):
return maybe_encode_timedelta(maybe_encode_datetime(var))

def set_cdms2_attrs(var, attrs):
for k, v in attrs.items():
setattr(var, k, v)

axes = []
for dim in dataarray.dims:
coord = encode(dataarray.coords[dim])
axis = cdms2.createAxis(coord.values, id=dim)
set_cdms2_attrs(axis, coord.attrs)
axes.append(axis)

var = encode(dataarray)
cdms2_var = cdms2.createVariable(var.values, axes=axes, id=dataarray.name)
set_cdms2_attrs(cdms2_var, var.attrs)
return cdms2_var
13 changes: 13 additions & 0 deletions xray/core/dataarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,19 @@ def from_series(cls, series):
ds = Dataset.from_dataframe(df)
return cls._new_from_dataset_no_copy(ds, series.name)

def to_cdms2(self):
"""Convert this array into a cdms2.Variable
"""
from ..convert import to_cdms2
return to_cdms2(self)

@classmethod
def from_cdms2(cls, variable):
"""Convert a cdms2.Variable into an xray.DataArray
"""
from ..convert import from_cdms2
return from_cdms2(variable)

def _all_compat(self, other, compat_str):
"""Helper function for equals and identical"""
compat = lambda x, y: getattr(x.variable, compat_str)(y.variable)
Expand Down
28 changes: 28 additions & 0 deletions xray/test/test_dataarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,34 @@ def test_to_and_from_series(self):
self.assertDataArrayIdentical(expected_da,
DataArray.from_series(actual))

def test_to_and_from_cdms2(self):
try:
import cdms2
except ImportError:
raise unittest.SkipTest('cdms2 not installed')

original = DataArray(np.arange(6).reshape(2, 3),
[('distance', [-2, 2], {'units': 'meters'}),
('time', pd.date_range('2000-01-01', periods=3))],
name='foo', attrs={'baz': 123})
expected_coords = [Coordinate('distance', [-2, 2]),
Coordinate('time', [0, 1, 2])]
actual = original.to_cdms2()
self.assertArrayEqual(actual, original)
self.assertEqual(actual.id, original.name)
self.assertItemsEqual(actual.getAxisIds(), original.dims)
for axis, coord in zip(actual.getAxisList(), expected_coords):
self.assertEqual(axis.id, coord.name)
self.assertArrayEqual(axis, coord.values)
self.assertEqual(actual.baz, original.attrs['baz'])

component_times = actual.getAxis(1).asComponentTime()
self.assertEqual(len(component_times), 3)
self.assertEqual(str(component_times[0]), '2000-1-1 0:0:0.0')

roundtripped = DataArray.from_cdms2(actual)
self.assertDataArrayIdentical(original, roundtripped)

def test_to_dataset(self):
unnamed = DataArray([1, 2], dims='x')
actual = unnamed.to_dataset()
Expand Down