示例#1
0
        /// <summary>
        /// Determines if the inputs match the specified mask.
        /// </summary>
        /// <param name="mask">Mask containing '1' (present) '0' (not present) and '-' (doesn't matter)</param>
        /// <returns>Whether inputs batch mask</returns>
        public bool CheckInputMask(string mask)
        {
            UiControls.Assert(InputNames.Length == mask.Length && mask.All(z => z == '0' || z == '1' || z == '-'), "Invalid quick calc check string.");

            for (int i = 0; i < InputNames.Length; i++)
            {
                char c = mask[i];

                switch (c)
                {
                case '0':
                    if (InputNames[i] != null)
                    {
                        return(false);
                    }
                    break;

                case '1':
                    if (InputNames[i] == null)
                    {
                        return(false);
                    }
                    break;

                case '-':
                default:
                    break;
                }
            }

            return(true);
        }
示例#2
0
        /// <summary>
        /// I wrote this when the program crashed after an hour.
        /// Intermediate results can now be saved to avoid losing data.
        /// </summary>
        private static ResultClusterer TryLoadIntermediateResult(Core core, Guid guid, int value, int repetition, ProgressReporter proggy)
        {
            string fileName = UiControls.GetTemporaryFile("." + value + "." + repetition + ".intermediate.dat", guid);

            if (!File.Exists(fileName))
            {
                return(null);
            }

            LookupByGuidSerialiser guidS = core.GetLookups();

            proggy.Enter("Loading saved results");
            ResultClusterer result;

            try
            {
                result = XmlSettings.LoadOrDefault <ResultClusterer>(new FileDescriptor(fileName, SerialisationFormat.MSerialiserFastBinary), null, guidS, proggy);
            }
            catch
            {
                proggy.Leave(); // Result will probably be NULL rather than throwing an exception
                return(null);
            }

            UiControls.Assert(!guidS.HasLookupTableChanged, "Didn't expect the GUID lookup table to change when loading data.");
            proggy.Leave();

            return(result);
        }
示例#3
0
        public override double QuickCalculate(IReadOnlyList <double> a, IReadOnlyList <double> b, object[] args)
        {
            UiControls.Assert(SupportsQuickCalculate, "Quick calculate called on a non quick-calculate script.");

            object[] inputs = { a, b, null, null, null, null, null, null, null, null };
            return(Arr.Instance.RunScriptDouble(_script, inputs, args));
        }
示例#4
0
文件: Arr.cs 项目: mjr129/metaboclust
        /// <summary>
        /// Gets the overlapping time range for the specified [groups].
        /// </summary>

        private static Range GetOverlappingTimeRange(Core core, IntensityMatrix source, IEnumerable <GroupInfo> groups)
        {
            int min = int.MinValue; // sic
            int max = int.MaxValue;

            foreach (GroupInfo type in groups)
            {
                int minA = int.MaxValue;
                int maxA = int.MinValue;

                foreach (ObservationInfo cond in source.Columns.Select(z => z.Observation))
                {
                    if (cond.Group == type)
                    {
                        minA = Math.Min(minA, cond.Time);
                        maxA = Math.Max(maxA, cond.Time);
                    }
                }

                UiControls.Assert(minA != int.MaxValue && maxA != int.MinValue, "Failed to determine overlapping time range (type).");

                min = Math.Max(minA, min);
                max = Math.Min(maxA, max);
            }

            UiControls.Assert(min != int.MinValue && max != int.MaxValue, "Failed to determine overlapping time range (all).");

            return(new Range(min, max));
        }
示例#5
0
        public ClustererScript(string script, string id, string name, string fileName)
            : base(id, name)
        {
            this._script = new RScript(script, INPUT_TABLE, fileName);

            UiControls.Assert(_script.IsInputPresent(0) || _script.IsInputPresent(1), "ClustererScript must take at least one of value matrix or distance matrix");

            _usesDistanceMatrix = this._script.CheckInputMask("01");

            Comment = "Clusters based on an R script.";
        }
示例#6
0
        /// <summary>
        /// Action completed - calculate statisstics
        /// </summary>
        internal void FinalizeResults(Core core, ConfigurationMetric metric, IntensityMatrix vmatrix, DistanceMatrix dmatrix, EClustererStatistics statistics, ProgressReporter prog)
        {
            UiControls.Assert(Assignments.IsEmpty(), "FinalizeResults on ClusterResults already called.");

            // Get ALL the assignments
            foreach (Cluster cluster in RealClusters)
            {
                Assignments.AddRange(cluster.Assignments.List);
            }

            RecalculateStatistics(core, metric, vmatrix, dmatrix, statistics, prog);
        }
        internal static ArgsClusterer Show(Form owner, Core core, ArgsClusterer def, bool readOnly, bool hideOptimise)
        {
            using (FrmEditConfigurationCluster frm = new FrmEditConfigurationCluster(core, def, readOnly, hideOptimise))
            {
                if (UiControls.ShowWithDim(owner, frm) == DialogResult.OK)
                {
                    UiControls.Assert(!readOnly, "Didn't expect an OK result from a readonly dialogue.");
                    return(frm.GetSelection());
                }

                return(null);
            }
        }
示例#8
0
        private void EndWait()
        {
            this._waitCounter--;
            UiControls.Assert(this._waitCounter >= 0, "EndWait called when no wait preceded.");

            if (this._waitCounter == 0)
            {
                this.toolStripStatusLabel2.Visible = false;
                this.UseWaitCursor                 = false;
                this._statusMain.BackColor         = this.BackColor;
                this.toolStripProgressBar1.Visible = false;
            }
        }
示例#9
0
        protected override object InterpretArgs(object[] args)
        {
            int medianWindow = (int)args[0];

            int medianRadius = (medianWindow - 1) / 2;

            if (medianRadius != 0)
            {
                UiControls.Assert((medianRadius * 2) == (medianWindow - 1), "Moving average window must be an odd number.");
            }

            return(medianRadius);
        }
示例#10
0
        void _listView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
        {
            if (this._suspendVirtual)
            {
                e.Item = new ListViewItem(new string[this._listView.Columns.Count]);
                return;
            }

            object tag = this._filteredList[e.ItemIndex];

            e.Item = this.CreateNewListViewItem(tag);
            this.DoUpdate(e.Item, tag);
            UiControls.Assert(e.Item.SubItems.Count == this._listView.Columns.Count, "ListViewItem doesn't have the expected number of subitems.");
        }
示例#11
0
        /// <summary>
        ///
        /// </summary>
        private int FindMatch(List <List <Assignment> > uniqueCombinations, List <Assignment> pats)
        {
            for (int index = 0; index < uniqueCombinations.Count; index++)
            {
                var list = uniqueCombinations[index];
                UiControls.Assert(list.Count == pats.Count, "FindMatch requires the lists to be of equal length.");

                if (IsEqual(pats, list))
                {
                    return(index);
                }
            }

            return(-1);
        }
        /// <summary>
        /// Shows the form.
        /// </summary>
        /// <param name="autoSave">Controls whether to apply to core.DefaultStatistics</param>
        internal static ObsFilter.Condition Show(Form owner, Core core, ObsFilter.Condition defaults, bool readOnly)
        {
            using (FrmEditObsFilterCondition frm = new FrmEditObsFilterCondition(owner, core, defaults, readOnly))
            {
                if (UiControls.ShowWithDim(owner, frm) == DialogResult.OK)
                {
                    var r = frm.GetSelection();
                    UiControls.Assert(r != null, "Expected selection to be present if dialogue returned OK.");

                    return(r);
                }

                return(null);
            }
        }
示例#13
0
        /// <summary>
        /// Shows the PeakFlag editor
        /// </summary>
        public static bool Show(Form owner, UserFlag flag, bool readOnly)
        {
            UiControls.Assert(flag != null, "flag must not be null");

            using (FrmEditUserFlag frm = new FrmEditUserFlag(flag, readOnly))
            {
                if (UiControls.ShowWithDim(owner, frm) == DialogResult.OK)
                {
                    frm._binder1.Commit();
                    flag.Comment = frm._comment;
                    return(true);
                }

                return(false);
            }
        }
示例#14
0
        /// <summary>
        /// Shows the form.
        /// </summary>
        /// <param name="autoSave">Controls whether to apply to core.DefaultStatistics</param>
        internal static PeakFilter.Condition Show(Form owner, Core core, PeakFilter.Condition defaults, bool readOnly)
        {
            using (FrmEditPeakFilterCondition frm = new FrmEditPeakFilterCondition(owner, core, defaults, readOnly))
            {
                if (UiControls.ShowWithDim(owner, frm) == DialogResult.OK)
                {
                    string err;
                    var    r = frm.GetSelection(out err);
                    UiControls.Assert(r != null, err);

                    return(r);
                }

                return(null);
            }
        }
示例#15
0
        /// <summary>
        ///
        /// </summary>
        private static bool IsEqual(List <Assignment> pats, List <Assignment> list)
        {
            for (int index = 0; index < pats.Count; index++)
            {
                Assignment v = pats[index];
                Assignment t = list[index];

                UiControls.Assert(v.Vector.Group == t.Vector.Group, "IsEqual expects the vector groups to match.");

                if (t.Cluster != v.Cluster)
                {
                    return(false);
                }
            }

            return(true);
        }
示例#16
0
文件: Arr.cs 项目: mjr129/metaboclust
        /// <summary>
        /// (Private) Applies the inputs of an RScript object.
        /// </summary>
        private void ApplyInputs(RScript script, object[] inputs)
        {
            UiControls.Assert(inputs.Length == script.InputNames.Length, "Number of inputs requested by script must match number of inputs provided.");

            for (int i = 0; i < inputs.Length; i++)
            {
                string name = script.InputNames[i];

                if (name != null)
                {
                    var obj = inputs[i];
                    SymbolicExpression sym;

                    if (obj is double[, ])
                    {
                        sym = _r.CreateNumericMatrix((double[, ])obj);
                    }
                    else if (obj is IReadOnlyList <double> )
                    {
                        sym = _r.CreateNumericVector((IReadOnlyList <double>)obj);
                    }
                    else if (obj is IEnumerable <double> )
                    {
                        sym = _r.CreateNumericVector((IEnumerable <double>)obj);
                    }
                    else if (obj is int[])
                    {
                        sym = _r.CreateIntegerVector((int[])obj);
                    }
                    else if (obj is int[, ])
                    {
                        sym = _r.CreateIntegerMatrix((int[, ])obj);
                    }
                    else if (obj is IEnumerable <int> )
                    {
                        sym = _r.CreateIntegerVector((IEnumerable <int>)obj);
                    }
                    else
                    {
                        throw new InvalidOperationException("Cannot create R object for obj: " + obj);
                    }

                    _r.SetSymbol(name, sym);
                }
            }
        }
示例#17
0
        /// <summary>
        /// Returns the minimum ([v] = -1) or maximum ([v] = 1) of vars.
        /// </summary>
        private static T GenericMax <T>(IEnumerable <T> vars, int v) where T : IComparable
        {
            bool assigned = false;
            T    result   = default(T);

            foreach (T var in vars)
            {
                if (!assigned || var.CompareTo(result) == v)
                {
                    assigned = true;
                    result   = var;
                }
            }

            UiControls.Assert(assigned, "Maths.GenericMax: Expects at least one value.");

            return(result);
        }
        /// <summary>
        ///
        /// </summary>
        private static Assignment GetMostDistantAssignment(IEnumerable <Cluster> clusters)
        {
            Assignment furthest = null;

            foreach (Cluster cluster in clusters)
            {
                foreach (var assignment in cluster.Assignments.List)
                {
                    if (furthest == null || assignment.Score > furthest.Score)
                    {
                        furthest = assignment;
                    }
                }
            }

            UiControls.Assert(furthest != null, "GetMostDistant: Failed to find furthest assignment.");

            return(furthest);
        }
示例#19
0
文件: Arr.cs 项目: mjr129/metaboclust
        /// <summary>
        /// (Private) Applies the arguments of an RScript object.
        /// </summary>
        private void ApplyArgs(RScript script, object[] args)
        {
            args = args ?? new object[0]; // Todo: Necessary for legacy only

            UiControls.Assert((args.Length != 0) == script.RequiredParameters.HasCustomisableParams, "No arguments provided when algorithm has customisable parameters, or arguments provided when the algorithm has no customisable parameters.");

            if (script.RequiredParameters.HasCustomisableParams)
            {
                var req = script.RequiredParameters;

                UiControls.Assert(req.Count == args.Length, "Argument count passed in doesn't match the count expected by the script");

                for (int i = 0; i < req.Count; i++)
                {
                    var    p = req[i];
                    object v = args[i];

                    p.Type.SetSymbol(_r, p.Name, v);
                }
            }
        }
示例#20
0
        /// <summary>
        /// Returns the absolute minimum ([v] = -1) or maximum ([v] = 1) of vars.
        /// </summary>
        private static double AbsMax(IEnumerable <double> vars, int v)
        {
            bool   assigned  = false;
            double absResult = 0;
            double result    = 0;

            foreach (double val in vars)
            {
                double absVal = Math.Abs(val);

                if (!assigned || absVal.CompareTo(absResult) == v)
                {
                    assigned  = true;
                    absResult = absVal;
                    result    = val;
                }
            }

            UiControls.Assert(assigned, "Maths.GenericMax: Expects at least one value.");

            return(result);
        }
示例#21
0
        /// <summary>
        /// Calculate the median of matrix "S".
        /// Exclude the diagonal from the calculation.
        /// </summary>
        private static double ApMedian(double[,] s)
        {
            // Flatten the matrix and remove the diagnonal
            double[] flat = new double[s.Length - s.GetLength(0)];
            int      k    = 0;

            for (int i = 0; i < s.GetLength(0); i++)
            {
                for (int j = 0; j < s.GetLength(1); j++)
                {
                    if (i != j)
                    {
                        flat[k] = s[i, j];
                        k++;
                    }
                }
            }

            UiControls.Assert(k == flat.Length, "ApMedian count mismatch. Maybe the matrix wasn't square.");

            return(Maths.Median(flat));
        }
示例#22
0
        /// <summary>
        /// My interpretation of the local clustering distance metric as detailed by Qian et al.
        ///
        /// Jiang Qian, Marisa Dolled-Filhart, Jimmy Lin, Haiyuan Yu, and Mark Gerstein.
        /// Beyond synexpression relationships: local clustering of time-shifted and inverted
        /// gene expression profiles identifies new, biologically relevant interactions. Journal of
        /// molecular biology, 314(5):1053–1066, 2001.
        /// </summary>
        public static double Qian(double[] X, double[] y)
        {
            UiControls.Assert(X.Length == y.Length, "Qian metric, X and Y lengths differ");

            X = Normalise(X);
            y = Normalise(y);

            int c = X.Length;

            // Score matrix M
            // Mij = Xi * Yj
            double[,] M = new double[c, c];

            for (int i = 0; i < c; i++)
            {
                for (int j = 0; j < c; j++)
                {
                    M[i, j] = X[i] * y[j];
                }
            }

            // Sum matrices D/E
            // Eij = max( Mij * Ei-1j-1 , 0)
            double[,] D = new double[c, c];
            double[,] E = new double[c, c];

            for (int i = 0; i < c; i++)
            {
                for (int j = 0; j < c; j++)
                {
                    double d;
                    double e;

                    if (i != 0 && j != 0)
                    {
                        d = D[i - 1, j - 1];
                        e = E[i - 1, j - 1];
                    }
                    else
                    {
                        d = 0d;
                        e = 0d;
                    }

                    D[i, j] = Math.Max(d - M[i, j], 0d);
                    E[i, j] = Math.Max(e + M[i, j], 0d);
                }
            }

            // Match score s
            double s = E[0, 0];

            for (int i = 0; i < c; i++)
            {
                s = Maths.AbsMax(s, E[i, c - 1]);
                s = Maths.AbsMax(s, E[c - 1, i]);
                s = Maths.AbsMax(s, D[c - 1, i]);
                s = Maths.AbsMax(s, D[c - 1, i]);
            }

            return(s);
        }
示例#23
0
        private void PerformSelectionActions()
        {
            // Series are tagged with the variables they represent
            // When multiple series are selected they all have the same variable so no worries about using the first one
            Peak peak = this._chart.SelectedItem.SelectedSeries != null ? (Peak)this._chart.SelectedItem.SelectedSeries.Tag : null;

            // Points are tagged with the observation
            IntensityInfo dataPoint;

            if (this._chart.SelectedItem.DataPoint != null)
            {
                if (this._chart.SelectedItem.DataPoint.Tag is IntensityInfo)
                {
                    dataPoint = (IntensityInfo)this._chart.SelectedItem.DataPoint.Tag;
                }
                else if (this._chart.SelectedItem.DataPoint.Tag is IntensityInfo[])
                {
                    IntensityInfo[] dataPointArray = (IntensityInfo[])this._chart.SelectedItem.DataPoint.Tag;
                    dataPoint = dataPointArray != null ? dataPointArray[this._chart.SelectedItem.YIndex] : default(IntensityInfo);
                }
                else
                {
                    UiControls.Assert(false, "Unexpected data point format.");
                    dataPoint = null;
                }
            }
            else
            {
                // Clear selection
                dataPoint = null;
            }

            if (this.SelectionChanged != null)
            {
                string name;

                if (this._chart.SelectedItem.Series.Length != 0)
                {
                    // Select the first series names as all series are usually similar
                    name = this._chart.SelectedItem.Series[0].Name;

                    // Trim off everything after the | as this is just used so the chart doesn't complain
                    if (name.Contains("|"))
                    {
                        name = name.Substring(0, name.LastIndexOf('|')).Trim();
                    }
                }
                else
                {
                    name = null;
                }

                ChartSelectionEventArgs e = new ChartSelectionEventArgs(peak, dataPoint, name);
                this.SelectionChanged(this, e);
            }

            // Update text
            if (this._mnuSelectedPeak != null)
            {
                if (peak != null && this._chkShowPeak.Checked)
                {
                    this._mnuSelectedPeak.Text    = peak.DisplayName;
                    this._mnuSelectedPeak.Image   = UiControls.GetImage(((Visualisable)peak).Icon, true);
                    this._mnuSelectedPeak.Visible = true;
                }
                else
                {
                    this._mnuSelectedPeak.Visible = false;
                }

                if (dataPoint != null)
                {
                    if (dataPoint.Rep.HasValue && this._chkShowReplicate.Checked)
                    {
                        this._mnuSelectedReplicate.Text    = dataPoint.Rep.Value.ToString();
                        this._mnuSelectedReplicate.Visible = true;
                    }
                    else
                    {
                        this._mnuSelectedReplicate.Visible = false;
                    }

                    if (dataPoint.Time.HasValue && this._chkShowTime.Checked)
                    {
                        this._mnuSelectedTime.Text    = dataPoint.Time.Value.ToString();
                        this._mnuSelectedTime.Visible = true;
                    }
                    else
                    {
                        this._mnuSelectedTime.Visible = false;
                    }


                    if (this._chkShowIntensity.Checked)
                    {
                        this._mnuSelectedIntensity.Text    = dataPoint.Intensity.ToString();
                        this._mnuSelectedIntensity.Visible = true;
                    }
                    else
                    {
                        this._mnuSelectedIntensity.Visible = false;
                    }

                    if (dataPoint.Group != null && this._chkShowGroup.Checked)
                    {
                        this._mnuSelectedGroup.Text    = dataPoint.Group.DisplayName;
                        this._mnuSelectedGroup.Image   = UiControls.CreateExperimentalGroupImage(true, dataPoint.Group, false);
                        this._mnuSelectedGroup.Visible = true;
                    }
                    else
                    {
                        this._mnuSelectedGroup.Visible = false;
                    }

                    if (this._chkShowSeries.Checked && this._chart.SelectedItem.Series.Length != 0)
                    {
                        this._mnuSelectedSeries.Text    = this._chart.SelectedItem.Series[0].Name;
                        this._mnuSelectedSeries.Image   = this._chart.SelectedItem.Series[0].DrawLegendKey(this._menuBar.ImageScalingSize.Width, this._menuBar.ImageScalingSize.Height);
                        this._mnuSelectedSeries.Visible = true;
                    }
                    else
                    {
                        this._mnuSelectedSeries.Visible = false;
                    }
                }
                else
                {
                    this._mnuSelectedReplicate.Visible = false;
                    this._mnuSelectedTime.Visible      = false;
                    this._mnuSelectedIntensity.Visible = false;
                    this._mnuSelectedSeries.Visible    = false;
                    this._mnuSelectedGroup.Visible     = false;
                }
            }

            // Perform derived-class-specific actions
            this.OnSelection(peak, dataPoint);
        }
示例#24
0
        /// <summary>
        /// Actual test running
        /// </summary>
        private static ClusterEvaluationPointer RunTest(Core core, ClusterEvaluationPointer origPointer, ClusterEvaluationConfiguration test, ProgressReporter proggy, bool paranoid, int index, int of)
        {
            UiControls.Assert(core.FileNames.Session != null, "Didn't expect the session filename to be null for cluster evaluation.");

            List <ClusterEvaluationParameterResult> results = new List <ClusterEvaluationParameterResult>();

            // Iterate over parameters
            for (int valueIndex = 0; valueIndex < test.ParameterValues.Length; valueIndex++)
            {
                object value = test.ParameterValues[valueIndex];

                List <ResultClusterer> repetitions = new List <ResultClusterer>();

                // Iterate over repetitions
                for (int repetition = 0; repetition < test.NumberOfRepeats; repetition++)
                {
                    proggy.Enter("Test " + index + "/" + of + ", parameter " + valueIndex + "/" + test.ParameterValues.Length + ", repetition " + repetition + "/" + test.NumberOfRepeats);

                    // Create config
                    string   newName          = AlgoParameterCollection.ParamToString(value) + " " + StringHelper.Circle(repetition + 1);
                    object[] copyOfParameters = test.ClustererConfiguration.Parameters.ToArray();
                    copyOfParameters[test.ParameterIndex] = value;
                    ArgsClusterer copyOfArgs = new ArgsClusterer(
                        test.ClustererConfiguration.Id,
                        test.ClustererConfiguration.SourceProvider,
                        test.ClustererConfiguration.PeakFilter,
                        test.ClustererConfiguration.Distance,
                        test.ClustererConfiguration.ObsFilter,
                        test.ClustererConfiguration.SplitGroups,
                        test.ClustererConfiguration.Statistics,
                        copyOfParameters,
                        test.ClustererConfiguration.OverrideShortName)
                    {
                        OverrideDisplayName = newName,
                        Comment             = test.ClustererConfiguration.Comment
                    };
                    var copyOfConfig = new ConfigurationClusterer()
                    {
                        Args = copyOfArgs
                    };

                    // Try load previus result
                    ResultClusterer result = null;

                    if (paranoid)
                    {
                        result = TryLoadIntermediateResult(core, test.Guid, valueIndex, repetition, proggy);
                    }

                    if (result == null)
                    {
                        // DO CLUSTERING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        proggy.Enter("Clustering");
                        result = copyOfConfig.Cluster(core, 0, proggy);
                        proggy.Leave();

                        if (paranoid)
                        {
                            SaveIntermediateResult(core, test.Guid, valueIndex, repetition, result, proggy);
                        }
                    }

                    // Add result
                    repetitions.Add(result);

                    string name = AlgoParameterCollection.ParamToString(value);

                    results.Add(new ClusterEvaluationParameterResult(name, test, valueIndex, repetitions));

                    proggy.Leave();
                }
            }

            ClusterEvaluationResults final = new ClusterEvaluationResults(core, test, results);

            string folder      = UiControls.GetOrCreateFixedFolder(UiControls.EInitialFolder.Evaluations);
            string sessionName = Path.GetFileNameWithoutExtension(core.FileNames.Session);
            string fileName    = core.Options.ClusteringEvaluationResultsFileName;

            fileName = fileName.Replace("{SESSION}", sessionName);
            fileName = fileName.Replace("{RESULTS}", folder + "\\");
            fileName = UiControls.GetNewFile(fileName);

            return(SaveResults(core, fileName, origPointer, final, proggy));
        }
示例#25
0
文件: Arr.cs 项目: mjr129/metaboclust
        /// <summary>
        /// Does Arian's PCA-ANOVA idea.
        /// </summary>
        public double PcaAnova(IntensityMatrix source, Peak peak, Core core, List <GroupInfo> types, List <int> replicates)
        {
            Range times = GetOverlappingTimeRange(core, source, types);

            // Create a matrix thusly:
            // Control Replicate 1: <day1> <day2> <day3> ...
            // Control Replicate 2: <day1> <day2> <day3> ...
            // Control Replicate 3: <day1> <day2> <day3> ...
            // Drought Replicate 1: <day1> <day2> <day3> ...
            // Drought Replicate 2: <day1> <day2> <day3> ...
            // ...

            // Create and clear the matrix
            int rowCount = types.Count * replicates.Count;
            int colCount = times.Count;

            double[,] matrix = new double[rowCount, colCount];

            for (int r = 0; r < rowCount; r++)
            {
                for (int c = 0; c < colCount; c++)
                {
                    matrix[r, c] = double.NaN;
                }
            }

            // Create the group vector
            double[] groups = new double[rowCount];

            for (int r = 0; r < rowCount; r++)
            {
                groups[r] = types[r / replicates.Count].Order;
            }

            IReadOnlyList <double> raw = source.Find(peak).Values;  // TODO: Dirty

            // Fill out the values we know
            for (int i = 0; i < core.Observations.Count; i++)
            {
                ObservationInfo o         = core.Observations[i];
                int             typeIndex = types.IndexOf(o.Group);
                int             repIndex  = replicates.IndexOf(o.Rep);

                if (times.Contains(o.Time) && typeIndex != -1 && repIndex != -1)
                {
                    int timeIndex = o.Time - times.Min;

                    int row = typeIndex * replicates.Count + repIndex;
                    UiControls.Assert(double.IsNaN(matrix[row, timeIndex]), "Duplicate day/time/rep observations in dataset are not allowed.");
                    matrix[row, timeIndex] = raw[i];
                }
            }

            // Guess missing values
            for (int r = 0; r < rowCount; r++)
            {
                for (int c = 0; c < colCount; c++)
                {
                    if (double.IsNaN(matrix[r, c]))
                    {
                        // Missing values - average other values for this point
                        int repIndex  = r % replicates.Count;
                        int typeStart = r - repIndex;

                        double total = 0;
                        int    count = 0;

                        for (int rep = 0; rep < replicates.Count; rep++)
                        {
                            int newRow = typeStart + rep;

                            if (!double.IsNaN(matrix[newRow, c]))
                            {
                                total += matrix[newRow, c];
                                count += 1;
                            }
                        }

                        matrix[r, c] = total / count;
                    }
                }
            }

            // Now do that R stuff...

            var rMatrix = _r.CreateNumericMatrix(matrix);
            var rVector = _r.CreateNumericVector(groups);

            _r.SetSymbol("a", rMatrix);
            _r.SetSymbol("g", rVector);

            //R.Evaluate("write.csv(a, file = \"E:/MJR/Project/05. PEAS/AbstressData/Leaf/Positive/CCor/LP1131.cs.csv\")");

            try
            {
                double result = _r.Evaluate(
                    @"p = prcomp(a)
f = data.frame(y = p$x[,1], group = factor(g))
fit = lm(y ~ group, f)
an = anova(fit)
pval = an$""Pr(>F)""[1]").AsNumeric()[0];

                return(result);
            }
            catch (Exception ex)
            {
                throw new Exception("Something went wrong calculating PCA-ANOVA statistics. See inner exception for details. (Note that this error can occur if only 1 replicate is specified and PCA-ANOVA is calculated with missing values - make sure the replicates are specified correctly.)", ex);
            }
        }