Cookbook

This page contains five self-contained recipes that cover the most common SPC analysis scenarios. Each recipe describes its input data shape, provides working code samples in C#, Visual Basic, and F#, explains what the results mean, and highlights the single most common mistake practitioners make in that scenario.

These recipes assume the reader has already read Getting Started with Numerics.NET SPC. For guidance on selecting the right chart type, see Choosing the Right Analysis.

Recipe 1: Individuals Process Analysis (I‑MR)

Scenario. A chemical batch process produces one measurement per batch: the yield (%) of a distillation step. Batches arrive in time order and cannot be grouped into rational subgroups. The goal is to determine whether the yield process is in statistical control and, if so, to estimate process capability against a specification of 90 %–105 %.

Input shape. A single IReadOnlyList<double> of n observations in time order. Missing values should be omitted before passing to the API; the library treats NaN as a contract violation in individuals data.

C#
// Recipe: Individuals process analysis (I-MR)
double[] measurements = {
    23.1, 24.0, 22.8, 24.5, 23.3, 24.8, 22.5, 24.2,
    23.7, 24.1, 23.4, 24.3, 23.6, 24.0, 23.2
};
Vector<double> obs = Vector.Create(measurements);

// 1. Construct and analyze the chart
IndividualsMovingRangeChartSet chart =
    new IndividualsMovingRangeChartSet(obs);
chart.Analyze();

FixedLimitSeries iSeries = chart.IndividualsSeries;
Console.WriteLine(
    $"I chart: CL={iSeries.CenterLine:F2} " +
    $"UCL={iSeries.UpperControlLimit:F2} " +
    $"LCL={iSeries.LowerControlLimit:F2}");

// 2. Evaluate rules
RuleSetEvaluation rules =
    chart.IndividualsSeries.EvaluateRules(ControlRuleSets.Nelson);
bool isStable = !rules.HasViolations;
Console.WriteLine($"Stable: {isStable}");

// 3. Capability analysis (independent step)
if (isStable)
{
    var specs = new SpecificationLimits(Lower: 21.0, Upper: 27.0);
    CapabilityAnalysisResult cap = Capability.Analyze(obs, specs);
    Console.WriteLine($"Cpk = {cap.Cpk:F3}");
}

Reading the result. After calling Analyze(), the IndividualsMovingRangeChartSet exposes each observation with its plotted value, control limits, and rule violations through its IndividualsSeries series. The capability section exposes Cp, Cpk (using MovingRange by default) and the corresponding performance indices Pp, Ppk. A process in control with a Cpk above 1.33 meets the conventional automotive/manufacturing benchmark.

  Caution

Do not pass observations in non-chronological order: the moving-range computation is order-dependent, and reordering data changes both control limits and capability estimates.

Recipe 2: Subgrouped Manufacturing Analysis (XBar‑R)

Scenario. A machining centre produces shafts in a continuous run. Every hour an operator measures five consecutive shafts (a rational subgroup) and records the diameters in millimetres. Twenty-five subgroups have been collected. The engineering tolerance is 50.00 mm ± 0.05 mm.

Input shape. A jagged or rectangular 2-D collection where each inner sequence is one subgroup of equal or near-equal size. XBar‑R is reliable for subgroup sizes 2–10; for larger subgroups use XBar‑S instead. See Variables Charts for the supported size range.

C#
// Recipe: Subgrouped manufacturing analysis (XBar-R, n=4)
double[,] rawData = {
    { 23.1, 24.0, 22.8, 24.5 },
    { 23.3, 24.8, 22.5, 24.2 },
    { 23.7, 24.1, 23.4, 24.3 },
    { 23.6, 24.0, 23.2, 24.4 },
    { 23.0, 24.6, 22.9, 24.1 }
};
Matrix<double> subgroups = Matrix.CopyFrom(rawData);

// 1. Construct and analyze
XBarRChartSet chart = new XBarRChartSet(subgroups);
chart.Analyze();

Console.WriteLine(
    $"XBar UCL={chart.MeansSeries.UpperControlLimit:F3}");

// 2. Rules on XBar component
RuleSetEvaluation rules =
    chart.MeansSeries.EvaluateRules(ControlRuleSets.Nelson);
Console.WriteLine($"XBar rule violations: {rules.Violations.Count}");

// 3. Capability (independent step)
var specs = new SpecificationLimits(Lower: 21.0, Upper: 27.0);
CapabilityAnalysisResult cap = Capability.Analyze(subgroups, specs);
Console.WriteLine($"Cpk = {cap.Cpk:F3}");

Reading the result. The fitted XBarRChartSet exposes separate series for the XBar chart (monitoring the subgroup mean) and the R chart (monitoring the subgroup range). Stability must be evaluated on both charts. An R chart out of control indicates that within-subgroup variation itself is unstable, which invalidates the XBar control limits. If both charts show control, the capability section will report Cp and Cpk using the pooled within-subgroup range estimator.

  Caution

Do not use XBar‑R with subgroup sizes larger than 10; the range statistic becomes inefficient and the d2 unbiasing constant is not defined beyond size 25. Use XBar‑S for larger subgroups.

Recipe 3: Variable Sample-Size P Chart

Scenario. A circuit-board assembly line inspects every board in each production lot, but lot sizes vary from 50 to 300 boards. The quality metric is the proportion defective per lot. Because the denominator changes from lot to lot, the control limits must be recalculated individually for each point.

Input shape. Two parallel sequences of equal length: a sequence of defective counts (integers or doubles) and a sequence of sample sizes (integers). The sample sizes may all differ. Do not pre-compute proportions and pass them as the first sequence; the API requires raw counts so it can compute Poisson or binomial limits correctly.

C#
// Recipe: Variable sample-size P chart
Vector<double> defects = Vector.Create(
    new double[] { 3, 5, 2, 7, 4, 6, 3, 8, 2, 5 });
Vector<double> sizes = Vector.Create(
    new double[] { 50, 60, 45, 70, 55, 65, 50, 80, 45, 60 });

PChart chart = new PChart(defects, sizes);
chart.Analyze();

Console.WriteLine($"P bar = {chart.Series.CenterLine:F4}");

// Render with pointwise limits
Vector<double> pts  = chart.Series.Values;
Vector<double> ucls = chart.Series.UpperControlLimits;
for (int i = 0; i < pts.Count; i++)
    Console.WriteLine(
        $"  [{i}] p={pts[i]:F4} " +
        $"UCL={ucls[i]:F4}");

Reading the result. Because sample sizes differ, each plotted point has its own pair of upper and lower control limits. The result model exposes per-point limit vectors; do not read a single scalar limit from the result and apply it to all points. Points where the computed lower control limit would be negative are floored at zero. See Result Model and Rendering Semantics for details on accessing per-point vectors.

  Caution

Never pass a single scalar control limit to your chart renderer when the sample size varies; always use the per-point limit vectors from the result.

Recipe 4: Capability Analysis with Assumption Diagnostics

Scenario. After confirming an XBar‑R process is in control (Recipe 2), the team needs to produce a formal capability report that includes a normality test so the customer can evaluate whether the standard Cp/Cpk indices are valid for this process.

Input shape. The same subgrouped data as Recipe 2, plus a SpecificationLimits class with at minimum one non-null limit, and the flag assumptionDiagnostics: true passed to the analysis call.

C#
// Recipe: Capability analysis with assumption diagnostics
double[] data = {
    23.1, 24.0, 22.8, 24.5, 23.3, 24.8, 22.5, 24.2,
    23.7, 24.1, 23.4, 24.3, 23.6, 24.0, 23.2, 24.4,
    23.0, 24.6, 22.9, 24.1
};
Vector<double> obs = Vector.Create(data);
var specs = new SpecificationLimits(Lower: 21.0, Upper: 27.0);

// 1. Chart analysis
IndividualsMovingRangeChartSet chart =
    new IndividualsMovingRangeChartSet(obs);
chart.Analyze();
bool stable =
    !chart.IndividualsSeries.EvaluateRules(ControlRuleSets.Nelson).HasViolations;
Console.WriteLine($"Stable: {stable}");

// 2. Capability (request normality testing)
CapabilityAnalysisResult cap = Capability.Analyze(
    obs, specs,
    normalityTest: TestOfNormality.ShapiroWilk);
Console.WriteLine($"Cp={cap.Cp:F3} Cpk={cap.Cpk:F3}");
Console.WriteLine($"Pp={cap.Pp:F3} Ppk={cap.Ppk:F3}");

// 3. Assumption diagnostics from the capability result
AssumptionDiagnostics? diag = cap.Diagnostics;
if (diag?.NormalityTest != null)
    Console.WriteLine(
        $"Normality p = {diag.NormalityTest.PValue:F4}");

Reading the result. The AssumptionDiagnostics object is returned alongside the CapabilityAnalysisResult. Check the PValue from the normality test (accessible via NormalityTest). A p-value above 0.05 does not contradict normality; a value below 0.05 warrants investigation before submitting the Cpk figure. The capability indices themselves are always computed regardless of the test outcome. For a full discussion, see Capability, Performance, and Assumption Diagnostics.

  Caution

Do not skip the stability check in Recipe 2 and jump straight to this recipe: capability computed on unstable data is not interpretable.

Recipe 5: Serialize and Restore a Result

Scenario. A web service computes SPC results on a background worker and needs to cache or transmit the result to a front-end that renders the chart. The front-end should not re-run the analysis; it should deserialize the result and read the per-point vectors directly for rendering without any further computation.

Input shape. A previously computed fitted chart. After calling Analyze(), the chart object is self-contained and can be serialized via its ToJson() method. All plotted values, control limits, and rule violation flags are stored as immutable vectors and survive a round-trip. See Integration and Persistence for serializer configuration details.

C#
// Recipe: Serialize and restore a deployed chart
double[] data = {
    23.1, 24.0, 22.8, 24.5, 23.3, 24.8, 22.5, 24.2, 23.7, 24.1
};

// 1. Fit the chart
IndividualsMovingRangeChartSet original =
    new IndividualsMovingRangeChartSet(Vector.Create(data));
original.Analyze();

// 2. Deploy (freeze parameters) and serialize
IndividualsMovingRangeChartSet deployed = original.Deploy();
string json = deployed.ToJson();

// 3. Restore from JSON (e.g., from database or API response)
IndividualsMovingRangeChartSet restored =
    IndividualsMovingRangeChartSet.FromJson(json);

Console.WriteLine(
    $"Original CL = {original.IndividualsSeries.CenterLine:F4}");
Console.WriteLine(
    $"Restored CL = {restored.IndividualsSeries.CenterLine:F4}");

Reading the result. After deserialization the restored object is functionally identical to the original: all indexed vectors are present at the same positions, control limits match, and rule violation flags are preserved. The receiver does not need access to the original raw data at all. This pattern is the recommended approach for any architecture that separates the computation tier from the presentation tier.

  Caution

Do not re-run the analysis on the front-end using only the deserialized result as input; serialize and transmit the result, not the raw data, to avoid recomputing control limits with a different sample context.

Recipe 6: Phase II Monitoring (Deploy and Apply)

Scenario. A manufacturing line has completed Phase I baseline estimation. New batches arrive daily and must be plotted against the frozen control limits. The baseline chart is persisted to JSON and restored on each monitoring run.

Workflow.

  1. Fit the chart with Analyze() during Phase I.

  2. Call Deploy() to create a deployed chart that holds frozen control limits and discards the original observations.

  3. Serialize the deployed chart with ToJson() and store it.

  4. When new data arrives, restore the deployed chart with FromJson() and call Apply(Vector<Double>) to produce a new fitted chart evaluated against the frozen baseline.

This pattern supports charts-only monitoring without rules or capability analysis—simply inspect the plotted values and control limits after Apply(Vector<Double>).

  Note

For charts requiring additional inputs (subgroup matrices for XBar‑R / XBar‑S, or variable sample sizes for P / U charts), cast to the concrete chart type and use the type-specific Apply(Vector<Double>) overload. See API Surface Map: Phase II monitoring for the full list of overloads.

See Also