legend-pydataobj 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- legend_pydataobj-1.0.0.dist-info/LICENSE +674 -0
- legend_pydataobj-1.0.0.dist-info/METADATA +63 -0
- legend_pydataobj-1.0.0.dist-info/RECORD +26 -0
- legend_pydataobj-1.0.0.dist-info/WHEEL +5 -0
- legend_pydataobj-1.0.0.dist-info/top_level.txt +1 -0
- lgdo/__init__.py +75 -0
- lgdo/_version.py +4 -0
- lgdo/compression/__init__.py +36 -0
- lgdo/compression/base.py +29 -0
- lgdo/compression/generic.py +77 -0
- lgdo/compression/radware.py +579 -0
- lgdo/compression/utils.py +34 -0
- lgdo/compression/varlen.py +449 -0
- lgdo/lgdo_utils.py +196 -0
- lgdo/lh5_store.py +1711 -0
- lgdo/types/__init__.py +30 -0
- lgdo/types/array.py +140 -0
- lgdo/types/arrayofequalsizedarrays.py +133 -0
- lgdo/types/encoded.py +390 -0
- lgdo/types/fixedsizearray.py +43 -0
- lgdo/types/lgdo.py +51 -0
- lgdo/types/scalar.py +59 -0
- lgdo/types/struct.py +108 -0
- lgdo/types/table.py +349 -0
- lgdo/types/vectorofvectors.py +627 -0
- lgdo/types/waveform_table.py +264 -0
@@ -0,0 +1,264 @@
|
|
1
|
+
"""
|
2
|
+
Implements a LEGEND Data Object representing a special
|
3
|
+
:class:`~.lgdo.table.Table` to store blocks of one-dimensional time-series
|
4
|
+
data.
|
5
|
+
"""
|
6
|
+
from __future__ import annotations
|
7
|
+
|
8
|
+
import logging
|
9
|
+
from typing import Any
|
10
|
+
|
11
|
+
import numpy as np
|
12
|
+
|
13
|
+
from .array import Array
|
14
|
+
from .arrayofequalsizedarrays import ArrayOfEqualSizedArrays
|
15
|
+
from .encoded import ArrayOfEncodedEqualSizedArrays, VectorOfEncodedVectors
|
16
|
+
from .table import Table
|
17
|
+
from .vectorofvectors import VectorOfVectors
|
18
|
+
|
19
|
+
log = logging.getLogger(__name__)
|
20
|
+
|
21
|
+
|
22
|
+
class WaveformTable(Table):
|
23
|
+
r"""An LGDO for storing blocks of (1D) time-series data.
|
24
|
+
|
25
|
+
A :class:`WaveformTable` is an LGDO :class:`.Table` with the 3
|
26
|
+
columns ``t0``, ``dt``, and ``values``:
|
27
|
+
|
28
|
+
* ``t0[i]`` is a time offset (relative to a user-defined global reference)
|
29
|
+
for the sample in ``values[i][0]``. Implemented as an LGDO
|
30
|
+
:class:`.Array` with optional attribute ``units``.
|
31
|
+
* ``dt[i]`` is the sampling period for the waveform at ``values[i]``.
|
32
|
+
Implemented as an LGDO :class:`.Array` with optional attribute ``units``.
|
33
|
+
* ``values[i]`` is the ``i``'th waveform in the table. Internally, the
|
34
|
+
waveforms values may be either an LGDO :class:`.ArrayOfEqualSizedArrays`\
|
35
|
+
``<1,1>``, an LGDO :class:`.VectorOfVectors` or
|
36
|
+
:class:`.VectorOfEncodedVectors` that supports waveforms of unequal
|
37
|
+
length. Can optionally be given a ``units`` attribute.
|
38
|
+
|
39
|
+
Note
|
40
|
+
----
|
41
|
+
On-disk and in-memory versions could be different e.g. if a compression
|
42
|
+
routine is used.
|
43
|
+
"""
|
44
|
+
|
45
|
+
def __init__(
|
46
|
+
self,
|
47
|
+
size: int = None,
|
48
|
+
t0: float | Array | np.ndarray = 0,
|
49
|
+
t0_units: str = None,
|
50
|
+
dt: float | Array | np.ndarray = 1,
|
51
|
+
dt_units: str = None,
|
52
|
+
values: ArrayOfEqualSizedArrays | VectorOfVectors | np.ndarray = None,
|
53
|
+
values_units: str = None,
|
54
|
+
wf_len: int = None,
|
55
|
+
dtype: np.dtype = None,
|
56
|
+
attrs: dict[str, Any] = None,
|
57
|
+
) -> None:
|
58
|
+
r"""
|
59
|
+
Parameters
|
60
|
+
----------
|
61
|
+
size
|
62
|
+
sets the number of rows in the table. If ``None``, the size will be
|
63
|
+
determined from the first among `t0`, `dt`, or `values` to return a
|
64
|
+
valid length. If not ``None``, `t0`, `dt`, and values will be
|
65
|
+
resized as necessary to match `size`. If `size` is ``None`` and
|
66
|
+
`t0`, `dt`, and `values` are all non-array-like, a default size of
|
67
|
+
1024 is used.
|
68
|
+
t0
|
69
|
+
:math:`t_0` values to be used (or broadcast) to the `t0` column.
|
70
|
+
t0_units
|
71
|
+
units for the :math:`t_0` values. If not ``None`` and `t0` is an
|
72
|
+
LGDO :class:`.Array`, overrides what's in `t0`.
|
73
|
+
dt
|
74
|
+
:math:`\delta t` values (sampling period) to be used (or
|
75
|
+
broadcasted) to the `t0` column.
|
76
|
+
dt_units
|
77
|
+
units for the `dt` values. If not ``None`` and `dt` is an LGDO
|
78
|
+
:class:`.Array`, overrides what's in `dt`.
|
79
|
+
values
|
80
|
+
The waveform data to be stored in the table. If ``None`` a block of
|
81
|
+
data is prepared based on the `wf_len` and `dtype` arguments.
|
82
|
+
values_units
|
83
|
+
units for the waveform values. If not ``None`` and `values` is an
|
84
|
+
LGDO :class:`.Array`, overrides what's in `values`.
|
85
|
+
wf_len
|
86
|
+
The length of the waveforms in each entry of a table. If ``None``
|
87
|
+
(the default), unequal lengths are assumed and
|
88
|
+
:class:`.VectorOfVectors` is used for the `values` column. Ignored
|
89
|
+
if `values` is a 2D ndarray, in which case ``values.shape[1]`` is
|
90
|
+
used.
|
91
|
+
dtype
|
92
|
+
The NumPy :class:`numpy.dtype` of the waveform data. If `values` is
|
93
|
+
not ``None``, this argument is ignored. If both `values` and
|
94
|
+
`dtype` are ``None``, :class:`numpy.float64` is used.
|
95
|
+
attrs
|
96
|
+
A set of user attributes to be carried along with this LGDO.
|
97
|
+
"""
|
98
|
+
|
99
|
+
if size is None:
|
100
|
+
if hasattr(t0, "__len__"):
|
101
|
+
size = len(t0)
|
102
|
+
elif hasattr(dt, "__len__"):
|
103
|
+
size = len(dt)
|
104
|
+
elif hasattr(values, "__len__"):
|
105
|
+
size = len(values)
|
106
|
+
if size is None:
|
107
|
+
size = 1024
|
108
|
+
|
109
|
+
if not isinstance(t0, Array):
|
110
|
+
shape = (size,)
|
111
|
+
t0_dtype = t0.dtype if hasattr(t0, "dtype") else np.float32
|
112
|
+
nda = (
|
113
|
+
t0 if isinstance(t0, np.ndarray) else np.full(shape, t0, dtype=t0_dtype)
|
114
|
+
)
|
115
|
+
if nda.shape != shape:
|
116
|
+
nda.resize(shape, refcheck=True)
|
117
|
+
t0 = Array(nda=nda)
|
118
|
+
|
119
|
+
if t0_units is not None:
|
120
|
+
t0.attrs["units"] = f"{t0_units}"
|
121
|
+
|
122
|
+
if not isinstance(dt, Array):
|
123
|
+
shape = (size,)
|
124
|
+
dt_dtype = dt.dtype if hasattr(dt, "dtype") else np.float32
|
125
|
+
nda = (
|
126
|
+
dt if isinstance(dt, np.ndarray) else np.full(shape, dt, dtype=dt_dtype)
|
127
|
+
)
|
128
|
+
if nda.shape != shape:
|
129
|
+
nda.resize(shape, refcheck=True)
|
130
|
+
dt = Array(nda=nda)
|
131
|
+
if dt_units is not None:
|
132
|
+
dt.attrs["units"] = f"{dt_units}"
|
133
|
+
|
134
|
+
if not isinstance(
|
135
|
+
values,
|
136
|
+
(
|
137
|
+
ArrayOfEqualSizedArrays,
|
138
|
+
VectorOfVectors,
|
139
|
+
VectorOfEncodedVectors,
|
140
|
+
ArrayOfEncodedEqualSizedArrays,
|
141
|
+
),
|
142
|
+
):
|
143
|
+
if isinstance(values, np.ndarray):
|
144
|
+
try:
|
145
|
+
wf_len = values.shape[1]
|
146
|
+
except Exception:
|
147
|
+
wf_len = None
|
148
|
+
if wf_len is None: # make a VectorOfVectors
|
149
|
+
shape_guess = (size, 100)
|
150
|
+
if dtype is None:
|
151
|
+
dtype = np.dtype(np.float64)
|
152
|
+
if values is None:
|
153
|
+
values = VectorOfVectors(shape_guess=shape_guess, dtype=dtype)
|
154
|
+
else:
|
155
|
+
flattened_data = np.concatenate(values)
|
156
|
+
length = 0
|
157
|
+
cumulative_length = []
|
158
|
+
for i in range(size):
|
159
|
+
length += len(values[i])
|
160
|
+
cumulative_length.append(length)
|
161
|
+
values = VectorOfVectors(
|
162
|
+
flattened_data=flattened_data,
|
163
|
+
cumulative_length=cumulative_length,
|
164
|
+
dtype=dtype,
|
165
|
+
)
|
166
|
+
else: # make a ArrayOfEqualSizedArrays
|
167
|
+
shape = (size, wf_len)
|
168
|
+
if dtype is None:
|
169
|
+
dtype = (
|
170
|
+
values.dtype
|
171
|
+
if hasattr(values, "dtype")
|
172
|
+
else np.dtype(np.float64)
|
173
|
+
)
|
174
|
+
nda = (
|
175
|
+
values
|
176
|
+
if isinstance(values, np.ndarray)
|
177
|
+
else np.zeros(shape, dtype=dtype)
|
178
|
+
)
|
179
|
+
if nda.shape != shape:
|
180
|
+
nda.resize(shape, refcheck=True)
|
181
|
+
values = ArrayOfEqualSizedArrays(dims=(1, 1), nda=nda)
|
182
|
+
if values_units is not None:
|
183
|
+
values.attrs["units"] = f"{values_units}"
|
184
|
+
|
185
|
+
col_dict = {}
|
186
|
+
col_dict["t0"] = t0
|
187
|
+
col_dict["dt"] = dt
|
188
|
+
col_dict["values"] = values
|
189
|
+
super().__init__(size=size, col_dict=col_dict, attrs=attrs)
|
190
|
+
|
191
|
+
@property
|
192
|
+
def values(self) -> ArrayOfEqualSizedArrays | VectorOfVectors:
|
193
|
+
return self["values"]
|
194
|
+
|
195
|
+
@property
|
196
|
+
def values_units(self) -> str:
|
197
|
+
return self.values.attrs.get("units", None)
|
198
|
+
|
199
|
+
@values_units.setter
|
200
|
+
def values_units(self, units) -> None:
|
201
|
+
self.values.attrs["units"] = f"{units}"
|
202
|
+
|
203
|
+
@property
|
204
|
+
def wf_len(self) -> int:
|
205
|
+
if isinstance(self.values, VectorOfVectors):
|
206
|
+
return -1
|
207
|
+
return self.values.nda.shape[1]
|
208
|
+
|
209
|
+
@wf_len.setter
|
210
|
+
def wf_len(self, wf_len) -> None:
|
211
|
+
if isinstance(self.values, VectorOfVectors):
|
212
|
+
return
|
213
|
+
shape = self.values.nda.shape
|
214
|
+
shape = (shape[0], wf_len)
|
215
|
+
self.values.nda.resize(shape, refcheck=True)
|
216
|
+
|
217
|
+
def resize_wf_len(self, new_len: int) -> None:
|
218
|
+
"""Alias for `wf_len.setter`, for when we want to make it clear in
|
219
|
+
the code that memory is being reallocated.
|
220
|
+
"""
|
221
|
+
self.wf_len = new_len
|
222
|
+
|
223
|
+
@property
|
224
|
+
def t0(self) -> Array:
|
225
|
+
return self["t0"]
|
226
|
+
|
227
|
+
@property
|
228
|
+
def t0_units(self) -> str:
|
229
|
+
return self.t0.attrs.get("units", None)
|
230
|
+
|
231
|
+
@t0_units.setter
|
232
|
+
def t0_units(self, units: str) -> None:
|
233
|
+
self.t0.attrs["units"] = f"{units}"
|
234
|
+
|
235
|
+
@property
|
236
|
+
def dt(self) -> Array:
|
237
|
+
return self["dt"]
|
238
|
+
|
239
|
+
@property
|
240
|
+
def dt_units(self) -> str:
|
241
|
+
return self.dt.attrs.get("units", None)
|
242
|
+
|
243
|
+
@dt_units.setter
|
244
|
+
def dt_units(self, units: str) -> None:
|
245
|
+
self.dt.attrs["units"] = f"{units}"
|
246
|
+
|
247
|
+
def __str__(self):
|
248
|
+
npopt = np.get_printoptions()
|
249
|
+
np.set_printoptions(threshold=100)
|
250
|
+
|
251
|
+
string = ""
|
252
|
+
|
253
|
+
for i in range(self.size):
|
254
|
+
string += f"{self.values[i]}, dt={self.dt[i]}"
|
255
|
+
if self.dt_units:
|
256
|
+
string += f" {self.dt_units}"
|
257
|
+
string += f", t0={self.t0[i]}"
|
258
|
+
if self.t0_units:
|
259
|
+
string += f" {self.t0_units}"
|
260
|
+
if i < self.size - 1:
|
261
|
+
string += "\n"
|
262
|
+
|
263
|
+
np.set_printoptions(**npopt)
|
264
|
+
return string
|