Skip to content

Commit 4b07e1c

Browse files
committed
Fix point order in each crossbar (#89)
Pointed out here: #87 (comment) And also in #89.
1 parent 63ba9ff commit 4b07e1c

File tree

1 file changed

+34
-38
lines changed

1 file changed

+34
-38
lines changed

scikit_posthocs/_plotting.py

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -519,49 +519,45 @@ def critical_difference_diagram(
519519
)
520520

521521
# Sets of points under the same crossbar
522-
crossbar_sets = [bar for bar in _find_maximal_cliques(adj_matrix) if len(bar) > 1]
523-
524-
if crossbar_sets: # If there are any crossbars to plot
525-
crossbar_min_max = [ # Will be used to check if two crossbars intersect
526-
ranks.reindex(bar).agg(["min", "max"])
527-
for bar in crossbar_sets
522+
crossbar_ranks = (
523+
ranks.reindex(bar).sort_values().values
524+
for bar in _find_maximal_cliques(adj_matrix)
525+
if len(bar) > 1
526+
)
527+
# Try to fit wider crossbars first
528+
crossbar_ranks = list(sorted(crossbar_ranks, key=lambda x: x[0] - x[-1]))
529+
530+
# If any crossbar is found, plot them
531+
if crossbar_ranks:
532+
# Create stacking of crossbars: for each level, try to fit the crossbar,
533+
# so that it does not intersect with any other in the level. If it does not
534+
# fit in any level, create a new level for it.
535+
crossbar_levels: list[list[np.ndarray]] = []
536+
for bar_i in crossbar_ranks:
537+
for bars_in_level in crossbar_levels:
538+
if all(
539+
(bar_i[-1] < bar_j[0]) or (bar_i[0] > bar_j[-1])
540+
for bar_j in bars_in_level
541+
):
542+
bars_in_level.append(bar_i)
543+
break
544+
else:
545+
crossbar_levels.append([bar_i]) # Create a new level
546+
547+
# Plot crossbars
548+
# We could plot a single line segment between min and max. However,
549+
# adding a separate segment between each pair enables showing a
550+
# marker over each elbow, e.g. crossbar_props={'marker': 'o'}.
551+
crossbars = [
552+
[ax.plot(bar, [-i] * len(bar), **crossbar_props) for bar in level]
553+
for i, level in enumerate(crossbar_levels)
528554
]
529555

530-
# Create an adjacency matrix of the crossbars, where 1 means that the two
531-
# crossbars do not intersect, meaning that they can be plotted on the same
532-
# level.
533-
n_bars = len(crossbar_sets)
534-
on_same_level = DataFrame(True, index=range(n_bars), columns=range(n_bars))
535-
536-
for (i, bar_i), (j, bar_j) in combinations(enumerate(crossbar_min_max), 2):
537-
on_same_level.loc[i, j] = on_same_level.loc[j, i] = (
538-
(bar_i["max"] < bar_j["min"]) or (bar_i["min"] > bar_j["max"])
539-
)
540-
541-
# The levels are the maximal cliques of the crossbar adjacency matrix.
542-
crossbar_levels = _find_maximal_cliques(on_same_level)
543-
544-
# Plot the crossbars in each level
545-
for level, bars_in_level in enumerate(crossbar_levels):
546-
plotted_bars_in_level = []
547-
for bar_index in bars_in_level:
548-
bar = crossbar_sets[bar_index]
549-
plotted_bar, *_ = ax.plot(
550-
# We could plot a single line segment between min and max. However,
551-
# adding a separate segment between each pair enables showing a
552-
# marker over each elbow, e.g. crossbar_props={'marker': 'o'}.
553-
[ranks[i] for i in bar],
554-
[-level - 1] * len(bar),
555-
**crossbar_props,
556-
)
557-
plotted_bars_in_level.append(plotted_bar)
558-
crossbars.append(plotted_bars_in_level)
559-
560-
lowest_crossbar_ypos = -len(crossbars)
556+
elbow_start_y = -len(crossbars)
561557

562558
def plot_items(points, xpos, label_fmt, color_palette, label_props):
563559
"""Plot each marker + elbow + label."""
564-
ypos = lowest_crossbar_ypos - 1
560+
ypos = elbow_start_y
565561
for idx, (label, rank) in enumerate(points.items()):
566562
if not color_palette or len(color_palette) == 0:
567563
elbow, *_ = ax.plot(

0 commit comments

Comments
 (0)