Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
12/17/16 15:42:19 (7 years ago)
Author:
gkronber
Message:

#2650: merged r14457:14494 from trunk to branch (resolving conflicts)

Location:
branches/symbreg-factors-2650
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • branches/symbreg-factors-2650

  • branches/symbreg-factors-2650/HeuristicLab.Analysis.Views

  • branches/symbreg-factors-2650/HeuristicLab.Analysis.Views/3.3/ScatterPlotControl.cs

    r14449 r14498  
    2424using System.Drawing;
    2525using System.Linq;
     26using System.Text;
    2627using System.Windows.Forms;
    2728using System.Windows.Forms.DataVisualization.Charting;
     
    3334    protected List<Series> invisibleSeries;
    3435    protected Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow> pointsRowsTable;
     36    protected Dictionary<Series, Series> seriesToRegressionSeriesTable;
    3537    private double xMin, xMax, yMin, yMax;
    3638
     
    5153      InitializeComponent();
    5254      pointsRowsTable = new Dictionary<IObservableList<Point2D<double>>, ScatterPlotDataRow>();
     55      seriesToRegressionSeriesTable = new Dictionary<Series, Series>();
    5356      invisibleSeries = new List<Series>();
    5457      chart.CustomizeAllChartAreas();
     
    126129      foreach (var row in rows) {
    127130        RegisterScatterPlotDataRowEvents(row);
    128         Series series = new Series(row.Name);
     131        Series series = new Series(row.Name) {
     132          Tag = row
     133        };
    129134        if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
    130135        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);
    132145        FillSeriesWithRowValues(series, row);
    133146        chart.Series.Add(series);
     147        chart.Series.Add(regressionSeries);
     148        FillRegressionSeries(regressionSeries, row);
    134149      }
    135150      ConfigureChartArea(chart.ChartAreas[0]);
     
    137152      RecalculateAxesScale(chart.ChartAreas[0]);
    138153      UpdateYCursorInterval();
     154      UpdateRegressionSeriesColors();
    139155    }
    140156
     
    146162        if (invisibleSeries.Contains(series))
    147163          invisibleSeries.Remove(series);
     164        chart.Series.Remove(seriesToRegressionSeriesTable[series]);
     165        seriesToRegressionSeriesTable.Remove(series);
    148166      }
    149167      RecalculateMinMaxPointValues();
     
    151169    }
    152170
    153     private void ConfigureSeries(Series series, ScatterPlotDataRow row) {
     171    private void ConfigureSeries(Series series, Series regressionSeries, ScatterPlotDataRow row) {
    154172      series.BorderWidth = 1;
    155173      series.BorderDashStyle = ChartDashStyle.Solid;
     
    179197        xAxisTitle + " = " + "#VALX," + Environment.NewLine +
    180198        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;
    181206    }
    182207
     
    250275    }
    251276
     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
    252286    #region Event Handlers
    253287    #region Content Event Handlers
     
    300334        ScatterPlotDataRow row = (ScatterPlotDataRow)sender;
    301335        Series series = chart.Series[row.Name];
     336        Series regressionSeries = seriesToRegressionSeriesTable[series];
    302337        series.Points.Clear();
    303         ConfigureSeries(series, row);
     338        regressionSeries.Points.Clear();
     339        ConfigureSeries(series, regressionSeries, row);
    304340        FillSeriesWithRowValues(series, row);
     341        FillRegressionSeries(regressionSeries, row);
    305342        RecalculateMinMaxPointValues();
    306343        RecalculateAxesScale(chart.ChartAreas[0]);
     344        UpdateRegressionSeriesColors();
    307345      }
    308346    }
     
    325363        if (row != null) {
    326364          Series rowSeries = chart.Series[row.Name];
     365          Series regressionSeries = seriesToRegressionSeriesTable[rowSeries];
    327366          if (!invisibleSeries.Contains(rowSeries)) {
    328367            rowSeries.Points.Clear();
     368            regressionSeries.Points.Clear();
    329369            FillSeriesWithRowValues(rowSeries, row);
     370            FillRegressionSeries(regressionSeries, row);
    330371            RecalculateMinMaxPointValues();
    331372            RecalculateAxesScale(chart.ChartAreas[0]);
     
    343384        if (row != null) {
    344385          Series rowSeries = chart.Series[row.Name];
     386          Series regressionSeries = seriesToRegressionSeriesTable[rowSeries];
    345387          if (!invisibleSeries.Contains(rowSeries)) {
    346388            rowSeries.Points.Clear();
     389            regressionSeries.Points.Clear();
    347390            FillSeriesWithRowValues(rowSeries, row);
     391            FillRegressionSeries(regressionSeries, row);
    348392            RecalculateMinMaxPointValues();
    349393            RecalculateAxesScale(chart.ChartAreas[0]);
     
    361405        if (row != null) {
    362406          Series rowSeries = chart.Series[row.Name];
     407          Series regressionSeries = seriesToRegressionSeriesTable[rowSeries];
    363408          if (!invisibleSeries.Contains(rowSeries)) {
    364409            rowSeries.Points.Clear();
     410            regressionSeries.Points.Clear();
    365411            FillSeriesWithRowValues(rowSeries, row);
     412            FillRegressionSeries(regressionSeries, row);
    366413            RecalculateMinMaxPointValues();
    367414            RecalculateAxesScale(chart.ChartAreas[0]);
     
    379426        if (row != null) {
    380427          Series rowSeries = chart.Series[row.Name];
     428          Series regressionSeries = seriesToRegressionSeriesTable[rowSeries];
    381429          if (!invisibleSeries.Contains(rowSeries)) {
    382430            rowSeries.Points.Clear();
     431            regressionSeries.Points.Clear();
    383432            FillSeriesWithRowValues(rowSeries, row);
     433            FillRegressionSeries(regressionSeries, row);
    384434            RecalculateMinMaxPointValues();
    385435            RecalculateAxesScale(chart.ChartAreas[0]);
     
    430480        invisibleSeries.Remove(series);
    431481        if (Content != null) {
    432 
    433           var row = (from r in Content.Rows
    434                      where r.Name == series.Name
    435                      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);
    437487          RecalculateMinMaxPointValues();
    438488          this.chart.Legends[series.Legend].ForeColor = Color.Black;
     
    469519        series.Points.Add(point);
    470520      }
     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);
    471548    }
    472549
     
    501578    }
    502579    #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
    503747  }
    504748}
Note: See TracChangeset for help on using the changeset viewer.