10

I want to create a plot for two different datasets similar to the one presented in this answer:

enter image description here

In the above image, the author managed to fix the overlapping problem of the error bars by adding some small random scatter in x to the new dataset.

In my problem, I must plot a similar graphic, but having some categorical data in the x axis:

enter image description here

Any ideas on how to slightly move one the error bars of the second dataset using categorical variables at the x axis? I want to avoid the overlapping between the bars for making the visualization easier.

3 Answers 3

12

You can translate each errorbar by adding the default data transform to a prior translation in data space. This is possible when knowing that categories are in general one data unit away from each other.

import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
from matplotlib.transforms import Affine2D

x = list("ABCDEF")
y1, y2 = np.random.randn(2, len(x))
yerr1, yerr2 = np.random.rand(2, len(x))*4+0.3

fig, ax = plt.subplots()

trans1 = Affine2D().translate(-0.1, 0.0) + ax.transData
trans2 = Affine2D().translate(+0.1, 0.0) + ax.transData
er1 = ax.errorbar(x, y1, yerr=yerr1, marker="o", linestyle="none", transform=trans1)
er2 = ax.errorbar(x, y2, yerr=yerr2, marker="o", linestyle="none", transform=trans2)

plt.show()

enter image description here

Alternatively, you could translate the errorbars after applying the data transform and hence move them in units of points.

import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
from matplotlib.transforms import ScaledTranslation

x = list("ABCDEF")
y1, y2 = np.random.randn(2, len(x))
yerr1, yerr2 = np.random.rand(2, len(x))*4+0.3

fig, ax = plt.subplots()

trans1 = ax.transData + ScaledTranslation(-5/72, 0, fig.dpi_scale_trans)
trans2 = ax.transData + ScaledTranslation(+5/72, 0, fig.dpi_scale_trans)
er1 = ax.errorbar(x, y1, yerr=yerr1, marker="o", linestyle="none", transform=trans1)
er2 = ax.errorbar(x, y2, yerr=yerr2, marker="o", linestyle="none", transform=trans2)

plt.show()

enter image description here

While results look similar in both cases, they are fundamentally different. You will observe this difference when interactively zooming the axes or changing the figure size.

Sign up to request clarification or add additional context in comments.

Comments

11

Consider the following approach to highlight plots - combination of errorbar and fill_between with non-zero transparency:

import random
import matplotlib.pyplot as plt

# create sample data
N = 8
data_1 = {
    'x': list(range(N)),
    'y': [10. + random.random() for dummy in range(N)],
    'yerr': [.25 + random.random() for dummy in range(N)]}
data_2 = {
    'x': list(range(N)),
    'y': [10.25 + .5 * random.random() for dummy in range(N)],
    'yerr': [.5 * random.random() for dummy in range(N)]}

# plot
plt.figure()
# only errorbar
plt.subplot(211)
for data in [data_1, data_2]:
    plt.errorbar(**data, fmt='o')
# errorbar + fill_between
plt.subplot(212)
for data in [data_1, data_2]:
    plt.errorbar(**data, alpha=.75, fmt=':', capsize=3, capthick=1)
    data = {
        'x': data['x'],
        'y1': [y - e for y, e in zip(data['y'], data['yerr'])],
        'y2': [y + e for y, e in zip(data['y'], data['yerr'])]}
    plt.fill_between(**data, alpha=.25)

Result:

enter image description here

3 Comments

Thank you for your suggestion. This idea, however, is not well-suited for my problem. All the variables are categorical and I cannot have lines connecting them.
@revy Categorical or numerical - does not matter. They are just labels. Change integers with any objects of your choice and you will get exactly the same figure with different labels next to X or Y axis. For example, replace list(range(N)) with N words - alegria, desgosto, etc. That easy.
Great answer! I love that way of defining your data as a dictionary. I'll do it always like this myself from now on!
0

Threre is example on lib site: https://matplotlib.org/stable/gallery/lines_bars_and_markers/errorbar_subsample.html enter image description here

You need parameter errorevery=(m, n), n - how often plot error lines, m - shift with range from 0 to n

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.