Changeset 14498 for branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/ScatterPlotControl.cs
- Timestamp:
- 12/17/16 15:42:19 (7 years ago)
- Location:
- branches/symbreg-factors-2650
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/symbreg-factors-2650
- Property svn:mergeinfo changed
/trunk/sources merged: 14457-14458,14463-14465,14468-14469,14475-14476,14478-14479,14481-14483,14486,14493-14494
- Property svn:mergeinfo changed
-
branches/symbreg-factors-2650/HeuristicLab.Analysis.Views
- Property svn:mergeinfo changed
/trunk/sources/HeuristicLab.Analysis.Views merged: 14457-14458,14493
- Property svn:mergeinfo changed
-
branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/ScatterPlotControl.cs
r14449 r14498 24 24 using System.Drawing; 25 25 using System.Linq; 26 using System.Text; 26 27 using System.Windows.Forms; 27 28 using System.Windows.Forms.DataVisualization.Charting; … … 33 34 protected List<Series> invisibleSeries; 34 35 protected Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow> pointsRowsTable; 36 protected Dictionary<Series, Series> seriesToRegressionSeriesTable; 35 37 private double xMin, xMax, yMin, yMax; 36 38 … … 51 53 InitializeComponent(); 52 54 pointsRowsTable = new Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow>(); 55 seriesToRegressionSeriesTable = new Dictionary<Series, Series>(); 53 56 invisibleSeries = new List<Series>(); 54 57 chart.CustomizeAllChartAreas(); … … 126 129 foreach (var row in rows) { 127 130 RegisterScatterPlotDataRowEvents(row); 128 Series series = new Series(row.Name); 131 Series series = new Series(row.Name) { 132 Tag = row 133 }; 129 134 if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName; 130 135 else series.LegendText = row.Name; 131 ConfigureSeries(series, row); 136 var regressionSeries = new Series(row.Name + "_Regression") { 137 Tag = row, 138 ChartType = SeriesChartType.Line, 139 BorderDashStyle = ChartDashStyle.Dot, 140 IsVisibleInLegend = false, 141 Color = Color.Transparent // to avoid auto color assignment via color palette 142 }; 143 seriesToRegressionSeriesTable.Add(series, regressionSeries); 144 ConfigureSeries(series, regressionSeries, row); 132 145 FillSeriesWithRowValues(series, row); 133 146 chart.Series.Add(series); 147 chart.Series.Add(regressionSeries); 148 FillRegressionSeries(regressionSeries, row); 134 149 } 135 150 ConfigureChartArea(chart.ChartAreas[0]); … … 137 152 RecalculateAxesScale(chart.ChartAreas[0]); 138 153 UpdateYCursorInterval(); 154 UpdateRegressionSeriesColors(); 139 155 } 140 156 … … 146 162 if (invisibleSeries.Contains(series)) 147 163 invisibleSeries.Remove(series); 164 chart.Series.Remove(seriesToRegressionSeriesTable[series]); 165 seriesToRegressionSeriesTable.Remove(series); 148 166 } 149 167 RecalculateMinMaxPointValues(); … … 151 169 } 152 170 153 private void ConfigureSeries(Series series, S catterPlotDataRow row) {171 private void ConfigureSeries(Series series, Series regressionSeries, ScatterPlotDataRow row) { 154 172 series.BorderWidth = 1; 155 173 series.BorderDashStyle = ChartDashStyle.Solid; … … 179 197 xAxisTitle + " = " + "#VALX," + Environment.NewLine + 180 198 yAxisTitle + " = " + "#VAL"; 199 200 regressionSeries.BorderWidth = Math.Max(1, row.VisualProperties.PointSize / 2); 201 regressionSeries.IsVisibleInLegend = row.VisualProperties.IsRegressionVisibleInLegend && 202 row.VisualProperties.RegressionType != ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.None; 203 regressionSeries.LegendText = string.IsNullOrEmpty(row.VisualProperties.RegressionDisplayName) 204 ? string.Format("{0}({1})", row.VisualProperties.RegressionType, row.Name) 205 : row.VisualProperties.RegressionDisplayName; 181 206 } 182 207 … … 250 275 } 251 276 277 protected void UpdateRegressionSeriesColors() { 278 chart.ApplyPaletteColors(); 279 foreach (var row in Content.Rows) { 280 var series = chart.Series[row.Name]; 281 var regressionSeries = seriesToRegressionSeriesTable[series]; 282 regressionSeries.Color = series.Color; 283 } 284 } 285 252 286 #region Event Handlers 253 287 #region Content Event Handlers … … 300 334 ScatterPlotDataRow row = (ScatterPlotDataRow)sender; 301 335 Series series = chart.Series[row.Name]; 336 Series regressionSeries = seriesToRegressionSeriesTable[series]; 302 337 series.Points.Clear(); 303 ConfigureSeries(series, row); 338 regressionSeries.Points.Clear(); 339 ConfigureSeries(series, regressionSeries, row); 304 340 FillSeriesWithRowValues(series, row); 341 FillRegressionSeries(regressionSeries, row); 305 342 RecalculateMinMaxPointValues(); 306 343 RecalculateAxesScale(chart.ChartAreas[0]); 344 UpdateRegressionSeriesColors(); 307 345 } 308 346 } … … 325 363 if (row != null) { 326 364 Series rowSeries = chart.Series[row.Name]; 365 Series regressionSeries = seriesToRegressionSeriesTable[rowSeries]; 327 366 if (!invisibleSeries.Contains(rowSeries)) { 328 367 rowSeries.Points.Clear(); 368 regressionSeries.Points.Clear(); 329 369 FillSeriesWithRowValues(rowSeries, row); 370 FillRegressionSeries(regressionSeries, row); 330 371 RecalculateMinMaxPointValues(); 331 372 RecalculateAxesScale(chart.ChartAreas[0]); … … 343 384 if (row != null) { 344 385 Series rowSeries = chart.Series[row.Name]; 386 Series regressionSeries = seriesToRegressionSeriesTable[rowSeries]; 345 387 if (!invisibleSeries.Contains(rowSeries)) { 346 388 rowSeries.Points.Clear(); 389 regressionSeries.Points.Clear(); 347 390 FillSeriesWithRowValues(rowSeries, row); 391 FillRegressionSeries(regressionSeries, row); 348 392 RecalculateMinMaxPointValues(); 349 393 RecalculateAxesScale(chart.ChartAreas[0]); … … 361 405 if (row != null) { 362 406 Series rowSeries = chart.Series[row.Name]; 407 Series regressionSeries = seriesToRegressionSeriesTable[rowSeries]; 363 408 if (!invisibleSeries.Contains(rowSeries)) { 364 409 rowSeries.Points.Clear(); 410 regressionSeries.Points.Clear(); 365 411 FillSeriesWithRowValues(rowSeries, row); 412 FillRegressionSeries(regressionSeries, row); 366 413 RecalculateMinMaxPointValues(); 367 414 RecalculateAxesScale(chart.ChartAreas[0]); … … 379 426 if (row != null) { 380 427 Series rowSeries = chart.Series[row.Name]; 428 Series regressionSeries = seriesToRegressionSeriesTable[rowSeries]; 381 429 if (!invisibleSeries.Contains(rowSeries)) { 382 430 rowSeries.Points.Clear(); 431 regressionSeries.Points.Clear(); 383 432 FillSeriesWithRowValues(rowSeries, row); 433 FillRegressionSeries(regressionSeries, row); 384 434 RecalculateMinMaxPointValues(); 385 435 RecalculateAxesScale(chart.ChartAreas[0]); … … 430 480 invisibleSeries.Remove(series); 431 481 if (Content != null) { 432 433 var row = (from r in Content.Rows434 where r.Name == series.Name435 select r).Single();436 FillSeriesWithRowValues(series, row);482 var row = (ScatterPlotDataRow)series.Tag; 483 if (seriesToRegressionSeriesTable.ContainsKey(series)) 484 FillSeriesWithRowValues(series, row); 485 else 486 FillRegressionSeries(series, row); 437 487 RecalculateMinMaxPointValues(); 438 488 this.chart.Legends[series.Legend].ForeColor = Color.Black; … … 469 519 series.Points.Add(point); 470 520 } 521 double correlation = Correlation(row.Points); 522 series.LegendToolTip = string.Format("Correlation (R²) = {0:G4}", correlation * correlation); 523 } 524 525 private void FillRegressionSeries(Series regressionSeries, ScatterPlotDataRow row) { 526 if (row.VisualProperties.RegressionType == ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.None 527 || invisibleSeries.Contains(regressionSeries)) 528 return; 529 530 double[] coefficients; 531 if (!Fitting(row, out coefficients)) 532 return; 533 534 // Fill regrssion series 535 var validPoints = row.Points.Where(p => !IsInvalidValue(p.X)); 536 double min = validPoints.Min(p => p.X), max = validPoints.Max(p => p.X); 537 double range = max - min, delta = range / row.Points.Count; 538 for (double x = min; x < max; x += delta) { 539 regressionSeries.Points.AddXY(x, Estimate(x, row, coefficients)); 540 } 541 542 // Correlation 543 var data = row.Points.Select(p => new Point2D<double>(p.Y, Estimate(p.X, row, coefficients))); 544 double correlation = Correlation(data.ToList()); 545 regressionSeries.LegendToolTip = GetStringFormula(row, coefficients) + Environment.NewLine + 546 string.Format("Correlation (R²) = {0:G4}", correlation * correlation); 547 regressionSeries.ToolTip = GetStringFormula(row, coefficients); 471 548 } 472 549 … … 501 578 } 502 579 #endregion 580 581 #region Correlation and Fitting Helper 582 protected static double Correlation(IList<Point2D<double>> values) { 583 // sums of x, y, x squared etc. 584 double sx = 0.0; 585 double sy = 0.0; 586 double sxx = 0.0; 587 double syy = 0.0; 588 double sxy = 0.0; 589 590 int n = 0; 591 for (int i = 0; i < values.Count; i++) { 592 double x = values[i].X; 593 double y = values[i].Y; 594 if (IsInvalidValue(x) || IsInvalidValue(y)) 595 continue; 596 597 sx += x; 598 sy += y; 599 sxx += x * x; 600 syy += y * y; 601 sxy += x * y; 602 n++; 603 } 604 605 // covariation 606 double cov = sxy / n - sx * sy / n / n; 607 // standard error of x 608 double sigmaX = Math.Sqrt(sxx / n - sx * sx / n / n); 609 // standard error of y 610 double sigmaY = Math.Sqrt(syy / n - sy * sy / n / n); 611 612 // correlation 613 return cov / sigmaX / sigmaY; 614 } 615 616 protected static bool Fitting(ScatterPlotDataRow row, out double[] coefficients) { 617 var xs = row.Points.Select(p => p.X).ToList(); 618 var ys = row.Points.Select(p => p.Y).ToList(); 619 620 // Input transformations 621 double[,] matrix; 622 int nRows; 623 switch (row.VisualProperties.RegressionType) { 624 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Linear: 625 matrix = CreateMatrix(out nRows, ys, xs); 626 break; 627 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Polynomial: 628 var xss = Enumerable.Range(1, row.VisualProperties.PolynomialRegressionOrder) 629 .Select(o => xs.Select(x => Math.Pow(x, o)).ToList()) 630 .Reverse(); // higher order first 631 matrix = CreateMatrix(out nRows, ys, xss.ToArray()); 632 break; 633 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Exponential: 634 matrix = CreateMatrix(out nRows, ys.Select(y => Math.Log(y)).ToList(), xs); 635 break; 636 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Power: 637 matrix = CreateMatrix(out nRows, ys.Select(y => Math.Log(y)).ToList(), xs.Select(x => Math.Log(x)).ToList()); 638 break; 639 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Logarithmic: 640 matrix = CreateMatrix(out nRows, ys, xs.Select(x => Math.Log(x)).ToList()); 641 break; 642 default: 643 throw new ArgumentException("Unknown RegressionType: " + row.VisualProperties.RegressionType); 644 } 645 646 // Linear fitting 647 bool success = LinearFitting(matrix, nRows, out coefficients); 648 if (!success) return success; 649 650 // Output transformation 651 switch (row.VisualProperties.RegressionType) { 652 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Exponential: 653 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Power: 654 coefficients[1] = Math.Exp(coefficients[1]); 655 break; 656 } 657 658 return true; 659 } 660 protected static double[,] CreateMatrix(out int nRows, IList<double> ys, params IList<double>[] xss) { 661 var matrix = new double[ys.Count, xss.Length + 1]; 662 int rowIdx = 0; 663 for (int i = 0; i < ys.Count; i++) { 664 if (IsInvalidValue(ys[i]) || xss.Any(xs => IsInvalidValue(xs[i]))) 665 continue; 666 for (int j = 0; j < xss.Length; j++) { 667 matrix[rowIdx, j] = xss[j][i]; 668 } 669 matrix[rowIdx, xss.Length] = ys[i]; 670 rowIdx++; 671 } 672 nRows = rowIdx; 673 return matrix; 674 } 675 676 protected static bool LinearFitting(double[,] xsy, int nRows, out double[] coefficients) { 677 int nFeatures = xsy.GetLength(1) - 1; 678 679 alglib.linearmodel lm; 680 alglib.lrreport ar; 681 int retVal; 682 alglib.lrbuild(xsy, nRows, nFeatures, out retVal, out lm, out ar); 683 if (retVal != 1) { 684 coefficients = new double[0]; 685 return false; 686 } 687 688 alglib.lrunpack(lm, out coefficients, out nFeatures); 689 return true; 690 } 691 692 protected static double Estimate(double x, ScatterPlotDataRow row, double[] coefficients) { 693 switch (row.VisualProperties.RegressionType) { 694 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Linear: 695 return coefficients[0] * x + coefficients[1]; 696 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Polynomial: 697 return coefficients 698 .Reverse() // to match index and order 699 .Select((c, o) => c * Math.Pow(x, o)) 700 .Sum(); 701 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Exponential: 702 return coefficients[1] * Math.Exp(coefficients[0] * x); 703 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Power: 704 return coefficients[1] * Math.Pow(x, coefficients[0]); 705 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Logarithmic: 706 return coefficients[0] * Math.Log(x) + coefficients[1]; 707 default: 708 throw new ArgumentException("Unknown RegressionType: " + row.VisualProperties.RegressionType); 709 } 710 } 711 712 protected static string GetStringFormula(ScatterPlotDataRow row, double[] coefficients) { 713 switch (row.VisualProperties.RegressionType) { 714 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Linear: 715 return string.Format("{0:G4} x {1} {2:G4}", coefficients[0], Sign(coefficients[1]), Math.Abs(coefficients[1])); 716 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Polynomial: 717 var sb = new StringBuilder(); 718 sb.AppendFormat("{0:G4}{1}", coefficients[0], PolyFactor(coefficients.Length - 1)); 719 foreach (var x in coefficients 720 .Reverse() // match index and order 721 .Select((c, o) => new { c, o }) 722 .Reverse() // higher order first 723 .Skip(1)) // highest order poly already added 724 sb.AppendFormat(" {0} {1:G4}{2}", Sign(x.c), Math.Abs(x.c), PolyFactor(x.o)); 725 return sb.ToString(); 726 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Exponential: 727 return string.Format("{0:G4} e^({1:G4} x)", coefficients[1], coefficients[0]); 728 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Power: 729 return string.Format("{0:G4} x^({1:G4})", coefficients[1], coefficients[0]); 730 case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Logarithmic: 731 return string.Format("{0:G4} ln(x) {1} {2:G4}", coefficients[0], Sign(coefficients[1]), Math.Abs(coefficients[1])); 732 default: 733 throw new ArgumentException("Unknown RegressionType: " + row.VisualProperties.RegressionType); 734 } 735 } 736 private static string Sign(double value) { 737 return value >= 0 ? "+" : "-"; 738 } 739 private static string PolyFactor(int order) { 740 if (order == 0) return ""; 741 if (order == 1) return " x"; 742 if (order == 2) return " x²"; 743 if (order == 3) return " x³"; 744 return " x^" + order; 745 } 746 #endregion 503 747 } 504 748 }
Note: See TracChangeset
for help on using the changeset viewer.