Skip to content

Commit

Permalink
Add unmasked property and filled method
Browse files Browse the repository at this point in the history
  • Loading branch information
mhvk committed Oct 7, 2023
1 parent 4593b18 commit 33162b2
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 2 deletions.
33 changes: 33 additions & 0 deletions astropy/time/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,39 @@ def mask(self):
def masked(self):
return isinstance(self._time.jd1, Masked)

@property
def unmasked(self):
"""Get an instance without the mask.
Note that while one gets a new instance, the underlying data will be shared.
See Also
--------
astropy.time.Time.filled
"""
return (
self._apply(operator.attrgetter("unmasked"))
if self.masked
else self.replicate()
)

def filled(self, fill_value):
"""Get a copy of the underlying data, with masked values filled in.
Parameters
----------
fill_value : object
Value to replace masked values with. Note that if this value is masked
See Also
--------
astropy.time.Time.unmasked
"""
# TODO: once we support Not-a-Time, that can be the default fill_value.
unmasked = self.unmasked.copy()
unmasked[self.mask] = fill_value
return unmasked

def insert(self, obj, values, axis=0):
"""
Insert values before the given indices in the column and return
Expand Down
28 changes: 28 additions & 0 deletions astropy/time/tests/test_mask.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,34 @@ def test_simple():
assert t.masked


def test_unmasked():
t = Time([1, 2, 3], format="cxcsec")
t[2] = np.ma.masked
assert t.masked
t_unmasked = t.unmasked
assert not t_unmasked.masked
assert not hasattr(t_unmasked.value, "mask")
assert not hasattr(t_unmasked.unix, "mask")
assert (t_unmasked.value == t.value.unmasked).all()
# Check that data is shared.
assert np.may_share_memory(t_unmasked._time.jd1, t._time.jd1)


@pytest.mark.parametrize("fill_value", [4, Time(5, format="cxcsec")])
def test_filled(fill_value):
t = Time([1, 2, 3], format="cxcsec")
t[2] = np.ma.masked
# Fill with something suitable.
t_filled = t.filled(fill_value)
assert not t_filled.masked
assert not hasattr(t_filled.value, "mask")
assert not hasattr(t_filled.unix, "mask")
expected = t.value.filled(Time(fill_value, format="cxcsec").value)
assert (t_filled.value == expected).all()
# Check that data is not shared.
assert not np.may_share_memory(t_filled._time.jd1, t._time.jd1)


def test_scalar_init():
t = Time("2000:001")
assert t.masked is False
Expand Down
31 changes: 29 additions & 2 deletions docs/time/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -862,13 +862,40 @@ To set one or more items as missing, assign the special value
>>> print(t)
['2001:020' '2001:040' ——— '2001:080']

One can unset the mask by assigning another special value,
`numpy.ma.nomask`::
If one wants to get unmasked data, one can get those either by just
removing the mask using the `~astropy.time.Time.unmasked` attribute,
or by filling any masked data with a chosen value::

>>> print(t.unmasked)
['2001:020' '2001:040' '2001:060' '2001:080']
>>> t_filled = t.filled('1999:365')
>>> print(t_filled)
['2001:020' '2001:040' '1999:365' '2001:080']

One can also unset the mask on individual elements by assigning
another special value, `numpy.ma.nomask`::

>>> t[2] = np.ma.nomask
>>> print(t)
['2001:020' '2001:040' '2001:060' '2001:080']

A subtle difference between the two approaches is that when one unsets
the mask by setting with `numpy.ma.nomask`, a mask is still present
internally, and hence any output will have a mask as well. In
contrast, using `~astropy.time.Time.unmasked` or
:meth:`~astropy.time.Time.filled` removes all masking, and hence any
output is not masked. The `~astropy.time.Time.masked` property can be
used to check whether or not a mask is in use internally::

>>> t.masked
True
>>> t.value
MaskedNDArray(['2001:020', '2001:040', '2001:060', '2001:080'],
dtype='<U8')
>>> t_filled.masked
False
>>> t_filled.value
array(['2001:020', '2001:040', '1999:365', '2001:080'], dtype='<U8')

.. note:: When setting the mask, actual time data are kept. However,
when *initializing* with a masked array, any masked time
Expand Down

0 comments on commit 33162b2

Please sign in to comment.