Skip to content

Commit

Permalink
Merge pull request #357 from davidhassell/dask-units-calendar
Browse files Browse the repository at this point in the history
dask: `Data` units and calendar methods inherited from cfdm
  • Loading branch information
davidhassell authored Mar 22, 2022
2 parents c684ca6 + 9ad43e3 commit 61beb27
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 78 deletions.
166 changes: 116 additions & 50 deletions cf/data/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -7722,7 +7722,7 @@ def get_units(self, default=ValueError()):
The units.
**Examples:**
**Examples**
>>> d.set_units('metres')
>>> d.get_units()
Expand Down Expand Up @@ -7775,7 +7775,8 @@ def get_calendar(self, default=ValueError()):
def set_calendar(self, calendar):
"""Set the calendar.
.. seealso:: `del_calendar`, `get_calendar`
.. seealso:: `override_calendar`, `override_units`,
`del_calendar`, `get_calendar`
:Parameters:
Expand All @@ -7786,7 +7787,7 @@ def set_calendar(self, calendar):
`None`
**Examples:**
**Examples**
>>> d.set_calendar('none')
>>> d.get_calendar
Expand All @@ -7803,7 +7804,8 @@ def set_calendar(self, calendar):
def set_units(self, value):
"""Set the units.
.. seealso:: `del_units`, `get_units`, `has_units`
.. seealso:: `override_units`, `del_units`, `get_units`,
`has_units`, `Units`
:Parameters:
Expand All @@ -7814,7 +7816,7 @@ def set_units(self, value):
`None`
**Examples:**
**Examples**
>>> d.set_units('watt')
>>> d.get_units()
Expand Down Expand Up @@ -9865,6 +9867,70 @@ def harden_mask(self):
self._map_blocks(cf_harden_mask, dtype=self.dtype)
self._hardmask = True

def has_calendar(self):
"""Whether a calendar has been set.
.. seealso:: `del_calendar`, `get_calendar`, `set_calendar`,
`has_units`, `Units`
:Returns:
`bool`
True if the calendar has been set, otherwise False.
**Examples**
>>> d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
>>> d.has_calendar()
True
>>> d = cf.Data(1, calendar="noleap")
>>> d.has_calendar()
True
>>> d = cf.Data(1, "days since 2000-1-1")
>>> d.has_calendar()
False
>>> d = cf.Data(1, "m")
>>> d.has_calendar()
False
"""
return hasattr(self.Units, "calendar")

def has_units(self):
"""Whether units have been set.
.. seealso:: `del_units`, `get_units`, `set_units`,
`has_calendar`, `Units`
:Returns:
`bool`
True if units have been set, otherwise False.
**Examples**
>>> d = cf.Data(1, "")
>>> d.has_units()
True
>>> d = cf.Data(1, "m")
>>> d.has_units()
True
>>> d = cf.Data(1)
>>> d.has_units()
False
>>> d = cf.Data(1, calendar='noleap')
>>> d.has_units()
False
"""
return hasattr(self.Units, "units")

def soften_mask(self):
"""Force the mask to soft.
Expand Down Expand Up @@ -10819,7 +10885,7 @@ def del_calendar(self, default=ValueError()):
"""Delete the calendar.
.. seealso:: `get_calendar`, `has_calendar`, `set_calendar`,
`del_units`
`del_units`, `Units`
:Parameters:
Expand All @@ -10834,38 +10900,42 @@ def del_calendar(self, default=ValueError()):
`str`
The value of the deleted calendar.
**Examples:**
**Examples**
>>> d.set_calendar('360_day')
>>> d.has_calendar()
True
>>> d.get_calendar()
'360_day'
>>> d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
>>> d.del_calendar()
>>> d.has_calendar()
False
>>> d.get_calendar()
ValueError: Can't get non-existent calendar
>>> print(d.get_calendar(None))
'noleap'
>>> print(d.del_calendar())
None
>>> print(d.del_calendar(None))
>>> d = cf.Data(1, "days since 2000-1-1")
>>> print(d.del_calendar())
None
>>> d = cf.Data(1, "m")
Traceback (most recent call last):
...
ValueError: Units <Units: m> have no calendar
"""
calendar = getattr(self.Units, "calendar", None)
units = self.Units
if not units.isreftime:
return self._default(default, f"Units {units!r} have no calendar")

if calendar is not None:
self.override_calendar(None, inplace=True)
return calendar
calendar = getattr(units, "calendar", None)
if calendar is None:
return self._default(
default, f"{self.__class__.__name__} has no calendar"
)

raise self._default(
default, f"{self.__class__.__name__} has no 'calendar' component"
)
self.override_calendar(None, inplace=True)
return calendar

def del_units(self, default=ValueError()):
"""Delete the units.
.. seealso:: `get_units`, `has_units`, `set_units`, `del_calendar`
.. seealso:: `get_units`, `has_units`, `set_units`,
`del_calendar`, `Units`
:Parameters:
Expand All @@ -10880,39 +10950,35 @@ def del_units(self, default=ValueError()):
`str`
The value of the deleted units.
**Examples:**
**Examples**
>>> d.set_units('metres')
>>> d.has_units()
True
>>> d.get_units()
'metres'
>>> d = cf.Data(1, "m")
>>> d.del_units()
>>> d.has_units()
False
>>> d.get_units()
ValueError: Can't get non-existent units
>>> print(d.get_units(None))
None
>>> print(d.del_units(None))
None
"""
out = self.Units
'm'
>>> d.Units
<Units: >
>>> d.del_units()
Traceback (most recent call last):
...
ValueError: Data has no units
units = getattr(out, "units", None)
calendar = getattr(out, "calendar", None)
>>> d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
>>> d.del_units()
'days since 2000-1-1'
>>> d.Units
<Units: noleap>
if calendar is not None:
self.Units = Units(None, calendar)
else:
del self.Units
"""
u = self.Units
units = getattr(u, "units", None)
calendar = getattr(u, "calendar", None)
self.override_units(Units(None, calendar), inplace=True)

if units is not None:
return units

return self._default(
default, f"{self.__class__.__name__} has no 'units' component"
default, f"{self.__class__.__name__} has no units"
)

@classmethod
Expand Down
92 changes: 64 additions & 28 deletions cf/test/test_Data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3592,67 +3592,66 @@ def test_Data_filled(self):
d = cf.Data(["a", "b", "c"], mask=[1, 0, 0])
self.assertTrue((d.filled().array == ["", "b", "c"]).all())

@unittest.skipIf(TEST_DASKIFIED_ONLY, "units-related problem")
def test_Data_del_units(self):
d = cf.Data(1)
with self.assertRaises(ValueError):
d.del_units()

d = cf.Data(1, "")
self.assertEqual(d.del_units(), "")
d = cf.Data(1, "m")
self.assertEqual(d.del_units(), "m")
with self.assertRaises(ValueError):
d.del_units()

d = cf.Data(1, "days since 2000-1-1")
self.assertTrue(d.del_units(), "days since 2000-1-1")
self.assertEqual(d.del_units(), "days since 2000-1-1")
with self.assertRaises(ValueError):
d.del_units()

d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertEqual(d.del_units(), "days since 2000-1-1")
self.assertEqual(d.Units, cf.Units(None, "noleap"))
with self.assertRaises(ValueError):
d.del_units()

def test_Data_del_calendar(self):
d = cf.Data(1)
with self.assertRaises(ValueError):
d.del_calendar()

d = cf.Data(1, "")
with self.assertRaises(ValueError):
d.del_calendar()
for units in (None, "", "m", "days since 2000-1-1"):
d = cf.Data(1, units)
with self.assertRaises(ValueError):
d.del_calendar()

d = cf.Data(1, "m")
d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertEqual(d.del_calendar(), "noleap")
with self.assertRaises(ValueError):
d.del_calendar()

d = cf.Data(1, "days since 2000-1-1")
with self.assertRaises(ValueError):
d.del_calendar()
def test_Data_get_calendar(self):
for units in (None, "", "m", "days since 2000-1-1"):
d = cf.Data(1, units)
with self.assertRaises(ValueError):
d.get_calendar()

d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertTrue(d.del_calendar(), "noleap")
self.assertTrue(d.get_calendar(), "noleap")

@unittest.skipIf(TEST_DASKIFIED_ONLY, "units-related problem")
def test_Data_has_units(self):
d = cf.Data(1)
self.assertFalse(d.has_units())
d = cf.Data(1, "")
self.assertTrue(d.has_units())
d = cf.Data(1, "m")
self.assertTrue(d.has_units())

@unittest.skipIf(TEST_DASKIFIED_ONLY, "units-related problem")
def test_Data_has_calendar(self):
d = cf.Data(1)
self.assertFalse(d.has_calendar())
d = cf.Data(1, "")
self.assertFalse(d.has_calendar())
d = cf.Data(1, "m")
self.assertFalse(d.has_calendar())
self.assertFalse(d.has_units())
d = cf.Data(1, calendar="noleap")
self.assertFalse(d.has_units())

d = cf.Data(1, "days since 2000-1-1")
self.assertFalse(d.has_calendar())
def test_Data_has_calendar(self):
d = cf.Data(1, "days since 2000-1-1", calendar="noleap")
self.assertTrue(d.has_calendar())

for units in (None, "", "m", "days since 2000-1-1"):
d = cf.Data(1, units)
self.assertFalse(d.has_calendar())

def test_Data_where(self):
a = np.arange(10)
d = cf.Data(a)
Expand Down Expand Up @@ -3891,6 +3890,43 @@ def test_Data_rechunk(self):
self.assertEqual(e.chunks, ((4,), (5,)))
self.assertTrue(e.equals(d))

def test_Data_get_units(self):
for units in ("", "m", "days since 2000-01-01"):
d = cf.Data(1, units)
self.assertEqual(d.get_units(), units)

d = cf.Data(1)
with self.assertRaises(ValueError):
d.get_units()

def test_Data_set_calendar(self):
d = cf.Data(1, "days since 2000-01-01")
d.set_calendar("standard")

with self.assertRaises(ValueError):
d.set_calendar("noleap")

d = cf.Data(1, "m")
d.set_calendar("noleap")
self.assertEqual(d.Units, cf.Units("m"))

def test_Data_set_units(self):
for units in (None, "", "m", "days since 2000-01-01"):
d = cf.Data(1, units)
self.assertEqual(d.Units, cf.Units(units))

d = cf.Data(1, "m")
d.set_units("km")
self.assertEqual(d.array, 0.001)

d = cf.Data(1, "days since 2000-01-01", calendar="noleap")
d.set_units("days since 1999-12-31")
self.assertEqual(d.array, 2)

# Can't set to Units that are not equivalent
with self.assertRaises(ValueError):
d.set_units("km")


if __name__ == "__main__":
print("Run date:", datetime.datetime.now())
Expand Down

0 comments on commit 61beb27

Please sign in to comment.