[reportlab-users] Stacked bar chart broken when negative values are	involved
    Brian 
    jerald_13 at yahoo.com
       
    Fri Sep  5 14:38:49 EDT 2008
    
    
  
There seems to be a problem with negative values being fed into the data.  Results seem very erratic.
Here is what I need to do:
Plot 4 stacked bars, that will usually span from negative to positive,
that include a "data span" range (min value to max value of a data
set), and a bar on each side of the mean value that represents the
standard deviation.
So to recap:
Bar0: (invisible): 0 to Min
Bar1: Min to (Mean - STD)
Bar2: (Mean-STD) to Mean
Bar3: Mean to (Mean + STD)
Bar4: (Mean+STD) to Max
The problem is, any time a bar crosses the axis, strange things start
happening. It will do up to the axes in the expected color, and add
whatever's left to the next bar (in the color of the next bar).
I read somewhere that the issues can be avoided by plotting from top to
bottom, but this doesn't work either. Sometimes bars disappear,
sometimes they're plotted in the wrong color, sometimes they're cut off
and added to the next bar, and this is only the case when dealing with
negatives. Here is some sample code to reproduce the problem.
Here is the code (before i tried height tracking to work around boundaries, which broke because it started plotting things in the wrong color):
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.charts.legends import *
from reportlab.lib import colors
import math
class CompressedBarChart:
    def __init__(self, title, stdcolor="green", spancolor="yellow"):
        self.mindata = []
        self.mincolor = None
        self.maxdata = []
        self.maxcolor = None
        self.meandata = []
        self.meancolor = None
        self.stddata = []
        self.stdcolor = None
        self.minvalue = 0
        self.maxvalue = -90000000
        self.ticks = []
        self.title=title
        self.curbars = []
        
        if hasattr(colors, stdcolor):
            self.stdcolor = getattr(colors, stdcolor)
        else:
            self.stdcolor = colors.green
        
        if hasattr(colors, spancolor):
            self.spancolor = getattr(colors, spancolor)
        else:
            self.spancolor = colors.yellow
            
    def addBar(self, name, minval, maxval, meanval=None, stdval=None):
        self.ticks.append(name)
        if not minval == None:
            self.mindata.append(minval)
            if minval < self.minvalue:
                self.minvalue = math.ceil(minval/10 - 2)*10
        if not maxval == None:
            self.maxdata.append(maxval)
            if maxval > self.maxvalue:
                self.maxvalue = math.ceil(maxval/10 + 2)*10
    
        if not meanval == None:
            self.meandata.append(meanval)
    
            if not stdval == None:
                self.stddata.append(stdval)
        self.curbars.append(0)
                
    
    def createPlots(self):
        if len(self.mindata) == 0 or len(self.maxdata) == 0:
            return
        
        if not len(self.mindata) == len(self.maxdata):
            return
        
        if not len(self.mindata) == len(self.meandata):
            hasmean = False
            hasstd = False
        else:
            hasmean = True
            if not len(self.mindata) == len(self.stddata):
                hasstd = False
            else:
                hasstd = True
        data = []
        
        bc = VerticalBarChart()
        bc.categoryAxis.style='stacked'
        
        if hasstd:
            lspan = [x- y-z for x,y,z in zip(self.meandata, self.mindata,  self.stddata)]
            uspan = [x- (y+z) for x,y,z in zip(self.maxdata, self.meandata,  self.stddata)]
    
            
            data.append(tuple(self.mindata))
            data.append(tuple(lspan))
            data.append(tuple(self.stddata))
            data.append(tuple(self.stddata))
            data.append(tuple(uspan))
            
            bc.bars[0].fillColor = None
            bc.bars[0].strokeColor = None
            bc.bars[1].fillColor = self.spancolor
            bc.bars[2].fillColor = self.stdcolor
            bc.bars[3].fillColor = self.stdcolor
            bc.bars[4].fillColor = self.spancolor
        print str(data)
        bc.data = data
        bc.x = 50
        bc.y = 50
        bc.height = 125
        bc.width = 300
        bc.strokeColor = colors.black
            
        #set the range and tick marks on the y-axis
        bc.valueAxis.valueMin = self.minvalue
        bc.valueAxis.valueMax = self.maxvalue
        
        #make the ticks on some multiple of 10
        bc.valueAxis.valueStep = int((self.maxvalue-self.minvalue)/10)
        bc.valueAxis.valueStep += (10-bc.valueAxis.valueStep % 10)
        bc.categoryAxis.labels.boxAnchor = 'ne'
        bc.categoryAxis.labels.dx = 8
        bc.categoryAxis.labels.dy = -2
        bc.categoryAxis.labels.angle = 30
        
        bc.categoryAxis.categoryNames = self.ticks
        
        bc.valueAxis.visibleGrid=0 #set 1 for horizontal tick lines
        bc.groupSpacing=5 #change to change spacing between bars
        
        drawing = Drawing(550,200)   
        drawing.add(bc)
        legend = Legend()
        legend.x = 300+70
        legend.y = 125-5
        legend.dx = 20
        legend.dy = 5
        legend.deltax = 0
        legend.boxAnchor = 'nw'
        legend.colorNamePairs = []
        legend.colorNamePairs.append((self.spancolor, 'Span Min to Max'))
        if hasstd:
            legend.colorNamePairs.append((self.stdcolor, 'Mean +/- one STD'))
        drawing.add(legend)
        drawing.add(String(175,190,self.title), name='title')
        
        from reportlab.graphics import renderPDF
        renderPDF.drawToFile(drawing, 'funchart.pdf', 'Simple Bar Chart')
graphobj = CompressedBarChart(title="Sample Chart", stdcolor="purple", spancolor="yellow")
graphobj.addBar(name="BAD1", minval=-15, maxval=0, meanval=-7, stdval=3)# everything is shifted up because it made thee lower bounds of the span invisible
graphobj.addBar(name="BAD2", minval=-15, maxval=15, meanval=2, stdval=10)# The lower bound STD is cut off at the axis and the remainder is left as part of the span
graphobj.addBar(name="GOOD!", minval=15, maxval=50, meanval=35, stdval=5)
graphobj.createPlots()
      
    
    
More information about the reportlab-users
mailing list