mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
@@ -1,13 +1,11 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
from typing import TYPE_CHECKING, Dict, Iterable
|
from typing import TYPE_CHECKING, Dict, Iterable
|
||||||
|
|
||||||
from funcy import lmap
|
from funcy import lmap
|
||||||
|
|
||||||
from dvc.cli import completion
|
|
||||||
from dvc.cli.command import CmdBase
|
from dvc.cli.command import CmdBase
|
||||||
from dvc.cli.utils import append_doc_link
|
from dvc.cli.utils import append_doc_link
|
||||||
from dvc.commands.metrics import DEFAULT_PRECISION
|
from dvc.commands.metrics import DEFAULT_PRECISION
|
||||||
@@ -59,7 +57,7 @@ def baseline_styler(typ):
|
|||||||
return {"style": "bold"} if typ == "baseline" else {}
|
return {"style": "bold"} if typ == "baseline" else {}
|
||||||
|
|
||||||
|
|
||||||
def show_experiments( # noqa: C901, PLR0912
|
def show_experiments( # noqa: C901
|
||||||
td: "TabularData",
|
td: "TabularData",
|
||||||
headers: Dict[str, Iterable[str]],
|
headers: Dict[str, Iterable[str]],
|
||||||
keep=None,
|
keep=None,
|
||||||
@@ -67,7 +65,6 @@ def show_experiments( # noqa: C901, PLR0912
|
|||||||
pager=True,
|
pager=True,
|
||||||
csv=False,
|
csv=False,
|
||||||
markdown=False,
|
markdown=False,
|
||||||
pcp=False,
|
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
if keep:
|
if keep:
|
||||||
@@ -110,51 +107,23 @@ def show_experiments( # noqa: C901, PLR0912
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if kwargs.get("only_changed", False) or pcp:
|
if kwargs.get("only_changed", False):
|
||||||
td.drop_duplicates("cols", ignore_empty=False)
|
td.drop_duplicates("cols", ignore_empty=False)
|
||||||
|
|
||||||
cols_to_drop = set()
|
cols_to_drop = set()
|
||||||
if drop is not None:
|
if drop is not None:
|
||||||
cols_to_drop = {col for col in td.keys() if re.match(drop, col)} # noqa: SIM118
|
cols_to_drop = {col for col in td.keys() if re.match(drop, col)} # noqa: SIM118
|
||||||
if pcp:
|
|
||||||
cols_to_drop.add("Created")
|
|
||||||
td.drop(*cols_to_drop)
|
td.drop(*cols_to_drop)
|
||||||
|
|
||||||
if pcp:
|
td.render(
|
||||||
subset = {x for x in td.keys() if x != "Experiment"} # noqa: SIM118
|
pager=pager,
|
||||||
td.dropna(
|
borders="horizontals",
|
||||||
"rows",
|
rich_table=True,
|
||||||
how="all",
|
header_styles=styles,
|
||||||
subset=subset,
|
row_styles=row_styles,
|
||||||
)
|
csv=csv,
|
||||||
td.drop_duplicates("rows", subset=subset)
|
markdown=markdown,
|
||||||
if "Experiment" in td:
|
)
|
||||||
td.column("Experiment")[:] = [
|
|
||||||
# remove tree characters
|
|
||||||
str(x).encode("ascii", "ignore").strip().decode()
|
|
||||||
for x in td.column("Experiment")
|
|
||||||
]
|
|
||||||
out = kwargs.get("out") or "dvc_plots"
|
|
||||||
output_file = os.path.join(out, "index.html")
|
|
||||||
ui.write(
|
|
||||||
td.to_parallel_coordinates(
|
|
||||||
output_path=os.path.abspath(output_file),
|
|
||||||
color_by=kwargs.get("sort_by") or "Experiment",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if kwargs.get("open"):
|
|
||||||
return ui.open_browser(output_file)
|
|
||||||
|
|
||||||
else:
|
|
||||||
td.render(
|
|
||||||
pager=pager,
|
|
||||||
borders="horizontals",
|
|
||||||
rich_table=True,
|
|
||||||
header_styles=styles,
|
|
||||||
row_styles=row_styles,
|
|
||||||
csv=csv,
|
|
||||||
markdown=markdown,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_headers(names, count):
|
def _normalize_headers(names, count):
|
||||||
@@ -221,9 +190,6 @@ class CmdExperimentsShow(CmdBase):
|
|||||||
csv=self.args.csv,
|
csv=self.args.csv,
|
||||||
markdown=self.args.markdown,
|
markdown=self.args.markdown,
|
||||||
only_changed=self.args.only_changed,
|
only_changed=self.args.only_changed,
|
||||||
pcp=self.args.pcp,
|
|
||||||
out=self.args.out,
|
|
||||||
open=self.args.open,
|
|
||||||
)
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -342,27 +308,6 @@ def add_parser(experiments_subparsers, parent_parser):
|
|||||||
),
|
),
|
||||||
metavar="<n>",
|
metavar="<n>",
|
||||||
)
|
)
|
||||||
experiments_show_parser.add_argument(
|
|
||||||
"--parallel-coordinates-plot",
|
|
||||||
"--pcp",
|
|
||||||
dest="pcp",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="Generate a Parallel Coordinates Plot from the tabulated output.",
|
|
||||||
)
|
|
||||||
experiments_show_parser.add_argument(
|
|
||||||
"-o",
|
|
||||||
"--out",
|
|
||||||
default=None,
|
|
||||||
help="Destination folder to save the Parallel Coordinates Plot to",
|
|
||||||
metavar="<path>",
|
|
||||||
).complete = completion.DIR
|
|
||||||
experiments_show_parser.add_argument(
|
|
||||||
"--open",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="Open the Parallel Coordinates Plot directly in the browser.",
|
|
||||||
)
|
|
||||||
experiments_show_parser.add_argument(
|
experiments_show_parser.add_argument(
|
||||||
"--no-fetch",
|
"--no-fetch",
|
||||||
dest="fetch_running",
|
dest="fetch_running",
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ from typing import (
|
|||||||
from funcy import reraise
|
from funcy import reraise
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from dvc.types import StrPath
|
|
||||||
from dvc.ui.table import CellT
|
from dvc.ui.table import CellT
|
||||||
|
|
||||||
|
|
||||||
@@ -161,24 +160,6 @@ class TabularData(MutableSequence[Sequence["CellT"]]):
|
|||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
return buff.getvalue()
|
return buff.getvalue()
|
||||||
|
|
||||||
def to_parallel_coordinates(
|
|
||||||
self, output_path: "StrPath", color_by: Optional[str] = None
|
|
||||||
) -> "StrPath":
|
|
||||||
from dvc_render.html import render_html
|
|
||||||
from dvc_render.plotly import ParallelCoordinatesRenderer
|
|
||||||
|
|
||||||
render_html(
|
|
||||||
renderers=[
|
|
||||||
ParallelCoordinatesRenderer(
|
|
||||||
self.as_dict(),
|
|
||||||
color_by=color_by,
|
|
||||||
fill_value=self._fill_value,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
output_file=output_path,
|
|
||||||
)
|
|
||||||
return output_path
|
|
||||||
|
|
||||||
def add_column(self, name: str) -> None:
|
def add_column(self, name: str) -> None:
|
||||||
self._columns[name] = Column([self._fill_value] * len(self))
|
self._columns[name] = Column([self._fill_value] * len(self))
|
||||||
self._keys.append(name)
|
self._keys.append(name)
|
||||||
|
|||||||
@@ -551,85 +551,6 @@ def test_show_only_changed(tmp_dir, dvc, scm, capsys, copy_script):
|
|||||||
assert "metrics.yaml:goobar" in cap.out
|
assert "metrics.yaml:goobar" in cap.out
|
||||||
|
|
||||||
|
|
||||||
def test_show_parallel_coordinates(tmp_dir, dvc, scm, mocker, capsys, copy_script):
|
|
||||||
from dvc.commands.experiments import show
|
|
||||||
|
|
||||||
webbroser_open = mocker.patch("webbrowser.open")
|
|
||||||
show_experiments = mocker.spy(show, "show_experiments")
|
|
||||||
|
|
||||||
params_file = tmp_dir / "params.yaml"
|
|
||||||
params_data = {
|
|
||||||
"foo": 1,
|
|
||||||
"bar": 1,
|
|
||||||
}
|
|
||||||
(tmp_dir / params_file).dump(params_data)
|
|
||||||
|
|
||||||
dvc.run(
|
|
||||||
cmd="python copy.py params.yaml metrics.yaml",
|
|
||||||
metrics_no_cache=["metrics.yaml"],
|
|
||||||
params=["foo", "bar"],
|
|
||||||
name="copy-file",
|
|
||||||
deps=["copy.py"],
|
|
||||||
)
|
|
||||||
scm.add(
|
|
||||||
[
|
|
||||||
"dvc.yaml",
|
|
||||||
"dvc.lock",
|
|
||||||
"copy.py",
|
|
||||||
"params.yaml",
|
|
||||||
"metrics.yaml",
|
|
||||||
".gitignore",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
scm.commit("init")
|
|
||||||
|
|
||||||
dvc.experiments.run(params=["foo=2"])
|
|
||||||
|
|
||||||
assert main(["exp", "show", "--pcp"]) == 0
|
|
||||||
kwargs = show_experiments.call_args[1]
|
|
||||||
|
|
||||||
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
|
|
||||||
assert all(rev in html_text for rev in ["workspace", "master"])
|
|
||||||
assert "[exp-" not in html_text
|
|
||||||
|
|
||||||
assert '{"label": "metrics.yaml:foo", "values": [2.0, 1.0]}' in html_text
|
|
||||||
assert '{"label": "params.yaml:foo", "values": [2.0, 1.0]}' in html_text
|
|
||||||
assert '"line": {"color": [1, 0]' in html_text
|
|
||||||
assert '"label": "metrics.yaml:bar"' not in html_text
|
|
||||||
assert '"label": "Created"' not in html_text
|
|
||||||
|
|
||||||
assert main(["exp", "show", "--pcp", "--sort-by", "metrics.yaml:foo"]) == 0
|
|
||||||
kwargs = show_experiments.call_args[1]
|
|
||||||
|
|
||||||
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
|
|
||||||
assert '"line": {"color": [2.0, 1.0]' in html_text
|
|
||||||
|
|
||||||
assert main(["exp", "show", "--pcp", "--out", "experiments"]) == 0
|
|
||||||
kwargs = show_experiments.call_args[1]
|
|
||||||
|
|
||||||
assert kwargs["out"] == "experiments"
|
|
||||||
assert (tmp_dir / "experiments" / "index.html").exists()
|
|
||||||
|
|
||||||
assert main(["exp", "show", "--pcp", "--open"]) == 0
|
|
||||||
|
|
||||||
webbroser_open.assert_called()
|
|
||||||
|
|
||||||
params_data = {"foo": 1, "bar": 1, "foobar": 2}
|
|
||||||
(tmp_dir / params_file).dump(params_data)
|
|
||||||
assert main(["exp", "show", "--pcp"]) == 0
|
|
||||||
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
|
|
||||||
assert '{"label": "foobar", "values": [2.0, null, null]}' in html_text
|
|
||||||
|
|
||||||
assert main(["exp", "show", "--pcp", "--drop", "foobar"]) == 0
|
|
||||||
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
|
|
||||||
assert '"label": "Created"' not in html_text
|
|
||||||
assert '"label": "foobar"' not in html_text
|
|
||||||
|
|
||||||
assert main(["exp", "show", "--pcp", "--drop", "Experiment"]) == 0
|
|
||||||
html_text = (tmp_dir / "dvc_plots" / "index.html").read_text()
|
|
||||||
assert '"label": "Experiment"' not in html_text
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.vscode
|
@pytest.mark.vscode
|
||||||
def test_show_outs(tmp_dir, dvc, scm, erepo_dir, copy_script):
|
def test_show_outs(tmp_dir, dvc, scm, erepo_dir, copy_script):
|
||||||
params_file = tmp_dir / "params.yaml"
|
params_file = tmp_dir / "params.yaml"
|
||||||
|
|||||||
@@ -374,21 +374,3 @@ def test_drop_duplicates_invalid_axis():
|
|||||||
|
|
||||||
with pytest.raises(ValueError, match="Invalid 'axis' value foo."):
|
with pytest.raises(ValueError, match="Invalid 'axis' value foo."):
|
||||||
td.drop_duplicates("foo")
|
td.drop_duplicates("foo")
|
||||||
|
|
||||||
|
|
||||||
def test_to_parallel_coordinates(tmp_dir, mocker):
|
|
||||||
(tmp_dir / "foo").mkdir()
|
|
||||||
td = TabularData(["categorical", "scalar"])
|
|
||||||
td.extend([["foo", "0.1"], ["bar", "2"]])
|
|
||||||
|
|
||||||
write = mocker.patch("dvc_render.html.render_html")
|
|
||||||
renderer_class = mocker.patch("dvc_render.plotly.ParallelCoordinatesRenderer")
|
|
||||||
renderer = renderer_class.return_value
|
|
||||||
|
|
||||||
td.to_parallel_coordinates(output_path="foo")
|
|
||||||
|
|
||||||
renderer_class.assert_called_with(
|
|
||||||
td.as_dict(), color_by=None, fill_value=td._fill_value
|
|
||||||
)
|
|
||||||
|
|
||||||
write.assert_called_with(renderers=[renderer], output_file="foo")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user