Factor Analysis (FA) in C# QuickStart Sample
Illustrates how to perform a Factor Analysis using classes in the Numerics.NET.Statistics.Multivariate namespace in C#.
View this sample in: Visual Basic F#
using System;
using Numerics.NET.Data.Stata;
using Numerics.NET;
using Numerics.NET.Statistics.Multivariate;
namespace Numerics.NET.Quickstart.CSharp
{
/// <summary>
/// Demonstrates how to use classes that implement
/// Factor Analysis.
/// </summary>
class FactorAnalysisExample
{
static void Main(string[] args)
{
// 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("64542-18980-57619-62268");
// This QuickStart Sample demonstrates how to perform
// a factor analysis on a set of data.
//
// The classes used in this sample reside in the
// Numerics.NET.Statistics.Multivariate namespace.
// First, our dataset, 'm255.dta', from Professor James Sidanius.
// See http://www.ats.ucla.edu/stat/sas/output/factor.htm
// Note: tolerances used to test for convergence in factor analysis
// algorithms are usually set very low (around 0.001). As a result,
// when comparing results from different programs, usually only
// about the first 3 digits will be equal.
// The data is in Stata format. Use a matrix reader to load it into a matrix.
var frame = StataFile.ReadDataFrame(@"..\..\..\..\Data\m255.dta");
// We'll use only these columns:
string[] names = { "item13", "item14", "item15", "item16",
"item17", "item18", "item19", "item20", "item21",
"item22", "item23", "item24" };
// First, filter out any rows with missing values:
frame = frame.RemoveRowsWithMissingValues(names);
//
// Factor analysis
//
// We can construct FA objects in many ways. Since we have the data in a matrix,
// we use the constructor that takes a data matrix as input.
var fa = new FactorAnalysis(frame, names);
// Alternatively, we could have used a formula to specify
// the variables we want in the model:
fa = new FactorAnalysis(frame, string.Join(" + ", names));
// We set the number of factors:
fa.NumberOfFactors = 3;
// and immediately perform the analysis:
fa.Fit();
// We can get the unrotated factors:
var unrotatedFactors = fa.GetUnrotatedFactors();
// We can get the contributions of each factor:
Console.WriteLine(" # Eigenvalue Difference Contribution Contrib. %");
foreach (Factor factor in unrotatedFactors)
{
// and write out its properties
Console.WriteLine("{0,2}{1,12:F4}{2,11:F4}{3,14:F3}{4,10:F3}",
factor.Index, factor.Eigenvalue, factor.EigenvalueDifference,
factor.ProportionOfVariance,
factor.CumulativeProportionOfVariance);
}
Console.WriteLine("\nVarimax rotation");
// Here are the loadings for each of the variables:
Console.WriteLine("\nUnrotated loadings:");
Console.WriteLine("Variable 1 2 3 Uniqueness");
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine(" {0,8}{1,10:F5} {2,10:F5} {3,10:F5}{4,10:F5}",
names[i],
unrotatedFactors[0].Loadings[i],
unrotatedFactors[1].Loadings[i],
unrotatedFactors[2].Loadings[i],
fa.Uniqueness[i]);
}
// Now we'll look at the rotated factors:
var rotatedFactors = fa.GetRotatedFactors();
Console.WriteLine(" # Variance Difference Proportion Cumulative");
foreach (Factor factor in rotatedFactors)
{
Console.WriteLine("{0,2}{1,12:F4}{2,11:F4}{3,13:F4}{4,11:F4}",
factor.Index, factor.VarianceExplained, "-",
factor.ProportionOfVariance,
factor.CumulativeProportionOfVariance);
}
// Here are the rotated loadings for each of the variables:
Console.WriteLine("\nRotated loadings (Varimax):");
Console.WriteLine("Variable 1 2 3 Uniqueness");
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine(" {0,8}{1,10:F5} {2,10:F5} {3,10:F5}{4,10:F5}",
names[i],
rotatedFactors[0].Loadings[i],
rotatedFactors[1].Loadings[i],
rotatedFactors[2].Loadings[i],
fa.Uniqueness[i]);
}
// And the matrix that rotates the factors
Console.WriteLine("Factor transformation matrix:\n{0:F4}",
fa.FactorTransformationMatrix);
Console.WriteLine("\nPromax rotation (power = 3)");
// Now let's use an (oblique) Promax rotation:
fa.RotationMethod = FactorRotationMethod.Promax;
fa.PromaxPower = 3;
fa.Fit();
// Now we'll look at the rotated factors:
Console.WriteLine("\nRotated factor variance explained:");
rotatedFactors = fa.GetRotatedFactors();
Console.WriteLine(" # Variance");
foreach (Factor factor in rotatedFactors)
{
Console.WriteLine("{0,2}{1,12:F4}",
factor.Index, factor.VarianceExplained);
}
// Here are the rotated loadings for each of the variables:
Console.WriteLine("\nRotated loadings/pattern (Promax):");
Console.WriteLine("Variable 1 2 3 Communality Uniqueness");
for (int i = 0; i < names.Length; i++)
{
// and write out its properties
Console.WriteLine(" {0,8}{1,10:F5}{2,10:F5}{3,10:F5}{4,10:F5} {5,10:F5}",
names[i],
rotatedFactors[0].Loadings[i],
rotatedFactors[1].Loadings[i],
rotatedFactors[2].Loadings[i],
fa.Communalities[i],
fa.Uniqueness[i]);
}
// Here are the rotated loadings for each of the variables:
Console.WriteLine("\nRotated factor structure:");
Console.WriteLine("Variable 1 2 3");
for (int i = 0; i < names.Length; i++)
{
// and write out its properties
Console.WriteLine(" {0,8}{1,10:F5} {2,10:F5} {3,10:F5}",
names[i],
rotatedFactors[0].Structure[i],
rotatedFactors[1].Structure[i],
rotatedFactors[2].Structure[i]);
}
// For oblique rotations, the factors are usually correlated:
Console.WriteLine("Factor correlation matrix:\n{0:F4}",
fa.FactorCorrelationMatrix);
//
// Factor analysis on a correlation matrix
//
Console.WriteLine("\nUsing a correlation matrix");
// This example is from Exploratory Factor Analysis
// http://www.oup.com/us/companion.websites/9780199734177/supplementary/example/
double[] values = {
1.000, 0.666, 0.150, 0.617, 0.541, 0.653, 0.473, 0.549, 0.566,
0.666, 1.000, 0.247, 0.576, 0.510, 0.642, 0.425, 0.544, 0.488,
0.150, 0.247, 1.000, 0.222, 0.081, 0.164, 0.091, 0.181, 0.120,
0.617, 0.576, 0.222, 1.000, 0.409, 0.560, 0.338, 0.448, 0.349,
0.541, 0.510, 0.081, 0.409, 1.000, 0.667, 0.734, 0.465, 0.754,
0.653, 0.642, 0.164, 0.560, 0.667, 1.000, 0.596, 0.540, 0.672,
0.473, 0.425, 0.091, 0.338, 0.734, 0.596, 1.000, 0.432, 0.718,
0.549, 0.544, 0.181, 0.448, 0.465, 0.540, 0.432, 1.000, 0.412,
0.566, 0.488, 0.120, 0.349, 0.754, 0.672, 0.718, 0.412, 1.000
};
var R = Matrix.CreateSymmetric(9, values, MatrixTriangle.Upper,
MatrixElementOrder.ColumnMajor, true);
fa = new FactorAnalysis(R, FactorMethod.Correlation);
fa.NumberOfFactors = 2;
fa.ExtractionMethod = FactorExtractionMethod.MaximumLikelihood;
fa.RotationMethod = FactorRotationMethod.Varimax;
fa.Fit();
names = new string[] { "Hugs", "Comps", "PerAd", "SocAd", "ProAd",
"ComSt", "PhyHlp", "Encour", "Tutor" };
// Here are the initial:
Console.WriteLine("\nRotated factor loadings:");
Console.WriteLine("Variable Initial Extracted");
for (int i = 0; i < names.Length; i++)
{
// and write out its properties
Console.WriteLine(" {0,8}{1,10:F5} {2,10:F5}",
names[i],
fa.InitialCommunalities[i],
fa.Communalities[i]);
}
// Here are the rotated loadings for each of the variables:
// Note that in the SPSS output, the ordering of the variables
// is different.
unrotatedFactors = fa.GetUnrotatedFactors();
Console.WriteLine("\nUnrotated factor loadings:");
Console.WriteLine("Variable 1 2");
for (int i = 0; i < names.Length; i++)
{
// and write out its properties
Console.WriteLine(" {0,8}{1,10:F5} {2,10:F5}",
names[i],
unrotatedFactors[0].Loadings[i],
unrotatedFactors[1].Loadings[i]);
}
// Here are the rotated loadings for each of the variables:
rotatedFactors = fa.GetRotatedFactors();
Console.WriteLine("\nRotated factor loadings:");
Console.WriteLine("Variable 1 2");
for (int i = 0; i < names.Length; i++)
{
// and write out its properties
Console.WriteLine(" {0,8}{1,10:F5} {2,10:F5}",
names[i],
rotatedFactors[0].Loadings[i],
rotatedFactors[1].Loadings[i]);
}
Console.Write("Press any key to exit.");
Console.ReadLine();
}
}
}