C# .Net Program to Find The Best Multiple Regression

For Three Variables

by Namir Shammas

The following program calculates the the best model and statistical coefficients for the following model:

H(Y) = A + B F(X1) + C G(X2)

Where X1, and X2 are independent variables and Y is the dependent variable. In addition, H(), F(), and G() are transformation functions for the regression variables. The program also calculates the coefficient of determination R-Square.

The program performs different transformations on all the variables. These transformations include:

The program attempts to fit a a large combination of different curves. For data that have only positive values, the program succeeds in calculating all different models. The presence of negative values and zeros will reduce the number of models tested. The application skips certain transformations for an entire data set if ANY value is zero and/or negative. The bypass prevents run-time errors. Skipping a transformation for an entire data set makes the models easier to compare since they all are based on the same number of observations.

Click here to download a ZIP file containing the project files for this program.

The Application Form

The program is a Windows application that has the following interface:

The above interface has the following controls:

  1. The Read Data button invokes an Open File dialog box and allows you to select the file containing the data. Once you select a text-based data file, the application displays the contents in the Results text box.
  2. The Best Regression button performs the task of searching for the best fit. The program displays the results in the Results text box. These results consist of the source filename, the current date/time stamp, the number of observations used, and a sorted list of regression statistics. Each list member consists of the coefficient of determination, model equation, slope, and intercept. The program sorts the list in descending order based on the values of the coefficient of determination. Thus, the application displays the best fits first and the worst fits last. In case of input or calculation errors, the application displays and error message box.
  3. The Save Results button allows you to save the contents of the Results text box to a file. You can also use this button to save the source data to a different file, possible after editing that data.
  4. The Close button closes the application after prompting you for a confirmation.
  5. The Help button offers a message box containing a modest help text. The message box prompts you if you want to see sample data in the Results text box.
  6. The text boxes labeled ShiftX1, ShiftX2, and ShiftY allow you to optionally specify shift values for the X1, X2, and Y data, respectively. Left blank, the application uses the default value of 0.
  7. The text boxes labeled ScaleX1, ScaleX2 and ScaleY allow you to optionally specify scale values for the X1, X2, and Y data, respectively. Left blank, the application uses the default value of 1.
  8. The Results multi-line text box serves the following three purposes:
    1. The display of the source data and allowing you to edit that data. If you edit the data and then click the Best Regression button, the program asks you if you want to save the data in the source file, before proceeding with the calculations. If you click Yes, the program updates the data file and proceeds with the calculations. If you click No, the program proceeds with the calculations using the original data in the source file.
    2. The display of the results of the regression calculations.
    3. The display of sample data created by the application's help.

The Data File

The application reads data from text files. Each line in the source  text file may be one of the following:

Here is an example of a data file:

Sample Data file
Created 1/31/2006
General format for a data line is (the Weight value is optional):
X1,X2,Y[,Weight]
Next we have X1 = 100, X2 = 23 and Y = 212
100,23,212
Notice leading spaces on next line
     10,50,43
The next line has a commented observation
! 33,45,67
25,77,98
Next data line has a weight value of 2 (X1 = 30, X2 = 45, Y = 86, and weight = 2)
30,45,86,2 

The application allows for flexible commenting throughout the text file and is able to extract the data. You can  add leading characters like !, #, or % as the first character of a comment line. This option may make it easier for the human eye to spot comment lines. It may also make it easier for a separate utility program to strip the comment lines.

One reason clicking the Read Data button displays the data is to allow you to double check the integrity of the data. If a data line has only one value, then the application generates flags an error. If a data line has more than 4 values, the program ignores the extra values and does not raise an error.

Scaling and Shifting Values

The application shifts and scales data using the following formulas:

X1' = ScaleX1 * (X1 - ShiftX1)

X2' = ScaleX2 * (X2 - ShiftX2)

Y' = ScaleY * (Y - ShiftY)

Keep the above equations in mind when you assign values for the shift and/or scale factors.

Offending Values?

Some of the mathematical transformations used take arguments that are only positive or only non-negative. In case you source data contains zeros and/or negative values, the application will avoid applying certain mathematical transformation to avoid causing run-time errors. Keep in mind that the program applies such avoidance to the entire data set and not just to those specific values that can cause error. You will notice the difference in the number of models display depending on your source data range. When using all-positive observations, the applications applies the entire set of transformations. When you have zeros or negative values, the application applies fewer transformations.

Here is a sample output:

The above output shows the first few best regression models. Here is the simple help message box:

The project file contains the following modules and classes of interest:

Here is the listing for class Form1:

using System.Diagnostics;
using System;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using Microsoft.VisualBasic;
using System.Data;
using System.Collections.Generic;
using System.IO;


namespace Best_ZYX_MLR
{
	public partial class Form1
	{
		public Form1()
		{
			InitializeComponent();
		}
		
		private string sDataFilename;
		private bool bEditMode;
		private bool bTextHasChanged;
		
		private void cmdCalc_Click(System.Object sender, System.EventArgs e)
		{
			CStatSum objLR;
			CResults objRes;
			int I;
			int N;
			int nDataCount = 0;
			// 			int N;
			// 			int nDataCount;
			double[] fShiftArr = new double[4];
			double[] fScaleArr = new double[4];
			string sBuffer;
			
			if (sDataFilename == "")
			{
				MessageBox.Show("Please select a data file first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
				return;
			}
			
			if (bTextHasChanged)
			{
				if (MessageBox.Show("Save changed data?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
				{
					File.WriteAllText(sDataFilename, txtRes.Text);
				}
				bTextHasChanged = false;
			}
			bEditMode = false;
			
			if (txtMaxResults.Text.Length > 0)
			{
				N = int.Parse(txtMaxResults.Text);
				if (N < 1)
				{
					N = 32000;
				}
			}
			else
			{
				N = 32000;
			}
			
			objLR = new CStatSum();
			objRes = new CResults();
			
			// check the Shift Y text box
			if (txtShiftY.Text.Length > 0)
			{
				fShiftArr[0] = double.Parse(txtShiftY.Text);
			}
			else
			{
				fShiftArr[0] = 0;
			}
			
			// check the Shift X1 text box
			if (txtShiftX1.Text.Length > 0)
			{
				fShiftArr[1] = double.Parse(txtShiftX1.Text);
			}
			else
			{
				fShiftArr[1] = 0;
			}
			
			// check the Shift X2 text box
			if (txtShiftX2.Text.Length > 0)
			{
				fShiftArr[2] = double.Parse(txtShiftX2.Text);
			}
			else
			{
				fShiftArr[2] = 0;
			}
			
			// check the Scale Y text box
			if (txtScaleY.Text.Length > 0)
			{
				fScaleArr[0] = double.Parse(txtScaleY.Text);
				if (fScaleArr[0] == 0)
				{
					fScaleArr[0] = 1;
				}
			}
			else
			{
				fScaleArr[0] = 1;
			}
			
			// check the Scale X1 text box
			if (txtScaleX1.Text.Length > 0)
			{
				fScaleArr[1] = double.Parse(txtScaleX1.Text);
				if (fScaleArr[1] == 0)
				{
					fScaleArr[1] = 1;
				}
			}
			else
			{
				fScaleArr[1] = 1;
			}
			
			// check the Scale X2 text box
			if (txtScaleX2.Text.Length > 0)
			{
				fScaleArr[2] = double.Parse(txtScaleX2.Text);
				if (fScaleArr[2] == 0)
				{
					fScaleArr[2] = 1;
				}
			}
			else
			{
				fScaleArr[2] = 1;
			}
			
			if (objLR.GetData(sDataFilename, ref nDataCount, ref fShiftArr, ref fScaleArr))
			{
				Cursor = Cursors.WaitCursor;
				objLR.FindBestFit(ref objRes);
				objRes.SortResults();
				sBuffer = "Source Data File: " + sDataFilename + "\r\n" + "\r\n";
				sBuffer = sBuffer + "Date/Time: " + DateTime.Now+ "\r\n" + "\r\n";
				sBuffer = sBuffer + "Nnmber of observations = " + nDataCount + "\r\n" + "\r\n";
				if (fScaleArr[0] != 1)
				{
					sBuffer = sBuffer + "Scale Y = " + fScaleArr[0] + "\r\n";
				}
				if (fShiftArr[0] != 0)
				{
					sBuffer = sBuffer + "Shift Y = " + fShiftArr[0] + "\r\n";
				}
				if (fScaleArr[1] != 1)
				{
					sBuffer = sBuffer + "Scale X1 = " + fScaleArr[1] + "\r\n";
				}
				if (fShiftArr[1] != 0)
				{
					sBuffer = sBuffer + "Shift X1 = " + fShiftArr[1] + "\r\n";
				}
				if (fScaleArr[2] != 1)
				{
					sBuffer = sBuffer + "Scale X2 = " + fScaleArr[2] + "\r\n";
				}
				if (fShiftArr[2] != 0)
				{
					sBuffer = sBuffer + "Shift X2 = " + fShiftArr[2] + "\r\n";
				}
				N = objRes.Count() > N ? N : objRes.Count();
				for (I = 0; I <= N - 1; I++)
				{
					sBuffer = sBuffer + "R-Sqr = " + objRes.GetR2(I).ToString() + "\r\n";
					sBuffer = sBuffer + "Model: " + objRes.GetModel(I) + "\r\n";
					sBuffer = sBuffer + "A  = " + objRes.GetIntercept(I).ToString() + ", B1 = " + objRes.GetSlope1(I).ToString() + ", B2 = " + objRes.GetSlope2(I).ToString() + "\r\n";
				}
				txtRes.Text = sBuffer;
				sBuffer = "";
				Cursor = Cursors.Default;
				cmdSaveRes.Enabled = true;
			}
			else
			{
				MessageBox.Show("Error in processing data", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		
		private void Form1_Load(System.Object sender, System.EventArgs e)
		{
			sDataFilename = "";
			cmdCalc.Enabled = false;
			cmdSaveRes.Enabled = false;
			bEditMode = false;
		}
		
		private void cmdReadData_Click(System.Object sender, System.EventArgs e)
		{
			dlgReadData.Filter = "All files (*.*)|*.*|Text files|*.txt|Data files (*.dat)|*.dat";
			if (dlgReadData.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				sDataFilename = dlgReadData.FileName;
				txtRes.Text = File.ReadAllText(sDataFilename);
				cmdCalc.Enabled = true;
				cmdSaveRes.Enabled = true;
				bTextHasChanged = false;
				bEditMode = true;
			}
		}
		
		private void cmdSaveRes_Click(System.Object sender, System.EventArgs e)
		{
			dlgSaveRes.Filter = "All files (*.*)|*.*|Text files|*.txt|Data files (*.dat)|*.dat";
			if (dlgSaveRes.ShowDialog() == System.Windows.Forms.DialogResult.OK)
			{
				File.WriteAllText(dlgSaveRes.FileName, txtRes.Text);
				bTextHasChanged = false;
			}
		}
		
		private void cmdClose_Click(System.Object sender, System.EventArgs e)
		{
			if (MessageBox.Show("Close application?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
			{
				Close();
			}
		}
		
		private void txtRes_TextChanged(System.Object sender, System.EventArgs e)
		{
			if (bEditMode)
			{
				bTextHasChanged = true;
			}
		}
		
		        private void cmdHelp_Click(System.Object sender, System.EventArgs e)
        {
            string sText;
            double x1, x2, x3;
            int i;
            Random r = new Random();

            sText = "Each line can be 1) empty, 2) a comment line or 3) a data line" + "\r\n" + 
                    "A data line has a pair of y and x values separated by a comma" + "\r\n" + 
                    "A data line can have a weight value that is appended after x and is separated by a comma" + "\r\n" + 
                    "Weights are optional and need to appear when their values are not 1" + "\r\n" + 
                    "A comment line must NOT start with any of the chatacters +-.0123456789" + "\r\n"  + 
                    "Show an example?";
            if (MessageBox.Show(sText, "Help", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == System.Windows.Forms.DialogResult.Yes)
            {
                bEditMode = false;
                sText = "Sample data (example of free form comment line)" + "\r\n" + 
                       "45,32,76" + "\r\n" + "67,34,98" + "\r\n" + 
                       "Next line is an observation that is temporaryly commente dout" + "\r\n" +
                       "! 56,23,44" + "\r\n";
                for (i = 1; i < 11; i++)
                {
                    x1 = (int)(200 * r.NextDouble());
                    x2 = (int)(200 * r.NextDouble());
                    x3 = (int)(200 * r.NextDouble());
                    sText = sText + x1.ToString() + "," + x2.ToString() +  "," + x3.ToString() + "\r\n";
                }
                txtRes.Text = sText;
            }			
		}
	}
	
}
Here is the listing for module TypeModule:

using System.Diagnostics;
using System;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using Microsoft.VisualBasic;
using System.Data;
using System.Collections.Generic;

namespace Best_ZYX_MLR
{
	sealed class TypeModule
	{
		public enum FitType
		{
			eLinear,
			eSquare,
			eCube,
			eCubeRoot,
			eRecip,
			eRecipCubeRoot,
			eRecipSquare,
			eRecipCube,
			eSqrt,
			eRecipSqrt,
			eLn
		}
		
		public struct ResType
		{
			public string m_sModel;
			public double m_fR2;
			public double m_fSlope1;
			public double m_fSlope2;
			public double m_fIntercept;
			public string m_sErr;
		}
	}
	
}

Here is the listing for class CErrors:

using System.Diagnostics;
using System;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using Microsoft.VisualBasic;
using System.Data;
using System.Collections.Generic;
using Microsoft.VisualBasic.CompilerServices;

namespace Best_ZYX_MLR
{
	public class CErrors
	{
		
		private string[] m_sErrors;
		private int m_nNumErrs;
		
		public CErrors()
		{
			Clear();
		}
		
		public void Add(string sErr)
		{
			m_sErrors = (string[]) Microsoft.VisualBasic.CompilerServices.Utils.CopyArray((Array) m_sErrors, new string[m_nNumErrs+1 + 1]);
			m_sErrors[m_nNumErrs] = sErr;
			m_nNumErrs++;
		}
		
		public int GetCount()
		{
			return m_nNumErrs;
		}
		
		public string GetErrText(int nIndex)
		{
			string sErrText = "Out of Bound Index";
			try
			{
				sErrText = m_sErrors[nIndex];
			}
			catch
			{
			}
			return sErrText;
		}
		
		public void Clear()
		{
			m_nNumErrs = 0;
			m_sErrors = new string[2];
		}
	}	
}

Here is the listing for class CResults::

using System.Diagnostics;
using System;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using Microsoft.VisualBasic;
using System.Data;
using System.Collections.Generic;
using Microsoft.VisualBasic.CompilerServices;

namespace Best_ZYX_MLR
{
	public class CResults
	{
		
		private int m_nResCount;
		private TypeModule.ResType[] m_uResRec;
		
		public CResults()
		{
			m_nResCount = 0;
		}
		
		public void Add(string sModel, double fR2, double fSlope1, double fSlope2, double fIntercept, string sErr)
		{
			m_uResRec = (TypeModule.ResType[]) Microsoft.VisualBasic.CompilerServices.Utils.CopyArray((Array) m_uResRec, new TypeModule.ResType[m_nResCount+1 + 1]);
			
			m_uResRec[m_nResCount].m_sModel = sModel;
			m_uResRec[m_nResCount].m_fR2 = fR2;
			m_uResRec[m_nResCount].m_fSlope1 = fSlope1;
			m_uResRec[m_nResCount].m_fSlope2 = fSlope2;
			m_uResRec[m_nResCount].m_fIntercept = fIntercept;
			m_uResRec[m_nResCount].m_sErr = sErr;
			m_nResCount++;
		}
		
		public void Clear()
		{
			m_nResCount = 0;
		}
		
		public int Count()
		{
			return m_nResCount;
		}
		
		public string GetModel(int nIndex)
		{
			return (nIndex > - 1 && nIndex < m_nResCount ? (m_uResRec[nIndex].m_sModel) : "");
		}
		
		public double GetR2(int nIndex)
		{
			return (nIndex > - 1 && nIndex < m_nResCount ? (m_uResRec[nIndex].m_fR2) : - 1);
		}
		
		public double GetSlope1(int nIndex)
		{
			return (nIndex > - 1 && nIndex < m_nResCount ? (m_uResRec[nIndex].m_fSlope1) : - 1.0E+30);
		}
		
		public double GetSlope2(int nIndex)
		{
			return (nIndex > - 1 && nIndex < m_nResCount ? (m_uResRec[nIndex].m_fSlope2) : - 1.0E+30);
		}
		
		public double GetIntercept(int nIndex)
		{
			return (nIndex > - 1 && nIndex < m_nResCount ? (m_uResRec[nIndex].m_fIntercept) : - 1.0E+30);
		}
		
		public string GetErr(int nIndex)
		{
			return (nIndex > - 1 && nIndex < m_nResCount ? (m_uResRec[nIndex].m_sErr) : "");
		}
		
		public void SortResults()
		{
			bool bInorder;
			int I;
			int J;
			int N;
			int nOffset;
			int nResetCounter;
			TypeModule.ResType uBuffer;
			
			N = m_nResCount;
			nOffset = N;
			nResetCounter = 0;
			do
			{
				nOffset = (5 * nOffset) / 11;
				if (nOffset == 0)
				{
					nOffset = 1;
				}
				bInorder = true;
				for (I = 0; I <= N - nOffset - 1; I++)
				{
					J = I + nOffset;
					if (m_uResRec[I].m_fR2 < m_uResRec[J].m_fR2)
					{
						uBuffer = m_uResRec[I];
						m_uResRec[I] = m_uResRec[J];
						m_uResRec[J] = uBuffer;
						bInorder = false;
					}
				}
				if (bInorder)
				{
					nResetCounter++;
				}
				if ((! bInorder) &&(nOffset == 1))
				{
					nOffset = N;
					for (I = 1; I <= nResetCounter; I++)
					{
						nOffset = (5 * nOffset) / 11;
					}
					if (nOffset == 0)
					{
						nOffset = 1;
					}
				}
			} while (!(nOffset == 1 && bInorder));
		}
	}	
}

Here is the listing for class CStatSum:

using System.Diagnostics;
using System;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using Microsoft.VisualBasic;
using System.Data;
using System.Collections.Generic;
using System.IO;
using Microsoft.VisualBasic.CompilerServices;


namespace Best_ZYX_MLR
{
	public class CStatSum
	{
		
		const double EPSILON = 0.0000000001;
		private const string DIGIT_MARKERS = "-+0123456789.";
		
		private bool m_bZeroX1;
		private bool m_bZeroX2;
		private bool m_bZeroY;
		private bool m_bNegX1;
		private bool m_bNegX2;
		private bool m_bNegY;
		
		private double m_fSum;
		private double m_fSumX11;
		private double m_fSumX21;
		private double m_fSumX22;
		private double m_fSumX12;
		private double m_fSumY;
		private double m_fSumY2;
		private double m_fSumX1Y;
		private double m_fSumX2Y;
		private double m_fSumX1X2;
		private double m_fMeanX1;
		private double m_fMeanX2;
		private double m_fMeanY;
		private double m_fSdevX1;
		private double m_fSdevX2;
		private double m_fSdevY;
		private double m_fSlope1;
		private double m_fSlope2;
		private double m_fIntercept;
		private double m_fR2;
		private string m_sTX1;
		private string m_sTX2;
		private string m_sTY;
		
		private int m_nDataCount;
		private double[] m_fX1;
		private double[] m_fX2;
		private double[] m_fY;
		private double[] m_fWt;
		
		
		public void InitSums()
		{
			m_fSum = 0;
			m_fSumX11 = 0;
			m_fSumX12 = 0;
			m_fSumX21 = 0;
			m_fSumX22 = 0;
			m_fSumY = 0;
			m_fSumY2 = 0;
			m_fSumX1Y = 0;
			m_fSumX2Y = 0;
			m_fSumX1X2 = 0;
			m_sTX1 = "";
			m_sTX2 = "";
			m_sTY = "";
		}
		
		public CStatSum()
		{
			InitSums();
		}
		
		public bool GetData(string sDataFilename, ref int nDataCount, ref double[] fShiftArr, ref double[] fScaleArr)
		{
			string sLine;
			string[] sLines;
			string[] sData;
			int I, J, K, N;
            bool bRes = true;
            char[] cDelim = new char[1];

			
			try
			{
				sLines = File.ReadAllLines(sDataFilename);
				
				nDataCount = sLines.GetUpperBound(0);
				
				// Dimension arrays for maximum capacity
				m_fX1 = new double[nDataCount + 1];
				m_fX2 = new double[nDataCount + 1];
				m_fY = new double[nDataCount + 1];
				m_fWt = new double[nDataCount + 1];
				
				J = 0;
				m_nDataCount = 0;
                cDelim[0] = ',';
				while (!(J == sLines.Length))
				{
					sLine = sLines[J].Trim(null);
					// is line not empty?
					if (sLine.Length > 0)
					{
						// is it NOT a comment?
						if (DIGIT_MARKERS.IndexOf(sLine.Substring(0, 1)) >= 0)
						{
                            sData = sLine.Split(cDelim);
							N = sData.GetUpperBound(0);
							m_fY[m_nDataCount] = double.Parse(sData[0]);
							m_fX1[m_nDataCount] = double.Parse(sData[1]);
							m_fX2[m_nDataCount] = double.Parse(sData[1]);
							if (N < 3)
							{
								m_fWt[m_nDataCount] = 1;
							}
							else
							{
								m_fWt[m_nDataCount] = double.Parse(sData[2]);
							}
							m_nDataCount++;
						}
					}
					J++;
				}
				
				// adjust arrays to actual number of data
				m_fX1 = (double[]) Microsoft.VisualBasic.CompilerServices.Utils.CopyArray((Array) m_fX1, new double[m_nDataCount + 1]);
				m_fX2 = (double[]) Microsoft.VisualBasic.CompilerServices.Utils.CopyArray((Array) m_fX2, new double[m_nDataCount + 1]);
				m_fY = (double[]) Microsoft.VisualBasic.CompilerServices.Utils.CopyArray((Array) m_fY, new double[m_nDataCount + 1]);
				m_fWt = (double[]) Microsoft.VisualBasic.CompilerServices.Utils.CopyArray((Array) m_fWt, new double[m_nDataCount + 1]);
				nDataCount = m_nDataCount;
				
				for (I = 0; I <= m_nDataCount - 1; I++)
				{
					m_fX1[I] = fScaleArr[1] *(m_fX1[I] - fShiftArr[1]);
					m_fX2[I] = fScaleArr[2] *(m_fX2[I] - fShiftArr[2]);
					m_fY[I] = fScaleArr[0] *(m_fY[I] - fShiftArr[0]);
				}
				
			}
			catch (Exception)
			{
				bRes = false;
			}
			
			return bRes;
			
		}
		
		private void Add(double X1, double X2, double Y, double Wt)
		{
			m_fSum = m_fSum + Wt;
			m_fSumX11 = m_fSumX11 + X1 * Wt;
			m_fSumX21 = m_fSumX21 + X2 * Wt;
			m_fSumX12 = m_fSumX12 + X1 * X1 * Wt;
			m_fSumX22 = m_fSumX22 + X2 * X2 * Wt;
			m_fSumY = m_fSumY + Y * Wt;
			m_fSumY2 = m_fSumY2 + Y * Y * Wt;
			m_fSumX1Y = m_fSumX1Y + X1 * Y * Wt;
			m_fSumX2Y = m_fSumX2Y + X2 * Y * Wt;
			m_fSumX1X2 = m_fSumX1X2 + X1 * X2 * Wt;
		}
		
		public void FindBestFit(ref CResults objRes)
		{
			int I;
			TypeModule.FitType ITX1;
			TypeModule.FitType ITX2;
			TypeModule.FitType ITY;
			bool bOK;
			double fXt1 = 0;
			double fXt2 = 0;
			double fYt = 0;
			CErrors objErrs;
			string sModel;
			string sErr;
			
			objErrs = new CErrors();
			try
			{
				m_bZeroX1 = false;
				m_bZeroX1 = false;
				m_bZeroY = false;
				m_bNegX1 = false;
				m_bNegX2 = false;
				m_bNegY = false;
				
				objRes.Clear();
				
				for (I = 0; I <= m_nDataCount - 1; I++)
				{
					if (m_fX1[I] < 0)
					{
						m_bNegX1 = true;
					}
					if (m_fX2[I] < 0)
					{
						m_bNegX2 = true;
					}
					if (m_fY[I] < 0)
					{
						m_bNegY = true;
					}
					if (Math.Abs(m_fX1[I]) < EPSILON)
					{
						m_bZeroX1 = true;
					}
					if (Math.Abs(m_fX2[I]) < EPSILON)
					{
						m_bZeroX2 = true;
					}
					if (Math.Abs(m_fY[I]) < EPSILON)
					{
						m_bZeroY = true;
					}
				}
				
				for (ITY = TypeModule.FitType.eLinear; ITY <= TypeModule.FitType.eLn; ITY++)
				{
					// validate transformations
					if (m_bZeroY && m_bNegY)
					{
						bOK = CanHandleZeroAndNegative(ITY);
					}
					else if (m_bZeroY)
					{
						bOK = CanHandleZero(ITY);
					}
					else if (m_bNegY)
					{
						bOK = CanHandleNegative(ITY);
					}
					else
					{
						bOK = true;
					}
					
					// Can proceed?
					if (bOK)
					{
						
						for (ITX1 = TypeModule.FitType.eLinear; ITX1 <= TypeModule.FitType.eLn; ITX1++)
						{
							
							// validate transformations
							if (m_bZeroX1 && m_bNegX1)
							{
								bOK = CanHandleZeroAndNegative(ITX1);
							}
							else if (m_bZeroX1)
							{
								bOK = CanHandleZero(ITX1);
							}
							else if (m_bNegX1)
							{
								bOK = CanHandleNegative(ITX1);
							}
							else
							{
								bOK = true;
							}
							
							// Can proceed?
							if (bOK)
							{
								
								for (ITX2 = TypeModule.FitType.eLinear; ITX2 <= TypeModule.FitType.eLn; ITX2++)
								{
									
									// validate transformations
									if (m_bZeroX2 && m_bNegX2)
									{
										bOK = CanHandleZeroAndNegative(ITX2);
									}
									else if (m_bZeroX2)
									{
										bOK = CanHandleZero(ITX2);
									}
									else if (m_bNegX2)
									{
										bOK = CanHandleNegative(ITX2);
									}
									else
									{
										bOK = true;
									}
									
									// lastly check if two transformations are the same
									if (ITX1 == ITX2)
									{
										bOK = false;
									}
									
									if (bOK)
									{
										
										// initialize summations
										InitSums();
										
										for (I = 0; I <= m_nDataCount - 1; I++)
										{
											
											switch (ITX1)
											{
												case TypeModule.FitType.eLinear:
													
													fXt1 = m_fX1[I];
													break;
												case TypeModule.FitType.eSquare:
													
													fXt1 = Math.Pow(m_fX1[I], 2);
													break;
												case TypeModule.FitType.eCube:
													
													fXt1 = Math.Pow(m_fX1[I], 3);
													break;
												case TypeModule.FitType.eCubeRoot:
													
													fXt1 = Math.Pow(m_fX1[I], (1.0 / 3.0));
													break;
												case TypeModule.FitType.eRecip:
													
													fXt1 = 1 / m_fX1[I];
													break;
												case TypeModule.FitType.eRecipCubeRoot:
													
													fXt1 = Math.Pow(1 / m_fX1[I], (1.0 / 3.0));
													break;
												case TypeModule.FitType.eRecipSquare:
													
													fXt1 = Math.Pow(1 / m_fX1[I], 2);
													break;
												case TypeModule.FitType.eRecipCube:
													
													fXt1 = Math.Pow(1 / m_fX1[I], 3);
													break;
												case TypeModule.FitType.eSqrt:
													
													fXt1 = Math.Sqrt(m_fX1[I]);
													break;
												case TypeModule.FitType.eRecipSqrt:
													
													fXt1 = 1 / Math.Sqrt(m_fX1[I]);
													break;
												case TypeModule.FitType.eLn:
													
													fXt1 = Math.Log(m_fX1[I]);
													break;
											}
											
											switch (ITX2)
											{
												case TypeModule.FitType.eLinear:
													
													fXt2 = m_fX2[I];
													break;
												case TypeModule.FitType.eSquare:
													
													fXt2 = Math.Pow(m_fX2[I], 2);
													break;
												case TypeModule.FitType.eCube:
													
													fXt2 = Math.Pow(m_fX2[I], 3);
													break;
												case TypeModule.FitType.eCubeRoot:
													
													fXt2 = Math.Pow(m_fX2[I], (1.0 / 3.0));
													break;
												case TypeModule.FitType.eRecip:
													
													fXt2 = 1 / m_fX2[I];
													break;
												case TypeModule.FitType.eRecipCubeRoot:
													
													fXt2 = Math.Pow(1 / m_fX2[I], (1.0 / 3.0));
													break;
												case TypeModule.FitType.eRecipSquare:
													
													fXt2 = Math.Pow(1 / m_fX2[I], 2);
													break;
												case TypeModule.FitType.eRecipCube:
													
													fXt2 = Math.Pow(1 / m_fX2[I], 3);
													break;
												case TypeModule.FitType.eSqrt:
													
													fXt2 = Math.Sqrt(m_fX2[I]);
													break;
												case TypeModule.FitType.eRecipSqrt:
													
													fXt2 = 1 / Math.Sqrt(m_fX2[I]);
													break;
												case TypeModule.FitType.eLn:
													
													fXt2 = Math.Log(m_fX2[I]);
													break;
											}
											
											
											switch (ITY)
											{
												case TypeModule.FitType.eLinear:
													
													fYt = m_fY[I];
													break;
												case TypeModule.FitType.eSquare:
													
													fYt = Math.Pow(m_fY[I], 2);
													break;
												case TypeModule.FitType.eCube:
													
													fYt = Math.Pow(m_fY[I], 3);
													break;
												case TypeModule.FitType.eCubeRoot:
													
													fYt = Math.Pow(m_fY[I], (1.0 / 3.0));
													break;
												case TypeModule.FitType.eRecip:
													
													fYt = 1 / m_fY[I];
													break;
												case TypeModule.FitType.eRecipCubeRoot:
													
													fYt = Math.Pow(1 / m_fY[I], (1.0 / 3.0));
													break;
												case TypeModule.FitType.eRecipSquare:
													
													fYt = Math.Pow(1 / m_fY[I], 2);
													break;
												case TypeModule.FitType.eRecipCube:
													
													fYt = Math.Pow(1 / m_fY[I], 3);
													break;
												case TypeModule.FitType.eSqrt:
													
													fYt = Math.Sqrt(m_fY[I]);
													break;
												case TypeModule.FitType.eRecipSqrt:
													
													fYt = 1 / Math.Sqrt(m_fY[I]);
													break;
												case TypeModule.FitType.eLn:
													
													fYt = Math.Log(m_fY[I]);
													break;
											}
											
											// add transformed data to statistical summations
											Add(fXt1, fXt2, fYt, m_fWt[I]);
											
										}
										
										// store transformation data
										m_sTX1 = SayTransform(ITX1, "X1");
										m_sTX2 = SayTransform(ITX2, "X2");
										m_sTY = SayTransform(ITY, "Y");
										sModel = m_sTY + " = A + B1 * " + m_sTX1 + " + B2 * " + m_sTX2;
										// calculate regression statistics and store in
										// object accessed by m_objRes
										object temp_object = objRes;
										Best_ZYX_MLR.CResults temp_Best_ZYX_MLRCResults =  (Best_ZYX_MLR.CResults) temp_object;
										CalcLR(ref temp_Best_ZYX_MLRCResults, ref objErrs);
										
										if (objErrs.GetCount() > 0)
										{
											sErr = objErrs.GetErrText(0);
										}
										else
										{
											sErr = "";
										}
										objErrs.Clear(); // reset error object
										
										objRes.Add(sModel, m_fR2, m_fSlope1, m_fSlope2, m_fIntercept, sErr);
										
									}
									
								}
								
							}
							
						}
						
					}
				}
				
			}
			catch (Exception ex)
			{
				objErrs.Add(ex.Message);
			}
			
		}

        private string SayTransform(TypeModule.FitType eVal, string sVar)
        {
            switch (eVal)
            {
                case TypeModule.FitType.eLinear:

                    return sVar;
                    break;
                case TypeModule.FitType.eSquare:
                    return sVar + "^2";
                    break;

                case TypeModule.FitType.eCube:
                    return sVar + "^3";
                    break;
                case TypeModule.FitType.eCubeRoot:
                    return sVar + "^1/3";
                    break;
                case TypeModule.FitType.eRecip:
                    return "1/" + sVar;
                    break;
                case TypeModule.FitType.eRecipCubeRoot:
                    return "1/" + sVar + "^1/3";
                    break;
                case TypeModule.FitType.eRecipSquare:
                    return "1/" + sVar + "^2";
                    break;
                case TypeModule.FitType.eRecipCube:
                    return "1/" + sVar + "^3";
                    break;
                case TypeModule.FitType.eSqrt:
                    return sVar + "^1/2";
                    break;
                case TypeModule.FitType.eRecipSqrt:
                    return "1/" + sVar + "^1/2";
                    break;
                case TypeModule.FitType.eLn:
                    return "Ln(" + sVar + ")";
                    break;
                default:
                    return "";
            }
        }

        private bool CanHandleZero(TypeModule.FitType eVal)
        {
            bool returnValue;

            switch (eVal)
            {
                case TypeModule.FitType.eLinear:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eSquare:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eCube:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eCubeRoot:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eSqrt:

                    returnValue = true;
                    break;
                default:

                    returnValue = false;
                    break;
            }
            return returnValue;
        }

        private bool CanHandleZeroAndNegative(TypeModule.FitType eVal)
        {
            bool returnValue;

            switch (eVal)
            {
                case TypeModule.FitType.eLinear:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eSquare:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eCube:

                    returnValue = true;
                    break;
                default:

                    returnValue = false;
                    break;
            }
            return returnValue;
        }

        private bool CanHandleNegative(TypeModule.FitType eVal)
        {
            bool returnValue;

            switch (eVal)
            {
                case TypeModule.FitType.eLinear:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eSquare:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eCube:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eRecip:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eRecipSquare:
                    returnValue = true;
                    break;

                case TypeModule.FitType.eRecipCube:

                    returnValue = true;
                    break;
                default:

                    returnValue = false;
                    break;
            }
            return returnValue;
        }

		
		
		private void CalcLR(ref CResults objRes, ref CErrors objErrs)
		{
			double A;
			double B;
			
			if (m_fSum < 2)
			{
				return;
			}
			// caluclate regression
			try
			{
				m_fMeanX1 = m_fSumX11 / m_fSum;
				m_fMeanX2 = m_fSumX21 / m_fSum;
				m_fMeanY = m_fSumY / m_fSum;
				m_fSdevX1 = Math.Sqrt((m_fSumX12 -Math.Pow(m_fSumX11, 2 )/ m_fSum) /(m_fSum - 1));
				m_fSdevX2 = Math.Sqrt((m_fSumX22 -Math.Pow(m_fSumX21, 2 )/ m_fSum) /(m_fSum - 1));
				m_fSdevY = Math.Sqrt((m_fSumY2 -Math.Pow(m_fSumY, 2 )/ m_fSum) /(m_fSum - 1));
				
				A = (m_fSum * m_fSumX12 -Math.Pow(m_fSumX11, 2)) *(m_fSum * m_fSumX2Y - m_fSumX21 * m_fSumY);
				B = (m_fSum * m_fSumX1X2 - m_fSumX11 * m_fSumX21) *(m_fSum * m_fSumX1Y - m_fSumX11 * m_fSumY);

                m_fSlope2 = (A - B) / (
                 (m_fSum * m_fSumX12 - m_fSumX11 * m_fSumX11) *
                 (m_fSum * m_fSumX22 - m_fSumX21 * m_fSumX21) -
                 Math.Pow(m_fSum * m_fSumX1X2 - m_fSumX11 * m_fSumX21, 2)
                 );
                m_fSlope1 = ((m_fSum * m_fSumX1Y - m_fSumX11 * m_fSumY) -
                 m_fSlope2 * (m_fSum * m_fSumX1X2 - m_fSumX11 * m_fSumX21)) /
                 (m_fSum * m_fSumX12 - m_fSumX11 * m_fSumX11);
                m_fIntercept = m_fMeanY - m_fSlope1 * m_fMeanX1 - m_fSlope2 * m_fMeanX2;
                m_fR2 = (m_fIntercept * m_fSumY + m_fSlope1 * m_fSumX1Y + m_fSlope2 * m_fSumX2Y -
                    m_fSumY * m_fSumY / m_fSum) / (m_fSumY2 - m_fSumY * m_fSumY / m_fSum);
				
			}
			catch (Exception ex)
			{
				objErrs.Add("Error in model " + m_sTY + " = A0 + A1 " + m_sTX1 + " + A2 " + m_sTX2 + "\r\n" + ex.Message);
			}
		}		
	}	
}

BACK

Copyright (c) Namir Shammas. All rights reserved.