Errorbar points with randomized z order

I have 3 different data vector with associated errors, and I’d like to plot each of them with a different color on the same ax, but randomizing the plot order of the points, so that the last data vector does not hide the two underlying ones.

So far, I have:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# ## generating pseudodata
x1 = np.logspace(-1, 2, 100)

X, Y = [], []
for i in range(3):
    X.append(x1*(1 + 0.2*np.random.random((100,))))
    Y.append(x1**2*(1 + 0.2*np.random.random((100,))))

fig, axarr = plt.subplots(1, 2, constrained_layout=True)

# only errorbars
ax = axarr[0]
for i, (x, y) in enumerate(zip(X, Y)):
    ax.errorbar(x, y, xerr=0.3*x, yerr=0.3*y, fmt='.', label = str(i))
ax.set_xscale('log')
ax.set_yscale('log')
ax.legend()

# only scatter plot but randomized
ax = axarr[1]

X = np.concatenate(X)
Y = np.concatenate(Y)
plot_idx = np.random.permutation(X.size)
color_cycle = plt.rcParams['axes.prop_cycle'].by_key()['color']
colors = np.concatenate([np.repeat(color_cycle[i], 100) for i in range(3)])
labels = np.concatenate([np.repeat(str(i), 100) for i in range(3)])

sc = ax.scatter(X[plot_idx], Y[plot_idx], c=colors[plot_idx], s=10)

leg_elements = [plt.Line2D([0],[0],color=c, ls="",marker=".", label=str(i))
                for i, c in enumerate(color_cycle[:3])]
ax.legend(handles=leg_elements)
ax.set_xscale('log')
ax.set_yscale('log')

random_error

How can I mix up the two, as I can not pass a vector for the color argument of ax.errorbar ?
I was thinking about getting the points afterwards, and changing their zorder but I could not manage to do so

Unfortunately the way the Artists involved in errorbar are drawn as (mostly) atomic units per ax.errorbar call (and one color per call). On the other hand scatter is drawn as one unit for the whole data set, but can do color per point and you are seeing the intra-collection ordering.

I think the options are:

  • use scatter and manually create the line collections for the error bars. However, I think that this will also produce some “Escher Effects” where the the markers will all be at one z and the lines will all be at one z so you could have odd layer effects.
  • split the errorbar calls into N sets per color and cycle between them
  • use the alpha channel to show the overlap

It might also be worth considering finding some other way to visualize this data that de-conflicts it (maybe vertical offsets? subplot?), but that is going to be very context/application dependent.

Thank you for your reply.

Is there anyway to change the zorder of Artists already drawn, for example after calling ax.errorbar ?

Otherwise I will split the errorbar calls into N sets per color and cycle between them as suggested.

1 Like

Every artist has a set_zorder method (matplotlib.artist.Artist.set_zorder — Matplotlib 3.6.2 documentation), however errorbar returns a Container of artist so you would have to do

ebar = ax.errorbar(....)
[art.set_zorder(-1) for art in ebar]

and then re-render.