/// <summary> /// Retrieves the data points of the current active plot. /// </summary> /// <param name="ctrl">The graph controller which controls the graph from which the points are to retrieve.</param> /// <param name="xarr">The array of the data point's x values.</param> /// <param name="yarr">The array of the data point's y values.</param> /// <returns>Null if all is ok, or error message if not.</returns> public static string GetActivePlotPoints(Altaxo.Gui.Graph.Gdi.Viewing.IGraphController ctrl, out double[] xarr, out double[] yarr) { var xlist = new List <double>(); var ylist = new List <double>(); xarr = yarr = null; ctrl.EnsureValidityOfCurrentLayerNumber(); ctrl.EnsureValidityOfCurrentPlotNumber(); var xylayer = ctrl.ActiveLayer as XYPlotLayer; if (null == xylayer || ctrl.CurrentPlotNumber < 0) { return("No active plot available"); } IGPlotItem plotItem = xylayer.PlotItems.Flattened[ctrl.CurrentPlotNumber]; var xyPlotItem = plotItem as XYColumnPlotItem; if (xyPlotItem == null) { return("No active plot!"); } XYColumnPlotData data = xyPlotItem.XYColumnPlotData; if (data == null) { return("Active plot item has no data"); } if (!(data.XColumn is Altaxo.Data.INumericColumn) || !(data.YColumn is Altaxo.Data.INumericColumn)) { return("X-Y values of plot data are not both numeric"); } var xcol = (Altaxo.Data.INumericColumn)data.XColumn; var ycol = (Altaxo.Data.INumericColumn)data.YColumn; int maxRowIndex = data.GetMaximumRowIndexFromDataColumns(); foreach (int i in data.DataRowSelection.GetSelectedRowIndicesFromTo(0, maxRowIndex, data.DataTable?.DataColumns, maxRowIndex)) { double x = xcol[i]; double y = ycol[i]; if (double.IsNaN(x) || double.IsNaN(y)) { continue; } xlist.Add(x); ylist.Add(y); } xarr = xlist.ToArray(); yarr = ylist.ToArray(); return(null); }
/// <summary> /// Get the names of the x and y column of the active plot. /// </summary> /// <param name="ctrl">The current active graph controller.</param> /// <returns>An array of two strings. The first string is the name of the x-column, the second /// the name of the y-column.</returns> public static string[] GetActivePlotName(Altaxo.Gui.Graph.Gdi.Viewing.IGraphController ctrl) { string[] result = new string[2] { string.Empty, string.Empty }; var xylayer = ctrl.ActiveLayer as XYPlotLayer; if (null == xylayer || ctrl.CurrentPlotNumber < 0) { return(result); } IGPlotItem plotItem = xylayer.PlotItems.Flattened[ctrl.CurrentPlotNumber]; var xyPlotItem = plotItem as XYColumnPlotItem; if (xyPlotItem == null) { return(result); } XYColumnPlotData data = xyPlotItem.XYColumnPlotData; if (data == null) { return(result); } result[0] = data.XColumn.FullName; result[1] = data.YColumn.FullName; return(result); }
public static string ShowFitDialog(Altaxo.Gui.Graph.Gdi.Viewing.IGraphController ctrl) { var tuple = SelectFitDocument(ctrl); if (!string.IsNullOrEmpty(tuple.Item1)) { return(tuple.Item1); } var fitDocument = tuple.Item2; var fitDocumentIdentifier = tuple.Item3; var activeLayer = tuple.Item4; // we assume we have a fit document by now if (null == tuple.Item2) { throw new InvalidProgramException("At this place, fit document should always be != null"); } if (!string.IsNullOrEmpty(fitDocumentIdentifier)) { var answer = Current.Gui.YesNoCancelMessageBox( "At least one fit function plot item was found in the document from which the fit document could be retrieved.\r\n" + "When changing the fit or the parameters, these fit function plot items would be changed, too.\r\n" + "Sometimes, you might want to keep the previous fit function plot items, e.g. in order to compare them with the new ones.\r\n" + "\r\n" + "Do you want to keep the previous fit function plot item(s) ?", "Keep previous fit function plot items?", false); if (null == answer) { return(null); } if (true == answer) { fitDocumentIdentifier = null; // by setting the identifier to null, we will keep the old fit functions } } var fitController = (Gui.IMVCANController)Current.Gui.GetControllerAndControl(new object[] { fitDocument, fitDocumentIdentifier, activeLayer }, typeof(Gui.IMVCANController)); // before showing the fit dialog, deselect all objects selected if (!ctrl.SelectedObjects.IsReadOnly) // with some graph tools, this is a read-only collection, which can not be cleared { ctrl.SelectedObjects.Clear(); } if (true == Current.Gui.ShowDialog(fitController, "Non-linear fitting")) { var localdoc = fitController.ModelObject as NonlinearFitDocument; // store the fit document in the graphs property ctrl.Doc.SetGraphProperty(FitDocumentPropertyName, localdoc); _lastFitDocument = (Altaxo.Calc.Regression.Nonlinear.NonlinearFitDocument)localdoc.Clone(); } return(null); }
public static string Fit(Altaxo.Gui.Graph.Gdi.Viewing.IGraphController ctrl, int order, double fitCurveXmin, double fitCurveXmax, bool showFormulaOnGraph) { string error; error = GetActivePlotPoints(ctrl, out var xarr, out var yarr); int numberOfDataPoints = xarr.Length; if (null != error) { return(error); } string[] plotNames = GetActivePlotName(ctrl); int numberOfParameter = order + 1; double[] parameter = new double[numberOfParameter]; var fit = LinearFitBySvd.FitPolymomialDestructive(order, xarr, yarr, null, numberOfDataPoints); // Output of results Current.Console.WriteLine(""); Current.Console.WriteLine("---- " + DateTime.Now.ToString() + " -----------------------"); Current.Console.WriteLine("Polynomial regression of order {0} of {1} over {2}", order, plotNames[1], plotNames[0]); Current.Console.WriteLine( "Name Value Error F-Value Prob>F"); for (int i = 0; i < fit.Parameter.Length; i++) { Current.Console.WriteLine("A{0,-3} {1,20} {2,20} {3,20} {4,20}", i, fit.Parameter[i], fit.StandardErrorOfParameter(i), fit.TofParameter(i), 1 - FDistribution.CDF(fit.TofParameter(i), numberOfParameter, numberOfDataPoints - 1) ); } Current.Console.WriteLine("R²: {0}, Adjusted R²: {1}", fit.RSquared, fit.AdjustedRSquared); Current.Console.WriteLine("Condition number: {0}, Loss of precision (digits): {1}", fit.ConditionNumber, Math.Log10(fit.ConditionNumber)); Current.Console.WriteLine("------------------------------------------------------------"); Current.Console.WriteLine("Source of Degrees of"); Current.Console.WriteLine("variation freedom Sum of Squares Mean Square F0 P value"); double regressionmeansquare = fit.RegressionCorrectedSumOfSquares / numberOfParameter; double residualmeansquare = fit.ResidualSumOfSquares / (numberOfDataPoints - numberOfParameter - 1); Current.Console.WriteLine("Regression {0,10} {1,20} {2,20} {3,20} {4,20}", numberOfParameter, fit.RegressionCorrectedSumOfSquares, fit.RegressionCorrectedSumOfSquares / numberOfParameter, regressionmeansquare / residualmeansquare, 1 - FDistribution.CDF(regressionmeansquare / residualmeansquare, numberOfParameter, numberOfDataPoints - 1) ); Current.Console.WriteLine("Residual {0,10} {1,20} {2,20}", numberOfDataPoints - 1 - numberOfParameter, fit.ResidualSumOfSquares, residualmeansquare ); Current.Console.WriteLine("Total {0,10} {1,20}", numberOfDataPoints - 1, fit.TotalCorrectedSumOfSquares ); Current.Console.WriteLine("------------------------------------------------------------"); // add the fit curve to the graph IScalarFunctionDD plotfunction = new PolynomialFunction(fit.Parameter); var fittedCurve = new XYFunctionPlotItem(new XYFunctionPlotData(plotfunction), new G2DPlotStyleCollection(LineScatterPlotStyleKind.Line, ctrl.Doc.GetPropertyContext())); var xylayer = ctrl.ActiveLayer as XYPlotLayer; if (null != xylayer) { xylayer.PlotItems.Add(fittedCurve); } return(null); }
/// <summary> /// Gets a new or recycled fit document for a given plot item <see cref="XYColumnPlotItem"/>. /// </summary> /// <param name="xyPlotItem">The xy plot item.</param> /// <param name="ctrl">The control.</param> /// <returns></returns> /// <exception cref="ArgumentNullException">xyPlotItem</exception> private static Tuple <string, NonlinearFitDocument, string, XYPlotLayer> GetNewFitDocumentFor(XYColumnPlotItem xyPlotItem, Altaxo.Gui.Graph.Gdi.Viewing.IGraphController ctrl) { if (null == xyPlotItem) { throw new ArgumentNullException(nameof(xyPlotItem)); } var activeLayer = Altaxo.Main.AbsoluteDocumentPath.GetRootNodeImplementing <XYPlotLayer>(xyPlotItem); var xColumn = xyPlotItem.XYColumnPlotData.XColumn; var yColumn = xyPlotItem.XYColumnPlotData.YColumn; if (xColumn == null || xColumn.ItemType != typeof(double)) { return(new Tuple <string, NonlinearFitDocument, string, XYPlotLayer>("The x-column is not numeric", null, null, activeLayer)); } if (yColumn == null || yColumn.ItemType != typeof(double)) { return(new Tuple <string, NonlinearFitDocument, string, XYPlotLayer>("The y-column is not numeric", null, null, activeLayer)); } var localdoc = (ctrl.Doc.GetGraphProperty(FitDocumentPropertyName) as Calc.Regression.Nonlinear.NonlinearFitDocument) ?? (Altaxo.Calc.Regression.Nonlinear.NonlinearFitDocument)_lastFitDocument?.Clone() ?? new Altaxo.Calc.Regression.Nonlinear.NonlinearFitDocument(); if (localdoc.FitEnsemble.Count == 0) // if there was no fit before { var fitele = new Altaxo.Calc.Regression.Nonlinear.FitElement( xyPlotItem.Data.DataTable, xyPlotItem.Data.GroupNumber, xyPlotItem.Data.DataRowSelection, xColumn, yColumn); localdoc.FitEnsemble.Add(fitele); } else // there was a fit before, thus localdoc.FitEnsemble.Count>0 { bool hasColumnsChanged = false; hasColumnsChanged |= !(object.ReferenceEquals(localdoc.FitEnsemble[0].DataTable, xyPlotItem.Data.DataTable)); hasColumnsChanged |= !(object.ReferenceEquals(localdoc.FitEnsemble[0].GroupNumber, xyPlotItem.Data.GroupNumber)); hasColumnsChanged |= !(object.ReferenceEquals(localdoc.FitEnsemble[0].IndependentVariables(0), xColumn)); hasColumnsChanged |= !(object.ReferenceEquals(localdoc.FitEnsemble[0].DependentVariables(0), yColumn)); localdoc.FitEnsemble[0].SetIndependentVariable(0, xColumn); localdoc.FitEnsemble[0].SetDependentVariable(0, yColumn); if (hasColumnsChanged) // if some of the columns has changed, take the data row selection of the plot item { localdoc.FitEnsemble[0].DataRowSelection = xyPlotItem.Data.DataRowSelection; } } return(new Tuple <string, NonlinearFitDocument, string, XYPlotLayer>(string.Empty, localdoc, null, activeLayer)); }
private static Tuple <string, NonlinearFitDocument, string, XYPlotLayer> SelectFitDocument(Altaxo.Gui.Graph.Gdi.Viewing.IGraphController ctrl) { XYPlotLayer activeLayer = null; // is a nonlinear fit function plot item selected ? var funcPlotItem = ctrl.SelectedRealObjects.OfType <XYNonlinearFitFunctionPlotItem>().FirstOrDefault(); if (null != funcPlotItem) { activeLayer = Altaxo.Main.AbsoluteDocumentPath.GetRootNodeImplementing <XYPlotLayer>(funcPlotItem); return(new Tuple <string, NonlinearFitDocument, string, XYPlotLayer>(string.Empty, funcPlotItem.FitDocumentCopy, funcPlotItem.FitDocumentIdentifier, activeLayer)); // if a fit function plot item was selected, then use the fit document of this item } // is a normal plot item selected ? // ------------------------------------------------------------------------------------ var columnPlotItem = ctrl.SelectedRealObjects.OfType <XYColumnPlotItem>().FirstOrDefault(); if (null != columnPlotItem) { return(SelectFitDocument(ctrl, columnPlotItem)); } // is the active layer an XY-plot layer ? Or do we have any XY-plot-layer ? // ------------------------------------------------------------------------------------ activeLayer = (ctrl.ActiveLayer as XYPlotLayer); if (null != activeLayer) { var result = SelectFitDocument(ctrl, activeLayer); if (result.Item2 != null) { return(result); } } // null != activeLayer activeLayer = TreeNodeExtensions.TakeFromHereToFirstLeaves(ctrl.Doc.RootLayer).OfType <XYPlotLayer>().FirstOrDefault(); if (null != activeLayer) { var result = SelectFitDocument(ctrl, activeLayer); if (result.Item2 != null) { return(result); } else { var localdoc = (ctrl.Doc.GetGraphProperty(FitDocumentPropertyName) as Calc.Regression.Nonlinear.NonlinearFitDocument) ?? (Altaxo.Calc.Regression.Nonlinear.NonlinearFitDocument)_lastFitDocument?.Clone() ?? new Altaxo.Calc.Regression.Nonlinear.NonlinearFitDocument(); return(new Tuple <string, NonlinearFitDocument, string, XYPlotLayer>(null, localdoc, null, activeLayer)); } } // null != activeLayer // no idea what to fit - there is not even an XY plot layer return(new Tuple <string, NonlinearFitDocument, string, XYPlotLayer>("The graph has no XYPlotLayer to host any fit function", null, null, null)); }