pymodaq_data 5.0.4__tar.gz → 5.0.6__tar.gz
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.
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/PKG-INFO +1 -1
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/_version.py +2 -2
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/data.py +232 -39
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/data_saving.py +2 -2
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/numpy_func.py +14 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/slicing.py +8 -5
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/.gitignore +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/LICENSE +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/README.rst +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/pyproject.toml +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/backends.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/browsing.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/exporter.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/exporters/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/exporters/base.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/exporters/flimj.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/exporters/hyperspy.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/saving.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/h5modules/utils.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/icon.ico +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/plotting/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/plotting/plotter/plotter.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/plotting/plotter/plotters/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/plotting/plotter/plotters/matplotlib_plotters.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/post_treatment/__init__.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/post_treatment/process_to_scalar.py +0 -0
- {pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/splash.png +0 -0
|
@@ -20,6 +20,7 @@ import warnings
|
|
|
20
20
|
from time import time
|
|
21
21
|
import copy
|
|
22
22
|
import pint
|
|
23
|
+
from pint.compat import upcast_type_map
|
|
23
24
|
|
|
24
25
|
from multipledispatch import dispatch
|
|
25
26
|
|
|
@@ -158,6 +159,24 @@ class DataDistribution(BaseEnum):
|
|
|
158
159
|
spread = 1
|
|
159
160
|
|
|
160
161
|
|
|
162
|
+
def _compute_slices_from_axis(axis: Axis, _slice, *ignored, is_index=True, **ignored_also):
|
|
163
|
+
if not is_index:
|
|
164
|
+
if isinstance(_slice, numbers.Number):
|
|
165
|
+
if not is_index:
|
|
166
|
+
_slice = axis.find_index(_slice)
|
|
167
|
+
elif _slice is Ellipsis:
|
|
168
|
+
return _slice
|
|
169
|
+
elif isinstance(_slice, slice):
|
|
170
|
+
if not (_slice.start is None and
|
|
171
|
+
_slice.stop is None and _slice.step is None):
|
|
172
|
+
start = axis.find_index(
|
|
173
|
+
_slice.start if _slice.start is not None else axis.get_data()[0])
|
|
174
|
+
stop = axis.find_index(
|
|
175
|
+
_slice.stop if _slice.stop is not None else axis.get_data()[-1])
|
|
176
|
+
_slice = slice(start, stop)
|
|
177
|
+
return _slice
|
|
178
|
+
|
|
179
|
+
|
|
161
180
|
class Axis:
|
|
162
181
|
"""Object holding info and data about physical axis of some data
|
|
163
182
|
|
|
@@ -195,6 +214,7 @@ class Axis:
|
|
|
195
214
|
super().__init__()
|
|
196
215
|
|
|
197
216
|
self.iaxis: Axis = SpecialSlicersData(self, False)
|
|
217
|
+
self.vaxis: Axis = SpecialSlicersData(self, False, False)
|
|
198
218
|
|
|
199
219
|
self._size = size
|
|
200
220
|
self._data = None
|
|
@@ -212,13 +232,22 @@ class Axis:
|
|
|
212
232
|
if (scaling is None or offset is None or size is None) and data is not None:
|
|
213
233
|
self.get_scale_offset_from_data(data)
|
|
214
234
|
|
|
235
|
+
@staticmethod
|
|
236
|
+
def from_quantity(quantity: Q_[np.ndarray], label='axis', index=0) -> Axis:
|
|
237
|
+
return Axis(label, str(quantity.units), data=quantity.magnitude,
|
|
238
|
+
index=index)
|
|
239
|
+
|
|
215
240
|
def copy(self):
|
|
216
241
|
return copy.copy(self)
|
|
217
242
|
|
|
218
|
-
def as_dwa(self) -> DataWithAxes:
|
|
219
|
-
dwa = DataRaw(self.label,
|
|
243
|
+
def as_dwa(self, set_itself_as_axis=False) -> DataWithAxes:
|
|
244
|
+
dwa = DataRaw(self.label, units=self.units,
|
|
245
|
+
data=[self.get_data()],
|
|
220
246
|
labels=[f'{self.label}_{self.units}'])
|
|
221
|
-
|
|
247
|
+
if not set_itself_as_axis:
|
|
248
|
+
dwa.create_missing_axes()
|
|
249
|
+
else:
|
|
250
|
+
dwa.axes = [self.copy()]
|
|
222
251
|
return dwa
|
|
223
252
|
|
|
224
253
|
@property
|
|
@@ -234,16 +263,54 @@ class Axis:
|
|
|
234
263
|
|
|
235
264
|
@property
|
|
236
265
|
def units(self) -> str:
|
|
237
|
-
"""str: get/set the units for this axis
|
|
266
|
+
"""str: get/set the units for this axis without conversion (equivalent to
|
|
267
|
+
force_units)"""
|
|
238
268
|
return self._units
|
|
239
269
|
|
|
240
270
|
@units.setter
|
|
241
271
|
def units(self, units: str):
|
|
242
|
-
|
|
243
|
-
|
|
272
|
+
self.force_units(units)
|
|
273
|
+
|
|
274
|
+
def force_units(self, units: str):
|
|
275
|
+
""" Change immediately the units to whatever else. Use this with care!"""
|
|
244
276
|
units = check_units(units)
|
|
245
277
|
self._units = units
|
|
246
278
|
|
|
279
|
+
def units_as(self, units: str, inplace=True, context: str = None, **context_kwargs):
|
|
280
|
+
if inplace:
|
|
281
|
+
#self._units = units
|
|
282
|
+
if context is None:
|
|
283
|
+
self.data = self.get_quantity().to(units).magnitude
|
|
284
|
+
else:
|
|
285
|
+
self.data = self.get_quantity().to(units, context, **context_kwargs).magnitude
|
|
286
|
+
self.force_units(units)
|
|
287
|
+
return
|
|
288
|
+
else:
|
|
289
|
+
if context is not None:
|
|
290
|
+
return Axis(self.label, units,
|
|
291
|
+
data=self.get_quantity().to(units, context, **context_kwargs).magnitude)
|
|
292
|
+
else:
|
|
293
|
+
return Axis(self.label, units,
|
|
294
|
+
data=self.get_quantity().to(units))
|
|
295
|
+
|
|
296
|
+
def to_reduced_units(self, inplace=False):
|
|
297
|
+
quantity = self.get_quantity().to_reduced_units()
|
|
298
|
+
if inplace:
|
|
299
|
+
self.data = quantity.magnitude
|
|
300
|
+
self.force_units(str(quantity.units))
|
|
301
|
+
else:
|
|
302
|
+
return Axis(self.label, units=str(quantity.units),
|
|
303
|
+
data=quantity.magnitude)
|
|
304
|
+
|
|
305
|
+
def to_base_units(self, inplace=False):
|
|
306
|
+
quantity = self.get_quantity().to_base_units()
|
|
307
|
+
if inplace:
|
|
308
|
+
self.data = quantity.magnitude
|
|
309
|
+
self.force_units(str(quantity.units))
|
|
310
|
+
else:
|
|
311
|
+
return Axis(self.label, units=str(quantity.units),
|
|
312
|
+
data=quantity.magnitude)
|
|
313
|
+
|
|
247
314
|
@property
|
|
248
315
|
def index(self) -> int:
|
|
249
316
|
"""int: get/set the index this axis corresponds to in a DataWithAxis object"""
|
|
@@ -260,7 +327,7 @@ class Axis:
|
|
|
260
327
|
return self._data
|
|
261
328
|
|
|
262
329
|
@data.setter
|
|
263
|
-
def data(self, data: np.ndarray):
|
|
330
|
+
def data(self, data: Union[np.ndarray, Q_]):
|
|
264
331
|
if data is not None:
|
|
265
332
|
self._check_data_valid(data)
|
|
266
333
|
self.get_scale_offset_from_data(data)
|
|
@@ -273,6 +340,10 @@ class Axis:
|
|
|
273
340
|
"""Convenience method to obtain the axis data (usually None because scaling and offset are used)"""
|
|
274
341
|
return self._data if self._data is not None else self._linear_data(self.size)
|
|
275
342
|
|
|
343
|
+
def get_quantity(self) -> Q_:
|
|
344
|
+
""" Convenience method to obtain the numerical data as a quantity array"""
|
|
345
|
+
return Q_(self.get_data(), self.units)
|
|
346
|
+
|
|
276
347
|
def get_data_at(self, indexes: Union[int, IterableType, slice]) -> np.ndarray:
|
|
277
348
|
""" Get data at specified indexes
|
|
278
349
|
|
|
@@ -346,8 +417,10 @@ class Axis:
|
|
|
346
417
|
elif index < 0:
|
|
347
418
|
raise ValueError('index for the Axis class should be a positive integer')
|
|
348
419
|
|
|
349
|
-
|
|
350
|
-
|
|
420
|
+
def _check_data_valid(self, data: Union[np.ndarray, Q_]):
|
|
421
|
+
if isinstance(data, Q_):
|
|
422
|
+
self.units = str(data.units)
|
|
423
|
+
data = data.magnitude
|
|
351
424
|
if not isinstance(data, np.ndarray):
|
|
352
425
|
raise TypeError(f'data for the Axis class should be a 1D numpy array')
|
|
353
426
|
elif len(data.shape) != 1:
|
|
@@ -368,12 +441,14 @@ class Axis:
|
|
|
368
441
|
def __len__(self):
|
|
369
442
|
return self.size
|
|
370
443
|
|
|
371
|
-
def _compute_slices(self,
|
|
372
|
-
|
|
444
|
+
def _compute_slices(self, _slice, *ignored, is_index=True, **ignored_also):
|
|
445
|
+
_slice = _compute_slices_from_axis(self, _slice, is_index=is_index)
|
|
446
|
+
return _slice, _slice
|
|
373
447
|
|
|
374
|
-
def _slicer(self, _slice, *ignored, **ignored_also):
|
|
448
|
+
def _slicer(self, _slice, *ignored, is_index=True, **ignored_also):
|
|
375
449
|
ax: Axis = copy.deepcopy(self)
|
|
376
|
-
|
|
450
|
+
_slice, _slice = self._compute_slices(_slice, is_index=is_index)
|
|
451
|
+
if isinstance(_slice, numbers.Number):
|
|
377
452
|
ax.data = np.array([ax.get_data()[_slice]])
|
|
378
453
|
return ax
|
|
379
454
|
elif _slice is Ellipsis:
|
|
@@ -442,6 +517,11 @@ class Axis:
|
|
|
442
517
|
else:
|
|
443
518
|
return self.offset + self.size / 2 * self.scaling
|
|
444
519
|
|
|
520
|
+
def flip(self):
|
|
521
|
+
""" flip the direction of the axis"""
|
|
522
|
+
self.data = self.get_data()[::-1]
|
|
523
|
+
|
|
524
|
+
|
|
445
525
|
def min(self):
|
|
446
526
|
if self._data is not None:
|
|
447
527
|
return np.min(self._data)
|
|
@@ -533,8 +613,9 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
533
613
|
distribution: DataDistribution or str
|
|
534
614
|
The distribution type of the data: uniform if distributed on a regular grid or spread if on
|
|
535
615
|
specific unordered points
|
|
536
|
-
data: list of ndarray
|
|
537
|
-
The data the object is storing
|
|
616
|
+
data: list of ndarray or Quantities
|
|
617
|
+
The data the object is storing. In case of Quantities, the object units attribute will
|
|
618
|
+
be forced to the unit of this quantity, ignoring the units argument.
|
|
538
619
|
labels: list of str
|
|
539
620
|
The labels of the data nd-arrays
|
|
540
621
|
origin: str
|
|
@@ -601,6 +682,13 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
601
682
|
|
|
602
683
|
base_type = 'Data'
|
|
603
684
|
|
|
685
|
+
def __new__(cls, *args, **kwargs):
|
|
686
|
+
if not pint.compat.is_upcast_type(cls):
|
|
687
|
+
upcast_type_map.update({f'{cls.__module__}.{cls.__name__}': None})
|
|
688
|
+
# this will make sure, these
|
|
689
|
+
# objects are not wrapped by pint
|
|
690
|
+
return super().__new__(cls)
|
|
691
|
+
|
|
604
692
|
def __init__(self, name: str,
|
|
605
693
|
source: DataSource = None, dim: DataDim = None,
|
|
606
694
|
distribution: DataDistribution = DataDistribution.uniform,
|
|
@@ -642,7 +730,8 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
642
730
|
units = check_units(units)
|
|
643
731
|
self.units_as(units, inplace=True)
|
|
644
732
|
|
|
645
|
-
def units_as(self, units: str, inplace=True
|
|
733
|
+
def units_as(self, units: str, inplace=True, context: str = None,
|
|
734
|
+
**context_kwargs) -> 'DataBase':
|
|
646
735
|
""" Set the object units to the new one (if possible)
|
|
647
736
|
|
|
648
737
|
Parameters
|
|
@@ -653,11 +742,18 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
653
742
|
default True.
|
|
654
743
|
If True replace the data's arrays by array in the new units
|
|
655
744
|
If False, return a new data object
|
|
745
|
+
context: str
|
|
746
|
+
See pint documentation
|
|
656
747
|
"""
|
|
657
748
|
arrays = []
|
|
658
749
|
try:
|
|
659
750
|
for ind_array in range(len(self)):
|
|
660
|
-
|
|
751
|
+
if context is None:
|
|
752
|
+
arrays.append(
|
|
753
|
+
self.quantities[ind_array].to(units).magnitude)
|
|
754
|
+
else:
|
|
755
|
+
arrays.append(
|
|
756
|
+
self.quantities[ind_array].to(units, context, **context_kwargs).magnitude)
|
|
661
757
|
|
|
662
758
|
except pint.errors.DimensionalityError as e:
|
|
663
759
|
raise DataUnitError(
|
|
@@ -678,6 +774,13 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
678
774
|
""" Change immediately the units to whatever else. Use this with care!"""
|
|
679
775
|
self._units = units
|
|
680
776
|
|
|
777
|
+
def to_base_units(self):
|
|
778
|
+
dwa = self.deepcopy()
|
|
779
|
+
data_quantities = [quantity.to_base_units() for quantity in self.quantities]
|
|
780
|
+
dwa.data = [quantity.magnitude for quantity in data_quantities]
|
|
781
|
+
dwa.force_units(str(data_quantities[0].units))
|
|
782
|
+
return dwa
|
|
783
|
+
|
|
681
784
|
def value(self, units: str = None) -> float:
|
|
682
785
|
"""Returns the underlying float value (of the first elt in the data list) if this data
|
|
683
786
|
holds only a float otherwise returns a mean of the underlying data
|
|
@@ -788,6 +891,7 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
788
891
|
units = dwa.units
|
|
789
892
|
ufunc_results = [ufunc(*zipped, **kwargs) for zipped in list(zip(*elts))]
|
|
790
893
|
if isinstance(ufunc_results[0], Q_):
|
|
894
|
+
ufunc_results = [ufunc_result.to_reduced_units() for ufunc_result in ufunc_results]
|
|
791
895
|
units = str(ufunc_results[0].units)
|
|
792
896
|
ufunc_results = [ufunc_result.magnitude for ufunc_result in ufunc_results]
|
|
793
897
|
dwa.data = ufunc_results
|
|
@@ -881,7 +985,13 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
881
985
|
|
|
882
986
|
def angle(self):
|
|
883
987
|
""" Take the phase value of itself"""
|
|
884
|
-
|
|
988
|
+
dwa_angle = np.angle(self)
|
|
989
|
+
dwa_angle.force_units('rad')
|
|
990
|
+
return dwa_angle
|
|
991
|
+
|
|
992
|
+
def unwrap(self):
|
|
993
|
+
""" unwrap the underlying array (should be angles otherwise meaningless)"""
|
|
994
|
+
return np.unwrap(self)
|
|
885
995
|
|
|
886
996
|
def real(self):
|
|
887
997
|
""" Take the real part of itself"""
|
|
@@ -1002,15 +1112,17 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
1002
1112
|
"""Get the data by its index in the list, same as self[index]"""
|
|
1003
1113
|
return self.data[index]
|
|
1004
1114
|
|
|
1005
|
-
|
|
1006
|
-
def _check_data_type(data: List[np.ndarray]) -> List[np.ndarray]:
|
|
1115
|
+
def _check_data_type(self, data: List[Union[np.ndarray, Q_]]) -> List[np.ndarray]:
|
|
1007
1116
|
"""make sure data is a list of nd-arrays"""
|
|
1008
1117
|
is_valid = True
|
|
1009
1118
|
if data is None:
|
|
1010
1119
|
is_valid = False
|
|
1011
1120
|
if not isinstance(data, list):
|
|
1012
1121
|
# try to transform the data to regular type
|
|
1013
|
-
if isinstance(data,
|
|
1122
|
+
if isinstance(data, Q_):
|
|
1123
|
+
self.force_units(str(data.units))
|
|
1124
|
+
data = [data.magnitude]
|
|
1125
|
+
elif isinstance(data, np.ndarray):
|
|
1014
1126
|
warnings.warn(DataTypeWarning(f'Your data should be a list of numpy arrays not just a single numpy'
|
|
1015
1127
|
f' array, wrapping them with a list'))
|
|
1016
1128
|
data = [data]
|
|
@@ -1023,12 +1135,16 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
1023
1135
|
if isinstance(data, list):
|
|
1024
1136
|
if len(data) == 0:
|
|
1025
1137
|
is_valid = False
|
|
1026
|
-
elif not isinstance(data[0], np.ndarray)
|
|
1138
|
+
elif not (isinstance(data[0], np.ndarray) or
|
|
1139
|
+
isinstance(data[0], Q_)):
|
|
1027
1140
|
is_valid = False
|
|
1028
1141
|
elif len(data[0].shape) == 0:
|
|
1029
1142
|
is_valid = False
|
|
1030
1143
|
if not is_valid:
|
|
1031
1144
|
raise TypeError(f'Data should be an non-empty list of non-empty numpy arrays')
|
|
1145
|
+
if isinstance(data[0], Q_):
|
|
1146
|
+
self.force_units(str(data[0].units))
|
|
1147
|
+
data = [array.magnitude for array in data]
|
|
1032
1148
|
return data
|
|
1033
1149
|
|
|
1034
1150
|
def check_shape_from_data(self, data: List[np.ndarray]):
|
|
@@ -1093,7 +1209,7 @@ class DataBase(DataLowLevel, NDArrayOperatorsMixin):
|
|
|
1093
1209
|
return self._data
|
|
1094
1210
|
|
|
1095
1211
|
@data.setter
|
|
1096
|
-
def data(self, data: List[np.ndarray]):
|
|
1212
|
+
def data(self, data: List[Union[np.ndarray, Q_]]):
|
|
1097
1213
|
data = self._check_data_type(data)
|
|
1098
1214
|
self._check_shape_dim_consistency(data)
|
|
1099
1215
|
self._check_same_shape(data)
|
|
@@ -1704,8 +1820,11 @@ class DataWithAxes(DataBase):
|
|
|
1704
1820
|
|
|
1705
1821
|
self.set_axes_manager(self.shape, axes=axes, nav_indexes=nav_indexes, **other_kwargs)
|
|
1706
1822
|
|
|
1707
|
-
self.inav
|
|
1708
|
-
self.isig
|
|
1823
|
+
self.inav = SpecialSlicersData(self, True)
|
|
1824
|
+
self.isig = SpecialSlicersData(self, False)
|
|
1825
|
+
|
|
1826
|
+
self.vnav = SpecialSlicersData(self, True, is_index=False)
|
|
1827
|
+
self.vsig = SpecialSlicersData(self, False, is_index=False)
|
|
1709
1828
|
|
|
1710
1829
|
self.get_dim_from_data_axes() # in DataBase, dim is processed from the shape of data, but if axes are provided
|
|
1711
1830
|
#then use get_dim_from axes
|
|
@@ -1891,7 +2010,32 @@ class DataWithAxes(DataBase):
|
|
|
1891
2010
|
mean = np.array([mean])
|
|
1892
2011
|
dat_mean.append(mean)
|
|
1893
2012
|
return self.deepcopy_with_new_data(dat_mean, remove_axes_index=axis)
|
|
1894
|
-
|
|
2013
|
+
|
|
2014
|
+
def moment(self) -> Tuple[DataWithAxes, DataWithAxes]:
|
|
2015
|
+
""" returns the two first moments of the data over the axis
|
|
2016
|
+
|
|
2017
|
+
only valid for Data1D data
|
|
2018
|
+
|
|
2019
|
+
Returns
|
|
2020
|
+
-------
|
|
2021
|
+
DataCalculated: containing the moment of order 0 (mean)
|
|
2022
|
+
DataCalculated: containing the moment of order 1 (std)
|
|
2023
|
+
"""
|
|
2024
|
+
if self.dim != DataDim.Data1D:
|
|
2025
|
+
raise DataDimError('the moment method is only valid for 1D data')
|
|
2026
|
+
arrays_mean = []
|
|
2027
|
+
arrays_std = []
|
|
2028
|
+
for data in self:
|
|
2029
|
+
mean, std = mutils.my_moment(self.axes[0].get_data(), data)
|
|
2030
|
+
arrays_mean.append(np.array([mean]))
|
|
2031
|
+
arrays_std.append(np.array([std]))
|
|
2032
|
+
|
|
2033
|
+
return (DataCalculated('mean', data=arrays_mean),
|
|
2034
|
+
DataCalculated('std', data=arrays_std))
|
|
2035
|
+
|
|
2036
|
+
|
|
2037
|
+
|
|
2038
|
+
|
|
1895
2039
|
def sum(self, axis: int = 0) -> DataWithAxes:
|
|
1896
2040
|
"""Process the sum of the data on the specified axis and returns the new data
|
|
1897
2041
|
|
|
@@ -1905,7 +2049,7 @@ class DataWithAxes(DataBase):
|
|
|
1905
2049
|
"""
|
|
1906
2050
|
dat_sum = []
|
|
1907
2051
|
for dat in self.data:
|
|
1908
|
-
dat_sum.append(np.sum(dat, axis=axis))
|
|
2052
|
+
dat_sum.append(np.atleast_1d(np.sum(dat, axis=axis)))
|
|
1909
2053
|
return self.deepcopy_with_new_data(dat_sum, remove_axes_index=axis)
|
|
1910
2054
|
|
|
1911
2055
|
def interp(self, new_axis_data: Union[Axis, np.ndarray], **kwargs) -> DataWithAxes:
|
|
@@ -1945,12 +2089,20 @@ class DataWithAxes(DataBase):
|
|
|
1945
2089
|
labels=self.labels)
|
|
1946
2090
|
return new_data
|
|
1947
2091
|
|
|
1948
|
-
def ft(self, axis: int = 0
|
|
2092
|
+
def ft(self, axis: int = 0, axis_label: str = None,
|
|
2093
|
+
axis_units: str = None, labels: List[str] = None) -> DataWithAxes:
|
|
1949
2094
|
"""Process the Fourier Transform of the data on the specified axis and returns the new data
|
|
1950
2095
|
|
|
1951
2096
|
Parameters
|
|
1952
2097
|
----------
|
|
1953
2098
|
axis: int
|
|
2099
|
+
Apply the FT on this axis index
|
|
2100
|
+
axis_label: str
|
|
2101
|
+
A new label for the FT computed axis
|
|
2102
|
+
axis_units: str
|
|
2103
|
+
New units (without conversion on top of the one from the FT) for the computed axis
|
|
2104
|
+
labels: List[str]
|
|
2105
|
+
list of string for new labels
|
|
1954
2106
|
|
|
1955
2107
|
Returns
|
|
1956
2108
|
-------
|
|
@@ -1967,19 +2119,34 @@ class DataWithAxes(DataBase):
|
|
|
1967
2119
|
for dat in self.data:
|
|
1968
2120
|
dat_ft.append(mutils.ft(dat, dim=axis))
|
|
1969
2121
|
new_data = self.deepcopy_with_new_data(dat_ft)
|
|
2122
|
+
if labels is not None:
|
|
2123
|
+
new_data.labels = labels
|
|
1970
2124
|
axis_obj = new_data.get_axis_from_index(axis)[0]
|
|
1971
2125
|
axis_obj.data = omega_grid
|
|
1972
|
-
|
|
1973
|
-
|
|
2126
|
+
if axis_label is not None:
|
|
2127
|
+
axis_obj.label = axis_label
|
|
2128
|
+
else:
|
|
2129
|
+
axis_obj.label = f'ft({axis_obj.label})'
|
|
2130
|
+
if axis_units is not None:
|
|
2131
|
+
axis_obj.force_units(axis_units)
|
|
2132
|
+
else:
|
|
2133
|
+
axis_obj.units = f'rad/{axis_obj.units}'
|
|
1974
2134
|
return new_data
|
|
1975
2135
|
|
|
1976
|
-
def ift(self, axis: int = 0
|
|
2136
|
+
def ift(self, axis: int = 0, axis_label: str = None,
|
|
2137
|
+
axis_units: str = None, labels: List[str] = None) -> DataWithAxes:
|
|
1977
2138
|
"""Process the inverse Fourier Transform of the data on the specified axis and returns the
|
|
1978
2139
|
new data
|
|
1979
2140
|
|
|
1980
2141
|
Parameters
|
|
1981
2142
|
----------
|
|
1982
2143
|
axis: int
|
|
2144
|
+
axis_label: str
|
|
2145
|
+
A new label for the FT computed axis
|
|
2146
|
+
axis_units: str
|
|
2147
|
+
New units (without conversion on top of the one from the FT) for the computed axis
|
|
2148
|
+
labels: List[str]
|
|
2149
|
+
list of string for new labels
|
|
1983
2150
|
|
|
1984
2151
|
Returns
|
|
1985
2152
|
-------
|
|
@@ -1996,10 +2163,18 @@ class DataWithAxes(DataBase):
|
|
|
1996
2163
|
for dat in self.data:
|
|
1997
2164
|
dat_ift.append(mutils.ift(dat, dim=axis))
|
|
1998
2165
|
new_data = self.deepcopy_with_new_data(dat_ift)
|
|
2166
|
+
if labels is not None:
|
|
2167
|
+
new_data.labels = labels
|
|
1999
2168
|
axis_obj = new_data.get_axis_from_index(axis)[0]
|
|
2000
2169
|
axis_obj.data = omega_grid
|
|
2001
|
-
|
|
2002
|
-
|
|
2170
|
+
if axis_label is not None:
|
|
2171
|
+
axis_obj.label = axis_label
|
|
2172
|
+
else:
|
|
2173
|
+
axis_obj.label = f'ift({axis_obj.label})'
|
|
2174
|
+
if axis_units is not None:
|
|
2175
|
+
axis_obj.force_units(axis_units)
|
|
2176
|
+
else:
|
|
2177
|
+
axis_obj.units = str(Unit(f'rad/({axis_obj.units})'))
|
|
2003
2178
|
return new_data
|
|
2004
2179
|
|
|
2005
2180
|
def fit(self, function: Callable, initial_guess: IterableType, data_index: int = None,
|
|
@@ -2199,11 +2374,22 @@ class DataWithAxes(DataBase):
|
|
|
2199
2374
|
axes.append(ax)
|
|
2200
2375
|
self.axes = axes
|
|
2201
2376
|
|
|
2202
|
-
def _compute_slices(self, slices, is_navigation=True):
|
|
2377
|
+
def _compute_slices(self, slices, is_navigation=True, is_index=True):
|
|
2203
2378
|
"""Compute the total slice to apply to the data
|
|
2204
2379
|
|
|
2205
2380
|
Filling in Ellipsis when no slicing should be done
|
|
2381
|
+
Parameters
|
|
2382
|
+
----------
|
|
2383
|
+
slices: List of slice
|
|
2384
|
+
is_navigation: bool
|
|
2385
|
+
is_index: bool
|
|
2386
|
+
if False, the slice are on the values of the underlying axes
|
|
2387
|
+
Returns
|
|
2388
|
+
-------
|
|
2389
|
+
list(slice): the computed slices as index (eventually for all axes)
|
|
2390
|
+
list(slice): a version as index of the input argument
|
|
2206
2391
|
"""
|
|
2392
|
+
_slices_as_index = []
|
|
2207
2393
|
if isinstance(slices, numbers.Number) or isinstance(slices, slice):
|
|
2208
2394
|
slices = [slices]
|
|
2209
2395
|
if is_navigation:
|
|
@@ -2214,13 +2400,18 @@ class DataWithAxes(DataBase):
|
|
|
2214
2400
|
slices = list(slices)
|
|
2215
2401
|
for ind in range(len(self.shape)):
|
|
2216
2402
|
if ind in indexes:
|
|
2217
|
-
|
|
2403
|
+
_slice = slices.pop(0)
|
|
2404
|
+
if not is_index:
|
|
2405
|
+
axis = self.get_axis_from_index(ind)[0]
|
|
2406
|
+
_slice = _compute_slices_from_axis(axis, _slice, is_index=is_index)
|
|
2407
|
+
_slices_as_index.append(_slice)
|
|
2408
|
+
total_slices.append(_slice)
|
|
2218
2409
|
elif len(total_slices) == 0:
|
|
2219
2410
|
total_slices.append(Ellipsis)
|
|
2220
2411
|
elif not (Ellipsis in total_slices and total_slices[-1] is Ellipsis):
|
|
2221
2412
|
total_slices.append(slice(None))
|
|
2222
2413
|
total_slices = tuple(total_slices)
|
|
2223
|
-
return total_slices
|
|
2414
|
+
return total_slices, _slices_as_index
|
|
2224
2415
|
|
|
2225
2416
|
def check_squeeze(self, total_slices: List[slice], is_navigation: bool):
|
|
2226
2417
|
|
|
@@ -2232,7 +2423,7 @@ class DataWithAxes(DataBase):
|
|
|
2232
2423
|
do_squeeze = False
|
|
2233
2424
|
return do_squeeze
|
|
2234
2425
|
|
|
2235
|
-
def _slicer(self, slices, is_navigation=True):
|
|
2426
|
+
def _slicer(self, slices, is_navigation=True, is_index=True):
|
|
2236
2427
|
"""Apply a given slice to the data either navigation or signal dimension
|
|
2237
2428
|
|
|
2238
2429
|
Parameters
|
|
@@ -2241,6 +2432,8 @@ class DataWithAxes(DataBase):
|
|
|
2241
2432
|
the slices to apply to the data
|
|
2242
2433
|
is_navigation: bool
|
|
2243
2434
|
if True apply the slices to the navigation dimension else to the signal ones
|
|
2435
|
+
is_index: bool
|
|
2436
|
+
if True the slices are indexes otherwise the slices are axes values to be indexed first
|
|
2244
2437
|
|
|
2245
2438
|
Returns
|
|
2246
2439
|
-------
|
|
@@ -2248,10 +2441,10 @@ class DataWithAxes(DataBase):
|
|
|
2248
2441
|
Object of the same type as the initial data, derived from DataWithAxes. But with lower
|
|
2249
2442
|
data size due to the slicing and with eventually less axes.
|
|
2250
2443
|
"""
|
|
2251
|
-
|
|
2252
2444
|
if isinstance(slices, numbers.Number) or isinstance(slices, slice):
|
|
2253
2445
|
slices = [slices]
|
|
2254
|
-
|
|
2446
|
+
|
|
2447
|
+
total_slices, slices = self._compute_slices(slices, is_navigation, is_index=is_index)
|
|
2255
2448
|
|
|
2256
2449
|
do_squeeze = self.check_squeeze(total_slices, is_navigation)
|
|
2257
2450
|
new_arrays_data = [squeeze(dat[total_slices], do_squeeze) for dat in self.data]
|
|
@@ -737,7 +737,7 @@ class DataToExportSaver:
|
|
|
737
737
|
def __init__(self, h5saver: Union[H5SaverLowLevel, Path, str]):
|
|
738
738
|
if isinstance(h5saver, Path) or isinstance(h5saver, str):
|
|
739
739
|
h5saver_tmp = H5SaverLowLevel()
|
|
740
|
-
h5saver_tmp.init_file(
|
|
740
|
+
h5saver_tmp.init_file(file_name=Path(h5saver))
|
|
741
741
|
h5saver = h5saver_tmp
|
|
742
742
|
|
|
743
743
|
self._h5saver = h5saver
|
|
@@ -748,7 +748,7 @@ class DataToExportSaver:
|
|
|
748
748
|
return self._h5saver.get_node(where)
|
|
749
749
|
|
|
750
750
|
def close(self):
|
|
751
|
-
self.
|
|
751
|
+
self.close_file()
|
|
752
752
|
|
|
753
753
|
def close_file(self):
|
|
754
754
|
self._h5saver.close_file()
|
|
@@ -84,11 +84,17 @@ def _min(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] =
|
|
|
84
84
|
def _std(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] = None, **kwargs):
|
|
85
85
|
return process_with_reduced_dimensions(np.std, dwa, *args, axis=axis, **kwargs)
|
|
86
86
|
|
|
87
|
+
|
|
87
88
|
@implements("mean")
|
|
88
89
|
def _mean(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] = None, **kwargs):
|
|
89
90
|
return process_with_reduced_dimensions(np.mean, dwa, *args, axis=axis, **kwargs)
|
|
90
91
|
|
|
91
92
|
|
|
93
|
+
@implements("sum")
|
|
94
|
+
def _sum(dwa: 'DataWithAxes', *args, axis: Optional[Union[int, Iterable[int]]] = None, **kwargs):
|
|
95
|
+
return process_with_reduced_dimensions(np.sum, dwa, *args, axis=axis, **kwargs)
|
|
96
|
+
|
|
97
|
+
|
|
92
98
|
# ************* FUNCTIONS that apply with units ********
|
|
93
99
|
|
|
94
100
|
@implements('angle')
|
|
@@ -100,6 +106,14 @@ def _angle(dwa: 'DataWithAxes', *args, **kwargs):
|
|
|
100
106
|
return dwa_func
|
|
101
107
|
|
|
102
108
|
|
|
109
|
+
@implements('unwrap')
|
|
110
|
+
def _unwrap(dwa: 'DataWithAxes', *args, **kwargs):
|
|
111
|
+
dwa_func = dwa.deepcopy_with_new_data(
|
|
112
|
+
data=[np.unwrap(array, *args, **kwargs) for array in dwa.data])
|
|
113
|
+
dwa_func.name += f"_{'unwrap'}"
|
|
114
|
+
return dwa_func
|
|
115
|
+
|
|
116
|
+
|
|
103
117
|
@implements('real')
|
|
104
118
|
def _real(dwa: 'DataWithAxes', *args, **kwargs):
|
|
105
119
|
|
|
@@ -13,12 +13,15 @@ if TYPE_CHECKING:
|
|
|
13
13
|
|
|
14
14
|
class SpecialSlicers(object):
|
|
15
15
|
"""make it elegant to apply a slice to navigation or signal dimensions"""
|
|
16
|
-
def __init__(self, obj: Union['DataWithAxes', 'Axis'], is_navigation):
|
|
16
|
+
def __init__(self, obj: Union['DataWithAxes', 'Axis'], is_navigation, is_index=True):
|
|
17
17
|
self.is_navigation = is_navigation
|
|
18
|
+
self.is_index = is_index
|
|
18
19
|
self.obj = obj
|
|
19
20
|
|
|
20
21
|
def __getitem__(self, slices) -> Union['DataWithAxes', 'Axis']:
|
|
21
|
-
|
|
22
|
+
if hasattr(self.obj, 'axes') and self.obj.distribution.name == 'spread' and not self.is_index:
|
|
23
|
+
raise NotImplementedError('Impossible to slice by value a spread data object')
|
|
24
|
+
return self.obj._slicer(slices, self.is_navigation, is_index=self.is_index)
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
class SpecialSlicersData(SpecialSlicers):
|
|
@@ -26,7 +29,7 @@ class SpecialSlicersData(SpecialSlicers):
|
|
|
26
29
|
def __setitem__(self, slices, data: Union[np.ndarray, 'DataWithAxes', 'Axis']):
|
|
27
30
|
"""x.__setitem__(slices, data) <==> x[slices]=data
|
|
28
31
|
"""
|
|
29
|
-
|
|
32
|
+
total_slices, slices_index = self.obj._compute_slices(slices, self.is_navigation, is_index=self.is_index)
|
|
30
33
|
|
|
31
34
|
if hasattr(self.obj, 'base_type') and self.obj.base_type == 'Axis':
|
|
32
35
|
if isinstance(data, np.ndarray):
|
|
@@ -35,14 +38,14 @@ class SpecialSlicersData(SpecialSlicers):
|
|
|
35
38
|
data_to_replace = data.get_data()
|
|
36
39
|
if hasattr(self.obj, 'units') and self.obj.data is None:
|
|
37
40
|
self.obj.create_linear_data(len(self.obj))
|
|
38
|
-
self.obj.data[
|
|
41
|
+
self.obj.data[total_slices] = data_to_replace
|
|
39
42
|
else:
|
|
40
43
|
for ind in range(len(self.obj)):
|
|
41
44
|
if isinstance(data, np.ndarray):
|
|
42
45
|
data_to_replace = data
|
|
43
46
|
else: # means it's a DataWithAxes
|
|
44
47
|
data_to_replace = data[ind]
|
|
45
|
-
self.obj[ind][
|
|
48
|
+
self.obj[ind][total_slices] = data_to_replace
|
|
46
49
|
|
|
47
50
|
def __len__(self):
|
|
48
51
|
return self.obj.axes_manager.sig_shape[0]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/plotting/plotter/plotters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pymodaq_data-5.0.4 → pymodaq_data-5.0.6}/src/pymodaq_data/post_treatment/process_to_scalar.py
RENAMED
|
File without changes
|
|
File without changes
|