Skip to content

Commit d295aed

Browse files
authored
Merge pull request #7407 from jenshnielsen/jenshnielsen/xarray_export
Xarray export cleanup and bugfixes
2 parents ab6131a + 938265b commit d295aed

File tree

4 files changed

+90
-10
lines changed

4 files changed

+90
-10
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ dependencies = [
4242
"uncertainties>=3.2.0",
4343
"versioningit>=2.2.1",
4444
"websockets>=11.0",
45-
"xarray>=2022.06.0",
45+
"xarray>=2023.08.0",
4646
"cf_xarray>=0.8.4",
4747
"opentelemetry-api>=1.17.0",
4848
"pillow>=9.2.0",

src/qcodes/dataset/exporters/export_to_pandas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def _generate_pandas_index(
108108
else:
109109
index_data.append(data[key].ravel())
110110

111-
index = pd.MultiIndex.from_arrays(index_data, names=keys[1:])
111+
index = pd.MultiIndex.from_arrays(index_data, names=list(deps_data.keys()))
112112
return index
113113

114114

src/qcodes/dataset/exporters/export_to_xarray.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,10 @@ def _load_to_xarray_dataarray_dict_no_metadata(
118118
if export_with_multi_index:
119119
assert isinstance(df.index, pd.MultiIndex)
120120

121-
if hasattr(xr, "Coordinates"):
122-
coords = xr.Coordinates.from_pandas_multiindex(
123-
df.index, "multi_index"
124-
)
125-
xrdarray = xr.DataArray(df[name], coords=coords)
126-
else:
127-
# support xarray < 2023.8.0, can be removed when we drop support for that
128-
xrdarray = xr.DataArray(df[name], [("multi_index", df.index)])
121+
coords = xr.Coordinates.from_pandas_multiindex(
122+
df.index, "multi_index"
123+
)
124+
xrdarray = xr.DataArray(df[name], coords=coords)
129125
else:
130126
xrdarray = df.to_xarray().get(name, xr.DataArray())
131127

tests/dataset/measurement/test_inferred_parameters_fix.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,90 @@ def test_inferred_parameters_in_actual_measurement_1d(experiment, DAC):
215215
assert len(df) == num_points
216216

217217

218+
def test_inferred_parameters_in_actual_measurement_2d(experiment, DAC):
219+
"""
220+
2D version: both axes are DelegateParameters inferred from DAC channels.
221+
Ensures inferred parameters on both axes are saved and exported correctly.
222+
"""
223+
num_points_x = 5
224+
num_points_y = 7
225+
226+
# Delegate parameters (axes)
227+
del_param_1 = DelegateParameter("del_param_1", label="del param 1", source=DAC.ch1)
228+
del_param_2 = DelegateParameter("del_param_2", label="del param 2", source=DAC.ch2)
229+
230+
# Measurement parameter depending on both delegate parameters
231+
meas_parameter = ManualParameter("meas_parameter", initial_value=0.0)
232+
233+
meas = Measurement(name="test_measurement_2d", exp=experiment)
234+
235+
# Register underlying basis (standalone) parameters
236+
meas.register_parameter(DAC.ch1)
237+
meas.register_parameter(DAC.ch2)
238+
239+
# Register delegate parameters inferred from their respective DAC channels
240+
meas.register_parameter(del_param_1, basis=(DAC.ch1,))
241+
meas.register_parameter(del_param_2, basis=(DAC.ch2,))
242+
243+
# Register measurement parameter with 2D setpoints
244+
meas.register_parameter(meas_parameter, setpoints=(del_param_1, del_param_2))
245+
246+
with meas.run() as datasaver:
247+
for x in np.linspace(0, 1, num_points_x):
248+
for y in np.linspace(0, 2, num_points_y):
249+
del_param_1.set(x)
250+
del_param_2.set(y)
251+
meas_parameter.set(x + y) # simple deterministic value
252+
253+
datasaver.add_result(
254+
(DAC.ch1, DAC.ch1()),
255+
(DAC.ch2, DAC.ch2()),
256+
(del_param_1, del_param_1()),
257+
(del_param_2, del_param_2()),
258+
(meas_parameter, meas_parameter()),
259+
)
260+
261+
dataset = datasaver.dataset
262+
param_data = dataset.get_parameter_data()
263+
264+
assert "meas_parameter" in param_data
265+
assert len(param_data) == 1 # only one top-level dependent parameter tree
266+
267+
tree = param_data["meas_parameter"]
268+
expected_keys = {
269+
"meas_parameter",
270+
"del_param_1",
271+
"del_param_2",
272+
"dummy_dac_ch1",
273+
"dummy_dac_ch2",
274+
}
275+
assert set(tree.keys()) == expected_keys
276+
277+
total_points = num_points_x * num_points_y
278+
for k in expected_keys:
279+
assert len(tree[k]) == total_points, f"{k} should have {total_points} entries"
280+
281+
# xarray export
282+
xarr = dataset.to_xarray_dataset()
283+
assert "meas_parameter" in xarr.data_vars
284+
assert "del_param_1" in xarr.coords
285+
assert "del_param_2" in xarr.coords
286+
# inferred (basis) parameters not exported
287+
assert "dummy_dac_ch1" not in xarr.coords
288+
assert "dummy_dac_ch2" not in xarr.coords
289+
290+
assert xarr.meas_parameter.dims == ("del_param_1", "del_param_2")
291+
assert xarr.meas_parameter.shape == (num_points_x, num_points_y)
292+
293+
# pandas export
294+
df = dataset.to_pandas_dataframe()
295+
assert "meas_parameter" in df.columns
296+
assert df.index.names == ["del_param_1", "del_param_2"]
297+
assert "dummy_dac_ch1" not in df.columns
298+
assert "dummy_dac_ch2" not in df.columns
299+
assert len(df) == total_points
300+
301+
218302
def test_multiple_dependent_parameters_no_cross_contamination(experiment):
219303
"""
220304
Test that multiple dependent parameters that depend on the same independent

0 commit comments

Comments
 (0)