def label_all( ax: pl.Axes, fontsize: int = None, xoffset: float = -0.01, yoffset: float = 0.03, x_to_float: Callable[[Any], float] = None, **kwargs: Any, ) -> None: """Label all lines in the axes - xoffset and yoffset are fractional (axes coordinate) offset to apply to the text added. """ data_to_axes = ax.transData + ax.transAxes.inverted() x_to_float = x_to_float or _x_to_float for line in ax.get_lines(): xdata, ydata = line.get_data(orig=True) # x, y in data coordinates nonnan_mask = ~np.isnan(ydata) x = x_to_float(xdata[nonnan_mask][-1]) y = ydata[nonnan_mask][-1] # x, y in axes coordinates x, y = data_to_axes.transform((x_to_float(x), y)) text = ax.text( x + xoffset, y + yoffset, line.get_label(), transform=ax.transAxes, fontsize=fontsize, **kwargs, )
def plot_median_labels( ax: pyplot.Axes, *, has_fliers: bool = False, # text_size: int = 10, # text_weight: str = "normal", stroke_width: int = 0, precision: int = 3, color: str = "black", edgecolor: str = "black", # also the stroke color ha: str = "center", va: str = "center", # bottom bbox: Tuple = semi_opaque_round_tight_bbox, ) -> None: """ """ lines = ax.get_lines() # depending on fliers, toggle between 5 and 6 lines per box lines_per_box = 5 + int(has_fliers) # iterate directly over all median lines, with an interval of lines_per_box # this enables labeling of grouped data without relying on tick positions for median_line in lines[4 : len(lines) : lines_per_box]: # get center of median line mean_x = sum(median_line._x) / len(median_line._x) mean_y = sum(median_line._y) / len(median_line._y) text = ax.text( mean_x, mean_y, f"{round(mean_y, precision)}", ha=ha, va=va, # fontweight=text_weight, # size=text_size, color=color, # edgecolor=edgecolor bbox=bbox, ) # print text to center coordinates if stroke_width: # create small black border around white text # for better readability on multi-colored boxes text.set_path_effects( [ patheffects.Stroke(linewidth=stroke_width, foreground=edgecolor), patheffects.Normal(), ] )