Repeated Measures Anova in C# QuickStart Sample

Illustrates how to use the OneWayRAnovaModel class to perform a one-way analysis of variance with repeated measures in C#.

This sample is also available in: Visual Basic, F#, IronPython.

Overview

This QuickStart sample demonstrates how to perform a one-way analysis of variance (ANOVA) with repeated measures using the OneWayRAnovaModel class from Numerics.NET.

The sample analyzes a study investigating the effects of four different drugs on test scores across five subjects. Each subject is tested with all four drugs, making this a repeated measures design. The code shows how to:

  • Create a DataFrame from sample data containing subject IDs, drug treatments, and test scores
  • Set up and fit a one-way repeated measures ANOVA model using both direct specification and formula syntax
  • Check if the experimental design is balanced
  • Display the ANOVA results table showing sources of variation, degrees of freedom, and significance
  • Access and analyze group means and variance for different treatment levels
  • Extract overall summary statistics like the grand mean and total number of observations

The example illustrates key concepts in repeated measures ANOVA including:

  • Working with within-subjects factors
  • Handling repeated observations on the same subjects
  • Analyzing treatment effects while accounting for individual differences
  • Interpreting ANOVA results for repeated measures designs

The code

using System;

using Numerics.NET.DataAnalysis;
using Numerics.NET.Statistics;

// Illustrates the use of the OneWayRAnovaModel class for performing
// a one-way analysis of variance with repeated measures.

// The license is verified at runtime. We're using
// a 30 day trial key here. For more information, see
//     https://numerics.net/trial-key
Numerics.NET.License.Verify("your-trial-key-here");

// This QuickStart Sample investigates the effect of the color of packages
// on the sales of the product. The data comes from 12 stores.
// Packages can be either red, green or blue.

// Set up the data as anonymous records:
var data = new[] {
    new { Person = 1, Drug = 1, Score = 30 },
    new { Person = 1, Drug = 2, Score = 28 },
    new { Person = 1, Drug = 3, Score = 16 },
    new { Person = 1, Drug = 4, Score = 34 },
    new { Person = 2, Drug = 1, Score = 14 },
    new { Person = 2, Drug = 2, Score = 18 },
    new { Person = 2, Drug = 3, Score = 10 },
    new { Person = 2, Drug = 4, Score = 22 },
    new { Person = 3, Drug = 1, Score = 24 },
    new { Person = 3, Drug = 2, Score = 20 },
    new { Person = 3, Drug = 3, Score = 18 },
    new { Person = 3, Drug = 4, Score = 30 },
    new { Person = 4, Drug = 1, Score = 38 },
    new { Person = 4, Drug = 2, Score = 34 },
    new { Person = 4, Drug = 3, Score = 20 },
    new { Person = 4, Drug = 4, Score = 44 },
    new { Person = 5, Drug = 1, Score = 26 },
    new { Person = 5, Drug = 2, Score = 28 },
    new { Person = 5, Drug = 3, Score = 14 },
    new { Person = 5, Drug = 4, Score = 30 }
};
var dataFrame = DataFrame.FromObjects(data);

// Construct the OneWayAnova object.
OneWayRAnovaModel anova = new OneWayRAnovaModel(dataFrame, "Score", "Drug", "Person");
// Alternatively, we can use a formula to specify the variables
// in the model:
anova = new OneWayRAnovaModel(dataFrame, "Score ~ Drug + Person");
// Perform the calculation.
anova.Fit();

// Verify that the design is balanced:
if (!anova.IsBalanced)
    Console.WriteLine("The design is not balanced.");

// The AnovaTable property gives us a classic anova table.
// We can write the table directly to the console:
Console.WriteLine(anova.AnovaTable.ToString());
Console.WriteLine();

// A Cell object represents the data in a cell of the model,
// i.e. the data related to one level of the factor.
// We can use it to access the group means for each drug.

// We need two indices here: the second index corresponds
// to the person factor.

// First we get the index so we can easily iterate
// through the levels:
var drugFactor = (Index<int>)anova.TreatmentFactor;
foreach(int level in drugFactor)
    Console.WriteLine("Mean for group '{0}': {1:F4}",
        level, anova.SubjectTotals.Get(level).Mean);

// We could have accessed the cells directly as well:
Console.WriteLine("Variance for second drug: {0}",
    anova.TreatmentTotals.Get(2).Variance);
Console.WriteLine();

// We can get the summary data for the entire model
// from the TotalCell property:
Cell totalSummary = anova.TotalCell;
Console.WriteLine("Summary data:");
Console.WriteLine($"# observations: {totalSummary.Count}");
Console.WriteLine($"Grand mean:     {totalSummary.Mean:F4}");

Console.Write("Press any key to exit.");
Console.ReadLine();