Skip to content

Commit 9f66897

Browse files
authored
plots: support x-dict in nested dvc.yaml (#10318)
1 parent 84a1750 commit 9f66897

File tree

3 files changed

+227
-5
lines changed

3 files changed

+227
-5
lines changed

dvc/repo/plots/__init__.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -423,11 +423,17 @@ def _id_is_path(plot_props=None):
423423

424424
def _adjust_sources(fs, plot_props, config_dir):
425425
new_plot_props = deepcopy(plot_props)
426-
old_y = new_plot_props.pop("y", {})
427-
new_y = {}
428-
for filepath, val in old_y.items():
429-
new_y[_normpath(fs.join(config_dir, filepath))] = val
430-
new_plot_props["y"] = new_y
426+
for axis in ["x", "y"]:
427+
x_is_inferred = axis == "x" and (
428+
axis not in new_plot_props or isinstance(new_plot_props[axis], str)
429+
)
430+
if x_is_inferred:
431+
continue
432+
old = new_plot_props.pop(axis, {})
433+
new = {}
434+
for filepath, val in old.items():
435+
new[_normpath(fs.join(config_dir, filepath))] = val
436+
new_plot_props[axis] = new
431437
return new_plot_props
432438

433439

tests/func/plots/test_show.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,87 @@ def test_plots_show_overlap(tmp_dir, dvc, run_copy_metrics, clear_before_run):
172172
)
173173

174174

175+
def test_plots_show_nested_x_dict(tmp_dir, dvc, scm):
176+
rel_pipeline_dir = "pipelines/data-increment"
177+
178+
pipeline_rel_dvclive_metrics_dir = "dvclive/plots/metrics"
179+
dvc_rel_dvclive_metrics_dir = (
180+
f"{rel_pipeline_dir}/{pipeline_rel_dvclive_metrics_dir}"
181+
)
182+
183+
pipeline_dir = tmp_dir / rel_pipeline_dir
184+
dvclive_metrics_dir = pipeline_dir / pipeline_rel_dvclive_metrics_dir
185+
dvclive_metrics_dir.mkdir(parents=True)
186+
187+
def _get_plot_defn(rel_dir: str) -> dict:
188+
return {
189+
"template": "simple",
190+
"x": {f"{rel_dir}/Max_Leaf_Nodes.tsv": "Max_Leaf_Nodes"},
191+
"y": {f"{rel_dir}/Error.tsv": "Error"},
192+
}
193+
194+
(pipeline_dir / "dvc.yaml").dump(
195+
{
196+
"plots": [
197+
{
198+
"Error vs max_leaf_nodes": _get_plot_defn(
199+
pipeline_rel_dvclive_metrics_dir
200+
)
201+
},
202+
]
203+
},
204+
)
205+
206+
dvclive_metrics_dir.gen(
207+
{
208+
"Error.tsv": "step\tError\n" "0\t0.11\n" "1\t0.22\n" "2\t0.44\n",
209+
"Max_Leaf_Nodes.tsv": "step\tMax_Leaf_Nodes\n"
210+
"0\t5\n"
211+
"1\t50\n"
212+
"2\t500\n",
213+
}
214+
)
215+
216+
scm.commit("add dvc.yaml and dvclive metrics")
217+
218+
result = dvc.plots.show()
219+
assert result == {
220+
"workspace": {
221+
"definitions": {
222+
"data": {
223+
f"{rel_pipeline_dir}/dvc.yaml": {
224+
"data": {
225+
"Error vs max_leaf_nodes": _get_plot_defn(
226+
dvc_rel_dvclive_metrics_dir
227+
)
228+
},
229+
}
230+
}
231+
},
232+
"sources": {
233+
"data": {
234+
f"{dvc_rel_dvclive_metrics_dir}/Error.tsv": {
235+
"data": [
236+
{"Error": "0.11", "step": "0"},
237+
{"Error": "0.22", "step": "1"},
238+
{"Error": "0.44", "step": "2"},
239+
],
240+
"props": {},
241+
},
242+
f"{dvc_rel_dvclive_metrics_dir}/Max_Leaf_Nodes.tsv": {
243+
"data": [
244+
{"Max_Leaf_Nodes": "5", "step": "0"},
245+
{"Max_Leaf_Nodes": "50", "step": "1"},
246+
{"Max_Leaf_Nodes": "500", "step": "2"},
247+
],
248+
"props": {},
249+
},
250+
}
251+
},
252+
}
253+
}
254+
255+
175256
def test_dir_plots(tmp_dir, dvc, run_copy_metrics):
176257
subdir = tmp_dir / "subdir"
177258
subdir.mkdir()

tests/integration/plots/test_plots.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,3 +481,138 @@ def test_repo_with_dvclive_plots(tmp_dir, capsys, repo_with_dvclive_plots):
481481
}
482482
assert json_result == expected_result
483483
assert split_json_result == expected_result
484+
485+
486+
@pytest.mark.vscode
487+
def test_nested_x_defn_collection(tmp_dir, dvc, scm, capsys):
488+
rel_pipeline_dir = "pipelines/data-increment"
489+
pipeline_rel_dvclive_metrics_dir = "dvclive/plots/metrics"
490+
pipeline_rel_other_logger_dir = "other/logger"
491+
492+
dvc_rel_dvclive_metrics_dir = (
493+
f"{rel_pipeline_dir}/{pipeline_rel_dvclive_metrics_dir}"
494+
)
495+
dvc_rel_other_logger_dir = f"{rel_pipeline_dir}/{pipeline_rel_other_logger_dir}"
496+
497+
pipeline_dir = tmp_dir / rel_pipeline_dir
498+
dvclive_metrics_dir = pipeline_dir / pipeline_rel_dvclive_metrics_dir
499+
dvclive_metrics_dir.mkdir(parents=True)
500+
other_logger_dir = pipeline_dir / pipeline_rel_other_logger_dir
501+
other_logger_dir.mkdir(parents=True)
502+
503+
(pipeline_dir / "dvc.yaml").dump(
504+
{
505+
"plots": [
506+
{
507+
"Error vs max_leaf_nodes": {
508+
"template": "simple",
509+
"x": {
510+
f"{pipeline_rel_dvclive_metrics_dir}"
511+
"/Max_Leaf_Nodes.tsv": "Max_Leaf_Nodes"
512+
},
513+
"y": {f"{pipeline_rel_dvclive_metrics_dir}/Error.tsv": "Error"},
514+
}
515+
},
516+
{
517+
f"{pipeline_rel_other_logger_dir}/multiple_metrics.json": {
518+
"x": "x",
519+
"y": ["y1", "y2"],
520+
},
521+
},
522+
{
523+
f"{pipeline_rel_dvclive_metrics_dir}/Error.tsv": {"y": ["Error"]},
524+
},
525+
{
526+
"max leaf nodes": {
527+
"y": {
528+
f"{pipeline_rel_dvclive_metrics_dir}"
529+
"/Max_Leaf_Nodes.tsv": "Max_Leaf_Nodes"
530+
}
531+
},
532+
},
533+
]
534+
},
535+
)
536+
dvclive_metrics_dir.gen(
537+
{
538+
"Error.tsv": "step\tError\n" "0\t0.11\n" "1\t0.22\n" "2\t0.44\n",
539+
"Max_Leaf_Nodes.tsv": "step\tMax_Leaf_Nodes\n"
540+
"0\t5\n"
541+
"1\t50\n"
542+
"2\t500\n",
543+
}
544+
)
545+
(other_logger_dir / "multiple_metrics.json").dump(
546+
[
547+
{"x": 0, "y1": 0.1, "y2": 10},
548+
{"x": 1, "y1": 0.2, "y2": 22},
549+
]
550+
)
551+
552+
scm.commit("add dvc.yaml and metrics")
553+
554+
_, _, split_json_result = call(capsys, subcommand="diff")
555+
assert len(split_json_result.keys()) == 1
556+
assert len(split_json_result["data"].keys()) == 4
557+
558+
separate_x_file = split_json_result["data"]["Error vs max_leaf_nodes"][0]
559+
560+
assert separate_x_file["anchor_definitions"]["<DVC_METRIC_DATA>"] == [
561+
{"Error": "0.11", "Max_Leaf_Nodes": "5", "step": "0", "rev": "workspace"},
562+
{"Error": "0.22", "Max_Leaf_Nodes": "50", "step": "1", "rev": "workspace"},
563+
{"Error": "0.44", "Max_Leaf_Nodes": "500", "step": "2", "rev": "workspace"},
564+
]
565+
566+
same_x_file = split_json_result["data"][
567+
f"{dvc_rel_other_logger_dir}/multiple_metrics.json"
568+
][0]
569+
assert same_x_file["anchor_definitions"]["<DVC_METRIC_DATA>"] == [
570+
{
571+
"x": 0,
572+
"y1": 0.1,
573+
"y2": 10,
574+
"dvc_inferred_y_value": 0.1,
575+
"field": "y1",
576+
"rev": "workspace",
577+
},
578+
{
579+
"x": 1,
580+
"y1": 0.2,
581+
"y2": 22,
582+
"dvc_inferred_y_value": 0.2,
583+
"field": "y1",
584+
"rev": "workspace",
585+
},
586+
{
587+
"x": 0,
588+
"y1": 0.1,
589+
"y2": 10,
590+
"dvc_inferred_y_value": 10,
591+
"field": "y2",
592+
"rev": "workspace",
593+
},
594+
{
595+
"x": 1,
596+
"y1": 0.2,
597+
"y2": 22,
598+
"dvc_inferred_y_value": 22,
599+
"field": "y2",
600+
"rev": "workspace",
601+
},
602+
]
603+
604+
inferred_x_from_str = split_json_result["data"][
605+
f"{dvc_rel_dvclive_metrics_dir}/Error.tsv"
606+
][0]
607+
assert inferred_x_from_str["anchor_definitions"]["<DVC_METRIC_DATA>"] == [
608+
{"step": 0, "Error": "0.11", "rev": "workspace"},
609+
{"step": 1, "Error": "0.22", "rev": "workspace"},
610+
{"step": 2, "Error": "0.44", "rev": "workspace"},
611+
]
612+
613+
inferred_x_from_dict = split_json_result["data"]["max leaf nodes"][0]
614+
assert inferred_x_from_dict["anchor_definitions"]["<DVC_METRIC_DATA>"] == [
615+
{"step": 0, "Max_Leaf_Nodes": "5", "rev": "workspace"},
616+
{"step": 1, "Max_Leaf_Nodes": "50", "rev": "workspace"},
617+
{"step": 2, "Max_Leaf_Nodes": "500", "rev": "workspace"},
618+
]

0 commit comments

Comments
 (0)