From: John H. <jdh...@ac...> - 2004-08-18 12:46:58
|
>>>>> "danny" == danny shevitz <dan...@ya...> writes: danny> howdy, I'm doing an errorbar plot. The cap colors are danny> always black, even when ecolor is explicitly set. The danny> documentation states that the second argument returned by danny> errorbar is a list of "error bar lines". I looked at the danny> return value and it appears to be a list of 3-tuples of danny> Line2D instances. I presume each 3-tuple is the errorbar, danny> and the two endcaps. If I explicitly set the color of the danny> whole erno collection, only the errorbar color is changed danny> not the cap color. I can't find any "errorbar Line2D" class danny> or documentation to that effect. As Gary noted, recently we changed the way error bars were drawn which apparently had some unintended consequences. The root of the problem is related to one that came up earlier on the list about differences in the way regular lines and marker lines are handled. A little background. The Line2D class makes a distinction between markers and lines: the color of markers, eg circles and squares, are determined by the 'markeredgecolor' and 'markerfacecolor' properties; the color of regular lines is determined by the 'color' property. There is another special thing about markers: their sizes are in points, and so do not depend on the axes scale or transformation. If you zoom in on a marker, it still looks the same. For errorbars, the last property is a nice, because we want the error bar caps to be a fixed capsize in points rather than depend on the scale. So we changed errorbar to use the new line *marker* symbols '_' and '|'. While they plot like a solid line, matplotlib considers them to be markers. So >> plot(x,y,'_') are horizontal lines centered at x, with height y, and length *in points* determined by the markersize. Ditto for '|, which are vertical lines. Perfect for tick marks or errorbar caps. However, as markers, their color is determined by the markerfacecolor and markeredgecolor, which is a bit whacked, I realize. Perhaps they should be factored into a separate category, or perhaps I should just change the Line2D class to forward calls from set_color to set_facecolor and set_edgecolor for these special markers. I rewrote (and simplified) the errorbar code to fix this problem. It should be faster too, because I plot the x and y caps in single calls to plot rather than creating a bunch of individual lines, which was how the '_' and '|' markers were meant to be used. Code below. danny> So what am I missing and how do I change errobar cap colors danny> without doing an explicit loop over the 3-tuples and danny> setting their colors explicitly? For future reference, 'set' will loop for you >>> lines, errbars = errorbar(t, s, [e,g], f, fmt='o') >>> set(errbars, markerfacecolor='g', markeredgecolor='g') but with the patched code, that won't be necessary. Replace matplotlib.axes.Axes.errorbar with the code below. Errorbar users, I would be much obliged if you test this. I tried all of Gary's errorbar_demo examples and they appeared to work fine.... def errorbar(self, x, y, yerr=None, xerr=None, fmt='b-', ecolor='k', capsize=3): """ Plot x versus y with error deltas in yerr and xerr. Vertical errorbars are plotted if yerr is not None Horizontal errorbars are plotted if xerr is not None xerr and yerr may be any of: a rank-0, Nx1 Numpy array - symmetric errorbars +/- value an N-element list or tuple - symmetric errorbars +/- value a rank-1, Nx2 Numpy array - asymmetric errorbars -column1/+column2 Alternatively, x, y, xerr, and yerr can all be scalars, which plots a single error bar at x, y. fmt is the plot format symbol for y. if fmt is None, just plot the errorbars with no line symbols. This can be useful for creating a bar plot with errorbars Return value is a length 2 tuple. The first element is a list of y symbol lines. The second element is a list of error bar lines. capsize is the size of the error bar caps in points """ if not self._hold: self.cla() # make sure all the args are iterable arrays if not iterable(x): x = asarray([x]) else: x = asarray(x) if not iterable(y): y = asarray([y]) else: y = asarray(y) if xerr is not None: if not iterable(xerr): xerr = asarray([xerr]) else: xerr = asarray(xerr) if yerr is not None: if not iterable(yerr): yerr = asarray([yerr]) else: yerr = asarray(yerr) if fmt is not None: l0 = self.plot(x,y,fmt) else: l0 = None caplines = [] barlines = [] capargs = {'c':ecolor, 'mfc':ecolor, 'mec':ecolor, 'ms':2*capsize} if xerr is not None: if len(xerr.shape) == 1: left = x-xerr right = x+xerr else: left = x-xerr[0] right = x+xerr[1] barlines.extend( self.hlines(y, x, left) ) barlines.extend( self.hlines(y, x, right) ) caplines.extend(self.plot(left, y, '|', **capargs)) caplines.extend(self.plot(right, y, '|', **capargs)) if yerr is not None: if len(yerr.shape) == 1: lower = y-yerr upper = y+yerr else: lower = y-yerr[0] upper = y+yerr[1] barlines.extend( self.vlines(x, y, upper ) ) barlines.extend( self.vlines(x, y, lower ) ) caplines.extend(self.plot(x, lower, '_', **capargs)) caplines.extend(self.plot(x, upper, '_', **capargs)) for l in barlines: l.set_color(ecolor) self.autoscale_view() return (l0, caplines+barlines) |