Gabriel
Gabriel

Reputation: 42439

Color errorbars in 2D according to colormap

This is a rather simple issue at first glance, but I haven't been able to find a solution. There's a similar (old) question here but the closest answer given does not seem to apply to error bars in both dimensions.

I need to generate an "errorbar" plot where both error bars (in the x and y dimensions) are colored according to the values in the cc array.

The code:

import numpy as np
import matplotlib.pyplot as plt

# Some random data
aa = np.array([3.581, -0.721, 0.137, 0.645, 0.12, 0., -3.236, 0.248, -5.687, 0.816])
e_aa = np.array([0.111, 0.991, 0.446, 0.07, 0.814, 0., 0.088, 0.805, 0.178, 0.552])
bb = np.array([6.671, 1.219, 0.119, -1.972, 1.834, 0., 4.93, 1.833, -11.542, -0.439])
e_bb = np.array([0.143, 1.316, 0.609, 0.094, 1.127, 0., 0.116, 1.227, 0.216, 0.726])

# The color array
cc = np.array([0.50344083, 0.49961867, 0.5055576, 0.48970365, 0.5078516, 0.49643923, 0.50089907, 0.50129157, 0.49627974, 0.5052376])

plt.errorbar(aa, bb, yerr=e_bb, xerr=e_aa, fmt='none', ecolor=cc)
plt.show()

fails with a:

ValueError: Invalid RGBA argument: 0.50344083

whether I use the ecolor or color argument.

Is there any way around this?

Upvotes: 2

Views: 2436

Answers (2)

Gabriel
Gabriel

Reputation: 42439

I've found a solution. The trick is to map the floats to a valid colormap using matplotlib.cm.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import Normalize

# Some random data
aa = np.array([3.581, -0.721, 0.137, 0.645, 0.12, 0., -3.236, 0.248, -5.687, 0.816])
e_aa = np.array([0.111, 0.991, 0.446, 0.07, 0.814, 0., 0.088, 0.805, 0.178, 0.552])
bb = np.array([6.671, 1.219, 0.119, -1.972, 1.834, 0., 4.93, 1.833, -11.542, -0.439])
e_bb = np.array([0.143, 1.316, 0.609, 0.094, 1.127, 0., 0.116, 1.227, 0.216, 0.726])

# The color array
cc = np.array([0.50344083, 0.49961867, 0.5055576, 0.48970365, 0.5078516, 0.49643923, 0.50089907, 0.50129157, 0.49627974, 0.5052376])

# Define function to map (normalized) values in `cc` to a colormap
cmap = cm.viridis
norm = Normalize(vmin=cc.min(), vmax=cc.max())

plt.errorbar(aa, bb, yerr=e_bb, xerr=e_aa, fmt='none', ecolor=cmap(norm(cc)))
plt.show()

enter image description here

Upvotes: 3

bnaecker
bnaecker

Reputation: 6440

plt.errorbar() generates a three separate objects: the line between points, the lines for each errorbar cap (if any), and a matplotlib.collections.LineCollection containing the errorbar lines themselves. The ecolor argument is ultimately passed to matplotlib.collections.LineCollection.set_color(), which accepts a:

Matplotlib color argument (all patches have the same color) or a sequence of rgba tuples; if it is a sequence the patches will cycle through the sequence.

Your array cc is neither of these. The method receives a sequence, so it tries to set the color of each line in the collection to each element of the sequence. But those elements are just single floats, which are not valid matplotlib colors.

To fix this, generate a list of tuples, with each tuple specifying the RGB[A] color of the line segments for each errrorbar. For example, using the other data you specified in your question, the colors can be set as:

cc = np.random.rand(aa.size, 3) # Random colors, RGB tuples uniform on (0, 1)
collections = plt.errorbar(aa, bb, yerr=e_bb, xerr=e_aa, fmt='none', ecolor=cc)

This generates a plot like so:

enter image description here

Upvotes: 2

Related Questions