散点图(Scatter Plot)也是折线图的一种,毕竟用线把点连上就是折线图了。这个散点图例子比较特殊,每组数据只有一个点,然后选择一个点做交叉瞄准线:
代码和注释如下,个别行的注释参见前面的帖子:
"Scatter plot with legend"
from reportlab.lib.colors import black, rgb2cmyk, toColor, red, green, blue
from reportlab.graphics.charts.lineplots import ScatterPlot
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin
from reportlab.lib.validators import Auto
from reportlab.graphics.widgets.markers import makeMarker
from reportlab.graphics.charts.textlabels import Label
class ScatterChart_CrossHair(_DrawingEditorMixin,Drawing):
'''
带交叉瞄准线的散点图
'''
def __init__(self,width=144,height=85,*args,**kw):
Drawing.__init__(self,width,height,*args,**kw)
# 公共变量
strokeDashArray = 1, 1
fontName = 'Helvetica'
fontSize = 6
# 散点图
self._add(self,ScatterPlot(),name='chart',validate=None,desc=None)
self.chart.leftPadding = 0 # 左留白
self.chart.rightPadding = 0 # 右留白
self.chart.topPadding = 0 # 上留白
self.chart.bottomPadding = 0 # 下留白
self.chart.lineLabelFormat = None
self.chart.outerBorderColor = black # 图表周线的颜色
# self.chart.outerBorderColor = red
# 定义散点的点符号
self.chart.lines.symbol = makeMarker('FilledSquare')
self.chart.lines.symbol.size = 6
# self.chart.lines.strokeWidth = 10
# 坐标轴自带的标签为空
self.chart.xLabel = ''
self.chart.yLabel = ''
# x轴
self.chart.xValueAxis.labels.fontName = fontName
self.chart.xValueAxis.labels.fontSize = fontSize
# self.chart.xValueAxis.valueStep = 0.5 # x轴上刻度步长
self.chart.xValueAxis.visibleGrid = 1
# 显示x轴上的格子线
self.chart.xValueAxis.gridStrokeDashArray = strokeDashArray
# 格子线的样式
self.chart.xValueAxis.minimumTickSpacing = 10
self.chart.xValueAxis.maximumTicks = 8
self.chart.xValueAxis.forceZero = 1
self.chart.xValueAxis.avoidBoundFrac = 1
self.chart.xValueAxis.visibleTicks = False
# y轴
self.chart.yValueAxis.labels.fontName = fontName
self.chart.yValueAxis.labels.fontSize = fontSize
self.chart.yValueAxis.labelTextFormat = None
self.chart.yValueAxis.visibleGrid = 1
self.chart.yValueAxis.gridStrokeDashArray = strokeDashArray
self.chart.yValueAxis.drawGridLast = True
# 如果为真,其他都画完后才画格子线,默认为False
self.chart.yValueAxis.minimumTickSpacing = 10
self.chart.yValueAxis.maximumTicks = 8
self.chart.yValueAxis.forceZero = 1
self.chart.yValueAxis.avoidBoundFrac = 1
self.chart.yValueAxis.visibleTicks = False
# 图例
self._add(self,Legend(),name='legend',validate=None,desc=None)
self.legend.fontName = fontName
self.legend.fontSize = 8
self.legend.strokeWidth = 3
self.legend.strokeColor = blue
self.legend.boxAnchor = 'w'
self.legend.alignment = 'right'
self.legend.dx = self.legend.dy = 6
self.legend.deltax = 30
self.legend.deltay = 0
self.legend.dxTextSpace = 2
self.legend.columnMaximum = 1
self.legend.variColumn = 1
# xal
self._add(self,Label(),name='xal',validate=None,desc=None)
self.xal.fontName = fontName
self.xal.fontSize = 8
self.xal._text = '5-year annualized standard deviation'
# yal
self._add(self,Label(),name='yal',validate=None,desc=None)
self.yal.fontName = fontName
self.yal.fontSize = 8
self.yal._text = '5-year average\nannual return (%)'
self.yal.angle = 90
self.yal.textAnchor ='middle'
# 原始样本数据
self._data = [('Portfolio', 2.25, 4.4800000000000004),
('Index', 3.5899999999999999, 4.4199999999999999),
('Universe Mean', 3.52, 3.48)]
self._primary = 2 # 要根据哪个数据点画交叉瞄准线
self._crossHairStrokeWidth = 2 # 交叉瞄准线的宽度
# self._colorsList = [red, green, blue] # 颜色方案
self._colorsList = [toColor(rgb2cmyk(*red.rgb())), toColor(rgb2cmyk(*green.rgb())), toColor(rgb2cmyk(*blue.rgb()))]
sd = [tuple(x[1:]) for x in self._data]
# 从原始样本数据中取出数据部分:[(2.25, 4.48), (3.59, 4.42), (3.52, 3.48)]
self.chart.data = [[x] for x in sd]
# 规整了数据存放格式:[[(2.25, 4.48)], [(3.59, 4.42)], [(3.52, 3.48)]]
nd = len(sd) # 有几组数据
for i in range(nd): # 每条数据线的标记、标记尺寸、颜色和代表的意义
# print(self.chart.lines[i].symbol.kind, self.chart.lines[i].symbol.size)
self.chart.lines[i].symbol = self.chart.lines.symbol
self.chart.lines[i].symbol.size = self.chart.lines.symbol.size
self.chart.lines[i].strokeColor = self._colorsList[i]
self.chart.lines[i].name = self._data[i][0]
# 颜色对应启动自动赋值
self.legend.colorNamePairs = Auto(obj=self.chart)
# self.legend.colorNamePairs = [(Auto(obj=self.chart),self._data[i][0]) for i in range(nd)]
# 选一个数据点做交叉瞄准线
# 数据点的坐标是(sd[self._primary][0], sd[self._primary][1])
if 0 < self._primary < nd:
self.chart.addCrossHair('pch', sd[self._primary][0], sd[self._primary][1],
strokeColor=self.chart.lines[self._primary].strokeColor,
strokeWidth=self._crossHairStrokeWidth)
# 添加交叉瞄准线的方法函数addCrossHair()所带参数依次为:名字,交叉瞄准线的中心坐标,颜色和宽度
# 画板宽高
self.height = 200
self.width =400
# 图表宽高和坐标
self.chart.y = 50
self.chart.x = 50
self.chart.height = 125
self.chart.width = 250
# 标签锚点和坐标
self.xal.boxAnchor = 'w'
self.xal.x = 50
self.xal.y = 30
self.yal.boxAnchor = 'c'
self.yal.y = 100
self.yal.x = 25
# 图例坐标
self.legend.x = 50
self.legend.y = 15
if __name__=="__main__": #NORUNTESTS
ScatterChart_CrossHair().save(formats=['pdf'],outDir='.',fnRoot=None)