用自己定义的水平直方图类SpecialHorizontalBarChart在一个画板上放两个直方图,这两个直方图共用一个图例。类SpecialHorizontalBarChart最理想是给两组数据(每组两个数据柱),否则需要调整刻度标签位置。
代码及注释:
"Dual Bar charts on one canvas."
from reportlab.lib.colors import PCMYKColor, black, red
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin
from reportlab.lib.formatters import DecimalFormatter
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.charts.barcharts import HorizontalBarChart
class SpecialHorizontalBarChart(HorizontalBarChart): # 注意它的父类
def __init__(drawing, data, categoryName): # 注意第一个参数是drawing画板
_fontName = 'Helvetica'
_fontSize = 5
HorizontalBarChart.__init__(drawing) # 父类的初始化函数直接拿来用
drawing.x = 45
drawing.y = 24
drawing.width = 52
drawing.height = 154
drawing.fillColor = None
drawing.reversePlotOrder = 1
# 垂直直方图,无论是数据还是类别轴标签都是从y的坐标原点开始,从下至上往正向铺排
# 代表数据的数据柱从下到上专程从上向下
# 设置数据柱
drawing.groupSpacing = 4
drawing.barWidth = 5
drawing.barSpacing = 0
drawing.bars.strokeWidth = 0
drawing.bars.strokeColor = None
drawing.barLabels.fontName = _fontName
drawing.barLabels.fontSize = _fontSize
drawing.barLabelFormat = DecimalFormatter(2, suffix='%')
# 数据柱标签两位小数,%后缀
drawing.barLabels.boxAnchor ='w'
drawing.barLabels.boxFillColor = None
drawing.barLabels.boxStrokeColor= None
drawing.barLabels.dx = 5
drawing.barLabels.dy = 0
# 柱标签相对于数据柱的位置
drawing.barLabels.boxTarget = 'hi'
# 'hi',数据柱右;'lo',数据柱左;'anti',坐标轴左右,正左负右
# 'normal',数据柱旁,正左负右
# value axis(啥都不可见)
drawing.valueAxis.visibleAxis = False
drawing.valueAxis.visibleGrid = False
drawing.valueAxis.visibleTicks = False
drawing.valueAxis.visibleLabels = False
# category axis
drawing.categoryAxis.labels.fontName = _fontName
drawing.categoryAxis.labels.fontSize = _fontSize+0.5
drawing.categoryAxis.visibleGrid = False
drawing.categoryAxis.visibleTicks = False
drawing.categoryAxis.strokeWidth = 0.25
drawing.categoryAxis.labelAxisMode ='low'
# 'low',坐标轴标签在坐标轴左;'high',刻度标签在整个图表的顶部
# 'axis',刻度标签在坐标轴旁;
# 'axispmv',刻度标签在坐标轴旁,在上在下就看数据柱的主要位置,反着来
drawing.categoryAxis.labels.textAnchor ='end'
# 'end'右靠齐,'start'左靠齐,'middle'居中
drawing.categoryAxis.labels.fillColor = black
drawing.categoryAxis.labels.angle = 0
drawing.categoryAxis.labels.boxAnchor = 'e'
drawing.categoryAxis.labels.leading = 5 # 行间距
drawing.categoryAxis.labels.dx = -5 # x轴方向微调坐标
drawing.categoryAxis.labels.dy = -5 # y轴方向微调坐标
drawing.categoryAxis.reverseDirection = 1
# 垂直直方图,无论是数据还是类别轴标签都是从y的坐标原点开始,从下至上往正向铺排
# reverseDirection是的类别标签从上向下铺排,最后的标签在y的坐标原点
drawing.categoryAxis.joinAxisMode = 'left'
# 指定类别轴的位置:'bottom',置于图表底部;'top',置于图表顶部
# 取值'points'和'value'时,跟joinAxisPos值配合使用
# 取值'points',属性joinAxisPos取值100,距离画板底部100个points的位置
# 取值'value',属性joinAxisPos= 30,y轴刻度30的位置
drawing.data = data
drawing.categoryAxis.categoryNames = categoryName
class BarChart_Dual(_DrawingEditorMixin,Drawing):
def __init__(self,width=264,height=190,*args,**kw):
Drawing.__init__(self,width,height,*args,**kw)
colorM = PCMYKColor(100,60,0,50,alpha=85)
colorR = PCMYKColor(66,13,0,22,alpha=85)
colorN = PCMYKColor(100,0,90,50,alpha=85)
self._fontName = 'Helvetica'
self._fontSize = 5
# 第一个直方图的数据
data = [[10.9, 10.84, 15.99, 18.75, 14.75, 9.1300000000000008, 9.2100000000000009, 5.21, 2.3500000000000001, 2.8799999999999999, 0.0],
[8.9900000000000002, 11.210000000000001, 11.859999999999999, 17.0, 12.59, 11.4, 12.279999999999999, 4.7199999999999998, 4.4699999999999998, 5.4900000000000002, 0.0]]
# 第一个直方图的类别标签
nameList = 'Consumer\nStaples', 'Consumer\nDiscretionary', 'Energy', 'Financials', 'Health Care', 'Industrials', 'Information\nTechnology', 'Materials', 'Telecomm.\nServices', 'Utilities', 'Other'
# 用自己的定义的类SpecialHorizontalBarChart生成水平直方图
self._add(self,SpecialHorizontalBarChart(data, nameList),name='chart1',validate=None,desc=None)
# 颜色序列,定义直方图数据柱的颜色
colorList1 = colorM, colorR
for i, _ in enumerate(self.chart1.data):
self.chart1.bars[i].fillColor = colorList1[i]
# 第二个直方图
data = [
[0.85999999999999999, 5.2599999999999998, 2.1899999999999999, 0.94999999999999996, 8.4600000000000009, 11.220000000000001, 3.25, 3.2799999999999998, 8.7400000000000002, 14.4, 41.380000000000003],
[0.050000000000000003, 3.0299999999999998, 0.26000000000000001, 0.01, 1.3799999999999999, 3.2599999999999998, 0.01, 0.0, 0.0, 92.0, 0.0],
]
nameList = 'Asset Backed\nSecurities', 'US\nTreasury/Agency', 'Commercial\nMortgage Backed', 'High Yield\nCorporate', 'Investment Grade\nCorporate', 'Mortgage Backed', 'Govt Emerging\nMarkets', 'Non US Currency', 'TIPS', 'Cash', 'Other'
self._add(self,SpecialHorizontalBarChart(data, nameList),name='chart2',validate=None,desc=None)
# 调整了第二个直方图的位置
self.chart2.x = 182
colorList2 = colorM, colorN
for i, _ in enumerate(self.chart2.data):
self.chart2.bars[i].fillColor = colorList2[i]
# 两个直方图共用的图例
self._add(self,Legend(),name='legend',validate=None,desc=None)
self.legend.fontName = self._fontName
self.legend.fontSize = self._fontSize+0.5
self.legend.boxAnchor = 'nw'
self.legend.x = 3
self.legend.y = 20
self.legend.deltax = 0
self.legend.deltay = 5
self.legend.autoXPadding = 0
self.legend.columnMaximum = 3
self.legend.alignment = 'right'
self.legend.strokeWidth = 0
self.legend.strokeColor = None
self.legend.dx = 4
self.legend.dy = 4
self.legend.variColumn = True
self.legend.dxTextSpace = 4
self.legend.colorNamePairs = [(colorR, 'Microsoft'),
(colorM, 'Russel 2000 Index'),
(colorN, 'NASDAQ Composite Index')]
# 第一个直方图的标题
self._add(self,Label(),name='label1',validate=None,desc=None)
self.label1.boxAnchor = 'nw'
self.label1.x = 0
self.label1.y = self.height
self.label1.fontName = self._fontName
self.label1.fontSize = 8
self.label1._text = 'Equity Composition'
# 第二个直方图的标题
self._add(self,Label(),name='label2',validate=None,desc=None)
self.label2.boxAnchor = 'nw'
self.label2.x = self.chart2.x-self.chart1.x
self.label2.y = self.height
self.label2.fontName = self._fontName
self.label2.fontSize = 8
self.label2.textAnchor ='middle'
self.label2._text = 'Fixed Income Composition'
if __name__=="__main__":
BarChart_Dual().save(formats=['pdf'],outDir='.',fnRoot=None)