protected virtual void DeregisterScatterPlotDataRowEvents(ScatterPlotDataRow row) {
   row.Points.ItemsAdded -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsAdded);
   row.Points.ItemsRemoved -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsRemoved);
   row.Points.ItemsReplaced -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_ItemsReplaced);
   row.Points.CollectionReset -= new CollectionItemsChangedEventHandler<IndexedItem<Point2D<double>>>(Points_CollectionReset);
   pointsRowsTable.Remove(row.Points);
   row.VisualPropertiesChanged -= new EventHandler(Row_VisualPropertiesChanged);
   row.NameChanged -= new EventHandler(Row_NameChanged);
 }
Example #2
0
        private void ConfigureSeries(Series series, ScatterPlotDataRow row)
        {
            series.BorderWidth     = 1;
            series.BorderDashStyle = ChartDashStyle.Solid;
            series.BorderColor     = Color.Empty;

            if (row.VisualProperties.Color != Color.Empty)
            {
                series.Color = row.VisualProperties.Color;
            }
            else
            {
                series.Color = Color.Empty;
            }
            series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
            series.ChartType         = SeriesChartType.FastPoint;
            series.MarkerSize        = row.VisualProperties.PointSize;
            series.MarkerStyle       = ConvertPointStyle(row.VisualProperties.PointStyle);
            series.XAxisType         = AxisType.Primary;
            series.YAxisType         = AxisType.Primary;

            if (row.VisualProperties.DisplayName.Trim() != String.Empty)
            {
                series.LegendText = row.VisualProperties.DisplayName;
            }
            else
            {
                series.LegendText = row.Name;
            }

            string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
                      ? "X"
                      : Content.VisualProperties.XAxisTitle;
            string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
                            ? "Y"
                            : Content.VisualProperties.YAxisTitle;

            series.ToolTip =
                series.LegendText + Environment.NewLine +
                xAxisTitle + " = " + "#VALX," + Environment.NewLine +
                yAxisTitle + " = " + "#VAL";
        }
        public ScatterPlot CreateScatterPlot(string variableNameX, string variableNameY)
        {
            ScatterPlot scatterPlot = new ScatterPlot();

            IList <double> xValues = PreprocessingData.GetValues <double>(PreprocessingData.GetColumnIndex(variableNameX));
            IList <double> yValues = PreprocessingData.GetValues <double>(PreprocessingData.GetColumnIndex(variableNameY));

            List <Point2D <double> > points = new List <Point2D <double> >();

            for (int i = 0; i < xValues.Count; i++)
            {
                Point2D <double> point = new Point2D <double>(xValues[i], yValues[i]);
                points.Add(point);
            }

            ScatterPlotDataRow scdr = new ScatterPlotDataRow(variableNameX + " - " + variableNameY, "", points);

            scatterPlot.Rows.Add(scdr);
            return(scatterPlot);
        }
 private void Row_VisualPropertiesChanged(object sender, EventArgs e)
 {
     if (InvokeRequired)
     {
         Invoke(new EventHandler(Row_VisualPropertiesChanged), sender, e);
     }
     else
     {
         ScatterPlotDataRow row              = (ScatterPlotDataRow)sender;
         Series             series           = chart.Series[row.Name];
         Series             regressionSeries = seriesToRegressionSeriesTable[series];
         series.Points.Clear();
         regressionSeries.Points.Clear();
         ConfigureSeries(series, regressionSeries, row);
         FillSeriesWithRowValues(series, row);
         FillRegressionSeries(regressionSeries, row);
         RecalculateMinMaxPointValues();
         RecalculateAxesScale(chart.ChartAreas[0]);
         UpdateRegressionSeriesColors();
     }
 }
Example #5
0
 private void Points_CollectionReset(object sender, CollectionItemsChangedEventArgs <IndexedItem <Point2D <double> > > e)
 {
     if (InvokeRequired)
     {
         Invoke(new CollectionItemsChangedEventHandler <IndexedItem <Point2D <double> > >(Points_CollectionReset), sender, e);
     }
     else
     {
         ScatterPlotDataRow row = null;
         pointsRowsTable.TryGetValue((IObservableList <Point2D <double> >)sender, out row);
         if (row != null)
         {
             Series rowSeries = chart.Series[row.Name];
             if (!invisibleSeries.Contains(rowSeries))
             {
                 rowSeries.Points.Clear();
                 FillSeriesWithRowValues(rowSeries, row);
                 RecalculateAxesScale(chart.ChartAreas[0]);
                 UpdateYCursorInterval();
             }
         }
     }
 }
        protected static bool Fitting(ScatterPlotDataRow row, out double[] coefficients)
        {
            if (!IsValidRegressionData(row))
            {
                coefficients = new double[0];
                return(false);
            }

            var xs = row.Points.Select(p => p.X).ToList();
            var ys = row.Points.Select(p => p.Y).ToList();

            // Input transformations
            double[,] matrix;
            int nRows;

            switch (row.VisualProperties.RegressionType)
            {
            case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Linear:
                matrix = CreateMatrix(out nRows, ys, xs);
                break;

            case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Polynomial:
                var xss = Enumerable.Range(1, row.VisualProperties.PolynomialRegressionOrder)
                          .Select(o => xs.Select(x => Math.Pow(x, o)).ToList())
                          .Reverse(); // higher order first
                matrix = CreateMatrix(out nRows, ys, xss.ToArray());
                break;

            case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Exponential:
                matrix = CreateMatrix(out nRows, ys.Select(y => Math.Log(y)).ToList(), xs);
                break;

            case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Power:
                matrix = CreateMatrix(out nRows, ys.Select(y => Math.Log(y)).ToList(), xs.Select(x => Math.Log(x)).ToList());
                break;

            case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Logarithmic:
                matrix = CreateMatrix(out nRows, ys, xs.Select(x => Math.Log(x)).ToList());
                break;

            default:
                throw new ArgumentException("Unknown RegressionType: " + row.VisualProperties.RegressionType);
            }

            // Linear fitting
            bool success = LinearFitting(matrix, nRows, out coefficients);

            if (!success)
            {
                return(false);
            }

            // Output transformation
            switch (row.VisualProperties.RegressionType)
            {
            case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Exponential:
            case ScatterPlotDataRowVisualProperties.ScatterPlotDataRowRegressionType.Power:
                coefficients[1] = Math.Exp(coefficients[1]);
                break;
            }

            return(true);
        }
Example #7
0
        private void SetUpResults(IReadOnlyList <int> allIndices)
        {
            if (Results == null)
            {
                return;
            }
            var results = Results;

            dataRowIndices = new Dictionary <string, IList <int> >();
            var problemData = Problem.ProblemData;

            if (!results.ContainsKey(IterationResultName))
            {
                results.Add(new Result(IterationResultName, new IntValue(0)));
            }
            if (!results.ContainsKey(ErrorResultName))
            {
                results.Add(new Result(ErrorResultName, new DoubleValue(0)));
            }
            if (!results.ContainsKey(ScatterPlotResultName))
            {
                results.Add(new Result(ScatterPlotResultName, "Plot of the projected data", new ScatterPlot(DataResultName, "")));
            }
            if (!results.ContainsKey(DataResultName))
            {
                results.Add(new Result(DataResultName, "Projected Data", new DoubleMatrix()));
            }
            if (!results.ContainsKey(ErrorPlotResultName))
            {
                var errortable = new DataTable(ErrorPlotResultName, "Development of errors during gradient descent")
                {
                    VisualProperties =
                    {
                        XAxisTitle    = "UpdateIntervall",
                        YAxisTitle    = "Error",
                        YAxisLogScale = true
                    }
                };
                errortable.Rows.Add(new DataRow("Errors"));
                errortable.Rows["Errors"].VisualProperties.StartIndexZero = true;
                results.Add(new Result(ErrorPlotResultName, errortable));
            }

            //color datapoints acording to classes variable (be it double, datetime or string)
            if (!problemData.Dataset.VariableNames.Contains(ClassesName))
            {
                dataRowIndices.Add("Training", problemData.TrainingIndices.ToList());
                dataRowIndices.Add("Test", problemData.TestIndices.ToList());
                return;
            }

            var classificationData = problemData as ClassificationProblemData;

            if (classificationData != null && classificationData.TargetVariable.Equals(ClassesName))
            {
                var classNames = classificationData.ClassValues.Zip(classificationData.ClassNames, (v, n) => new { v, n }).ToDictionary(x => x.v, x => x.n);
                var classes    = classificationData.Dataset.GetDoubleValues(classificationData.TargetVariable, allIndices).Select(v => classNames[v]).ToArray();
                for (var i = 0; i < classes.Length; i++)
                {
                    if (!dataRowIndices.ContainsKey(classes[i]))
                    {
                        dataRowIndices.Add(classes[i], new List <int>());
                    }
                    dataRowIndices[classes[i]].Add(i);
                }
            }
            else if (((Dataset)problemData.Dataset).VariableHasType <string>(ClassesName))
            {
                var classes = problemData.Dataset.GetStringValues(ClassesName, allIndices).ToArray();
                for (var i = 0; i < classes.Length; i++)
                {
                    if (!dataRowIndices.ContainsKey(classes[i]))
                    {
                        dataRowIndices.Add(classes[i], new List <int>());
                    }
                    dataRowIndices[classes[i]].Add(i);
                }
            }
            else if (((Dataset)problemData.Dataset).VariableHasType <double>(ClassesName))
            {
                var       clusterdata = new Dataset(problemData.Dataset.DoubleVariables, problemData.Dataset.DoubleVariables.Select(v => problemData.Dataset.GetDoubleValues(v, allIndices).ToList()));
                const int contours    = 8;
                Dictionary <int, string> contourMap;
                IClusteringModel         clusterModel;
                double[][] borders;
                CreateClusters(clusterdata, ClassesName, contours, out clusterModel, out contourMap, out borders);
                var contourorder = borders.Select((x, i) => new { x, i }).OrderBy(x => x.x[0]).Select(x => x.i).ToArray();
                for (var i = 0; i < contours; i++)
                {
                    var c           = contourorder[i];
                    var contourname = contourMap[c];
                    dataRowIndices.Add(contourname, new List <int>());
                    var row = new ScatterPlotDataRow(contourname, "", new List <Point2D <double> >())
                    {
                        VisualProperties = { Color = GetHeatMapColor(i, contours), PointSize = 8 }
                    };
                    ((ScatterPlot)results[ScatterPlotResultName].Value).Rows.Add(row);
                }
                var allClusters = clusterModel.GetClusterValues(clusterdata, Enumerable.Range(0, clusterdata.Rows)).ToArray();
                for (var i = 0; i < clusterdata.Rows; i++)
                {
                    dataRowIndices[contourMap[allClusters[i] - 1]].Add(i);
                }
            }
            else if (((Dataset)problemData.Dataset).VariableHasType <DateTime>(ClassesName))
            {
                var       clusterdata = new Dataset(problemData.Dataset.DateTimeVariables, problemData.Dataset.DateTimeVariables.Select(v => problemData.Dataset.GetDoubleValues(v, allIndices).ToList()));
                const int contours    = 8;
                Dictionary <int, string> contourMap;
                IClusteringModel         clusterModel;
                double[][] borders;
                CreateClusters(clusterdata, ClassesName, contours, out clusterModel, out contourMap, out borders);
                var contourorder = borders.Select((x, i) => new { x, i }).OrderBy(x => x.x[0]).Select(x => x.i).ToArray();
                for (var i = 0; i < contours; i++)
                {
                    var c           = contourorder[i];
                    var contourname = contourMap[c];
                    dataRowIndices.Add(contourname, new List <int>());
                    var row = new ScatterPlotDataRow(contourname, "", new List <Point2D <double> >())
                    {
                        VisualProperties = { Color = GetHeatMapColor(i, contours), PointSize = 8 }
                    };
                    row.VisualProperties.PointSize = 8;
                    ((ScatterPlot)results[ScatterPlotResultName].Value).Rows.Add(row);
                }
                var allClusters = clusterModel.GetClusterValues(clusterdata, Enumerable.Range(0, clusterdata.Rows)).ToArray();
                for (var i = 0; i < clusterdata.Rows; i++)
                {
                    dataRowIndices[contourMap[allClusters[i] - 1]].Add(i);
                }
            }
            else
            {
                dataRowIndices.Add("Training", problemData.TrainingIndices.ToList());
                dataRowIndices.Add("Test", problemData.TestIndices.ToList());
            }
        }
Example #8
0
        public override IOperation Apply()
        {
            var operation   = base.Apply();
            var paretoFront = TrainingBestSolutionsParameter.ActualValue;

            IResult     result;
            ScatterPlot qualityToTreeSize;

            if (!ResultCollection.TryGetValue("Pareto Front Analysis", out result))
            {
                qualityToTreeSize = new ScatterPlot("Quality vs Tree Size", "");
                qualityToTreeSize.VisualProperties.XAxisMinimumAuto = false;
                qualityToTreeSize.VisualProperties.XAxisMaximumAuto = false;
                qualityToTreeSize.VisualProperties.YAxisMinimumAuto = false;
                qualityToTreeSize.VisualProperties.YAxisMaximumAuto = false;

                qualityToTreeSize.VisualProperties.XAxisMinimumFixedValue = 0;
                qualityToTreeSize.VisualProperties.XAxisMaximumFixedValue = MaximumSymbolicExpressionTreeLengthParameter.ActualValue.Value;
                qualityToTreeSize.VisualProperties.YAxisMinimumFixedValue = 0;
                qualityToTreeSize.VisualProperties.YAxisMaximumFixedValue = 2;
                ResultCollection.Add(new Result("Pareto Front Analysis", qualityToTreeSize));
            }
            else
            {
                qualityToTreeSize = (ScatterPlot)result.Value;
            }


            int previousTreeLength = -1;
            var sizeParetoFront    = new LinkedList <ISymbolicRegressionSolution>();

            foreach (var solution in paretoFront.OrderBy(s => s.Model.SymbolicExpressionTree.Length))
            {
                int treeLength = solution.Model.SymbolicExpressionTree.Length;
                if (!sizeParetoFront.Any())
                {
                    sizeParetoFront.AddLast(solution);
                }
                if (solution.TrainingNormalizedMeanSquaredError < sizeParetoFront.Last.Value.TrainingNormalizedMeanSquaredError)
                {
                    if (treeLength == previousTreeLength)
                    {
                        sizeParetoFront.RemoveLast();
                    }
                    sizeParetoFront.AddLast(solution);
                }
                previousTreeLength = treeLength;
            }

            qualityToTreeSize.Rows.Clear();
            var trainingRow = new ScatterPlotDataRow("Training NMSE", "", sizeParetoFront.Select(x => new Point2D <double>(x.Model.SymbolicExpressionTree.Length, x.TrainingNormalizedMeanSquaredError, x)));

            trainingRow.VisualProperties.PointSize = 8;
            qualityToTreeSize.Rows.Add(trainingRow);

            if (AnalyzeTestError)
            {
                var testRow = new ScatterPlotDataRow("Test NMSE", "",
                                                     sizeParetoFront.Select(x => new Point2D <double>(x.Model.SymbolicExpressionTree.Length, x.TestNormalizedMeanSquaredError, x)));
                testRow.VisualProperties.PointSize = 8;
                qualityToTreeSize.Rows.Add(testRow);
            }

            var validationPartition = ValidationPartitionParameter.ActualValue;

            if (validationPartition.Size != 0)
            {
                var problemData       = ProblemDataParameter.ActualValue;
                var validationIndizes = Enumerable.Range(validationPartition.Start, validationPartition.Size).ToList();
                var targetValues      = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, validationIndizes).ToList();
                OnlineCalculatorError error;
                var validationRow = new ScatterPlotDataRow("Validation NMSE", "",
                                                           sizeParetoFront.Select(x => new Point2D <double>(x.Model.SymbolicExpressionTree.Length,
                                                                                                            OnlineNormalizedMeanSquaredErrorCalculator.Calculate(targetValues, x.GetEstimatedValues(validationIndizes), out error))));
                validationRow.VisualProperties.PointSize = 7;
                qualityToTreeSize.Rows.Add(validationRow);
            }

            return(operation);
        }
Example #9
0
        public static ScatterPlot CreateScatterPlot(IFilteredPreprocessingData preprocessingData, string variableNameX, string variableNameY, string variableNameGroup = "-", LegendOrder legendOrder = LegendOrder.Alphabetically)
        {
            ScatterPlot scatterPlot = new ScatterPlot();

            IList <double> xValues = preprocessingData.GetValues <double>(preprocessingData.GetColumnIndex(variableNameX));
            IList <double> yValues = preprocessingData.GetValues <double>(preprocessingData.GetColumnIndex(variableNameY));

            var points      = xValues.Zip(yValues, (x, y) => new Point2D <double>(x, y)).ToList();
            var validPoints = points.Where(p => !double.IsNaN(p.X) && !double.IsNaN(p.Y) && !double.IsInfinity(p.X) && !double.IsInfinity(p.Y)).ToList();

            if (validPoints.Any())
            {
                try {
                    double axisMin, axisMax, axisInterval;
                    ChartUtil.CalculateOptimalAxisInterval(validPoints.Min(p => p.X), validPoints.Max(p => p.X), out axisMin, out axisMax, out axisInterval);
                    scatterPlot.VisualProperties.XAxisMinimumAuto       = false;
                    scatterPlot.VisualProperties.XAxisMaximumAuto       = false;
                    scatterPlot.VisualProperties.XAxisMinimumFixedValue = axisMin;
                    scatterPlot.VisualProperties.XAxisMaximumFixedValue = axisMax;
                } catch (ArgumentOutOfRangeException) { } // error during CalculateOptimalAxisInterval
                try {
                    double axisMin, axisMax, axisInterval;
                    ChartUtil.CalculateOptimalAxisInterval(validPoints.Min(p => p.Y), validPoints.Max(p => p.Y), out axisMin, out axisMax, out axisInterval);
                    scatterPlot.VisualProperties.YAxisMinimumAuto       = false;
                    scatterPlot.VisualProperties.YAxisMaximumAuto       = false;
                    scatterPlot.VisualProperties.YAxisMinimumFixedValue = axisMin;
                    scatterPlot.VisualProperties.YAxisMaximumFixedValue = axisMax;
                } catch (ArgumentOutOfRangeException) { } // error during CalculateOptimalAxisInterval
            }


            //No Grouping
            if (string.IsNullOrEmpty(variableNameGroup) || variableNameGroup == "-")
            {
                ScatterPlotDataRow scdr = new ScatterPlotDataRow(variableNameX + " - " + variableNameY, "", validPoints);
                scdr.VisualProperties.IsVisibleInLegend = false;
                scatterPlot.Rows.Add(scdr);
                return(scatterPlot);
            }

            //Grouping
            int groupVariableIndex = preprocessingData.GetColumnIndex(variableNameGroup);
            var groupingValues     = Enumerable.Empty <string>();

            if (preprocessingData.VariableHasType <double>(groupVariableIndex))
            {
                groupingValues = preprocessingData.GetValues <double>(groupVariableIndex).Select(x => x.ToString());
            }
            else if (preprocessingData.VariableHasType <string>(groupVariableIndex))
            {
                groupingValues = preprocessingData.GetValues <string>(groupVariableIndex);
            }
            else if (preprocessingData.VariableHasType <DateTime>(groupVariableIndex))
            {
                groupingValues = preprocessingData.GetValues <DateTime>(groupVariableIndex).Select(x => x.ToString());
            }
            var groups = groupingValues.Zip(validPoints, Tuple.Create).GroupBy(t => t.Item1, t => t.Item2);

            if (legendOrder == LegendOrder.Alphabetically)
            {
                groups = groups.OrderBy(x => x.Key, new NaturalStringComparer());
            }

            foreach (var group in groups)
            {
                var scdr = new ScatterPlotDataRow {
                    Name             = group.Key,
                    VisualProperties =
                    {
                        IsVisibleInLegend = true,
                        PointSize         = 6
                    }
                };
                scdr.Points.AddRange(group);
                scatterPlot.Rows.Add(scdr);
            }
            return(scatterPlot);
        }
 private void FillSeriesWithRowValues(Series series, ScatterPlotDataRow row) {
   for (int i = 0; i < row.Points.Count; i++) {
     var value = row.Points[i];
     DataPoint point = new DataPoint();
     if (IsInvalidValue(value.X) || IsInvalidValue(value.Y))
       point.IsEmpty = true;
     else {
       point.XValue = value.X;
       point.YValues = new double[] { value.Y };
     }
     series.Points.Add(point);
   }
 }
    private void ConfigureSeries(Series series, ScatterPlotDataRow row) {
      series.BorderWidth = 1;
      series.BorderDashStyle = ChartDashStyle.Solid;
      series.BorderColor = Color.Empty;

      if (row.VisualProperties.Color != Color.Empty)
        series.Color = row.VisualProperties.Color;
      else series.Color = Color.Empty;
      series.IsVisibleInLegend = row.VisualProperties.IsVisibleInLegend;
      series.ChartType = SeriesChartType.FastPoint;
      series.MarkerSize = row.VisualProperties.PointSize;
      series.MarkerStyle = ConvertPointStyle(row.VisualProperties.PointStyle);
      series.XAxisType = AxisType.Primary;
      series.YAxisType = AxisType.Primary;

      if (row.VisualProperties.DisplayName.Trim() != String.Empty) series.LegendText = row.VisualProperties.DisplayName;
      else series.LegendText = row.Name;

      string xAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.XAxisTitle)
                      ? "X"
                      : Content.VisualProperties.XAxisTitle;
      string yAxisTitle = string.IsNullOrEmpty(Content.VisualProperties.YAxisTitle)
                            ? "Y"
                            : Content.VisualProperties.YAxisTitle;
      series.ToolTip =
        series.LegendText + Environment.NewLine +
        xAxisTitle + " = " + "#VALX," + Environment.NewLine +
        yAxisTitle + " = " + "#VAL";
    }
Example #12
0
        public static void PruningChart(RegressionNodeTreeModel tree, ComplexityPruning pruning, ResultCollection results)
        {
            var nodes = new Queue <RegressionNodeModel>();

            nodes.Enqueue(tree.Root);
            var max       = 0.0;
            var strenghts = new SortedList <double, int>();

            while (nodes.Count > 0)
            {
                var n = nodes.Dequeue();

                if (n.IsLeaf)
                {
                    max++;
                    continue;
                }

                if (!strenghts.ContainsKey(n.PruningStrength))
                {
                    strenghts.Add(n.PruningStrength, 0);
                }
                strenghts[n.PruningStrength]++;
                nodes.Enqueue(n.Left);
                nodes.Enqueue(n.Right);
            }
            if (strenghts.Count == 0)
            {
                return;
            }

            var plot = new ScatterPlot("Pruned Sizes", "")
            {
                VisualProperties =
                {
                    XAxisTitle             = "Pruning Strength",
                    YAxisTitle             = "Tree Size",
                    XAxisMinimumAuto       = false,
                    XAxisMinimumFixedValue = 0
                }
            };
            var row = new ScatterPlotDataRow("TreeSizes", "", new List <Point2D <double> >());

            row.Points.Add(new Point2D <double>(pruning.PruningStrength, max));

            var fillerDots = new Queue <double>();
            var minX       = pruning.PruningStrength;
            var maxX       = strenghts.Last().Key;
            var size       = (maxX - minX) / 200;

            for (var x = minX; x <= maxX; x += size)
            {
                fillerDots.Enqueue(x);
            }

            foreach (var strenght in strenghts.Keys)
            {
                while (fillerDots.Count > 0 && strenght > fillerDots.Peek())
                {
                    row.Points.Add(new Point2D <double>(fillerDots.Dequeue(), max));
                }
                max -= strenghts[strenght];
                row.Points.Add(new Point2D <double>(strenght, max));
            }


            row.VisualProperties.PointSize = 6;
            plot.Rows.Add(row);
            results.AddOrUpdateResult("PruningSizes", plot);
        }