Skip to content

Commit 63595f2

Browse files
committed
Refactors sasmanipulations to use new data objects for output
1 parent 6562d90 commit 63595f2

5 files changed

Lines changed: 159 additions & 107 deletions

File tree

sasdata/data_util/averaging.py

Lines changed: 76 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
from sasdata.data_util.binning import DirectionalAverage
1010
from sasdata.data_util.interval import IntervalType
1111
from sasdata.data_util.roi import CartesianROI, PolarROI
12-
from sasdata.dataloader.data_info import Data1D
12+
from sasdata.dataset_types import one_dim
1313
from sasdata.quantities.constants import Pi, TwoPi
14+
from sasdata.quantities.quantity import Quantity
1415

1516

1617
def get_dq_data(data2d: SasData) -> npt.NDArray[np.floating]:
@@ -140,7 +141,7 @@ class SlabX(CartesianROI):
140141
Average I(Q_x, Q_y) along the y direction (within a ROI), giving I(Q_x).
141142
142143
This class is initialised by specifying the boundaries of the ROI and is
143-
called by supplying a SasData object. It returns a Data1D object.
144+
called by supplying a SasData object. It returns a SasData object.
144145
The averaging process can also be thought of as projecting 2D -> 1D.
145146
146147
There also exists the option to "fold" the ROI, where Q data on opposite
@@ -170,12 +171,12 @@ def __init__(
170171
self.fold: bool = fold
171172
self.base: float | None = base
172173

173-
def __call__(self, data2d: SasData = None) -> Data1D:
174+
def __call__(self, data2d: SasData = None) -> SasData:
174175
"""
175176
Compute the 1D average of 2D data, projecting along the Q_x axis.
176177
177178
:param data2d: The SasData object for which the average is computed.
178-
:return: Data1D object for plotting.
179+
:return: SasData object for plotting.
179180
"""
180181
self.validate_and_assign_data(data2d)
181182

@@ -201,15 +202,20 @@ def __call__(self, data2d: SasData = None) -> Data1D:
201202

202203
qx_data, intensity, error = directional_average(data=self.data, err_data=self.err_data)
203204

204-
return Data1D(x=qx_data, y=intensity, dy=error)
205+
#return Data1D(x=qx_data, y=intensity, dy=error)
206+
data_contents = {
207+
"Q": Quantity(qx_data, data2d._data_contents["Qx"].units, None),
208+
"I": Quantity(intensity, data2d._data_contents["I"].units, error),
209+
}
210+
return SasData(f"{data2d.name}: Slab X Average", data_contents, one_dim, data2d.metadata)
205211

206212

207213
class SlabY(CartesianROI):
208214
"""
209215
Average I(Q_x, Q_y) along the x direction (within a ROI), giving I(Q_y).
210216
211217
This class is initialised by specifying the boundaries of the ROI and is
212-
called by supplying a SasData object. It returns a Data1D object.
218+
called by supplying a SasData object. It returns a SasData object.
213219
The averaging process can also be thought of as projecting 2D -> 1D.
214220
215221
There also exists the option to "fold" the ROI, where Q data on opposite
@@ -240,12 +246,12 @@ def __init__(
240246
self.fold: bool = fold
241247
self.base: float | None = base
242248

243-
def __call__(self, data2d: SasData = None) -> Data1D:
249+
def __call__(self, data2d: SasData = None) -> SasData:
244250
"""
245251
Compute the 1D average of 2D data, projecting along the Q_y axis.
246252
247253
:param data2d: The SasData object for which the average is computed.
248-
:return: Data1D object for plotting.
254+
:return: SasData object for plotting.
249255
"""
250256
self.validate_and_assign_data(data2d)
251257

@@ -270,7 +276,12 @@ def __call__(self, data2d: SasData = None) -> Data1D:
270276
)
271277
qy_data, intensity, error = directional_average(data=self.data, err_data=self.err_data)
272278

273-
return Data1D(x=qy_data, y=intensity, dy=error)
279+
#return Data1D(x=qy_data, y=intensity, dy=error)
280+
data_contents = {
281+
"Q": Quantity(qy_data, data2d._data_contents["Qy"].units, None),
282+
"I": Quantity(intensity, data2d._data_contents["I"].units, error),
283+
}
284+
return SasData(f"{data2d.name}: Slab Y Average", data_contents, one_dim, data2d.metadata)
274285

275286

276287
class CircularAverage(PolarROI):
@@ -280,7 +291,7 @@ class CircularAverage(PolarROI):
280291
This class is initialised by specifying lower and upper limits on the
281292
magnitude of Q values to consider during the averaging, though currently
282293
SasView always calls this class using the full range of data. When called,
283-
this class is supplied with a SasData object. It returns a Data1D object
294+
this class is supplied with a SasData object. It returns a SasData object
284295
where intensity is given as a function of Q only.
285296
"""
286297

@@ -310,7 +321,7 @@ def __call__(self, data2D: Data2D, ismask: bool = False) -> Data1D:
310321
311322
:param data2D: SasData object
312323
:param ismask: If True, respect data2D.mask (skip masked points). If False, ignore mask.
313-
:return: Data1D object with x (bin centers), y (intensity), dy and dx (if available)
324+
:return: SasData object with x (bin centers), y (intensity), dy and dx (if available)
314325
"""
315326
# Work on unmasked finite arrays first (matches legacy filtering)
316327
finite_mask = np.isfinite(data2D._data_contents["I"].value)
@@ -369,11 +380,17 @@ def __call__(self, data2D: Data2D, ismask: bool = False) -> Data1D:
369380
counts = np.sum(weights, axis=1)
370381
with np.errstate(divide="ignore", invalid="ignore"):
371382
dx_full = dq_weighted / counts
372-
dx = dx_full[populated]
383+
dQ = dx_full[populated]
373384
else:
374-
dx = None
385+
dQ = None
386+
387+
#return Data1D(x=x, y=intensity, dy=error, dx=dx)
388+
data_contents = {
389+
"Q": Quantity(x, data2D._data_contents["Qx"].units, dQ),
390+
"I": Quantity(intensity, data2D._data_contents["I"].units, error),
391+
}
392+
return SasData(f"{data2D.name}: Circular Average", data_contents, one_dim, data2D.metadata)
375393

376-
return Data1D(x=x, y=intensity, dy=error, dx=dx)
377394

378395

379396
class Ring(PolarROI):
@@ -382,9 +399,8 @@ class Ring(PolarROI):
382399
383400
This class is initialised by specifying lower and upper limits on the
384401
magnitude of Q values to consider during the averaging. When called,
385-
this class is supplied with a SasData object. It returns a Data1D object.
386-
This Data1D object gives intensity as a function of the angle from the
387-
positive x-axis, φ, only.
402+
this class is supplied with a SasData object. It returns a SasData object
403+
which gives intensity as a function of the angle from the positive x-axis, φ, only.
388404
"""
389405

390406
def __init__(
@@ -415,7 +431,7 @@ def __call__(self, data2D: Data2D) -> Data1D:
415431
416432
:param data2D: SasData object
417433
418-
:return: Data1D object
434+
:return: SasData object
419435
"""
420436
if not isinstance(data2D, SasData):
421437
msg = "Data supplied for ring averaging must be of type SasData."
@@ -489,7 +505,12 @@ def __call__(self, data2D: Data2D) -> Data1D:
489505
msg = "Average Error: No points inside ROI to average..."
490506
raise ValueError(msg)
491507

492-
return Data1D(x=phi_values[idx], y=phi_bins[idx], dy=phi_err[idx])
508+
#return Data1D(x=phi_values[idx], y=phi_bins[idx], dy=phi_err[idx])
509+
data_contents = {
510+
"Q": Quantity(phi_values[idx], data2D._data_contents["Qx"].units, None),
511+
"I": Quantity(phi_bins[idx], data2D._data_contents["I"].units, phi_err[idx]),
512+
}
513+
return SasData(f"{data2D.name}: Ring Average", data_contents, one_dim, data2D.metadata)
493514

494515

495516
class SectorQ(PolarROI):
@@ -511,7 +532,7 @@ class SectorQ(PolarROI):
511532
ROI data labelled using negative Q values.
512533
513534
When called, this class is supplied with a SasData object. It returns a
514-
Data1D object where intensity is given as a function of Q only.
535+
SasData object where intensity is given as a function of Q only.
515536
"""
516537

517538
def __init__(
@@ -539,12 +560,12 @@ def __init__(
539560
self.fold: bool = fold
540561
self.base: float | None = base
541562

542-
def __call__(self, data2d: SasData = None) -> Data1D:
563+
def __call__(self, data2d: SasData = None) -> SasData:
543564
"""
544565
Compute the 1D average of 2D data, projecting along the Q_y axis.
545566
546567
:param data2d: The SasData object for which the average is computed.
547-
:return: Data1D object for plotting.
568+
:return: SasData object for plotting.
548569
"""
549570
self.validate_and_assign_data(data2d)
550571

@@ -621,15 +642,27 @@ def __call__(self, data2d: SasData = None) -> Data1D:
621642

622643
finite = np.isfinite(average_intensity)
623644

624-
data1d = Data1D(x=combined_q[finite], y=average_intensity[finite], dy=combined_err[finite])
645+
#data1d = Data1D(x=combined_q[finite],
646+
# y=average_intensity[finite],
647+
# dy=combined_err[finite])
648+
data_contents = {
649+
"Q": Quantity(combined_q[finite], data2d._data_contents["Qx"].units, None),
650+
"I": Quantity(average_intensity[finite], data2d._data_contents["I"].units, combined_err[finite]),
651+
}
625652
else:
626653
# The secondary ROI is labelled with negative Q values.
627654
combined_q = np.append(np.flip(-1 * secondary_q), primary_q)
628655
combined_intensity = np.append(np.flip(secondary_I), primary_I)
629656
combined_error = np.append(np.flip(secondary_err), primary_err)
630-
data1d = Data1D(x=combined_q, y=combined_intensity, dy=combined_error)
657+
#data1d = Data1D(x=combined_q,
658+
# y=combined_intensity,
659+
# dy=combined_error)
660+
data_contents = {
661+
"Q": Quantity(combined_q, data2d._data_contents["Qx"].units, None),
662+
"I": Quantity(combined_intensity, data2d._data_contents["I"].units, combined_error),
663+
}
631664

632-
return data1d
665+
return SasData(f"{data2d.name}:SectorQ Average", data_contents, one_dim, data2d.metadata)
633666

634667

635668
class WedgeQ(PolarROI):
@@ -643,7 +676,7 @@ class WedgeQ(PolarROI):
643676
This class is initialised by specifying lower and upper limits on both the
644677
magnitude of Q and the angle φ.
645678
When called, this class is supplied with a SasData object. It returns a
646-
Data1D object where intensity is given as a function of Q only.
679+
sasData object where intensity is given as a function of Q only.
647680
"""
648681

649682
def __init__(
@@ -666,12 +699,12 @@ def __init__(
666699
self.nbins: int = nbins
667700
self.base: float | None = base
668701

669-
def __call__(self, data2d: SasData = None) -> Data1D:
702+
def __call__(self, data2d: SasData = None) -> SasData:
670703
"""
671704
Compute the 1D average of 2D data, projecting along the Q_y axis.
672705
673706
:param data2d: The SasData object for which the average is computed.
674-
:return: Data1D object for plotting.
707+
:return: SasData object for plotting.
675708
"""
676709
self.validate_and_assign_data(data2d)
677710

@@ -711,7 +744,12 @@ def __call__(self, data2d: SasData = None) -> Data1D:
711744

712745
q_data, intensity, error = directional_average(data=self.data, err_data=self.err_data)
713746

714-
return Data1D(x=q_data, y=intensity, dy=error)
747+
#return Data1D(x=q_data, y=intensity, dy=error)
748+
data_contents = {
749+
"Q": Quantity(q_data, data2d._data_contents["Qx"].units, None),
750+
"I": Quantity(intensity, data2d._data_contents["I"].units, error),
751+
}
752+
return SasData(f"{data2d.name}: Wedge Q Average", data_contents, one_dim, data2d.metadata)
715753

716754

717755
class WedgePhi(PolarROI):
@@ -724,7 +762,7 @@ class WedgePhi(PolarROI):
724762
This class is initialised by specifying lower and upper limits on both the
725763
magnitude of Q and the angle φ, measured anticlockwise from the positive
726764
x-axis. When called, this class is supplied with a SasData object. It returns
727-
a Data1D object where intensity is given as a function of Q only.
765+
a SasData object where intensity is given as a function of Q only.
728766
"""
729767

730768
def __init__(
@@ -748,12 +786,12 @@ def __init__(
748786
self.nbins: int = nbins
749787
self.base: float | None = base
750788

751-
def __call__(self, data2d: SasData = None) -> Data1D:
789+
def __call__(self, data2d: SasData = None) -> SasData:
752790
"""
753791
Compute the 1D average of 2D data, projecting along the Q_y axis.
754792
755793
:param data2d: The SasData object for which the average is computed.
756-
:return: Data1D object for plotting.
794+
:return: SasData object for plotting.
757795
"""
758796
self.validate_and_assign_data(data2d)
759797

@@ -815,7 +853,12 @@ def __call__(self, data2d: SasData = None) -> Data1D:
815853
phi_centers = full_phi[populated] + directional_average.bin_widths[populated] / 2.0
816854

817855
# intensity and error returned by DirectionalAverage are already filtered to the populated/finite bins
818-
return Data1D(x=phi_centers, y=intensity, dy=error)
856+
#return Data1D(x=phi_centers, y=intensity, dy=error)
857+
data_contents = {
858+
"Q": Quantity(phi_centers, data2d._data_contents["Qx"].units, None),
859+
"I": Quantity(intensity, data2d._data_contents["I"].units, error),
860+
}
861+
return SasData(f"{data2d.name}: Wedge Phi Average", data_contents, one_dim, data2d.metadata)
819862

820863
"""
821864
# Convert angular data back to the original phi range

sasdata/data_util/manipulations.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import numpy as np
2525

26+
from sasdata.data import SasData
27+
2628
################################################################################
2729
# Backwards-compatible wrappers that delegate to the new implementations
2830
# in averaging.py.
@@ -44,9 +46,11 @@
4446
WedgeQ,
4547
get_dq_data,
4648
)
47-
from sasdata.dataloader.data_info import Data1D, Data2D
49+
from sasdata.dataloader.data_info import Data2D
4850
from sasdata.dataloader.data_info import reader2D_converter as _di_reader2D_converter
51+
from sasdata.dataset_types import one_dim
4952
from sasdata.quantities.constants import Pi, TwoPi
53+
from sasdata.quantities.quantity import Quantity
5054

5155
warn("sasdata.data_util.manipulations is deprecated. Unless otherwise noted, update your import to "
5256
"sasdata.data_util.averaging.", DeprecationWarning, stacklevel=2)
@@ -384,7 +388,7 @@ def __call__(self, data2D, ismask=False):
384388
Perform circular averaging on the data
385389
386390
:param data2D: SasData object
387-
:return: Data1D object
391+
:return: SasData object
388392
"""
389393
# Get data W/ finite values
390394
finite_mask = np.isfinite(data2D._data_contents["I"].value)
@@ -468,15 +472,20 @@ def __call__(self, data2D, ismask=False):
468472
idx = (np.isfinite(y)) & (np.isfinite(x))
469473

470474
if err_x is not None:
471-
d_x = err_x[idx] / y_counts[idx]
475+
dQ = err_x[idx] / y_counts[idx]
472476
else:
473-
d_x = None
477+
dQ = None
474478

475479
if not idx.any():
476480
msg = "Average Error: No points inside ROI to average..."
477481
raise ValueError(msg)
478482

479-
return Data1D(x=x[idx], y=y[idx], dy=err_y[idx], dx=d_x)
483+
data_contents = {
484+
"Q": Quantity(x[idx], data2D._data_contents["Qx"].units, dQ),
485+
"I": Quantity(y[idx], data2D._data_contents["I"].units, err_y[idx]),
486+
}
487+
return SasData("Circular Average", data_contents, one_dim, data2D.metadata)
488+
#return Data1D(x=x[idx], y=y[idx], dy=err_y[idx], dx=d_x)
480489

481490
################################################################################
482491

@@ -531,7 +540,7 @@ def _agv(self, data2D, run='phi'):
531540
:param data2D: SasData object
532541
:param run: define the varying parameter ('phi' or 'sector')
533542
534-
:return: Data1D object
543+
:return: SasData object
535544
"""
536545
if not ("Qx" in data2D._data_contents and
537546
"Qy" in data2D._data_contents and
@@ -708,13 +717,20 @@ def _agv(self, data2D, run='phi'):
708717

709718
idx = (np.isfinite(y) & np.isfinite(y_err))
710719
if x_err is not None:
711-
d_x = x_err[idx] / y_counts[idx]
720+
dQ = x_err[idx] / y_counts[idx]
712721
else:
713-
d_x = None
722+
dQ = None
714723
if not idx.any():
715724
msg = "Average Error: No points inside sector of ROI to average..."
716725
raise ValueError(msg)
717-
return Data1D(x=x[idx], y=y[idx], dy=y_err[idx], dx=d_x)
726+
727+
#return Data1D(x=x[idx], y=y[idx], dy=y_err[idx], dx=d_x)
728+
data_contents = {
729+
"Q": Quantity(x[idx], data2D._data_contents["Qx"].units, dQ),
730+
"I": Quantity(y[idx], data2D._data_contents["I"].units, y_err[idx]),
731+
}
732+
return SasData("agv", data_contents, one_dim, data2D.metadata)
733+
718734

719735

720736
class SectorPhi(_Sector):
@@ -731,7 +747,7 @@ def __call__(self, data2D):
731747
Perform sector average and return I(phi).
732748
733749
:param data2D: SasData object
734-
:return: Data1D object
750+
:return: SasData object
735751
"""
736752
return self._agv(data2D, 'phi')
737753

@@ -755,7 +771,7 @@ def __call__(self, data2D):
755771
756772
:param data2D: SasData object
757773
758-
:return: Data1D object
774+
:return: SasData object
759775
"""
760776
return self._agv(data2D, 'sector')
761777

0 commit comments

Comments
 (0)