我想制作一个线性回归程序,以可视化方式向用户显示数据。我将EJML用于计算,将ScalaFX用于前端。一切都很好,但是当我使用散点图绘制数据时,从数据绘制的线被设置为覆盖原始数据点的矩形。我想知道如何更改打印点的大小,形状和透明度等。
关于JavaFX的几乎所有指南都说,我应该修改CSS文件(该文件不会自动存在)以设置图表样式。我不知道如何在ScalaFX中做到这一点,或者甚至有可能做到这一点。我搜索所有可能的教程的结果都是徒劳的。
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.chart.ScatterChart
import scalafx.collections.ObservableBuffer
import scalafx.scene.chart.NumberAxis
import scalafx.scene.chart.XYChart
import scalafx.scene.shape.Line
import org.ejml.simple.SimpleMatrix
import scala.math.pow
import scala.collection.mutable.Buffer
object Plotting extends JFXApp {
/*
* Below are some arbitrary x and y values for a regression line
*/
val xValues = Array(Array(1.0, 1.0, 1.0, 1.0, 1.0, 1.0), Array(14.0, 19.0, 22.0, 26.0, 31.0, 43.0))
val yValues = Array(Array(51.0, 57.0, 66.0, 71.0, 72.0, 84.0))
val temp = yValues.flatten
val wrapper = xValues(1).zip(temp)
/*
* In the lines before stage what happens is that matrices for the x and y values are created, coefficients
* for the regression line are calculated with matrix operations and (x, y) points are calculated for the
* regression line.
*/
val X = new SimpleMatrix(xValues).transpose
val Y = new SimpleMatrix(yValues).transpose
val secondX = new SimpleMatrix(xValues(0).size, 2)
for (i <- 0 until xValues(0).size) {
secondX.set(i, 0, xValues(0)(i))
secondX.set(i, 1, xValues(1)(i))
}
val invertedSecondX = secondX.pseudoInverse()
val B = invertedSecondX.mult(Y)
val graphPoints = Buffer[(Double, Double)]()
for (i <- 0 to xValues(1).max.toInt) {
graphPoints.append((i.toDouble, B.get(0, 0) + i * B.get(1, 0)))
}
stage = new JFXApp.PrimaryStage {
title = "Demo"
scene = new Scene(400, 400) {
val xAxis = NumberAxis()
val yAxis = NumberAxis()
val pData = XYChart.Series[Number, Number](
"Data",
ObservableBuffer(wrapper.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
val graph = XYChart.Series[Number, Number](
"RegressionLine",
ObservableBuffer(graphPoints.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
val plot = new ScatterChart(xAxis, yAxis, ObservableBuffer(graph, pData))
root = plot
}
}
}
答案 0 :(得分:2)
当然,记录的不如可能...:-(
样式表通常放置在项目的资源目录中。如果您使用的是 SBT (推荐),则为src/main/resources
。
在此示例中,我向此目录添加了名为MyCharts.css
的样式表,其内容如下:
/* Blue semi-transparent 4-pointed star, using SVG path. */
.default-color0.chart-symbol {
-fx-background-color: blue;
-fx-scale-shape: true;
-fx-shape: "M 0.0 10.0 L 3.0 3.0 L 10.0 0.0 L 3.0 -3.0 L 0.0 -10.0 L -3.0 -3.0 L -10.0 0.0 L -3.0 3.0 Z ";
-fx-opacity: 0.5;
}
/* Default shape is a rectangle. Here, we round it to become a red circle with a white
* center. Change the radius to control the size.
*/
.default-color1.chart-symbol {
-fx-background-color: red, white;
-fx-background-insets: 0, 2;
-fx-background-radius: 3px;
-fx-padding: 3px;
}
color0
将用于第一个数据系列(回归线),color1
将用于第二个数据系列(您的散点数据)。其他所有系列均使用默认的 JavaFX 样式。
(有关使用可缩放矢量图形( SVG )路径定义自定义形状的更多信息,请参见relevant section of the SVG specification。)
要让 ScalaFX ( JavaFX )使用此样式表,您可以选择。要使它们全局应用,请将其添加到主场景(这是我在下面所做的)。或者,如果每个图表需要不同的样式,则可以向特定的图表添加不同的样式表。 (顺便说一句,我还添加了包含导入的标准,因为这可以防止许多 JavaFX - ScalaFX 元素转换问题;否则,我没有对您的来源进行任何更改。)
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.chart.ScatterChart
import scalafx.collections.ObservableBuffer
import scalafx.scene.chart.NumberAxis
import scalafx.scene.chart.XYChart
import scalafx.scene.shape.Line
import org.ejml.simple.SimpleMatrix
import scala.math.pow
import scala.collection.mutable.Buffer
object Plotting extends JFXApp {
/*
* Below are some arbitrary x and y values for a regression line
*/
val xValues = Array(Array(1.0, 1.0, 1.0, 1.0, 1.0, 1.0), Array(14.0, 19.0, 22.0, 26.0, 31.0, 43.0))
val yValues = Array(Array(51.0, 57.0, 66.0, 71.0, 72.0, 84.0))
val temp = yValues.flatten
val wrapper = xValues(1).zip(temp)
/*
* In the lines before stage what happens is that matrices for the x and y values are created, coefficients
* for the regression line are calculated with matrix operations and (x, y) points are calculated for the
* regression line.
*/
val X = new SimpleMatrix(xValues).transpose
val Y = new SimpleMatrix(yValues).transpose
val secondX = new SimpleMatrix(xValues(0).size, 2)
for (i <- 0 until xValues(0).size) {
secondX.set(i, 0, xValues(0)(i))
secondX.set(i, 1, xValues(1)(i))
}
val invertedSecondX = secondX.pseudoInverse()
val B = invertedSecondX.mult(Y)
val graphPoints = Buffer[(Double, Double)]()
for (i <- 0 to xValues(1).max.toInt) {
graphPoints.append((i.toDouble, B.get(0, 0) + i * B.get(1, 0)))
}
stage = new JFXApp.PrimaryStage {
title = "Demo"
scene = new Scene(400, 400) {
// Add our stylesheet.
stylesheets.add("MyCharts.css")
val xAxis = NumberAxis()
val yAxis = NumberAxis()
val pData = XYChart.Series[Number, Number](
"Data",
ObservableBuffer(wrapper.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
val graph = XYChart.Series[Number, Number](
"RegressionLine",
ObservableBuffer(graphPoints.map(z => XYChart.Data[Number, Number](z._1, z._2)): _*))
val plot = new ScatterChart(xAxis, yAxis, ObservableBuffer(graph, pData))
root = plot
}
}
}
有关可用的CSS格式选项(更改形状,颜色,透明度等)的更多信息,请参见JavaFX CSS Reference Guide。
答案 1 :(得分:0)
我几乎不敢在Mike Allens解决方案中添加somethig(一如既往地非常好),但是这对我来说不起作用,因为我无法让scala查找和/或处理.css。文件。 如果可能的话,我会以这种方式完成的,但我只是无法使其正常工作。 这是我想出的:
假设我要显示一些数据:
val xyExampleData: ObservableBuffer[(Double, Double)] = ObservableBuffer(Seq(
1 -> 1,
2 -> 4,
3 -> 9))
然后将其转换为LineChart的系列:
val DataPoints = ObservableBuffer(xyExampleData map { case (x, y) => XYChart.Data[Number, Number](x, y) })
val PointsToDisplay = XYChart.Series[Number, Number]("Points", DataPoints)
现在我将其再次放入Buffer中,也许还有其他不同系列的数据。
val lineChartBuffer = ObservableBuffer(PointsToDisplay, ...)
最后,我创建了lineChart,直到我称之为(缺乏创造力)lineChart:
val lineChart = new LineChart(xAxis, yAxis, lineChartBuffer) {...}
现在可以轻松地为数据点之间的线重新着色:
lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;")
这将更改LineChartBuffer中FIRST数据集的线条颜色。 如果要更改线路属性,请致电
lineChart.lookup(".default-color1.chart-series-line")...
还有“ -fx-stroke-width:3px;”设置行的。
“-fx-opacity:0.1;”
“-fx-stroke-dash-array:10;”
-fx-fill:蓝色;”
也是有用的,但是不要重复调用上面的行,因为第二个调用将覆盖第一个调用。 而是将所有字符串合并为一个:
lineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: blue;-fx-opacity: 0.1;-fx-stroke-dash-array: 10;-fx-fill: blue;")
现在在每个数据点格式化符号:
不幸的是,除了单独格式化每个符号外,似乎别无他法:
lineChart.lookupAll(".default-color0.chart-line-symbol").asScala foreach { node => node.setStyle("-fx-background-color: blue, white;") }
要运行此程序,您需要import scala.collection.JavaConverters._
从Java设置转换为Scala设置。
一个人也可以仅隐藏一个数据集中的所有数据点,例如:
lineChart.lookupAll(".default-color1.chart-line-symbol").asScala foreach { node => node.setVisible(false) }
要说这是一个很好的解决方案,将被夸大。 它有一个很大的缺点,那就是在向LineChartBuffer中的一个序列添加新的Datapoint之后,必须重新着色或重新格式化每个Symbol。否则,新的符号将具有标准的颜色和设置。 线条留着,它们重新着色了,我不能说为什么。
但是,好的一面是,之后总是可以在折线图中重新格式化曲线!