在散点图上绘制置信椭圆
Drawing a confidence ellipse on top of a scatter plot
我目前正在开发一个 iOS 应用程序,我在其中使用 CorePlot 库(2.1 版)绘制散点图。我的散点图绘制得很好,在下一步中,我想在散点图的顶部绘制一个半透明的置信椭圆。我写了一个 class 计算椭圆的主轴和短轴以及所需的旋转角度。我的 ConfidenceEllipse class 实现了一个 getPath() 方法,其中 returns 一个代表要绘制的椭圆的 CGPath。
func getPath() -> CGPath
{
var ellipse: CGPath
var transform = CGAffineTransformIdentity
transform = CGAffineTransformTranslate (transform, CGFloat(-self.meanX), CGFloat(-self.meanY))
transform = CGAffineTransformRotate (transform, CGFloat(self.angle))
transform = CGAffineTransformTranslate (transform, CGFloat(self.meanX), CGFloat(self.meanY))
ellipse = CGPathCreateWithEllipseInRect(CGRectMake (CGFloat(-self.mainHalfAxis), CGFloat(-self.minorHalfAxis), CGFloat(2 * self.mainHalfAxis), CGFloat(2 * self.minorHalfAxis)),&transform)
return ellipse
}
在网上搜索了一段时间后,似乎注释是可行的方法,所以我尝试了这个:
let graph = hostView.hostedGraph!
let space = graph.defaultPlotSpace
let ellipse = ConfidenceEllipse(chiSquare: 5.991)
ellipse.analyze(self.samples)
let annotation = CPTPlotSpaceAnnotation (plotSpace: space!, anchorPlotPoint: [0,0])
let overlay = CPTBorderedLayer (frame: graph.frame)
overlay.outerBorderPath = ellipse.getPath()
let fillColor = CPTColor.yellowColor()
overlay.fill = CPTFill (color: fillColor)
annotation.contentLayer = overlay
annotation.contentLayer?.opacity = 0.5
graph.addAnnotation(annotation)
这样做,会给我以下信息
Screenshot
如您所见,叠加层占据了框架的全部大小,考虑到我在创建 CPTBorderedLayer 对象时传递了框架尺寸这一事实,这似乎是合乎逻辑的。我也尝试将构造函数留空,但叠加层根本不显示。所以我想知道,我在这里遗漏了什么吗?
您需要缩放椭圆以匹配绘图。使用绘图区bounds
作为标注层的边框,将标注附加到绘图区。在 x 和 y 方向缩放椭圆以匹配绘图使用的变换 space 以适合绘图区域中的绘图。
编辑:
在研究了边框图层的工作原理后,我意识到我上面的建议是行不通的。 CPTBorderedLayer
每当图层边界更改时自动设置 outerBorderPath
。不要试图影响图层边框,而是将椭圆绘制到图像中并将其用作带边框图层的 fill
。您应该调整层的大小,使椭圆恰好适合内部。
在未能使注释正常工作后,我决定走另一条路。我的最终解决方案是用第二个散点图覆盖我原来的散点图,它只包含一个数据点,即我的置信椭圆的中心。这是代码
func drawConfidenceEllipse () {
let graph = hostView.hostedGraph!
let plotSpace = graph.defaultPlotSpace as! CPTXYPlotSpace
let scaleX = (graph.bounds.size.width - graph.paddingLeft - graph.paddingRight) / CGFloat(plotSpace.xRange.lengthDouble)
let scaleY = (graph.bounds.size.height - graph.paddingTop - graph.paddingBottom) / CGFloat(plotSpace.yRange.lengthDouble)
let analysis = ConfidenceEllipse(chiSquare: 5.991)
analysis.analyze(self.samples)
let unscaledPath = analysis.getPath()
let bounds = CGPathGetBoundingBox(unscaledPath)
var scaler = CGAffineTransformIdentity
scaler = CGAffineTransformScale (scaler, scaleX, scaleY)
scaler = CGAffineTransformTranslate (scaler, CGFloat (-bounds.origin.x), CGFloat (-bounds.origin.y))
let scaledPath = CGPathCreateCopyByTransformingPath (unscaledPath, &scaler)
let scaledBounds = CGPathGetPathBoundingBox(scaledPath)
let symbol = CPTPlotSymbol ()
symbol.symbolType = CPTPlotSymbolType.Custom
symbol.customSymbolPath = scaledPath
symbol.fill = CPTFill (color: CPTColor.yellowColor().colorWithAlphaComponent(0.25))
symbol.size = CGSize (width: scaledBounds.size.width, height: scaledBounds.size.height)
let lineStyle = CPTMutableLineStyle()
lineStyle.lineWidth = 1
lineStyle.lineColor = CPTColor.yellowColor()
symbol.lineStyle = lineStyle
let ellipse = CPTScatterPlot (frame: hostView.frame)
ellipse.title = "Confidence Ellipse"
ellipse.delegate = self
ellipse.dataSource = self
ellipse.plotSymbol = symbol
ellipse.dataLineStyle = nil
graph.addPlot(ellipse)
}
这是最终结果的屏幕截图:
95% Confidence Ellipse on top of scatter plot
希望对您有所帮助
我目前正在开发一个 iOS 应用程序,我在其中使用 CorePlot 库(2.1 版)绘制散点图。我的散点图绘制得很好,在下一步中,我想在散点图的顶部绘制一个半透明的置信椭圆。我写了一个 class 计算椭圆的主轴和短轴以及所需的旋转角度。我的 ConfidenceEllipse class 实现了一个 getPath() 方法,其中 returns 一个代表要绘制的椭圆的 CGPath。
func getPath() -> CGPath
{
var ellipse: CGPath
var transform = CGAffineTransformIdentity
transform = CGAffineTransformTranslate (transform, CGFloat(-self.meanX), CGFloat(-self.meanY))
transform = CGAffineTransformRotate (transform, CGFloat(self.angle))
transform = CGAffineTransformTranslate (transform, CGFloat(self.meanX), CGFloat(self.meanY))
ellipse = CGPathCreateWithEllipseInRect(CGRectMake (CGFloat(-self.mainHalfAxis), CGFloat(-self.minorHalfAxis), CGFloat(2 * self.mainHalfAxis), CGFloat(2 * self.minorHalfAxis)),&transform)
return ellipse
}
在网上搜索了一段时间后,似乎注释是可行的方法,所以我尝试了这个:
let graph = hostView.hostedGraph!
let space = graph.defaultPlotSpace
let ellipse = ConfidenceEllipse(chiSquare: 5.991)
ellipse.analyze(self.samples)
let annotation = CPTPlotSpaceAnnotation (plotSpace: space!, anchorPlotPoint: [0,0])
let overlay = CPTBorderedLayer (frame: graph.frame)
overlay.outerBorderPath = ellipse.getPath()
let fillColor = CPTColor.yellowColor()
overlay.fill = CPTFill (color: fillColor)
annotation.contentLayer = overlay
annotation.contentLayer?.opacity = 0.5
graph.addAnnotation(annotation)
这样做,会给我以下信息 Screenshot 如您所见,叠加层占据了框架的全部大小,考虑到我在创建 CPTBorderedLayer 对象时传递了框架尺寸这一事实,这似乎是合乎逻辑的。我也尝试将构造函数留空,但叠加层根本不显示。所以我想知道,我在这里遗漏了什么吗?
您需要缩放椭圆以匹配绘图。使用绘图区bounds
作为标注层的边框,将标注附加到绘图区。在 x 和 y 方向缩放椭圆以匹配绘图使用的变换 space 以适合绘图区域中的绘图。
编辑:
在研究了边框图层的工作原理后,我意识到我上面的建议是行不通的。 CPTBorderedLayer
每当图层边界更改时自动设置 outerBorderPath
。不要试图影响图层边框,而是将椭圆绘制到图像中并将其用作带边框图层的 fill
。您应该调整层的大小,使椭圆恰好适合内部。
在未能使注释正常工作后,我决定走另一条路。我的最终解决方案是用第二个散点图覆盖我原来的散点图,它只包含一个数据点,即我的置信椭圆的中心。这是代码
func drawConfidenceEllipse () {
let graph = hostView.hostedGraph!
let plotSpace = graph.defaultPlotSpace as! CPTXYPlotSpace
let scaleX = (graph.bounds.size.width - graph.paddingLeft - graph.paddingRight) / CGFloat(plotSpace.xRange.lengthDouble)
let scaleY = (graph.bounds.size.height - graph.paddingTop - graph.paddingBottom) / CGFloat(plotSpace.yRange.lengthDouble)
let analysis = ConfidenceEllipse(chiSquare: 5.991)
analysis.analyze(self.samples)
let unscaledPath = analysis.getPath()
let bounds = CGPathGetBoundingBox(unscaledPath)
var scaler = CGAffineTransformIdentity
scaler = CGAffineTransformScale (scaler, scaleX, scaleY)
scaler = CGAffineTransformTranslate (scaler, CGFloat (-bounds.origin.x), CGFloat (-bounds.origin.y))
let scaledPath = CGPathCreateCopyByTransformingPath (unscaledPath, &scaler)
let scaledBounds = CGPathGetPathBoundingBox(scaledPath)
let symbol = CPTPlotSymbol ()
symbol.symbolType = CPTPlotSymbolType.Custom
symbol.customSymbolPath = scaledPath
symbol.fill = CPTFill (color: CPTColor.yellowColor().colorWithAlphaComponent(0.25))
symbol.size = CGSize (width: scaledBounds.size.width, height: scaledBounds.size.height)
let lineStyle = CPTMutableLineStyle()
lineStyle.lineWidth = 1
lineStyle.lineColor = CPTColor.yellowColor()
symbol.lineStyle = lineStyle
let ellipse = CPTScatterPlot (frame: hostView.frame)
ellipse.title = "Confidence Ellipse"
ellipse.delegate = self
ellipse.dataSource = self
ellipse.plotSymbol = symbol
ellipse.dataLineStyle = nil
graph.addPlot(ellipse)
}
这是最终结果的屏幕截图:
95% Confidence Ellipse on top of scatter plot
希望对您有所帮助