evefile 0.1.0rc2__py3-none-any.whl → 0.2.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.
- evefile/boundaries/__pycache__/evefile.cpython-311.pyc +0 -0
- evefile/boundaries/evefile.py +183 -8
- evefile/controllers/__pycache__/joining.cpython-311.pyc +0 -0
- evefile/controllers/__pycache__/timestamp_mapping.cpython-311.pyc +0 -0
- evefile/controllers/__pycache__/version_mapping.cpython-311.pyc +0 -0
- evefile/controllers/joining.py +63 -65
- evefile/controllers/timestamp_mapping.py +136 -50
- evefile/controllers/version_mapping.py +181 -1
- evefile/entities/__pycache__/data.cpython-311.pyc +0 -0
- evefile/entities/__pycache__/metadata.cpython-311.pyc +0 -0
- evefile/entities/data.py +636 -2
- evefile/entities/metadata.py +205 -2
- {evefile-0.1.0rc2.dist-info → evefile-0.2.0.dist-info}/METADATA +17 -3
- evefile-0.2.0.dist-info/RECORD +30 -0
- {evefile-0.1.0rc2.dist-info → evefile-0.2.0.dist-info}/WHEEL +1 -1
- evefile-0.1.0rc2.dist-info/RECORD +0 -30
- {evefile-0.1.0rc2.dist-info → evefile-0.2.0.dist-info}/licenses/COPYING +0 -0
- {evefile-0.1.0rc2.dist-info → evefile-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {evefile-0.1.0rc2.dist-info → evefile-0.2.0.dist-info}/top_level.txt +0 -0
evefile/entities/data.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
r"""
|
|
2
2
|
|
|
3
3
|
*Entities representing an eveH5 file on the data level.*
|
|
4
4
|
|
|
@@ -55,6 +55,77 @@ the :mod:`evefile.entities.metadata` module.
|
|
|
55
55
|
You may click on the image for a larger view.
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
Array channels
|
|
59
|
+
--------------
|
|
60
|
+
|
|
61
|
+
Array channels in their general form are channels collecting 1D data.
|
|
62
|
+
Typical devices used here are MCAs, but oscilloscopes and vector signal
|
|
63
|
+
analysers (VSA) would be other typical array channels. Hence, for these
|
|
64
|
+
quite different types of array channels, distinct subclasses of the
|
|
65
|
+
generic :class:`ArrayChannelData` class exist, see
|
|
66
|
+
:numref:`Fig. %s <fig-uml_arraychannel_api>`.
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
.. _fig-uml_arraychannel_api:
|
|
70
|
+
|
|
71
|
+
.. figure:: /uml/arraychannel.*
|
|
72
|
+
:align: center
|
|
73
|
+
:width: 500px
|
|
74
|
+
|
|
75
|
+
Preliminary data model for the :class:`ArrayChannelData` classes. The
|
|
76
|
+
basic hierarchy is identical to :numref:`Fig. %s
|
|
77
|
+
<fig-uml_evefile.data_api>`. Details for the
|
|
78
|
+
:class:`MCAChannelData` class can be found in :numref:`Fig. %s
|
|
79
|
+
<fig-uml_mcachannel_api>`.
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
Multi Channel Analysers (MCA) generally collect 1D data and typically have
|
|
83
|
+
separate regions of interest (ROI) defined, containing the sum of the
|
|
84
|
+
counts for the given region. For the EPICS MCA record,
|
|
85
|
+
see https://millenia.cars.aps.anl.gov/software/epics/mcaRecord.html.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
.. _fig-uml_mcachannel_api:
|
|
89
|
+
|
|
90
|
+
.. figure:: /uml/mcachannel.*
|
|
91
|
+
:align: center
|
|
92
|
+
:width: 750px
|
|
93
|
+
|
|
94
|
+
Preliminary data model for the :class:`MCAChannelData` classes. The basic
|
|
95
|
+
hierarchy is identical to :numref:`Fig. %s
|
|
96
|
+
<fig-uml_evefile.data_api>`, and here, the relevant part of the
|
|
97
|
+
metadata class hierarchy from :numref:`Fig. %s
|
|
98
|
+
<fig-uml_evefile_entities_metadata>` is shown as well. Separating the
|
|
99
|
+
:class:`MCAChannelCalibration
|
|
100
|
+
<evefile.entities.metadata.MCAChannelCalibration>` class from the
|
|
101
|
+
:class:`ArrayChannelMetadata
|
|
102
|
+
<evefile.entities.metadata.ArrayChannelMetadata>` allows to
|
|
103
|
+
add distinct behaviour, *e.g.* creating calibration curves from the
|
|
104
|
+
parameters.
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
Note: The scalar attributes for ArrayChannelROIs will currently be saved
|
|
108
|
+
as snapshots regardless of whether the actual ROI has been defined/used.
|
|
109
|
+
Hence, the evefile package needs to decide based on the existence of the
|
|
110
|
+
actual data whether to create a ROI object and attach it to
|
|
111
|
+
:class:`ArrayChannelData`.
|
|
112
|
+
|
|
113
|
+
The calibration parameters are needed to convert the *x* axis of the MCA
|
|
114
|
+
spectrum into a real energy axis. Hence,
|
|
115
|
+
the :class:`MCAChannelCalibration
|
|
116
|
+
<evefile.entities.metadata.MCAChannelCalibration>`
|
|
117
|
+
class will have methods for performing exactly this conversion. The
|
|
118
|
+
relationship between calibrated units (cal) and channel number (chan) is
|
|
119
|
+
defined as cal=CALO + chan\*CALS + chan^2\*CALQ. The first channel in the
|
|
120
|
+
spectrum is defined as chan=0. However, not all MCAs/SDDs have these
|
|
121
|
+
calibration values: Ketek SDDs seem to not have these values (internal
|
|
122
|
+
calibration?).
|
|
123
|
+
|
|
124
|
+
The real_time and life_time values can be used to get an idea of the
|
|
125
|
+
amount of pile up occurring, *i.e.* having two photons with same energy
|
|
126
|
+
within a short time interval reaching the detector being detected as one
|
|
127
|
+
photon with twice the energy. Hence, latest in the radiometry package,
|
|
128
|
+
distinct methods for this kind of analysis should be implemented.
|
|
58
129
|
|
|
59
130
|
|
|
60
131
|
Individual classes
|
|
@@ -86,6 +157,18 @@ documentation:
|
|
|
86
157
|
|
|
87
158
|
* :class:`IntervalNormalizedChannelData`
|
|
88
159
|
|
|
160
|
+
* :class:`ArrayChannelData`
|
|
161
|
+
|
|
162
|
+
* :class:`MCAChannelData`
|
|
163
|
+
|
|
164
|
+
* :class:`MCAChannelROIData`
|
|
165
|
+
|
|
166
|
+
* :class:`DataImporter`
|
|
167
|
+
|
|
168
|
+
* :class:`HDF5DataImporter`
|
|
169
|
+
|
|
170
|
+
* :class:`Axis`
|
|
171
|
+
|
|
89
172
|
|
|
90
173
|
|
|
91
174
|
Special aspects
|
|
@@ -392,6 +475,100 @@ class Data:
|
|
|
392
475
|
return dataframe
|
|
393
476
|
|
|
394
477
|
|
|
478
|
+
class Axis:
|
|
479
|
+
"""Axis for data.
|
|
480
|
+
|
|
481
|
+
An axis contains always both, numerical values and the metadata
|
|
482
|
+
necessary to create axis labels and to make sense of the numerical
|
|
483
|
+
information.
|
|
484
|
+
|
|
485
|
+
Attributes
|
|
486
|
+
----------
|
|
487
|
+
quantity : :class:`str`
|
|
488
|
+
quantity of the numerical data, usually used as first part of an
|
|
489
|
+
automatically generated axis label
|
|
490
|
+
|
|
491
|
+
unit : :class:`str`
|
|
492
|
+
unit of the numerical data, usually used as second part of an
|
|
493
|
+
automatically generated axis label
|
|
494
|
+
|
|
495
|
+
symbol : :class:`str`
|
|
496
|
+
symbol for the quantity of the numerical data, usually used as first
|
|
497
|
+
part of an automatically generated axis label
|
|
498
|
+
|
|
499
|
+
label : :class:`str`
|
|
500
|
+
manual label for the axis, particularly useful in cases where no
|
|
501
|
+
quantity and unit are provided or should be overwritten.
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
.. note::
|
|
505
|
+
There are three alternative ways of writing axis labels, one with
|
|
506
|
+
using the quantity name and the unit, one with using the quantity
|
|
507
|
+
symbol and the unit, and one using both, quantity name and symbol,
|
|
508
|
+
usually separated by comma. Quantity and unit shall always be
|
|
509
|
+
separated by a slash. Which way you prefer is a matter of personal
|
|
510
|
+
taste and given context.
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
Raises
|
|
514
|
+
------
|
|
515
|
+
ValueError
|
|
516
|
+
Raised when trying to set axis values to another type than numpy array
|
|
517
|
+
IndexError
|
|
518
|
+
Raised when trying to set axis values to an array with more than one
|
|
519
|
+
dimension.
|
|
520
|
+
Raised if index does not have the same length as values.
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
.. versionadded:: 0.2
|
|
524
|
+
|
|
525
|
+
"""
|
|
526
|
+
|
|
527
|
+
def __init__(self):
|
|
528
|
+
super().__init__()
|
|
529
|
+
self._values = np.zeros(0)
|
|
530
|
+
self.quantity = ""
|
|
531
|
+
self.symbol = ""
|
|
532
|
+
self.unit = ""
|
|
533
|
+
self.label = ""
|
|
534
|
+
|
|
535
|
+
@property
|
|
536
|
+
def values(self):
|
|
537
|
+
"""
|
|
538
|
+
Get or set the numerical axis values.
|
|
539
|
+
|
|
540
|
+
Values require to be a one-dimensional numpy array. Trying to set
|
|
541
|
+
values to either a different type that cannot be converted to a
|
|
542
|
+
numpy array or a numpy array with more than one dimension will raise
|
|
543
|
+
a corresponding error.
|
|
544
|
+
|
|
545
|
+
Raises
|
|
546
|
+
------
|
|
547
|
+
ValueError
|
|
548
|
+
Raised if axis values are of wrong type
|
|
549
|
+
IndexError
|
|
550
|
+
Raised if axis values are of wrong dimension, i.e. not a vector
|
|
551
|
+
|
|
552
|
+
"""
|
|
553
|
+
return self._values
|
|
554
|
+
|
|
555
|
+
@values.setter
|
|
556
|
+
def values(self, values):
|
|
557
|
+
if not isinstance(values, type(self._values)):
|
|
558
|
+
values = np.asarray(values)
|
|
559
|
+
if (
|
|
560
|
+
not isinstance(values, type(self._values))
|
|
561
|
+
or values.dtype != self._values.dtype
|
|
562
|
+
):
|
|
563
|
+
raise ValueError(
|
|
564
|
+
f"Wrong type: expected {self._values.dtype}, "
|
|
565
|
+
f"got {values.dtype}"
|
|
566
|
+
)
|
|
567
|
+
if values.ndim > 1:
|
|
568
|
+
raise IndexError("Values need to be one-dimensional")
|
|
569
|
+
self._values = values
|
|
570
|
+
|
|
571
|
+
|
|
395
572
|
class MonitorData(Data):
|
|
396
573
|
"""
|
|
397
574
|
Data from devices monitored, but not controlled by the eve engine.
|
|
@@ -562,7 +739,7 @@ class MeasureData(Data):
|
|
|
562
739
|
"""
|
|
563
740
|
Retrieve Pandas DataFrame with data as column.
|
|
564
741
|
|
|
565
|
-
The index is named "
|
|
742
|
+
The index is named "position" and contains the values of the
|
|
566
743
|
:attr:`position_counts` attribute of the data object.
|
|
567
744
|
|
|
568
745
|
.. important::
|
|
@@ -701,6 +878,47 @@ class DeviceData(MeasureData):
|
|
|
701
878
|
super().__init__()
|
|
702
879
|
self.metadata = metadata.DeviceMetadata()
|
|
703
880
|
|
|
881
|
+
def join(self, positions=None):
|
|
882
|
+
"""
|
|
883
|
+
Perform a left join of the data on the provided list of positions.
|
|
884
|
+
|
|
885
|
+
The main "quantisation" axis of the values for a device and the
|
|
886
|
+
common reference is the list of positions. To sensibly compare the
|
|
887
|
+
data of different devices or plot different device data against each
|
|
888
|
+
other, the data need to be harmonised, *i.e.* share a common set of
|
|
889
|
+
positions as indices.
|
|
890
|
+
|
|
891
|
+
If positions are not present in the original data, the previous
|
|
892
|
+
value present is automatically taken for this position. This is a
|
|
893
|
+
valid assumption, as the underlying EPICS monitors only record a
|
|
894
|
+
new value if the actual value has changed.
|
|
895
|
+
|
|
896
|
+
.. note::
|
|
897
|
+
|
|
898
|
+
The method will *alter* the data and positions of the underlying
|
|
899
|
+
:obj:`DeviceData` object. Hence, make sure to make a copy if
|
|
900
|
+
this is not your intended use case.
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
Parameters
|
|
904
|
+
----------
|
|
905
|
+
positions : :class:`numpy.ndarray`
|
|
906
|
+
Array with positions the data should be mapped to.
|
|
907
|
+
|
|
908
|
+
Raises
|
|
909
|
+
------
|
|
910
|
+
ValueError
|
|
911
|
+
Raised if no positions are provided
|
|
912
|
+
|
|
913
|
+
"""
|
|
914
|
+
if positions is None:
|
|
915
|
+
raise ValueError("No positions provided")
|
|
916
|
+
new_positions = (
|
|
917
|
+
np.searchsorted(self.position_counts, positions, side="right") - 1
|
|
918
|
+
)
|
|
919
|
+
self.position_counts = positions
|
|
920
|
+
self.data = self.data[new_positions]
|
|
921
|
+
|
|
704
922
|
|
|
705
923
|
class AxisData(MeasureData):
|
|
706
924
|
"""
|
|
@@ -788,6 +1006,10 @@ class AxisData(MeasureData):
|
|
|
788
1006
|
detailed discussion, see the :mod:`evefile.controllers.joining`
|
|
789
1007
|
module.
|
|
790
1008
|
|
|
1009
|
+
If a snapshot preceding a non-existing position is available,
|
|
1010
|
+
the value from this snapshot is automatically taken for the given
|
|
1011
|
+
position.
|
|
1012
|
+
|
|
791
1013
|
.. note::
|
|
792
1014
|
|
|
793
1015
|
The method will *alter* the data and positions of the underlying
|
|
@@ -1090,6 +1312,35 @@ class AverageChannelData(ChannelData):
|
|
|
1090
1312
|
"""
|
|
1091
1313
|
return self.data
|
|
1092
1314
|
|
|
1315
|
+
def get_dataframe(self): # pylint: disable=useless-parent-delegation
|
|
1316
|
+
"""
|
|
1317
|
+
Retrieve Pandas DataFrame with data as columns.
|
|
1318
|
+
|
|
1319
|
+
The DataFrame contains two columns, each corresponding to the
|
|
1320
|
+
respective attribute of the class:
|
|
1321
|
+
|
|
1322
|
+
* data
|
|
1323
|
+
* attempts
|
|
1324
|
+
|
|
1325
|
+
The index is named "position" and contains the values of the
|
|
1326
|
+
:attr:`position_counts` attribute of the data object.
|
|
1327
|
+
|
|
1328
|
+
.. important::
|
|
1329
|
+
|
|
1330
|
+
While working with a Pandas DataFrame may seem convenient,
|
|
1331
|
+
you're loosing basically all the relevant metadata of the
|
|
1332
|
+
datasets. Hence, this method is rather a convenience method to
|
|
1333
|
+
be backwards-compatible to older interfaces, but it is
|
|
1334
|
+
explicitly *not* suggested for extensive use.
|
|
1335
|
+
|
|
1336
|
+
Returns
|
|
1337
|
+
-------
|
|
1338
|
+
dataframe : :class:`pandas.DataFrame`
|
|
1339
|
+
Pandas DataFrame containing data as columns.
|
|
1340
|
+
|
|
1341
|
+
"""
|
|
1342
|
+
return super().get_dataframe()
|
|
1343
|
+
|
|
1093
1344
|
|
|
1094
1345
|
class IntervalChannelData(ChannelData):
|
|
1095
1346
|
"""
|
|
@@ -1187,6 +1438,36 @@ class IntervalChannelData(ChannelData):
|
|
|
1187
1438
|
"""
|
|
1188
1439
|
return self.data
|
|
1189
1440
|
|
|
1441
|
+
def get_dataframe(self): # pylint: disable=useless-parent-delegation
|
|
1442
|
+
"""
|
|
1443
|
+
Retrieve Pandas DataFrame with data as columns.
|
|
1444
|
+
|
|
1445
|
+
The DataFrame contains three columns, each corresponding to the
|
|
1446
|
+
respective attribute of the class:
|
|
1447
|
+
|
|
1448
|
+
* data
|
|
1449
|
+
* counts
|
|
1450
|
+
* std
|
|
1451
|
+
|
|
1452
|
+
The index is named "position" and contains the values of the
|
|
1453
|
+
:attr:`position_counts` attribute of the data object.
|
|
1454
|
+
|
|
1455
|
+
.. important::
|
|
1456
|
+
|
|
1457
|
+
While working with a Pandas DataFrame may seem convenient,
|
|
1458
|
+
you're loosing basically all the relevant metadata of the
|
|
1459
|
+
datasets. Hence, this method is rather a convenience method to
|
|
1460
|
+
be backwards-compatible to older interfaces, but it is
|
|
1461
|
+
explicitly *not* suggested for extensive use.
|
|
1462
|
+
|
|
1463
|
+
Returns
|
|
1464
|
+
-------
|
|
1465
|
+
dataframe : :class:`pandas.DataFrame`
|
|
1466
|
+
Pandas DataFrame containing data as columns.
|
|
1467
|
+
|
|
1468
|
+
"""
|
|
1469
|
+
return super().get_dataframe()
|
|
1470
|
+
|
|
1190
1471
|
|
|
1191
1472
|
class NormalizedChannelData:
|
|
1192
1473
|
"""
|
|
@@ -1349,6 +1630,36 @@ class SinglePointNormalizedChannelData(
|
|
|
1349
1630
|
def normalizing_data(self, normalizing_data=None):
|
|
1350
1631
|
self._normalizing_data = normalizing_data
|
|
1351
1632
|
|
|
1633
|
+
def get_dataframe(self): # pylint: disable=useless-parent-delegation
|
|
1634
|
+
"""
|
|
1635
|
+
Retrieve Pandas DataFrame with data as columns.
|
|
1636
|
+
|
|
1637
|
+
The DataFrame contains three columns, each corresponding to the
|
|
1638
|
+
respective attribute of the class:
|
|
1639
|
+
|
|
1640
|
+
* data
|
|
1641
|
+
* normalized_data
|
|
1642
|
+
* normalizing_data
|
|
1643
|
+
|
|
1644
|
+
The index is named "position" and contains the values of the
|
|
1645
|
+
:attr:`position_counts` attribute of the data object.
|
|
1646
|
+
|
|
1647
|
+
.. important::
|
|
1648
|
+
|
|
1649
|
+
While working with a Pandas DataFrame may seem convenient,
|
|
1650
|
+
you're loosing basically all the relevant metadata of the
|
|
1651
|
+
datasets. Hence, this method is rather a convenience method to
|
|
1652
|
+
be backwards-compatible to older interfaces, but it is
|
|
1653
|
+
explicitly *not* suggested for extensive use.
|
|
1654
|
+
|
|
1655
|
+
Returns
|
|
1656
|
+
-------
|
|
1657
|
+
dataframe : :class:`pandas.DataFrame`
|
|
1658
|
+
Pandas DataFrame containing data as columns.
|
|
1659
|
+
|
|
1660
|
+
"""
|
|
1661
|
+
return super().get_dataframe()
|
|
1662
|
+
|
|
1352
1663
|
|
|
1353
1664
|
class AverageNormalizedChannelData(AverageChannelData, NormalizedChannelData):
|
|
1354
1665
|
"""
|
|
@@ -1435,6 +1746,37 @@ class AverageNormalizedChannelData(AverageChannelData, NormalizedChannelData):
|
|
|
1435
1746
|
def normalizing_data(self, normalizing_data=None):
|
|
1436
1747
|
self._normalizing_data = normalizing_data
|
|
1437
1748
|
|
|
1749
|
+
def get_dataframe(self): # pylint: disable=useless-parent-delegation
|
|
1750
|
+
"""
|
|
1751
|
+
Retrieve Pandas DataFrame with data as columns.
|
|
1752
|
+
|
|
1753
|
+
The DataFrame contains four columns, each corresponding to the
|
|
1754
|
+
respective attribute of the class:
|
|
1755
|
+
|
|
1756
|
+
* data
|
|
1757
|
+
* attempts
|
|
1758
|
+
* normalized_data
|
|
1759
|
+
* normalizing_data
|
|
1760
|
+
|
|
1761
|
+
The index is named "position" and contains the values of the
|
|
1762
|
+
:attr:`position_counts` attribute of the data object.
|
|
1763
|
+
|
|
1764
|
+
.. important::
|
|
1765
|
+
|
|
1766
|
+
While working with a Pandas DataFrame may seem convenient,
|
|
1767
|
+
you're loosing basically all the relevant metadata of the
|
|
1768
|
+
datasets. Hence, this method is rather a convenience method to
|
|
1769
|
+
be backwards-compatible to older interfaces, but it is
|
|
1770
|
+
explicitly *not* suggested for extensive use.
|
|
1771
|
+
|
|
1772
|
+
Returns
|
|
1773
|
+
-------
|
|
1774
|
+
dataframe : :class:`pandas.DataFrame`
|
|
1775
|
+
Pandas DataFrame containing data as columns.
|
|
1776
|
+
|
|
1777
|
+
"""
|
|
1778
|
+
return super().get_dataframe()
|
|
1779
|
+
|
|
1438
1780
|
|
|
1439
1781
|
class IntervalNormalizedChannelData(
|
|
1440
1782
|
IntervalChannelData, NormalizedChannelData
|
|
@@ -1523,6 +1865,298 @@ class IntervalNormalizedChannelData(
|
|
|
1523
1865
|
def normalizing_data(self, normalizing_data=None):
|
|
1524
1866
|
self._normalizing_data = normalizing_data
|
|
1525
1867
|
|
|
1868
|
+
def get_dataframe(self): # pylint: disable=useless-parent-delegation
|
|
1869
|
+
"""
|
|
1870
|
+
Retrieve Pandas DataFrame with data as columns.
|
|
1871
|
+
|
|
1872
|
+
The DataFrame contains five columns, each corresponding to the
|
|
1873
|
+
respective attribute of the class:
|
|
1874
|
+
|
|
1875
|
+
* data
|
|
1876
|
+
* counts
|
|
1877
|
+
* std
|
|
1878
|
+
* normalized_data
|
|
1879
|
+
* normalizing_data
|
|
1880
|
+
|
|
1881
|
+
The index is named "position" and contains the values of the
|
|
1882
|
+
:attr:`position_counts` attribute of the data object.
|
|
1883
|
+
|
|
1884
|
+
.. important::
|
|
1885
|
+
|
|
1886
|
+
While working with a Pandas DataFrame may seem convenient,
|
|
1887
|
+
you're loosing basically all the relevant metadata of the
|
|
1888
|
+
datasets. Hence, this method is rather a convenience method to
|
|
1889
|
+
be backwards-compatible to older interfaces, but it is
|
|
1890
|
+
explicitly *not* suggested for extensive use.
|
|
1891
|
+
|
|
1892
|
+
Returns
|
|
1893
|
+
-------
|
|
1894
|
+
dataframe : :class:`pandas.DataFrame`
|
|
1895
|
+
Pandas DataFrame containing data as columns.
|
|
1896
|
+
|
|
1897
|
+
"""
|
|
1898
|
+
return super().get_dataframe()
|
|
1899
|
+
|
|
1900
|
+
|
|
1901
|
+
class ArrayChannelData(ChannelData):
|
|
1902
|
+
"""
|
|
1903
|
+
Data for channels with numeric 1D data.
|
|
1904
|
+
|
|
1905
|
+
Detector channels can be distinguished by the dimension of their data:
|
|
1906
|
+
|
|
1907
|
+
0D
|
|
1908
|
+
scalar values per position, including average and interval channels
|
|
1909
|
+
1D
|
|
1910
|
+
array values, *i.e.* vectors, per position
|
|
1911
|
+
2D
|
|
1912
|
+
area values, *i.e.* images, per position
|
|
1913
|
+
|
|
1914
|
+
This class represents 1D array values.
|
|
1915
|
+
|
|
1916
|
+
Individual arrays are stored one per row in the :attr:`data` attribute.
|
|
1917
|
+
This allows for intuitive indexing of the individual arrays.
|
|
1918
|
+
|
|
1919
|
+
|
|
1920
|
+
Attributes
|
|
1921
|
+
----------
|
|
1922
|
+
metadata : :class:`evefile.entities.metadata.ArrayChannelMetadata`
|
|
1923
|
+
Relevant metadata for the individual device.
|
|
1924
|
+
|
|
1925
|
+
|
|
1926
|
+
Examples
|
|
1927
|
+
--------
|
|
1928
|
+
The :class:`ArrayChannelData` class is not meant to be used
|
|
1929
|
+
directly, as any entities, but rather indirectly by means of the
|
|
1930
|
+
respective facades in the boundaries technical layer of the
|
|
1931
|
+
``evefile`` package.
|
|
1932
|
+
Hence, for the time being, there are no dedicated examples how to use
|
|
1933
|
+
this class. Of course, you can instantiate an object as usual.
|
|
1934
|
+
|
|
1935
|
+
|
|
1936
|
+
.. versionadded:: 0.2
|
|
1937
|
+
|
|
1938
|
+
"""
|
|
1939
|
+
|
|
1940
|
+
def __init__(self):
|
|
1941
|
+
super().__init__()
|
|
1942
|
+
self.metadata = metadata.ArrayChannelMetadata()
|
|
1943
|
+
|
|
1944
|
+
def get_data(self):
|
|
1945
|
+
"""
|
|
1946
|
+
Load data (and variable option data) using the respective importer.
|
|
1947
|
+
|
|
1948
|
+
Data are loaded only on demand. Hence, upon the first access of the
|
|
1949
|
+
:attr:`data` property, this method will be called, calling out to
|
|
1950
|
+
the respective importers.
|
|
1951
|
+
|
|
1952
|
+
As :obj:`Data` objects may contain (variable) options that are
|
|
1953
|
+
themselves data, but loading these data is only triggered when
|
|
1954
|
+
accessing the :attr:`data` property, you can either once access the
|
|
1955
|
+
:attr:`data` property or call this method.
|
|
1956
|
+
|
|
1957
|
+
Data may be spread over several HDF5 datasets, depending on the
|
|
1958
|
+
version of the eveH5 file read. Hence, there may be several
|
|
1959
|
+
importers, and they are dealt with sequentially.
|
|
1960
|
+
|
|
1961
|
+
Furthermore, for each importer type, there is a special private
|
|
1962
|
+
method ``_import_from_<importer-type>``, with ``<importer-type>``
|
|
1963
|
+
being the lowercase class name. Those classes using additional
|
|
1964
|
+
importers beyond :class:`HDF5DataImporter` need to implement
|
|
1965
|
+
additional private methods to handle the special importer classes. A
|
|
1966
|
+
typical use case is the :class:`AreaChannelData` class dealing with
|
|
1967
|
+
image data stored mostly in separate files.
|
|
1968
|
+
|
|
1969
|
+
"""
|
|
1970
|
+
data = []
|
|
1971
|
+
for idx, importer in enumerate(self.importer):
|
|
1972
|
+
importer.load()
|
|
1973
|
+
if "data" in importer.mapping.values():
|
|
1974
|
+
data.append(importer.data[:, 0])
|
|
1975
|
+
else:
|
|
1976
|
+
for column_name, attribute in importer.mapping.items():
|
|
1977
|
+
setattr(self, attribute, importer.data[column_name])
|
|
1978
|
+
if self._data is None and data:
|
|
1979
|
+
self._data = np.ndarray(
|
|
1980
|
+
[len(data), len(data[0])], dtype=data[0].dtype
|
|
1981
|
+
)
|
|
1982
|
+
for idx in range(len(data)): # noqa
|
|
1983
|
+
self._data[idx, :] = data[idx]
|
|
1984
|
+
|
|
1985
|
+
def get_dataframe(self):
|
|
1986
|
+
"""
|
|
1987
|
+
Retrieve Pandas DataFrame with data as column.
|
|
1988
|
+
|
|
1989
|
+
.. important::
|
|
1990
|
+
|
|
1991
|
+
While working with a Pandas DataFrame may seem convenient,
|
|
1992
|
+
you're loosing basically all the relevant metadata of the
|
|
1993
|
+
datasets. Hence, this method is rather a convenience method to
|
|
1994
|
+
be backwards-compatible to older interfaces, but it is
|
|
1995
|
+
explicitly *not* suggested for extensive use.
|
|
1996
|
+
|
|
1997
|
+
Returns
|
|
1998
|
+
-------
|
|
1999
|
+
dataframe : :class:`pandas.DataFrame`
|
|
2000
|
+
Pandas DataFrame containing data as column.
|
|
2001
|
+
|
|
2002
|
+
"""
|
|
2003
|
+
if self.data is not None:
|
|
2004
|
+
index = np.arange(1, self.data.shape[0] + 1)
|
|
2005
|
+
else:
|
|
2006
|
+
index = [0]
|
|
2007
|
+
dataframe = pd.DataFrame(
|
|
2008
|
+
columns=self._data_attributes,
|
|
2009
|
+
index=index,
|
|
2010
|
+
)
|
|
2011
|
+
dataframe["data"] = dataframe["data"].astype(object)
|
|
2012
|
+
for idx, row in enumerate(index):
|
|
2013
|
+
dataframe.loc[row, "data"] = self.data[idx, :]
|
|
2014
|
+
if self.position_counts is not None and self.position_counts.ndim:
|
|
2015
|
+
dataframe.index = self.position_counts
|
|
2016
|
+
dataframe.index.name = "position"
|
|
2017
|
+
return dataframe
|
|
2018
|
+
|
|
2019
|
+
|
|
2020
|
+
class MCAChannelData(ArrayChannelData):
|
|
2021
|
+
"""
|
|
2022
|
+
Data for multichannel analyzer (MCA) channels.
|
|
2023
|
+
|
|
2024
|
+
MCA channel data are usually 1D data, *i.e.* arrays or vectors.
|
|
2025
|
+
|
|
2026
|
+
|
|
2027
|
+
Attributes
|
|
2028
|
+
----------
|
|
2029
|
+
metadata : :class:`evefile.entities.metadata.MCAChannelMetadata`
|
|
2030
|
+
Relevant metadata for the individual device.
|
|
2031
|
+
|
|
2032
|
+
roi : :class:`list`
|
|
2033
|
+
List of data for the individual ROIs defined.
|
|
2034
|
+
|
|
2035
|
+
Individual items in the list are objects of class
|
|
2036
|
+
:class:`MCAChannelROIData`.
|
|
2037
|
+
|
|
2038
|
+
life_time : :class:`numpy.ndarray`
|
|
2039
|
+
Elapsed life time
|
|
2040
|
+
|
|
2041
|
+
After a read status operation, this field contains the elapsed
|
|
2042
|
+
live time, as reported by the hardware.
|
|
2043
|
+
|
|
2044
|
+
real_time : :class:`numpy.ndarray`
|
|
2045
|
+
Elapsed real time
|
|
2046
|
+
|
|
2047
|
+
After a read status operation, this field contains the elapsed
|
|
2048
|
+
real time, as reported by the hardware.
|
|
2049
|
+
|
|
2050
|
+
axis : :class:`Axis`
|
|
2051
|
+
Data and metadata for the x-axis of the array data
|
|
2052
|
+
|
|
2053
|
+
MCAs record array data, and to make sense of the indices of the
|
|
2054
|
+
arrays, usually some calibration parameters are recorded that can
|
|
2055
|
+
be used to convert the indices of the array to an actual axis - be
|
|
2056
|
+
it energy or time or else.
|
|
2057
|
+
|
|
2058
|
+
See :class:`evefile.entities.metadata.MCAChannelCalibration` for
|
|
2059
|
+
details of the calibration data that may be available for your MCA.
|
|
2060
|
+
|
|
2061
|
+
|
|
2062
|
+
Examples
|
|
2063
|
+
--------
|
|
2064
|
+
The :class:`MCAChannelData` class is not meant to be used
|
|
2065
|
+
directly, as any entities, but rather indirectly by means of the
|
|
2066
|
+
respective facades in the boundaries technical layer of the
|
|
2067
|
+
``evefile`` package.
|
|
2068
|
+
Hence, for the time being, there are no dedicated examples how to use
|
|
2069
|
+
this class. Of course, you can instantiate an object as usual.
|
|
2070
|
+
|
|
2071
|
+
|
|
2072
|
+
.. versionadded:: 0.2
|
|
2073
|
+
|
|
2074
|
+
"""
|
|
2075
|
+
|
|
2076
|
+
def __init__(self):
|
|
2077
|
+
super().__init__()
|
|
2078
|
+
self.metadata = metadata.MCAChannelMetadata()
|
|
2079
|
+
self.roi = []
|
|
2080
|
+
self.life_time = np.ndarray(shape=[])
|
|
2081
|
+
self.real_time = np.ndarray(shape=[])
|
|
2082
|
+
self.axis = Axis()
|
|
2083
|
+
|
|
2084
|
+
def get_data(self):
|
|
2085
|
+
"""
|
|
2086
|
+
Load data (and variable option data) using the respective importer.
|
|
2087
|
+
|
|
2088
|
+
Data are loaded only on demand. Hence, upon the first access of the
|
|
2089
|
+
:attr:`data` property, this method will be called, calling out to
|
|
2090
|
+
the respective importers.
|
|
2091
|
+
|
|
2092
|
+
As :obj:`Data` objects may contain (variable) options that are
|
|
2093
|
+
themselves data, but loading these data is only triggered when
|
|
2094
|
+
accessing the :attr:`data` property, you can either once access the
|
|
2095
|
+
:attr:`data` property or call this method.
|
|
2096
|
+
|
|
2097
|
+
Data may be spread over several HDF5 datasets, depending on the
|
|
2098
|
+
version of the eveH5 file read. Hence, there may be several
|
|
2099
|
+
importers, and they are dealt with sequentially.
|
|
2100
|
+
|
|
2101
|
+
Furthermore, for each importer type, there is a special private
|
|
2102
|
+
method ``_import_from_<importer-type>``, with ``<importer-type>``
|
|
2103
|
+
being the lowercase class name. Those classes using additional
|
|
2104
|
+
importers beyond :class:`HDF5DataImporter` need to implement
|
|
2105
|
+
additional private methods to handle the special importer classes. A
|
|
2106
|
+
typical use case is the :class:`AreaChannelData` class dealing with
|
|
2107
|
+
image data stored mostly in separate files.
|
|
2108
|
+
|
|
2109
|
+
"""
|
|
2110
|
+
super().get_data()
|
|
2111
|
+
if self._data is not None:
|
|
2112
|
+
indices = np.linspace(
|
|
2113
|
+
0, self.data.shape[1], self.data.shape[1], endpoint=False
|
|
2114
|
+
)
|
|
2115
|
+
if self.axis.values.size == 0:
|
|
2116
|
+
self.axis.values = (
|
|
2117
|
+
self.metadata.calibration.offset
|
|
2118
|
+
+ indices * self.metadata.calibration.slope
|
|
2119
|
+
+ indices**2 * self.metadata.calibration.quadratic
|
|
2120
|
+
)
|
|
2121
|
+
|
|
2122
|
+
|
|
2123
|
+
class MCAChannelROIData(ChannelData):
|
|
2124
|
+
"""
|
|
2125
|
+
Data for an individual ROI of an MCA detector channel.
|
|
2126
|
+
|
|
2127
|
+
Many MCAs allow to define one or several regions of interest (ROI).
|
|
2128
|
+
This class contains the relevant data for an individual ROI.
|
|
2129
|
+
|
|
2130
|
+
|
|
2131
|
+
Attributes
|
|
2132
|
+
----------
|
|
2133
|
+
label : :class:`str`
|
|
2134
|
+
Label for the ROI provided by the operator.
|
|
2135
|
+
|
|
2136
|
+
marker : :class:`numpy.ndarray`
|
|
2137
|
+
Two-element vector of integer values containing the left and right
|
|
2138
|
+
boundary of the ROI.
|
|
2139
|
+
|
|
2140
|
+
|
|
2141
|
+
Examples
|
|
2142
|
+
--------
|
|
2143
|
+
The :class:`MCAChannelROIData` class is not meant to be used
|
|
2144
|
+
directly, as any entities, but rather indirectly by means of the
|
|
2145
|
+
respective facades in the boundaries technical layer of the
|
|
2146
|
+
``evefile`` package.
|
|
2147
|
+
Hence, for the time being, there are no dedicated examples how to use
|
|
2148
|
+
this class. Of course, you can instantiate an object as usual.
|
|
2149
|
+
|
|
2150
|
+
|
|
2151
|
+
.. versionadded:: 0.2
|
|
2152
|
+
|
|
2153
|
+
"""
|
|
2154
|
+
|
|
2155
|
+
def __init__(self):
|
|
2156
|
+
super().__init__()
|
|
2157
|
+
self.label = ""
|
|
2158
|
+
self.marker = np.asarray([0, 0], dtype=int)
|
|
2159
|
+
|
|
1526
2160
|
|
|
1527
2161
|
class DataImporter:
|
|
1528
2162
|
"""
|