SonataSmooth.Tune
2.1.3
See the version list below for details.
dotnet add package SonataSmooth.Tune --version 2.1.3
NuGet\Install-Package SonataSmooth.Tune -Version 2.1.3
<PackageReference Include="SonataSmooth.Tune" Version="2.1.3" />
<PackageVersion Include="SonataSmooth.Tune" Version="2.1.3" />
<PackageReference Include="SonataSmooth.Tune" />
paket add SonataSmooth.Tune --version 2.1.3
#r "nuget: SonataSmooth.Tune, 2.1.3"
#:package SonataSmooth.Tune@2.1.3
#addin nuget:?package=SonataSmooth.Tune&version=2.1.3
#tool nuget:?package=SonataSmooth.Tune&version=2.1.3
SonataSmooth.Tune
High‑performance 1D numeric signal smoothing & export toolkit for .NET (C#).
Implements Rectangular (Moving Average), Binomial (Pascal) Average, Weighted Median (Binomial weights), Gaussian, and Savitzky-Golay smoothing with configurable boundary handling and optional parallelization. Includes CSV and Excel (COM) export helpers that materialize multiple smoothing "voices" side‑by‑side for inspection or charting.
Target : .NET Standard 2.0 (core algorithms & CSV export)
Optional Excel COM automation sections use conditional compilation (#if NET48
) or late binding (dynamic COM) when available.
Contents
- Features
- Installation
- Quick Start
- Concepts & Design Notes
- Boundary Handling
- Implemented Filters
- Performance Characteristics
- API Reference
- Enum:
BoundaryMode
- Core static class:
SmoothingConductor
ApplySmoothing
GetValueWithBoundary
CalcBinomialCoefficients
ComputeGaussianCoefficients
ComputeSavitzkyGolayCoefficients
ValidateSmoothingParameters
- Validation helper :
ScoreConformanceChecker
- CSV / Excel export helpers (
ExportCsvAsync
,ExportCsvAsyncFromResults
,ExportExcelAsync
)
- Additional CSV tuning parameters :
- progressCheckInterval (int, default 1): Throttles how often progress is recomputed / reported (every N rows).
- customFlushLineThreshold (int?): Overrides the adaptive buffered write flush batch size; null keeps adaptive logic.
- Data containers:
SmoothingScore
,SmoothingNotation
- CSV export types:
CsvScoreRequest
,CsvExportResult
,CsvScoreWriter
- Excel export types:
ExcelScoreRequest
,ExcelScoreWriter
,ExcelInteropNotAvailableException
- Enum:
- Error & Exception Reference
- Usage Examples
- Best Practices
- Thread Safety
- Limitations & Known Inconsistencies
- License (MIT)
Features
- Compute up to five smoothing variants in a single pass over the source data.
- Window-based convolution / order polynomial fitting (Savitzky-Golay) with user-specified radius.
- Weighted median using binomial coefficients for robust noise suppression.
- Automatic parallel execution threshold (
n ≥ 2000
) for CPU-bound steps. - Culture-invariant numeric formatting for export.
- Large dataset CSV partitioning honoring Excel row limit (1,048,576).
- Efficient buffered CSV writer with adaptive flush strategy.
- Excel export :
- Full multi-series line chart generation.
- Multi-column continuation for > 1,048,573 rows (per-column fill strategy).
- Document properties + playful meta "phrases" (retained).
- Late binding fallback when strong interop PIAs not available (non-
NET48
). - Boundary labels in Excel : high-level
ExcelScoreWriter
uses abbreviated labels ("Symmetric", "Replicate", "ZeroPad"); the core SmoothingConductor Excel export uses extended forms ("Symmetric (Mirror)", etc.) - Worksheet header note :
SmoothingConductor.ExportExcelAsync
writes "Subject" and "Comments" rows into the worksheet;ExcelScoreWriter.ExportAsync
stores these only as document properties (not repeated as sheet rows).
- Configurable boundary modes : Mirror (Symmetric), Replicate (Clamp), ZeroPad.
Installation
NuGet (preferred) : dotnet add package SonataSmooth.Tune
Package Manager : Install-Package SonataSmooth.Tune
Quick Start
using SonataSmooth.Tune;
double[] data = { 1, 2, 3, 10, 9, 5, 4, 3, 2 };
int radius = 2; // Smoothing window size calculated from radius: (2 * radius) + 1 = 5
int polyOrder = 2; // Degree of the polynomial used in Savitzky-Golay smoothing
var (rect, binomAvg, median, gauss, sg) = SmoothingConductor.ApplySmoothing(
data,
r: radius,
polyOrder: polyOrder,
boundaryMode: BoundaryMode.Symmetric,
doRect: true,
doAvg: true,
doMed: true,
doGauss: true,
doSG: true
);
// (Optional) Validate parameters first using the tuple helper
using SonataSmooth.Tune.Export;
var (ok, validationError) = ScoreConformanceChecker.Validate(
dataCount: data.Length,
r: radius,
polyOrder: polyOrder,
useSG: true // set to false if you disable SG
);
if (!ok)
{
Console.WriteLine(validationError);
return; // Abort before running smoothing/export
}
CSV export (computed internally) :
using SonataSmooth.Tune.Export;
var req = new CsvScoreRequest
{
Title = "Demo Dataset",
BaseFilePath = @"C:\Temp\demo.csv",
InitialData = data,
Radius = radius,
PolyOrder = polyOrder,
BoundaryMode = BoundaryMode.Symmetric,
Flags = new SmoothingNotation
{
Rectangular = true,
BinomialAverage = true,
BinomialMedian = true,
Gaussian = true,
SavitzkyGolay = true
}
};
var result = await CsvScoreWriter.ExportAsync(req);
Note : When using CsvScoreWriter.ExportAsync the "Boundary Method" header uses abbreviated labels ("Symmetric", "Replicate", "ZeroPad"). The lower-level SmoothingConductor.ExportCsvAsync / ExportCsvAsyncFromResults use extended labels ("Symmetric (Mirror)", "Replicate (Nearest)", "Zero Padding").
Concepts & Design Notes
Concept | Note |
---|---|
Radius | Half-width of kernel. Window size = 2 * r + 1 (must be odd, > 0). |
One-pass allocation | Arrays allocated once; each enabled filter computed per index. |
Parallelization | Parallel.For engaged when n >= 2000 . |
Weighted Median | Re-computes local window list + sort per index (O(w log w)); heavier than simple linear filters. |
Gaussian σ | Chosen as (2r + 1)/6 for approximate coverage of ±3σ across the window. |
Savitzky-Golay | Normalized to unit DC gain (coefficients sum = 1). |
Binomial limit | Length ≤ 63 to prevent overflow (64-bit safety). |
Boundary Handling
BoundaryMode
:
Symmetric
: Mirror index (reflective). Example: index -1 → element 0 mirrored (implemented as-idx - 1
mapping).Replicate
: Clamp to nearest endpoint (edge value continuation).ZeroPad
: Outside indices treated as 0.
Implemented Filters (Brief)
Filter | Characteristic | Complexity per sample |
---|---|---|
Rectangular Average | Uniform weights | O(w) |
Binomial Average | Pascal row weights, smoother than uniform | O(w) |
Weighted Median | Robust to spikes, preserves edges | O(w log w) |
Gaussian | Pre-normalized kernel | O(w) |
Savitzky-Golay | Local polynomial least squares (smoothing only, 0th derivative) | O(w) after precompute |
w = 2r + 1
where r
is the kernel radius.
Performance Characteristics
- Time : Dominated by selected filters; enabling all includes one sorting per sample (median).
- Memory : 5 output arrays ×
n
doubles (40n bytes) if all enabled. - Parallel : Gains most when median disabled or window moderate (sorting cost can reduce scaling).
- CSV Export : Streaming + buffered StringBuilder; partitioning to prevent Excel row overflow.
API Reference
Enum: BoundaryMode
public enum BoundaryMode
{
Symmetric, // Mirror : Reflect indices across boundaries (e.g., [3, 2, 1, 0, 1, 2, 3])
Replicate, // Clamp : Extend edge values beyond boundaries
ZeroPad // Pad with zeros : Use zero-padding for out-of-bound indices
}
Class: SmoothingConductor
(static)
ApplySmoothing
public static (
double[] Rect,
double[] Binom,
double[] Median,
double[] Gauss,
double[] SG
) ApplySmoothing(
double[] input,
int r,
int polyOrder,
BoundaryMode boundaryMode,
bool doRect,
bool doAvg,
bool doMed,
bool doGauss,
bool doSG
)
Parameters:
input
(required) : Source sequence.r
: Kernel radius ≥ 0.polyOrder
: Used only ifdoSG == true
. Must be< windowSize
.boundaryMode
: SeeBoundaryMode
.doRect
...doSG
: Flags enabling each smoothing.
Returns: Tuple in fixed order : (Rectangular, BinomialAverage, WeightedMedian, Gaussian, SavitzkyGolay). Disabled filters still allocate arrays (filled with 0) for positional consistency.
Exceptions:
ArgumentNullException
(input
)ArgumentOutOfRangeException
(r < 0
)- From subroutines (e.g., Savitzky-Golay coefficient generation constraints).
GetValueWithBoundary
public static double GetValueWithBoundary(
double[] data,
int idx,
BoundaryMode mode
)
Resolves and returns the value at a logical index using the selected boundary handling policy (mirror, clamp, or zero pad). No smoothing is performed here; this is an index resolution helper used by the filters.
ComputeGaussianCoefficients
public static double[] ComputeGaussianCoefficients(
int length,
double sigma
)
Constraints :
length
≥ 1 (typically odd to align with 2r+1 windows)sigma
> 0
Returns a symmetric Gaussian weight array normalized so the sum ≈ 1.
Exceptions :
- ArgumentException (length < 1 or sigma ≤ 0)
- InvalidOperationException (numerical anomaly resulting in non-positive sum)
ComputeSavitzkyGolayCoefficients
public static double[] ComputeSavitzkyGolayCoefficients(
int windowSize,
int polyOrder
)
Constraints :
windowSize
> 0 and odd.polyOrder >= 0 && polyOrder < windowSize
. Produces normalized smoothing (0th derivative) coefficients.
ValidateSmoothingParameters
public static bool ValidateSmoothingParameters(
int dataCount,
int r,
int polyOrder,
bool useSG,
out string error
)
Checks :
(2r + 1) ≤ dataCount
- If
useSG
,polyOrder < (2r + 1)
Returnsfalse
with descriptiveerror
block if invalid.
Validation Helper: ScoreConformanceChecker
A tuple-return wrapper around SmoothingConductor.ValidateSmoothingParameters
:
var (success, error) = ScoreConformanceChecker.Validate(
dataCount: input.Length,
r: r,
polyOrder: polyOrder,
useSG: doSG
);
if (!success)
{
// Abort early – error contains multi-line diagnostic
Console.WriteLine(error);
return;
}
**Rules enforced (delegated) 😗*
r >= 0
(2 * r + 1) <= dataCount
- If
useSG
:polyOrder < (2 * r + 1)
Use this for cleaner guard clauses (no out string
).
Note : The wrapper now enforces a non‑negative kernel radius (
r >= 0
) in addition to the existing rules.
Caution : Callers no longer need to pre‑checkr >= 0
before invoking this validator, as the check is performed internally.
ExportCsvAsync / ExportCsvAsyncFromResults
High-level CSV generation with partitioning (honors Excel max row limit).
Two overload paths:
ExportCsvAsync
: Recomputes smoothing internally.ExportCsvAsyncFromResults
: Accepts already-computed arrays to avoid recomputation cost.
Notable optional parameters:
progress
(Action<int>
) : Reports rough percent (resets to 0 at completion).progressCheckInterval
: Throttle frequency (default 1 = every line).customFlushLineThreshold
: Controls buffered flush size (auto if null).
ExportExcelAsync
Creates a live Excel instance (requires Microsoft Excel installed & COM available).
Populates per-filter columns (with multi-column continuation for very large arrays) and builds a composite line chart.
Releases COM RCWs after making Excel visible (workbook remains open to user).
Exceptions surfaced via showMessage
callback; some internal catch blocks suppress to keep UI responsive.
Data Container : SmoothingNotation
public sealed class SmoothingNotation
{
public bool Rectangular { get; set; }
public bool BinomialAverage { get; set; }
public bool BinomialMedian { get; set; }
public bool Gaussian { get; set; }
public bool SavitzkyGolay { get; set; }
public bool Any =>
Rectangular ||
BinomialAverage ||
BinomialMedian ||
Gaussian ||
SavitzkyGolay;
}
Utility flag bundle passed to export requests.
Data Container : SmoothingScore
public sealed class SmoothingScore
{
public double[] Initial { get; set; }
public double[] Rect { get; set; }
public double[] BinomAvg { get; set; }
public double[] BinomMed { get; set; }
public double[] Gauss { get; set; }
public double[] SavitzkyGolay { get; set; }
public int Length => Initial?.Length ?? 0;
}
(Not currently returned by core API; available for potential aggregation wrappers.)
CSV Export Types
CsvScoreRequest
public sealed class CsvScoreRequest
{
public string Title { get; set; }
public string BaseFilePath { get; set; }
public double[] InitialData { get; set; }
public int Radius { get; set; }
public int PolyOrder { get; set; }
public BoundaryMode BoundaryMode { get; set; }
public SmoothingNotation Flags { get; set; }
// Optional post-processing flag for UI actions (e.g., auto-open file)
public bool IncludeOpenAfterExportDecision { get; set; }
}
CsvExportResult
public sealed class CsvExportResult
{
public List<string> GeneratedFiles { get; } = new List<string>();
}
CsvScoreWriter
public static class CsvScoreWriter
{
public static Task<CsvExportResult> ExportAsync(
CsvScoreRequest request,
IProgress<int> progress = null,
CancellationToken cancellationToken = default
)
}
Behavior :
- Validates parameters via a checker (
ScoreConformanceChecker.Validate
in consuming project). - Splits output into parts if row limit exceeded.
- Column order : Initial Dataset + (enabled filters in consistent descriptive naming).
- Progress : 0 – 100 based on rows processed. Resets to 0 at completion (intentionally mirrors legacy semantics).
- Cancellation : Cooperative via
CancellationToken.ThrowIfCancellationRequested()
inside loop.
Excel Export Types
ExcelScoreRequest
public sealed class ExcelScoreRequest
{
public string DatasetTitle { get; set; }
public double[] InitialData { get; set; }
public int Radius { get; set; }
public int PolyOrder { get; set; }
public BoundaryMode BoundaryMode { get; set; }
public SmoothingNotation Flags { get; set; }
}
ExcelScoreWriter
public static class ExcelScoreWriter
{
public static Task ExportAsync(
ExcelScoreRequest request,
IProgress<int> progress = null
);
}
Notes:
- Two conditional implementations:
#if NET48
strong-typed reference to Excel PIA (method signature usesExcelExportRequest
in code region – naming mismatch withExcelScoreRequest
; see "Limitations & Known Inconsistencies").- Else : dynamic late binding (
Type.GetTypeFromProgID("Excel.Application")
), throwsExcelInteropNotAvailableException
if unavailable.
ExcelInteropNotAvailableException
Thrown when COM activation cannot proceed (Excel missing / ProgID failure).
Error & Exception Reference (Selected)
Source | Condition | Exception |
---|---|---|
ApplySmoothing |
input == null |
ArgumentNullException |
r < 0 |
ArgumentOutOfRangeException | |
CalcBinomialCoefficients |
length < 1 | ArgumentException |
length > 63 | ArgumentOutOfRangeException | |
ComputeSavitzkyGolayCoefficients |
even window or invalid poly order | ArgumentException |
Matrix inversion | singular / ill-conditioned | InvalidOperationException |
CSV export | InitialData null |
ArgumentNullException |
CSV export | data length 0 | InvalidOperationException |
CSV export | cancellation requested (token signaled) | OperationCanceledException |
Excel late binding | Excel not installed | ExcelInteropNotAvailableException |
Excel export | no enabled sections | InvalidOperationException |
Usage Examples
1. Savitzky-Golay Only
var (_, _, _, _, sg) = SmoothingConductor.ApplySmoothing(
input: signal,
r: 4,
polyOrder: 3,
boundaryMode: BoundaryMode.Replicate,
doRect: false,
doAvg: false,
doMed: false,
doGauss: false,
doSG: true
);
2. Precompute & Export CSV Without Recalculation
int r = 3;
int poly = 2;
// Execute a range of smoothing algorithms on the input data
var (rect, avg, med, gauss, sg) = SmoothingConductor.ApplySmoothing(
data,
r,
poly,
BoundaryMode.Symmetric,
doRect: true,
doAvg: true,
doMed: true,
doGauss: true,
doSG: true
);
// Write smoothing results to a CSV file
await SmoothingConductor.ExportCsvAsyncFromResults(
filePath: @"C:\out\multi.csv",
title: "Precomputed Export",
kernelRadius: r,
polyOrder: poly,
boundaryMode: BoundaryMode.Symmetric,
doRect: true,
doAvg: true,
doMed: true,
doGauss: true,
doSG: true,
initialData: data,
rectAvg: rect,
binomAvg: avg,
binomMed: med,
gaussFilt: gauss,
sgFilt: sg,
progress: p => Console.WriteLine($"CSV {p}%")
);
Important : ExportCsvAsyncFromResults does not internally validate that supplied smoothing arrays match initialData.Length. All arrays must be length-aligned; otherwise an index exception will occur during streaming.
3. Excel Export (Late Binding Mode)
var req = new ExcelScoreRequest
{
DatasetTitle = "Signal Run 42",
InitialData = data,
Radius = 5,
PolyOrder = 3,
BoundaryMode = BoundaryMode.Symmetric,
Flags = new SmoothingNotation
{
Rectangular = true,
BinomialAverage = true,
BinomialMedian = false,
Gaussian = true,
SavitzkyGolay = true
}
};
await ExcelScoreWriter.ExportAsync(
req,
new Progress<int>(p => Console.WriteLine($"Excel {p}%"))
);
Progress Semantics
Operation | Completion Behavior |
---|---|
CsvScoreWriter.ExportAsync / ExportCsvAsync* | Resets to 0 after finishing |
SmoothingConductor.ExportExcelAsync | Ends at 100% (no reset) |
ExcelScoreWriter.ExportAsync | May reach 100% during data fill then resets to 0 after chart creation |
Best Practices
- Keep radius modest (
3 – 9
) for interactive scenarios; large radii escalate median cost. - Validate parameters before UI commit using
ValidateSmoothingParameters
. - Reuse precomputed results when exporting multiple times (CSV or Excel) to avoid repeated smoothing.
- Use
BoundaryMode.Symmetric
for most natural edge continuity unless edge clamping desired. - Consider disabling median for extremely large datasets if throughput is critical.
- Use
ScoreConformanceChecker.Validate
for concise parameter guards instead of calling the out‑param form directly.
Note :
ValidateSmoothingParameters
now enforces a non‑negative kernel radius (r ≥ 0
) in addition to existing rules. Callers no longer need to pre‑check for negative radius values before invoking the validator.
Thread Safety
- Static methods are stateless; safe for concurrent calls.
- Do not reuse output arrays between concurrent operations.
- Excel export methods are inherently single-threaded due to COM constraints.
Limitations & Known Inconsistencies
Item | Detail |
---|---|
Naming discrepancy | README earlier referenced SmoothingTuner ; current implementation exposes SmoothingConductor . Update code or aliases if backward compatibility needed. |
Excel #if NET48 signature |
Strong-typed branch references ExcelExportRequest (not provided here) while public late-binding branch uses ExcelScoreRequest . Harmonize names to reduce confusion. |
Weighted median performance | O(n * w log w) cost may dominate for large windows; consider optional alternative (e.g., running selection algorithms) if needed. |
Progress reset | Exports intentionally report 0 at completion mirroring legacy UI expectations. |
Localization | Export uses invariant numeric format; headers partially English (intentionally stable for machine parsing). |
Large matrices | Savitzky-Golay inversion may throw if matrix ill-conditioned for extreme parameter combinations. |
Excel boundary label variance | High-level ExcelScoreWriter uses abbreviated labels; core SmoothingConductor Excel export uses extended labels. |
License
MIT License. Include the full text in distribution (see root LICENSE if present).
Changelog
- Consolidated multi-filter smoothing.
- Added robust CSV partitioning + buffered write optimizations.
- Added optional precomputed export path to remove redundant smoothing.
- Added Excel late binding path for non-
NET48
builds. - Normalized Savitzky-Golay coefficient sum.
Disclaimer
This library focuses on clarity and predictable numeric stability over hyper-optimized micro-ops. Benchmark with your dataset before large-scale batch deployment.
Happy smoothing.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CSharp (>= 4.7.0)
- Microsoft.Office.Interop.Excel (>= 15.0.4795.1001)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
3.1.5 | 0 | 9/17/2025 |
- Added Validation to enforce non‑negative kernel radius; returns an error if "radius < 0".
- Updated "README.md" with revised usage examples and parameter constraints reflecting the new "r ≥ 0" rule.
- Revised "API_Documentation.xml" summaries and remarks for "ScoreConformanceChecker" and "ValidateSmoothingParameters" to include the non‑negative radius requirement.
- Amended "API_Reference.md" to document updated validation rules and error message formats for smoothing parameter checks.