Skip to content

dyce.viz package reference

Experimental

This package is an attempt to explore conveniences for integration with Matplotlib. It is an explicit departure from RFC 1925, § 2.2 and should be considered experimental. Be warned that future release may introduce incompatibilities or remove this package altogether. Suggestions and contributions are welcome.

display_burst(ax: Axes, h_inner: H, outer: Optional[Union[H, Iterable[LabelT]]] = None, desc: Optional[str] = None, inner_color: str = 'RdYlGn_r', outer_color: Optional[str] = None, text_color: str = 'black', alpha: float = 0.5) -> None

Experimental

This method should be considered experimental and may change or disappear in future versions.

Creates a dual, overlapping, cocentric pie chart in ax, which can be useful for visualizing relative probability distributions. See the visualization tutorial for examples.

Source code in dyce/viz.py
@experimental
def display_burst(
    ax: Axes,
    h_inner: H,
    outer: Optional[Union[H, Iterable[LabelT]]] = None,
    desc: Optional[str] = None,
    inner_color: str = DEFAULT_GRAPH_COLOR,
    outer_color: Optional[str] = None,
    text_color: str = DEFAULT_TEXT_COLOR,
    alpha: float = DEFAULT_GRAPH_ALPHA,
) -> None:
    r"""
    !!! warning "Experimental"

        This method should be considered experimental and may change or disappear in
        future versions.

    Creates a dual, overlapping, cocentric pie chart in *ax*, which can be useful for
    visualizing relative probability distributions. See the
    [visualization tutorial](countin.md#visualization) for examples.
    """
    assert matplotlib
    inner_colors = graph_colors(inner_color, h_inner, alpha)

    if outer is None:
        outer = (
            ("{:.2%}".format(float(v)) if v >= _HIDE_LIM else "", v)
            for _, v in h_inner.distribution()
        )
    elif isinstance(outer, H):
        outer = ((str(outcome), count) for outcome, count in outer.distribution())

    outer_labels, outer_values = list(zip(*outer))
    outer_colors = graph_colors(
        inner_color if outer_color is None else outer_color,
        outer_values,
        alpha,
    )

    if desc:
        ax.set_title(desc, fontdict={"fontweight": "bold"}, pad=24.0)

    ax.pie(
        outer_values,
        labels=outer_labels,
        radius=1.0,
        labeldistance=1.1,
        startangle=90,
        colors=outer_colors,
        wedgeprops=dict(width=0.8, edgecolor=text_color),
    )
    ax.pie(
        h_inner.values(),
        labels=h_inner,
        radius=0.9,
        labeldistance=0.8,
        startangle=90,
        colors=inner_colors,
        textprops=dict(color=text_color),
        wedgeprops=dict(width=0.6, edgecolor=text_color),
    )
    ax.set(aspect="equal")

labels_cumulative(h: H) -> Iterator[LabelT]

Experimental

This method should be considered experimental and may change or disappear in future versions.

Enumerates label, probability pairs for each outcome in h where each label contains several percentages. This can be useful for passing as the outer value to either display_burst or plot_burst.

Source code in dyce/viz.py
@experimental
def labels_cumulative(
    h: H,
) -> Iterator[LabelT]:
    r"""
    !!! warning "Experimental"

        This method should be considered experimental and may change or disappear in
        future versions.

    Enumerates label, probability pairs for each outcome in *h* where each label
    contains several percentages. This can be useful for passing as the *outer* value to
    either [``display_burst``][dyce.viz.display_burst] or
    [``plot_burst``][dyce.viz.plot_burst].
    """
    le_total, ge_total = 0.0, 1.0
    for outcome, probability in h.distribution():
        le_total += probability
        label = "{} {:.2%}; ≥{:.2%}; ≤{:.2%}".format(
            outcome, float(probability), le_total, ge_total
        )
        ge_total -= probability
        yield (label, probability)

plot_burst(h_inner: H, outer: Optional[Union[H, Iterable[LabelT]]] = None, desc: Optional[str] = None, inner_color: str = 'RdYlGn_r', outer_color: Optional[str] = None, text_color: str = 'black', alpha: float = 0.5) -> Tuple[Figure, Axes]

Experimental

This method should be considered experimental and may change or disappear in future versions.

Wrapper around display_burst that creates a figure, axis pair and calls matplotlib.pyplot.tight_layout on the result.

Source code in dyce/viz.py
@experimental
def plot_burst(
    h_inner: H,
    outer: Optional[Union[H, Iterable[LabelT]]] = None,
    desc: Optional[str] = None,
    inner_color: str = DEFAULT_GRAPH_COLOR,
    outer_color: Optional[str] = None,
    text_color: str = DEFAULT_TEXT_COLOR,
    alpha: float = DEFAULT_GRAPH_ALPHA,
) -> Tuple[Figure, Axes]:
    r"""
    !!! warning "Experimental"

        This method should be considered experimental and may change or disappear in
        future versions.

    Wrapper around [``display_burst``][dyce.viz.display_burst] that creates a figure,
    axis pair and calls
    [``matplotlib.pyplot.tight_layout``](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.tight_layout.html)
    on the result.
    """
    assert matplotlib
    fig, ax = matplotlib.pyplot.subplots()
    display_burst(ax, h_inner, outer, desc, inner_color, outer_color, text_color, alpha)
    matplotlib.pyplot.tight_layout()

    return fig, ax

walk_r(g: Digraph, obj: Union[R, Roll, RollOutcome]) -> None

Experimental

This method should be considered experimental and is highly likely change or disappear in future versions.

Whoo boy. What a mess. Hasn’t anyone around here ever heard of the visitor pattern? Jeez!

This is a study. It‘s here so I can solve my immediate problem of generating pretty pictures for docs. But it’s also a vehicle for achieving enlightenment by being my own non-traditional customer of rollers and rolls.

Source code in dyce/viz.py
@experimental
def walk_r(
    g: Digraph,
    obj: Union[R, Roll, RollOutcome],
) -> None:
    r"""
    !!! warning "Experimental"

        This method should be considered experimental and is highly likely change or
        disappear in future versions.

    Whoo boy. What a *mess*. Hasn’t anyone around here ever heard of the visitor
    pattern? Jeez!

    This is a *study*. It‘s here so I can solve my immediate problem of generating
    pretty pictures for docs. But it’s also a vehicle for achieving enlightenment by
    being my own non-traditional customer of rollers and rolls.
    """
    serial = 1
    names: Dict[int, str] = {}

    def _name_for_obj(obj: Union[R, Roll, RollOutcome]) -> str:
        nonlocal serial, names

        if id(obj) not in names:
            names[id(obj)] = f"{type(obj).__name__}-{serial}"
            serial += 1

        return names[id(obj)]

    rollers: Dict[str, R] = {}
    rolls: Dict[str, Roll] = {}
    roll_outcomes: Dict[str, RollOutcome] = {}
    queue = deque((obj,))

    while queue:
        obj = queue.popleft()
        obj_name = _name_for_obj(obj)

        if isinstance(obj, R):
            if obj_name not in rollers:
                rollers[obj_name] = obj
                queue.extend(obj.sources)
        elif isinstance(obj, Roll):
            if obj_name not in rolls:
                rolls[obj_name] = obj
                queue.append(obj.r)

                attrs = obj.annotation if obj.annotation else {}
                g.edge(
                    obj_name,
                    _name_for_obj(obj.r),
                    label="r",
                    **attrs.get("edge", {}),
                )
                queue.extend(obj.sources)

                for i, roll_outcome in enumerate(obj):
                    attrs = roll_outcome.annotation if roll_outcome.annotation else {}
                    g.edge(
                        obj_name,
                        _name_for_obj(roll_outcome),
                        label=f"[{i}]",
                        **attrs.get("edge", {}),
                    )
                    queue.append(roll_outcome)
        elif isinstance(obj, RollOutcome):
            if obj_name not in roll_outcomes:
                roll_outcomes[obj_name] = obj
                queue.extend(obj.sources)

    if rollers:
        with g.subgraph(name="cluster_rollers") as c:
            c.attr(label="<<i>Roller Tree</i>>", style="dotted")

            for r_name, r in rollers.items():
                attrs = r.annotation if r.annotation else {}

                if isinstance(r, PoolRoller):
                    label = f"<<b>{type(r).__name__}</b>>"
                elif isinstance(r, RepeatRoller):
                    label = f"""<<b>{type(r).__name__}</b><br/><font face="Courier New">n={r.n!r}</font>>"""
                elif isinstance(r, SelectionRoller):
                    label = f"""<<b>{type(r).__name__}</b><br/><font face="Courier New">which={r.which!r}</font>>"""
                elif isinstance(r, ValueRoller):
                    value = _truncate(repr(r.value), is_html=True)
                    label = f"""<<b>{type(r).__name__}</b><br/><font face="Courier New">value={value}</font>>"""
                elif isinstance(r, (ChainRoller, SumRoller)):
                    if hasattr(r.op, "__name__"):
                        op = r.op.__name__  # type: ignore
                    else:
                        op = repr(r.op)

                    op = _truncate(op, is_html=True)
                    label = f"""<<b>{type(r).__name__}</b><br/><font face="Courier New">op={op}</font>>"""
                else:
                    label = f"<<b>{type(r).__name__}</b>>"

                c.node(r_name, label=label, **attrs.get("node", {}))

                for i, roller_src in enumerate(r.sources):
                    attrs = roller_src.annotation if roller_src.annotation else {}
                    c.edge(
                        r_name,
                        _name_for_obj(roller_src),
                        label=f"sources[{i}]",
                        **attrs.get("edge", {}),
                    )

    if rolls:
        with g.subgraph(name="cluster_rolls") as c:
            c.attr(label="<<i>Roll Tree</i>>", style="dotted")

            for roll_name, roll in rolls.items():
                attrs = roll.annotation if roll.annotation else {}
                c.node(
                    roll_name,
                    label=f"<<b>{type(roll).__name__}</b>>",
                    **attrs.get("node", {}),
                )

                for i, roll_src in enumerate(roll.sources):
                    attrs = roll_src.annotation if roll_src.annotation else {}
                    c.edge(
                        roll_name,
                        _name_for_obj(roll_src),
                        label=f"sources[{i}]",
                        **attrs.get("edge", {}),
                    )

    if roll_outcomes:
        with g.subgraph(name="cluster_roll_outcomes") as c:
            c.attr(label="<<i>Roll Outcomes</i>>", style="dotted")

            for roll_outcome_name, roll_outcome in roll_outcomes.items():
                attrs = roll_outcome.annotation if roll_outcome.annotation else {}
                c.node(
                    roll_outcome_name,
                    label=f"""<<b>{type(roll_outcome).__name__}</b><br/><font face="Courier New">value={roll_outcome.value}</font>>""",
                    **attrs.get(
                        "node",
                        {"style": "dotted"} if roll_outcome.value is None else {},
                    ),
                )

                for i, roll_outcome_src in enumerate(roll_outcome.sources):
                    attrs = (
                        roll_outcome_src.annotation
                        if roll_outcome_src.annotation
                        else {}
                    )
                    c.edge(
                        roll_outcome_name,
                        _name_for_obj(roll_outcome_src),
                        label=f"sources[{i}]",
                        **attrs.get("edge", {}),
                    )