如何在Kotlin的Lets-Plot中为股票成本线按购买状态设置双色?
优化股票交易图表线段着色的Kotlin实现方案
问题根源
直接将color映射到isPurchased布尔值时出现线条间隙,是因为状态切换时相邻点属于不同分组,绘图库不会绘制跨分组的线段。而多geomLine方案会重复加载数据,导致内存占用高、性能差。
优化方案
通过预处理数据拆分连续状态段,配合单geomLine的分组映射实现无间隙着色,同时避免数据重复。核心思路是:
- 将连续同状态的点划分为独立线段组
- 状态切换时,将切换点同时加入前后两个组,保证线段衔接
- 用
group映射区分不同线段组,color映射控制颜色
Kotlin实现代码(基于Lets-Plot库)
import org.jetbrains.letsPlot.letsPlot import org.jetbrains.letsPlot.geom.geomLine import org.jetbrains.letsPlot.scale.scaleColorManual import org.jetbrains.letsPlot.aes // 数据类定义 data class DataPoint(val index: Int, val cost: Double, val isPurchased: Boolean) fun main() { // 示例原始数据 val costs = listOf(100.0, 101.0, 102.0, 103.0, 102.5, 101.5, 100.5) val isPurchased = listOf(false, false, true, true, true, false, false) // 合并为带索引的数据点列表 val dataPoints = costs.mapIndexed { index, cost -> DataPoint(index, cost, isPurchased[index]) } // 拆分连续状态段,处理衔接点 val segments = mutableListOf<List<DataPoint>>() if (dataPoints.isEmpty()) return var currentSegment = mutableListOf(dataPoints.first()) var currentState = dataPoints.first().isPurchased for (i in 1 until dataPoints.size) { val point = dataPoints[i] if (point.isPurchased == currentState) { currentSegment.add(point) } else { // 切换点加入前一段,保证衔接 currentSegment.add(point) segments.add(currentSegment) // 初始化新段,以切换点为起点 currentSegment = mutableListOf(point) currentState = point.isPurchased } } segments.add(currentSegment) // 转换为绘图所需的扁平化数据结构 val plotData = segments.flatMapIndexed { segmentId, points -> points.map { point -> mapOf( "x" to point.index, "y" to point.cost, "isPurchased" to point.isPurchased, "segmentId" to segmentId ) } } // 绘制图表 val plot = letsPlot(plotData) + geomLine( mapping = aes(x = "x", y = "y", color = "isPurchased", group = "segmentId"), size = 1.2 ) + scaleColorManual( values = mapOf(false to "#00ff00", true to "#ff0000"), labels = mapOf(false to "未购买", true to "已购买") ) plot.show() }
方案优势
- 仅使用单个
geomLine,避免多图层带来的内存浪费和性能损耗 - 通过预处理保证线段无间隙衔接,符合视觉需求
- 数据仅在状态切换点少量重复,远优于多图层的数据重复问题
内容的提问来源于stack exchange,提问作者Roman Aymaletdinov
相关产品推荐
相关产品推荐

