Skip to content

Commit 871db8e

Browse files
committed
Improve off-axis outlier annotation
1 parent 54ae9d9 commit 871db8e

File tree

4 files changed

+65
-47
lines changed

4 files changed

+65
-47
lines changed

conduitpylib/viz/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Visualization tools."""
22

3+
from ._align_marker import align_marker
34
from ._annotate_spearman import annotate_spearman
45
from ._apply_symmetric_axes import apply_symmetric_axes
56
from ._beleaguerment_regplot import beleaguerment_regplot
@@ -34,6 +35,7 @@
3435

3536
# adapted from https://stackoverflow.com/a/31079085
3637
__all__ = [
38+
"align_marker",
3739
"apply_symmetric_axes",
3840
"annotate_spearman",
3941
"beleaguerment_facetplot",

conduitpylib/viz/_calc_performance_semantics_axis_lims.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def calc_performance_semantics_axis_lims(
6868
ymin = min(0.4, ymin)
6969

7070
# make room for rugplot elements
71-
xmax *= 1.1
71+
xmax *= 1.3
7272
ymax *= 3
7373

7474
return xmin, xmax, ymin, ymax

conduitpylib/viz/_draw_edge_markers.py

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
import typing
33

44
from frozendict import frozendict
5+
import numpy as np
56

67
from matplotlib.axes import Axes as mpl_Axes
78
from matplotlib.collections import PathCollection as mpl_PathCollection
9+
from matplotlib.container import ErrorbarContainer as mpl_ErrorbarContainer
10+
11+
from ._align_marker import align_marker
812

913

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

3235
xwidth = -operator.__sub__(*xlim)
3336
ywidth = -operator.__sub__(*ylim)
@@ -36,39 +39,23 @@ def draw_edge_marker(
3639
if x < xlim[0]:
3740
amount = int((xlim[0] - x) / xwidth)
3841
backslash = "\\"
39-
edge_x, marker, annotation, ha = (
40-
xlim[0] - xoff,
41-
f"$◅{backslash * amount}$",
42-
f"{x:.0f}",
43-
"right",
44-
)
42+
edge_x = xlim[0] - xoff
43+
markers.append(f"$◅{backslash * amount}$")
4544
elif x > xlim[1]:
4645
amount = int((x - xlim[1]) / xwidth)
47-
edge_x, marker, annotation, ha = (
48-
xlim[1] + xoff,
49-
rf"$| \!\! \leftrightarrow \!\!|{{\times}}{amount}\rangle\!\rangle\!\rangle$",
50-
f"{x:.0f}",
51-
"left",
52-
)
46+
m = rf"$| \!\! \leftrightarrow \!\!|{{\times}}{amount}\rangle\!\rangle\!\rangle$"
47+
edge_x = xlim[1] + xoff
48+
# markers.append(align_marker(m, halign="left"))
49+
markers.append(align_marker(m, halign="right", pad=1.3))
5350

5451
if y < ylim[0]:
55-
edge_y, marker, annotation, va, xytext = (
56-
ylim[0] - yoff,
57-
"^",
58-
f"{y:.2f}" if not annotation else f"({annotation}, {y:.2f})",
59-
"bottom",
60-
(0, 5),
61-
)
52+
edge_y = ylim[0] - yoff
53+
markers.append("^")
6254
elif y > ylim[1]:
63-
edge_y, marker, annotation, va, xytext = (
64-
ylim[1] + yoff,
65-
"v",
66-
f"{y:.2f}" if not annotation else f"({annotation}, {y:.2f})",
67-
"top",
68-
(0, -5),
69-
)
55+
edge_y = ylim[1] + yoff
56+
markers.append("v")
7057

71-
if marker:
58+
for marker in markers:
7259
ax.plot(
7360
edge_x,
7461
edge_y,
@@ -84,17 +71,6 @@ def draw_edge_marker(
8471
**marker_kwargs,
8572
},
8673
)
87-
ax.annotate(
88-
annotation,
89-
(edge_x, edge_y),
90-
**{
91-
"ha": ha,
92-
"va": va,
93-
"textcoords": "offset points",
94-
"xytext": xytext,
95-
**annotation_kwargs,
96-
},
97-
)
9874

9975
return edge_x, edge_y
10076

@@ -118,19 +94,50 @@ def draw_edge_markers(
11894
for collection in ax.collections:
11995
if isinstance(collection, mpl_PathCollection):
12096
# Extract points from the line
121-
points = list(collection.get_offsets())
122-
12397
new_offsets = []
124-
for x, y in points:
98+
for x, y in collection.get_offsets():
12599
# Draw edge marker if the point is out of bounds
126100
if x < xlim[0] or x > xlim[1] or y < ylim[0] or y > ylim[1]:
127-
x_, y_ = draw_edge_marker(
101+
x, y = draw_edge_marker(
128102
ax,
129103
x,
130104
y,
131105
annotation_kwargs=annotation_kwargs,
132106
marker_kwargs=marker_kwargs,
133107
offset=offset,
134108
)
135-
else:
136-
new_offsets.append((x, y))
109+
new_offsets.append((x, y))
110+
111+
collection.set_offsets(new_offsets)
112+
collection.set(clip_on=False)
113+
114+
x_width = -operator.__sub__(*xlim)
115+
x_thresh = xlim[1]
116+
x_offset = x_thresh + x_width * offset
117+
for container in ax.containers:
118+
if isinstance(container, mpl_ErrorbarContainer):
119+
# Unpack the container
120+
(
121+
plotline, # Line2D instance of x, y plot markers and/or line
122+
caplines, # A tuple of Line2D instances of the error bar caps
123+
# A tuple of LineCollection with the horizontal and vertical
124+
# error ranges.
125+
barlinecols,
126+
) = container
127+
128+
# Adjust each error bar
129+
for barlinecol in barlinecols:
130+
segments = barlinecol.get_segments()
131+
new_segments = []
132+
for segment in segments:
133+
(x0, y0), (x1, y1) = segment
134+
if x0 > x_thresh:
135+
assert x0 == x1
136+
x0, x1 = x_offset, x_offset
137+
print(f"{x0=}, {x1=}")
138+
print(f"{y0=}, {y1=}")
139+
new_segment = [(x0, y0), (x1, y1)]
140+
new_segments.append(new_segment)
141+
142+
barlinecol.set_segments(new_segments)
143+
barlinecol.set(clip_on=False)

conduitpylib/viz/_performance_semantics_scatterplot.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
from difflib import restore
23
import itertools as it
34
import sys
@@ -452,6 +453,14 @@ def remove_alpha(handle, orig) -> None:
452453
which="both",
453454
)
454455

456+
with contextlib.suppress(AttributeError):
457+
jointgrid.ax_joint.ticklabel_format(
458+
axis="x", style="sci", scilimits=(-2, 3)
459+
)
460+
with contextlib.suppress(AttributeError):
461+
jointgrid.ax_joint.ticklabel_format(
462+
axis="y", style="sci", scilimits=(-2, 3)
463+
)
455464
compact_xaxis_units(ax=jointgrid.ax_joint, base_unit="s")
456465

457466
if legend == "only":

0 commit comments

Comments
 (0)