Skip to content

Commit

Permalink
Merge pull request #199 from dstansby/epochs-typing
Browse files Browse the repository at this point in the history
Epochs typing
  • Loading branch information
dstansby committed May 26, 2023
2 parents 40e7ed7 + 4803c04 commit e279197
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 409 deletions.
11 changes: 4 additions & 7 deletions benchmarks/benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ class TimeSuite:
of iterating over dictionaries in Python.
"""

params = ([True, False],)
param_names = ["to_np"]

def setup(self, to_np):
def setup(self):
self.epochs = np.ones(1000) * 62567898765432.0
self.epochs_tt2000 = (np.ones(1000) * 186999622360321123).astype(int)

def time_epoch_encode(self, to_np):
def time_epoch_encode(self):
cdfepoch.encode(self.epochs)

def time_epoch_to_datetime(self, to_np):
def time_epoch_to_datetime(self):
cdfepoch.to_datetime(self.epochs)

def time_epoch_to_datetime_tt2000(self, to_np):
def time_epoch_to_datetime_tt2000(self):
cdfepoch.to_datetime(self.epochs_tt2000)
596 changes: 240 additions & 356 deletions cdflib/epochs.py

Large diffs are not rendered by default.

50 changes: 17 additions & 33 deletions cdflib/epochs_astropy.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,14 @@ def convert_to_astropy(epochs, format=None) -> Time:
if format is not None:
return Time(epochs, format=format, precision=9)

if isinstance(epochs, (list, np.ndarray)):
t = type(epochs[0])
else:
t = type(epochs)
epochs = np.array(epochs)

# Determine best format for the input type
if t in (int, np.int64):
if epochs.dtype == np.int64:
return Time(epochs, format="cdf_tt2000", precision=9)
elif t in (complex, np.complex128):
elif epochs.dtype == np.complex128:
return Time(epochs.real, epochs.imag / 1000000000000.0, format="cdf_epoch16", precision=9)
elif t in (float, np.float64):
elif epochs.dtype == np.float64:
return Time(epochs, format="cdf_epoch", precision=9)
else:
raise TypeError("Not sure how to handle type {}".format(type(epochs)))
Expand All @@ -93,15 +90,15 @@ def encode(epochs, iso_8601: bool = True): # @NoSelf
return epochs.strftime("%d-%b-%Y %H:%M:%S.%f")

@staticmethod
def breakdown(epochs, to_np: bool = False):
def breakdown(epochs):
# Returns either a single array, or a array of arrays depending on the input
epochs = CDFAstropy.convert_to_astropy(epochs)
if epochs.format == "cdf_tt2000":
return CDFAstropy.breakdown_tt2000(epochs, to_np)
return CDFAstropy.breakdown_tt2000(epochs)
elif epochs.format == "cdf_epoch":
return CDFAstropy.breakdown_epoch(epochs, to_np)
return CDFAstropy.breakdown_epoch(epochs)
elif epochs.format == "cdf_epoch16":
return CDFAstropy.breakdown_epoch16(epochs, to_np)
return CDFAstropy.breakdown_epoch16(epochs)
raise TypeError("Not sure how to handle type {}".format(type(epochs)))

@staticmethod
Expand All @@ -110,21 +107,16 @@ def to_datetime(cdf_time) -> Time:
return cdf_time.datetime

@staticmethod
def unixtime(cdf_time, to_np: bool = False): # @NoSelf
def unixtime(cdf_time): # @NoSelf
"""
Encodes the epoch(s) into seconds after 1970-01-01. Precision is only
kept to the nearest microsecond.
If to_np is True, then the values will be returned in a numpy array.
"""
epochs = CDFAstropy.convert_to_astropy(cdf_time)
if to_np:
return epochs.unix
else:
return epochs.unix.tolist()
return epochs.unix

@staticmethod
def compute(datetimes, to_np: bool = False): # @NoSelf
def compute(datetimes):
if not isinstance(datetimes[0], list):
datetimes = [datetimes]
cdf_time = []
Expand All @@ -142,10 +134,7 @@ def compute(datetimes, to_np: bool = False): # @NoSelf
remainder_seconds = (d[6] / 1000.0) + (d[7] / 1000000.0) + (d[8] / 1000000000.0) + (d[9] / 1000000000000.0)
astrotime = Time(unix_seconds, remainder_seconds, format="unix", precision=9)
cdf_time.append(astrotime.cdf_epoch16)
if to_np:
return np.array(cdf_time)
else:
return cdf_time
return np.array(cdf_time)

@staticmethod
def findepochrange(epochs, starttime=None, endtime=None): # @NoSelf
Expand All @@ -161,7 +150,7 @@ def findepochrange(epochs, starttime=None, endtime=None): # @NoSelf
return min(indices[0]), max(indices[0])

@staticmethod
def breakdown_tt2000(tt2000, to_np: bool = False):
def breakdown_tt2000(tt2000):
tt2000strings = tt2000.iso
if not isinstance(tt2000strings, (list, np.ndarray)):
tt2000strings = [tt2000strings]
Expand Down Expand Up @@ -191,7 +180,7 @@ def breakdown_tt2000(tt2000, to_np: bool = False):
return times

@staticmethod
def breakdown_epoch16(epochs, to_np: bool = False): # @NoSelf
def breakdown_epoch16(epochs):
epoch16strings = epochs.iso
if not isinstance(epoch16strings, (list, np.ndarray)):
epoch16strings = [epoch16strings]
Expand Down Expand Up @@ -224,7 +213,7 @@ def breakdown_epoch16(epochs, to_np: bool = False): # @NoSelf
return times

@staticmethod
def breakdown_epoch(epochs, to_np: bool = False): # @NoSelf
def breakdown_epoch(epochs):
epochstrings = epochs.iso
if not isinstance(epochstrings, (list, np.ndarray)):
epochstrings = [epochstrings]
Expand All @@ -249,7 +238,7 @@ def breakdown_epoch(epochs, to_np: bool = False): # @NoSelf
return times

@staticmethod
def parse(value, to_np: bool = False): # @NoSelf
def parse(value):
"""
Parses the provided date/time string(s) into CDF epoch value(s).
Expand All @@ -266,8 +255,6 @@ def parse(value, to_np: bool = False): # @NoSelf
The string has to be in the form of
'yyyy-mm-dd hh:mm:ss.mmmuuunnn' (in iso_8601). The string is
the output from encode function.
Specify to_np to True, if the result should be in numpy class.
"""
if not isinstance(value, (list, np.ndarray)):
value = [value]
Expand All @@ -283,7 +270,4 @@ def parse(value, to_np: bool = False): # @NoSelf
if len(subs) == 9:
time_list.append(int(Time(t, precision=9).cdf_tt2000))

if not to_np:
return time_list
else:
return np.array(time_list)
return np.array(time_list)
2 changes: 1 addition & 1 deletion cdflib/xarray_to_cdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ def _unixtime_to_tt2000(unixtime_data):
int(dt.microsecond % 1000),
0,
]
converted_data = cdfepoch.compute(dt_to_convert)
converted_data = float(cdfepoch.compute(dt_to_convert))
else:
converted_data = np.nan

Expand Down
15 changes: 11 additions & 4 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ for older versions of Python, but this is not guarenteed. If you need to
use ``cdflib`` on an older version of Python, please open an issue to
discuss whether the ``cdflib`` maintainers can support this.

Returning arrays
----------------
All ``to_np`` keyword arguments have been removed throughout the library, and the
code now behaves as if ``to_np=True`` throughout. This change has been made to
reduce code omplexity and make maintaining the code easier. If you need outputs
as lists, call ``.tolist()`` on the output array.

``to_np=True`` was the deafult in ``cdfread``, so if you weren't specifying it
behaviour will not change there. ``to_np=False`` was the default in ``epochs``,
so if you weren't specifying it there beahviour **will** change.

Changes to CDF method returns
-----------------------------
Most of the methods that return data from the CDF reader class have had their
Expand Down Expand Up @@ -46,10 +57,6 @@ Other breaking changes
- Removed ``cdflib.cdfepochs.CDFepoch.getLeapSecondLastUpdated``.
Directly inspect `CDFepoch.LTS` instead to get the last date at which a
leapsecond was added.
- All ``to_np`` keyword arguments have been removed in ``cdfread``, and the
code now behaves as if ``to_np=True`` throughout.
This change has been made to reduce code omplexity and make maintaining
the code easier.
- The ``expand`` keyword argument to `cdflib.cdfread.CDF.varget` has been removed.
Use ``CDF.varinq`` to get variable information instead.
- The ``expand`` keyword argument to ``CDF.globalattsget`` and ``CDF.varattsget`` has been removed.
Expand Down
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ disallow_untyped_defs = True
[mypy-cdflib.cdfwrite.*]
disallow_untyped_calls = True
disallow_incomplete_defs = True

[mypy-cdflib.epochs.*]
disallow_untyped_calls = True
9 changes: 2 additions & 7 deletions tests/test_astropy_epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from random import randint

import numpy as np
import pytest
from pytest import approx

from cdflib.epochs_astropy import CDFAstropy as cdfepoch
Expand All @@ -25,9 +24,9 @@ def test_encode_cdfepoch16():
shows a correct answer.
"""
x = cdfepoch.encode(np.complex128(63300946758.000000 + 176214648000.00000j))
x = cdfepoch.encode(np.array(63300946758.000000 + 176214648000.00000j))
assert x == "2005-12-04 20:19:18.176214648"
y = cdfepoch.encode(np.complex128([33300946758.000000 + 106014648000.00000j, 61234543210.000000 + 000011148000.00000j]))
y = cdfepoch.encode(np.array([33300946758.000000 + 106014648000.00000j, 61234543210.000000 + 000011148000.00000j]))
assert y[0] == "1055-04-07 14:59:18.106014648"
assert y[1] == "1940-06-12 03:20:10.000011148"

Expand Down Expand Up @@ -219,7 +218,3 @@ def test_findepochrange_cdftt2000():

assert time_array[index[-1]] <= cdfepoch.compute(test_end)
assert time_array[index[-1] + 1] >= cdfepoch.compute(test_end)


if __name__ == "__main__":
pytest.main([__file__])
2 changes: 1 addition & 1 deletion tests/test_epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def test_encode_cdfepoch16():
x = cdfepoch.encode(np.complex128(63300946758.000000 + 176214648000.00000j))
assert x == "2005-12-04T20:19:18.176214648000"
y = cdfepoch.encode(
np.complex128([33300946758.000000 + 106014648000.00000j, 61234543210.000000 + 000011148000.00000j]), iso_8601=False
np.array([33300946758.000000 + 106014648000.00000j, 61234543210.000000 + 000011148000.00000j]), iso_8601=False
)
assert y[0] == "07-Apr-1055 14:59:18.106.014.648.000"
assert y[1] == "12-Jun-1940 03:20:10.000.011.148.000"
Expand Down

0 comments on commit e279197

Please sign in to comment.