Skip to content

Commit

Permalink
Improve off-axis outlier annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
mmore500 committed Nov 20, 2023
1 parent 54ae9d9 commit 871db8e
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 47 deletions.
2 changes: 2 additions & 0 deletions conduitpylib/viz/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Visualization tools."""

from ._align_marker import align_marker
from ._annotate_spearman import annotate_spearman
from ._apply_symmetric_axes import apply_symmetric_axes
from ._beleaguerment_regplot import beleaguerment_regplot
Expand Down Expand Up @@ -34,6 +35,7 @@

# adapted from https://stackoverflow.com/a/31079085
__all__ = [
"align_marker",
"apply_symmetric_axes",
"annotate_spearman",
"beleaguerment_facetplot",
Expand Down
2 changes: 1 addition & 1 deletion conduitpylib/viz/_calc_performance_semantics_axis_lims.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def calc_performance_semantics_axis_lims(
ymin = min(0.4, ymin)

# make room for rugplot elements
xmax *= 1.1
xmax *= 1.3
ymax *= 3

return xmin, xmax, ymin, ymax
99 changes: 53 additions & 46 deletions conduitpylib/viz/_draw_edge_markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
import typing

from frozendict import frozendict
import numpy as np

from matplotlib.axes import Axes as mpl_Axes
from matplotlib.collections import PathCollection as mpl_PathCollection
from matplotlib.container import ErrorbarContainer as mpl_ErrorbarContainer

from ._align_marker import align_marker


def draw_edge_marker(
Expand All @@ -26,8 +30,7 @@ def draw_edge_marker(
The x and y coordinates of the point.
"""
xlim, ylim = ax.get_xlim(), ax.get_ylim()
edge_x, edge_y, marker, annotation = x, y, None, ""
ha, va, xytext = "left", "center", (5, 0) # default alignment and offset
edge_x, edge_y, markers = x, y, list()

xwidth = -operator.__sub__(*xlim)
ywidth = -operator.__sub__(*ylim)
Expand All @@ -36,39 +39,23 @@ def draw_edge_marker(
if x < xlim[0]:
amount = int((xlim[0] - x) / xwidth)
backslash = "\\"
edge_x, marker, annotation, ha = (
xlim[0] - xoff,
f"$◅{backslash * amount}$",
f"{x:.0f}",
"right",
)
edge_x = xlim[0] - xoff
markers.append(f"$◅{backslash * amount}$")
elif x > xlim[1]:
amount = int((x - xlim[1]) / xwidth)
edge_x, marker, annotation, ha = (
xlim[1] + xoff,
rf"$| \!\! \leftrightarrow \!\!|{{\times}}{amount}\rangle\!\rangle\!\rangle$",
f"{x:.0f}",
"left",
)
m = rf"$| \!\! \leftrightarrow \!\!|{{\times}}{amount}\rangle\!\rangle\!\rangle$"
edge_x = xlim[1] + xoff
# markers.append(align_marker(m, halign="left"))
markers.append(align_marker(m, halign="right", pad=1.3))

if y < ylim[0]:
edge_y, marker, annotation, va, xytext = (
ylim[0] - yoff,
"^",
f"{y:.2f}" if not annotation else f"({annotation}, {y:.2f})",
"bottom",
(0, 5),
)
edge_y = ylim[0] - yoff
markers.append("^")
elif y > ylim[1]:
edge_y, marker, annotation, va, xytext = (
ylim[1] + yoff,
"v",
f"{y:.2f}" if not annotation else f"({annotation}, {y:.2f})",
"top",
(0, -5),
)
edge_y = ylim[1] + yoff
markers.append("v")

if marker:
for marker in markers:
ax.plot(
edge_x,
edge_y,
Expand All @@ -84,17 +71,6 @@ def draw_edge_marker(
**marker_kwargs,
},
)
ax.annotate(
annotation,
(edge_x, edge_y),
**{
"ha": ha,
"va": va,
"textcoords": "offset points",
"xytext": xytext,
**annotation_kwargs,
},
)

return edge_x, edge_y

Expand All @@ -118,19 +94,50 @@ def draw_edge_markers(
for collection in ax.collections:
if isinstance(collection, mpl_PathCollection):
# Extract points from the line
points = list(collection.get_offsets())

new_offsets = []
for x, y in points:
for x, y in collection.get_offsets():
# Draw edge marker if the point is out of bounds
if x < xlim[0] or x > xlim[1] or y < ylim[0] or y > ylim[1]:
x_, y_ = draw_edge_marker(
x, y = draw_edge_marker(
ax,
x,
y,
annotation_kwargs=annotation_kwargs,
marker_kwargs=marker_kwargs,
offset=offset,
)
else:
new_offsets.append((x, y))
new_offsets.append((x, y))

collection.set_offsets(new_offsets)
collection.set(clip_on=False)

x_width = -operator.__sub__(*xlim)
x_thresh = xlim[1]
x_offset = x_thresh + x_width * offset
for container in ax.containers:
if isinstance(container, mpl_ErrorbarContainer):
# Unpack the container
(
plotline, # Line2D instance of x, y plot markers and/or line
caplines, # A tuple of Line2D instances of the error bar caps
# A tuple of LineCollection with the horizontal and vertical
# error ranges.
barlinecols,
) = container

# Adjust each error bar
for barlinecol in barlinecols:
segments = barlinecol.get_segments()
new_segments = []
for segment in segments:
(x0, y0), (x1, y1) = segment
if x0 > x_thresh:
assert x0 == x1
x0, x1 = x_offset, x_offset
print(f"{x0=}, {x1=}")
print(f"{y0=}, {y1=}")
new_segment = [(x0, y0), (x1, y1)]
new_segments.append(new_segment)

barlinecol.set_segments(new_segments)
barlinecol.set(clip_on=False)
9 changes: 9 additions & 0 deletions conduitpylib/viz/_performance_semantics_scatterplot.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
from difflib import restore
import itertools as it
import sys
Expand Down Expand Up @@ -452,6 +453,14 @@ def remove_alpha(handle, orig) -> None:
which="both",
)

with contextlib.suppress(AttributeError):
jointgrid.ax_joint.ticklabel_format(
axis="x", style="sci", scilimits=(-2, 3)
)
with contextlib.suppress(AttributeError):
jointgrid.ax_joint.ticklabel_format(
axis="y", style="sci", scilimits=(-2, 3)
)
compact_xaxis_units(ax=jointgrid.ax_joint, base_unit="s")

if legend == "only":
Expand Down

0 comments on commit 871db8e

Please sign in to comment.