Files
dvc/dvc/render/plotly.py
T
David de la Iglesia Castro 4d5be46ab4 exp show --html: Handle fill_value.
Adds new argument `fill_value` to ParallelCoordinatesRenderer.

For scalar columns, prevent converting to categorical by explictly inserting None when value is missing (value == fill_value).

For categorical columns, replace `fill_value` with `"Missing"` and ensure it is the last item so it gets rendered on top of the column.
2021-12-17 14:45:12 +09:00

93 lines
2.9 KiB
Python

import json
from collections import defaultdict
from typing import TYPE_CHECKING, Any, Dict, Optional
from dvc.render.base import Renderer
if TYPE_CHECKING:
from dvc.compare import TabularData
class ParallelCoordinatesRenderer(Renderer):
TYPE = "plotly"
DIV = """
<div id = "{id}">
<script type = "text/javascript">
var plotly_data = {partial};
Plotly.newPlot("{id}", plotly_data.data, plotly_data.layout);
</script>
</div>
"""
SCRIPTS = """
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
"""
# pylint: disable=W0231
def __init__(
self,
tabular_data: "TabularData",
color_by: Optional[str] = None,
fill_value: str = "",
):
self.tabular_data = tabular_data
self.color_by = color_by
self.filename = "experiments"
self.fill_value = fill_value
def partial_html(self, **kwargs):
return self.as_json()
def as_json(self, **kwargs) -> str:
tabular_dict = defaultdict(list)
for row in self.tabular_data.as_dict():
for col_name, value in row.items():
tabular_dict[col_name].append(str(value))
trace: Dict[str, Any] = {"type": "parcoords", "dimensions": []}
for label, values in tabular_dict.items():
is_categorical = False
try:
float_values = [
float(x) if x != self.fill_value else None for x in values
]
except ValueError:
is_categorical = True
if is_categorical:
non_missing = [x for x in values if x != self.fill_value]
unique_values = sorted(set(non_missing))
unique_values.append(self.fill_value)
dummy_values = [unique_values.index(x) for x in values]
values = [
x if x != self.fill_value else "Missing" for x in values
]
trace["dimensions"].append(
{
"label": label,
"values": dummy_values,
"tickvals": dummy_values,
"ticktext": values,
}
)
else:
trace["dimensions"].append(
{"label": label, "values": float_values}
)
if label == self.color_by:
trace["line"] = {
"color": dummy_values if is_categorical else float_values,
"showscale": True,
"colorbar": {"title": self.color_by},
}
if is_categorical:
trace["line"]["colorbar"]["tickmode"] = "array"
trace["line"]["colorbar"]["tickvals"] = dummy_values
trace["line"]["colorbar"]["ticktext"] = values
return json.dumps({"data": [trace], "layout": {}})