Drawing a median line in a histogram in Makie

I can’t figure out a non-manual way to do this:

mwe_fig = let
	fig = Figure()
	data = randn(1000) .+ 1
	ax = Axis(fig[1,1]; xlabel="Value", ylabel="Count")
	hist!(ax, data)
	m = median(data)
	m_color = Makie.wong_colors()[6]
	linesegments!(ax, [Point2f0(m, 0), Point2f0(m, 250)]; color=m_color, linewidth=2)
	#vlines!(ax, [m]; color=m_color, ymin=0.046, linewidth=2)
	text!(string("median: ", @sprintf("%.0f", m)), position = (m*1.1, 225), align = (:left, :center), color=m_color)
	fig
end

I’d prefer if I didn’t have to guess the height and tweak it manually. vlines! seems appropriate, except the line starts below 0, which looks a bit odd to me:

and I still need to guess the height of the text. I can adjust ymin by hand (ymin=0.046 seems to work pretty well) though.

I tried using ax.limits to get the height, but it’s (nothing, nothing).

The actual limits are in ax.finallimits. I usually prefer to do ylims!(low = 0) anyway with barplots if they show only positive values, in which case vlines would be ok. If you plot a line manually and don’t want it to change the limits itself, you should be able to set yautolimits = false as an attribute.

1 Like

ylims!(low = 0) plus ax.finallimits for the height works well, thanks!

mwe_fig = let
	fig = Figure()
	data = randn(1000) .+ 1
	ax = Axis(fig[1,1]; xlabel="Value", ylabel="Count")
	hist!(ax, data)
	m = median(data)
	m_color = Makie.wong_colors()[6]
	vlines!(ax, [m]; color=m_color, linewidth=2)
	h = ax.finallimits[].widths[2]
	text!(string("median: ", @sprintf("%.0f", m)), position = (m*1.1, h), align = (:left, :center), color=m_color)
	ylims!(low=0)
	fig
end

This kind of thing also comes up when labelling points on a line. For example, if I have something like

let
	xs = range(1, 100; length=100)
	ys = 1 ./ xs
	fig = Figure()
	ax = Axis(fig[1,1])
	lines!(ax, xs, ys; linewidth=3)
	i = 5 # a point we wish to label
	scatter!(ax, [Point2f0(xs[i], ys[i])])
	text!(ax, @sprintf("%0.2f", ys[i]); position = (xs[i], ys[i]), align=(:left, :bottom))
	fig
end

it comes out as

And to me it looks like the text is a little too close to the line. I can shift it over with say

text!(ax, @sprintf("%0.2f", ys[i]); position = (xs[i] + 1, ys[i]), align=(:left, :bottom))

which comes out fine:

But if I’m making several plots with different axes (say in some the x-limits are (0,1) and in others (0, 100)) then I think you need the finallimits thing to get the right scaling to be like 1% of the x-range or such.

I think both of these are cases in which there’s a need to mix data scale and axis scale. I wonder if Makie could have like a AxisScale object that you could do position = (xs[i] + .01*AxisScale(), ys[i]) or something? Or position_in_axis_scale = (convert_to_axis_units(xs[i]) + .01, convext_to_axis_units(ys[i])) or something like that (with better names)?

For text there is offset which works in screen coordinates. In principle I agree that axis transforms are useful but your specific issue is easier to handle with the offset parameter:

Have a look at the last example here http://makie.juliaplots.org/stable/plotting_functions/text.html

2 Likes

Ah, thanks! I had missed that section (only saw the docstring). That looks great for this.