C++ Examples¶
Complete C++ examples demonstrating the fastLoess C++ bindings with modern C++ features.
Batch Smoothing¶
Process complete datasets with the idiomatic C++ wrapper.
/**
* @file batch_smoothing.cpp
* @brief fastloess Batch Smoothing Example
*
* This example demonstrates batch LOESS smoothing features:
* - Basic smoothing with different parameters
* - Robustness iterations for outlier handling
* - Confidence and prediction intervals
* - Diagnostics and cross-validation
*
* The Loess class is the primary interface for
* processing complete datasets that fit in memory.
*/
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <exception>
#include <iomanip>
#include <iostream>
#include <limits>
#include <random>
#include <vector>
#include "../../bindings/cpp/include/fastloess.hpp"
namespace {
constexpr size_t k_default_point_count = 100;
constexpr unsigned int k_random_seed = 42;
constexpr double k_noise_std_dev = 1.5;
constexpr double k_outlier_magnitude_min = 10.0;
constexpr double k_outlier_magnitude_max = 20.0;
constexpr double k_x_range_max = 50.0;
constexpr double k_trend_slope = 0.5;
constexpr double k_seasonal_amplitude = 5.0;
constexpr double k_seasonal_frequency = 0.5;
constexpr size_t k_outlier_divisor = 10;
constexpr double k_basic_fraction = 0.05;
constexpr double k_confidence_level = 0.95;
constexpr double k_linear_range_max = 10.0;
constexpr size_t k_linear_point_count = 50;
constexpr double k_linear_slope = 2.0;
constexpr double k_linear_intercept = 1.0;
constexpr double k_boundary_fraction = 0.6;
constexpr double k_auto_converge_tol = 1e-3;
constexpr double k_cv_fraction_low = 0.1;
constexpr double k_cv_fraction_mid = 0.2;
constexpr int k_cv_k = 3;
struct Data {
std::vector<double> x;
std::vector<double> y;
std::vector<double> y_true;
};
Data generateSampleData(size_t point_count = k_default_point_count) {
Data data;
data.x.resize(point_count);
data.y.resize(point_count);
data.y_true.resize(point_count);
std::seed_seq generator_seed = {k_random_seed, k_random_seed, k_random_seed,
k_random_seed};
std::mt19937 generator(generator_seed);
std::normal_distribution<> noise(0.0, k_noise_std_dev);
std::uniform_real_distribution<> outlier_magnitude(k_outlier_magnitude_min,
k_outlier_magnitude_max);
std::uniform_int_distribution<> outlier_sign(0, 1);
for (size_t point_index = 0; point_index < point_count; ++point_index) {
data.x[point_index] = static_cast<double>(point_index) * k_x_range_max /
static_cast<double>(point_count - 1);
data.y_true[point_index] =
(k_trend_slope * data.x[point_index]) +
(k_seasonal_amplitude *
std::sin(data.x[point_index] * k_seasonal_frequency));
data.y[point_index] = data.y_true[point_index] + noise(generator);
}
const size_t outlier_count = point_count / k_outlier_divisor;
std::uniform_int_distribution<size_t> outlier_index(0, point_count - 1);
for (size_t outlier_number = 0; outlier_number < outlier_count;
++outlier_number) {
const size_t point_index = outlier_index(generator);
double outlier_value = outlier_magnitude(generator);
if (outlier_sign(generator) == 0) {
outlier_value = -outlier_value;
}
data.y[point_index] += outlier_value;
}
return data;
}
} // namespace
int main() {
try {
std::cout << "=== fastloess Batch Smoothing Example ===\n";
// 1. Generate Data
auto data = generateSampleData(k_default_point_count);
std::cout << "Generated " << data.x.size() << " data points with outliers."
<< '\n';
// 2. Basic Smoothing (Default parameters)
std::cout << "Running basic smoothing...\n";
fastloess::LoessOptions basic_opts;
basic_opts.fraction = k_basic_fraction;
basic_opts.iterations = 0;
fastloess::Loess model_basic(basic_opts);
auto res_basic = model_basic.fit(data.x, data.y).value();
// 3. Robust Smoothing (IRLS)
std::cout << "Running robust smoothing (3 iterations)...\n";
fastloess::LoessOptions robust_opts;
robust_opts.fraction = k_basic_fraction;
robust_opts.iterations = 3;
robust_opts.robustness_method = "bisquare";
robust_opts.return_robustness_weights = true;
fastloess::Loess model_robust(robust_opts);
auto res_robust = model_robust.fit(data.x, data.y).value();
// 4. Uncertainty Quantification
std::cout << "Computing confidence and prediction intervals..." << '\n';
fastloess::LoessOptions interval_opts;
interval_opts.fraction = k_basic_fraction;
interval_opts.confidence_intervals = k_confidence_level;
interval_opts.prediction_intervals = k_confidence_level;
interval_opts.return_diagnostics = true;
fastloess::Loess model_intervals(interval_opts);
auto res_intervals = model_intervals.fit(data.x, data.y).value();
// 5. Cross-Validation for optimal fraction
std::cout << "Running cross-validation to find optimal fraction..." << '\n';
// Manual CV search
const std::vector<double> fractions = {k_basic_fraction, 0.1, 0.2, 0.4};
double best_fraction = 0.0;
double min_rmse = std::numeric_limits<double>::max();
for (const double fraction : fractions) {
fastloess::LoessOptions cv_opts;
cv_opts.fraction = fraction;
cv_opts.return_diagnostics = true;
fastloess::Loess model(cv_opts);
auto res_exp = model.fit(data.x, data.y);
// Use non-throwing interface
if (res_exp.has_value()) {
auto &res = res_exp.value();
if (res.diagnostics().has_value()) {
const double rmse = res.diagnostics().rmse();
if (rmse < min_rmse) {
min_rmse = rmse;
best_fraction = fraction;
}
}
}
}
std::cout << "Optimal fraction found (manual CV): " << best_fraction
<< '\n';
// Diagnostics Printout
if (res_intervals.diagnostics().has_value()) {
const auto diag = res_intervals.diagnostics();
std::cout << "\nFit Statistics (Intervals Model):\n";
std::cout << " - R^2: " << diag.r_squared() << '\n';
std::cout << " - RMSE: " << diag.rmse() << '\n';
std::cout << " - MAE: " << diag.mae() << '\n';
}
// 6. Boundary Policy Comparison
std::cout << "\nDemonstrating boundary policy effects on linear data..."
<< '\n';
std::vector<double> linear_x(k_linear_point_count);
std::vector<double> linear_y(k_linear_point_count);
for (size_t point_index = 0; point_index < k_linear_point_count;
++point_index) {
linear_x[point_index] = static_cast<double>(point_index) *
k_linear_range_max /
static_cast<double>(k_linear_point_count - 1);
linear_y[point_index] =
(k_linear_slope * linear_x[point_index]) + k_linear_intercept;
}
fastloess::LoessOptions opt_ext;
opt_ext.fraction = k_boundary_fraction;
opt_ext.boundary_policy = "extend";
auto r_ext = fastloess::Loess(opt_ext).fit(linear_x, linear_y).value();
fastloess::LoessOptions opt_ref;
opt_ref.fraction = k_boundary_fraction;
opt_ref.boundary_policy = "reflect";
auto r_ref = fastloess::Loess(opt_ref).fit(linear_x, linear_y).value();
fastloess::LoessOptions opt_zero;
opt_zero.fraction = k_boundary_fraction;
opt_zero.boundary_policy = "zero";
auto r_zr = fastloess::Loess(opt_zero).fit(linear_x, linear_y).value();
std::cout << "Boundary policy comparison:\n";
std::cout << std::fixed << std::setprecision(2);
std::cout << " - Extend (Default): first=" << r_ext.y_value(0)
<< ", last=" << r_ext.y_value(k_linear_point_count - 1) << '\n';
std::cout << " - Reflect: first=" << r_ref.y_value(0)
<< ", last=" << r_ref.y_value(k_linear_point_count - 1) << '\n';
std::cout << " - Zero: first=" << r_zr.y_value(0)
<< ", last=" << r_zr.y_value(k_linear_point_count - 1) << '\n';
// 7. Boundary policy: noboundary
std::cout << "\n--- Boundary Policy: noboundary ---\n";
fastloess::LoessOptions nb_opts;
nb_opts.fraction = k_boundary_fraction;
nb_opts.boundary_policy = "noboundary";
auto nb_res = fastloess::Loess(nb_opts).fit(linear_x, linear_y).value();
std::cout << " - noboundary first=" << nb_res.y_value(0)
<< ", last=" << nb_res.y_value(k_linear_point_count - 1) << '\n';
// 8. Weight function variants
std::cout << "\n--- Weight Function Variants ---\n";
for (const char *wfn : {"tricube", "epanechnikov", "gaussian", "uniform",
"biweight", "triangle", "cosine"}) {
fastloess::LoessOptions wf_opts;
wf_opts.fraction = k_basic_fraction;
wf_opts.weight_function = wfn;
auto wf_res = fastloess::Loess(wf_opts).fit(data.x, data.y).value();
std::cout << " weight_function=" << wfn << " y[0]=" << wf_res.y_value(0)
<< '\n';
}
// 9. Polynomial degrees
std::cout << "\n--- Polynomial Degrees ---\n";
for (const char *deg : {"constant", "linear", "quadratic"}) {
fastloess::LoessOptions deg_opts;
deg_opts.fraction = k_basic_fraction;
deg_opts.degree = deg;
auto deg_res = fastloess::Loess(deg_opts).fit(data.x, data.y).value();
std::cout << " degree=" << deg << " y[0]=" << deg_res.y_value(0)
<< '\n';
}
// 10. Scaling methods
std::cout << "\n--- Scaling Methods ---\n";
for (const char *scl : {"mad", "mar", "mean"}) {
fastloess::LoessOptions scl_opts;
scl_opts.fraction = k_basic_fraction;
scl_opts.scaling_method = scl;
auto scl_res = fastloess::Loess(scl_opts).fit(data.x, data.y).value();
std::cout << " scaling_method=" << scl << " y[0]=" << scl_res.y_value(0)
<< '\n';
}
// 11. Distance metrics
std::cout << "\n--- Distance Metrics ---\n";
for (const char *met :
{"euclidean", "normalized", "manhattan", "chebyshev"}) {
fastloess::LoessOptions met_opts;
met_opts.fraction = k_basic_fraction;
met_opts.distance_metric = met;
auto met_res = fastloess::Loess(met_opts).fit(data.x, data.y).value();
std::cout << " distance_metric=" << met
<< " y[0]=" << met_res.y_value(0) << '\n';
}
// 12. Robustness method variants (bisquare shown above; add huber + talwar)
std::cout << "\n--- Robustness Method Variants ---\n";
for (const char *rob : {"huber", "talwar"}) {
fastloess::LoessOptions rob_opts;
rob_opts.fraction = k_basic_fraction;
rob_opts.iterations = 2;
rob_opts.robustness_method = rob;
auto rob_res = fastloess::Loess(rob_opts).fit(data.x, data.y).value();
std::cout << " robustness_method=" << rob
<< " y[0]=" << rob_res.y_value(0) << '\n';
}
// 13. Surface mode "direct" + return_se: standard errors and hat-matrix
// stats
std::cout << "\n--- Surface Mode: direct + Standard Errors ---\n";
fastloess::LoessOptions se_opts;
se_opts.fraction = k_basic_fraction;
se_opts.surface_mode = "direct";
se_opts.return_se = true;
auto se_res = fastloess::Loess(se_opts).fit(data.x, data.y).value();
std::cout << " enp: " << se_res.enp() << '\n';
std::cout << " trace_hat: " << se_res.trace_hat() << '\n';
std::cout << " delta1: " << se_res.delta1() << '\n';
std::cout << " delta2: " << se_res.delta2() << '\n';
std::cout << " residual_scale:" << se_res.residual_scale() << '\n';
{
const auto std_errors = se_res.standard_errors();
if (!std_errors.empty()) {
std::cout << " se[0]: " << std_errors[0] << '\n';
}
const auto leverage_vals = se_res.leverage();
if (!leverage_vals.empty()) {
std::cout << " leverage[0]: " << leverage_vals[0] << '\n';
}
}
// 14. return_residuals + return_robustness_weights + result metadata
std::cout
<< "\n--- Residuals, Robustness Weights, and Result Metadata ---\n";
fastloess::LoessOptions meta_opts;
meta_opts.fraction = k_basic_fraction;
meta_opts.iterations = 2;
meta_opts.robustness_method = "huber";
meta_opts.return_residuals = true;
meta_opts.return_robustness_weights = true;
auto meta_res = fastloess::Loess(meta_opts).fit(data.x, data.y).value();
std::cout << " fraction_used: " << meta_res.fraction_used() << '\n';
std::cout << " iterations_used: " << meta_res.iterations_used() << '\n';
std::cout << " dimensions: " << meta_res.dimensions() << '\n';
std::cout << " valid(): " << meta_res.valid() << '\n';
std::cout << " x_vector().size() " << meta_res.x_vector().size() << '\n';
std::cout << " y_vector().size() " << meta_res.y_vector().size() << '\n';
{
const auto residuals = meta_res.residuals();
if (!residuals.empty()) {
std::cout << " residuals[0]: " << residuals[0] << '\n';
}
const auto rob_weights = meta_res.robustness_weights();
if (!rob_weights.empty()) {
std::cout << " robWeight[0]: " << rob_weights[0] << '\n';
}
}
// 15. Auto-convergence
std::cout << "\n--- Auto-Convergence ---\n";
fastloess::LoessOptions conv_opts;
conv_opts.fraction = k_basic_fraction;
conv_opts.auto_converge = k_auto_converge_tol;
auto conv_res = fastloess::Loess(conv_opts).fit(data.x, data.y).value();
std::cout << " auto_converge=" << k_auto_converge_tol
<< " iterations_used=" << conv_res.iterations_used() << '\n';
// 16. Zero-weight fallback options
std::cout << "\n--- Zero-Weight Fallback ---\n";
for (const char *zfb :
{"use_local_mean", "return_original", "return_none"}) {
fastloess::LoessOptions zfb_opts;
zfb_opts.fraction = k_basic_fraction;
zfb_opts.zero_weight_fallback = zfb;
auto zfb_res = fastloess::Loess(zfb_opts).fit(data.x, data.y).value();
std::cout << " zero_weight_fallback=" << zfb
<< " y[0]=" << zfb_res.y_value(0) << '\n';
}
// 17. Built-in cross-validation (cv_fractions, cv_method, cv_k)
std::cout << "\n--- Built-in Cross-Validation ---\n";
fastloess::LoessOptions cv2_opts;
cv2_opts.cv_fractions = {k_cv_fraction_low, k_basic_fraction,
k_cv_fraction_mid};
cv2_opts.cv_method = "kfold";
cv2_opts.cv_k = k_cv_k;
auto cv2_res = fastloess::Loess(cv2_opts).fit(data.x, data.y).value();
std::cout << " CV-selected fraction: " << cv2_res.fraction_used() << '\n';
// 18. Parallel smoothing
std::cout << "\n--- Parallel Smoothing ---\n";
fastloess::LoessOptions par_opts;
par_opts.fraction = k_basic_fraction;
par_opts.parallel = true;
auto par_res = fastloess::Loess(par_opts).fit(data.x, data.y).value();
std::cout << " parallel result size: " << par_res.size() << '\n';
// 19. Expected<> error path: has_value() / error()
std::cout << "\n--- Expected<> Error Path ---\n";
{
const fastloess::LoessOptions err_opts;
const std::vector<double> short_x = {1.0, 2.0, 3.0};
const std::vector<double> short_y = {1.0, 2.0};
auto err_exp = fastloess::Loess(err_opts).fit(short_x, short_y);
if (!err_exp.has_value()) {
std::cout << " has_value()=false, error: " << err_exp.error() << '\n';
}
}
// 20. Full diagnostics (aic, aicc, effective_df, residual_sd, has_value)
std::cout << "\n--- Full Diagnostics ---\n";
fastloess::LoessOptions full_diag_opts;
full_diag_opts.fraction = k_basic_fraction;
full_diag_opts.return_diagnostics = true;
auto full_diag_res =
fastloess::Loess(full_diag_opts).fit(data.x, data.y).value();
const auto full_diag = full_diag_res.diagnostics();
if (full_diag.has_value()) {
std::cout << " aic: " << full_diag.aic() << '\n';
std::cout << " aicc: " << full_diag.aicc() << '\n';
std::cout << " effective_df: " << full_diag.effective_df() << '\n';
std::cout << " residual_sd: " << full_diag.residual_sd() << '\n';
}
std::cout << "\n=== Batch Smoothing Example Complete ===\n";
} catch (const std::exception &exception) {
std::fputs("Error: ", stderr);
std::fputs(exception.what(), stderr);
std::fputc('\n', stderr);
return 1;
}
return 0;
}
Streaming Smoothing¶
Process large datasets in memory-efficient chunks.
/**
* @file streaming_smoothing.cpp
* @brief Streaming LOESS smoothing example
*
* Demonstrates chunk-based processing for large datasets.
*/
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <exception>
#include <iostream>
#include <random>
#include <vector>
#include "../../bindings/cpp/include/fastloess.hpp"
namespace {
constexpr size_t k_point_count = 10000;
constexpr unsigned int k_random_seed = 42;
constexpr double k_noise_std_dev = 0.5;
constexpr double k_sine_divisor = 10.0;
constexpr double k_scale_divisor = 50.0;
constexpr double k_fraction = 0.1;
constexpr int k_chunk_size = 1000;
constexpr int k_overlap = 100;
constexpr size_t k_progress_interval = 2000;
} // namespace
int main() {
try {
std::cout << "=== Streaming LOESS Smoothing Example ===\n";
// Generate large synthetic dataset
const size_t point_count = k_point_count;
std::vector<double> x_values(point_count);
std::vector<double> y_values(point_count);
std::seed_seq generator_seed = {k_random_seed, k_random_seed, k_random_seed,
k_random_seed};
std::mt19937 generator(generator_seed);
std::normal_distribution<> noise(0.0, k_noise_std_dev);
for (size_t point_index = 0; point_index < point_count; ++point_index) {
x_values[point_index] = static_cast<double>(point_index) / k_overlap;
y_values[point_index] =
((std::sin(x_values[point_index] / k_sine_divisor) *
x_values[point_index]) /
k_scale_divisor) +
noise(generator);
}
std::cout << "Generated " << point_count << " data points\n";
// Streaming smoothing
fastloess::StreamingOptions opts;
opts.fraction = k_fraction;
opts.iterations = 2;
opts.chunk_size = k_chunk_size;
opts.overlap = k_overlap;
opts.return_diagnostics = true;
std::cout << "\nProcessing with chunk_size=" << opts.chunk_size
<< ", overlap=" << opts.overlap << '\n';
fastloess::StreamingLoess model(opts);
std::cout << "\nProcessing data in chunks...\n";
const size_t chunk_size = static_cast<size_t>(opts.chunk_size);
size_t total_processed = 0;
for (size_t chunk_start = 0; chunk_start < point_count;
chunk_start += chunk_size) {
const size_t current_chunk_len =
std::min(chunk_size, point_count - chunk_start);
std::vector<double> x_chunk(current_chunk_len);
std::vector<double> y_chunk(current_chunk_len);
std::copy_n(x_values.begin() + static_cast<std::ptrdiff_t>(chunk_start),
static_cast<std::ptrdiff_t>(current_chunk_len),
x_chunk.begin());
std::copy_n(y_values.begin() + static_cast<std::ptrdiff_t>(chunk_start),
static_cast<std::ptrdiff_t>(current_chunk_len),
y_chunk.begin());
auto res = model.process_chunk(x_chunk, y_chunk).value();
total_processed += res.size();
if (chunk_start % k_progress_interval == 0) {
std::cout << " Processed " << chunk_start << " points...\n";
}
}
auto final_res = model.finalize().value();
total_processed += final_res.size();
std::cout << "\nStreaming completed:\n";
std::cout << " Total points smoothed: " << total_processed << '\n';
// Show sample of final results
if (final_res.size() > 0) {
std::cout << "\nSample from final chunk:\n";
std::cout << " x=" << final_res.x_value(0)
<< " y=" << final_res.y_value(0) << '\n';
}
// Merge strategy variants
std::cout << "\n--- Merge Strategy Variants ---\n";
for (const char *strat : {"weighted", "average", "first", "last"}) {
fastloess::StreamingOptions ms_opts;
ms_opts.fraction = k_fraction;
ms_opts.chunk_size = k_chunk_size;
ms_opts.merge_strategy = strat;
fastloess::StreamingLoess ms_model(ms_opts);
const std::vector<double> x_s(x_values.begin(),
x_values.begin() + k_overlap);
const std::vector<double> y_s(y_values.begin(),
y_values.begin() + k_overlap);
auto ms_r1 = ms_model.process_chunk(x_s, y_s).value();
auto ms_r2 = ms_model.finalize().value();
std::cout << " merge_strategy=" << strat
<< " total=" << ms_r1.size() + ms_r2.size() << '\n';
}
// Advanced inherited options: degree, scaling_method, distance_metric,
// surface_mode, return_se, return_residuals, zero_weight_fallback
std::cout << "\n--- Advanced Streaming Options ---\n";
{
fastloess::StreamingOptions adv_opts;
adv_opts.fraction = k_fraction;
adv_opts.chunk_size = k_chunk_size;
adv_opts.degree = "quadratic";
adv_opts.scaling_method = "mean";
adv_opts.distance_metric = "euclidean";
adv_opts.surface_mode = "direct";
adv_opts.return_se = true;
adv_opts.return_residuals = true;
adv_opts.zero_weight_fallback = "return_original";
fastloess::StreamingLoess adv_model(adv_opts);
const std::vector<double> a_x(
x_values.begin(),
x_values.begin() + static_cast<std::ptrdiff_t>(k_chunk_size));
const std::vector<double> a_y(
y_values.begin(),
y_values.begin() + static_cast<std::ptrdiff_t>(k_chunk_size));
auto adv_r1 = adv_model.process_chunk(a_x, a_y).value();
auto adv_r2 = adv_model.finalize().value();
std::cout << " total points: " << adv_r1.size() + adv_r2.size() << '\n';
}
std::cout << "\n=== Example completed successfully ===\n";
} catch (const std::exception &exception) {
std::fputs("Error: ", stderr);
std::fputs(exception.what(), stderr);
std::fputc('\n', stderr);
return 1;
}
return 0;
}
Download streaming_smoothing.cpp
Online Smoothing¶
Real-time smoothing with sliding window for streaming data.
/**
* @file online_smoothing.cpp
* @brief Online LOESS smoothing example
*
* Demonstrates sliding window smoothing for real-time data.
*/
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <exception>
#include <iostream>
#include <random>
#include <vector>
#include "../../bindings/cpp/include/fastloess.hpp"
namespace {
constexpr size_t k_point_count = 200;
constexpr unsigned int k_random_seed = 42;
constexpr double k_noise_std_dev = 0.3;
constexpr double k_trend_slope = 0.1;
constexpr double k_seasonal_amplitude = 5.0;
constexpr double k_seasonal_period_divisor = 20.0;
constexpr double k_fraction = 0.5;
constexpr int k_window_capacity = 50;
constexpr int k_min_points = 10;
constexpr size_t k_progress_interval = 40;
} // namespace
int main() {
try {
std::cout << "=== Online LOESS Smoothing Example ===\n";
// Simulate streaming data arrival
const size_t point_count = k_point_count;
std::vector<double> x_values(point_count);
std::vector<double> y_values(point_count);
std::seed_seq generator_seed = {k_random_seed, k_random_seed, k_random_seed,
k_random_seed};
std::mt19937 generator(generator_seed);
std::normal_distribution<> noise(0.0, k_noise_std_dev);
for (size_t point_index = 0; point_index < point_count; ++point_index) {
x_values[point_index] = static_cast<double>(point_index);
// Trend with seasonal component + noise
y_values[point_index] =
(k_trend_slope * x_values[point_index]) +
(k_seasonal_amplitude *
std::sin(x_values[point_index] / k_seasonal_period_divisor)) +
noise(generator);
}
std::cout << "Generated " << point_count << " streaming data points\n";
// Online smoothing with sliding window
fastloess::OnlineOptions opts;
opts.fraction = k_fraction;
opts.iterations = 2;
opts.window_capacity = k_window_capacity;
opts.min_points = k_min_points;
opts.update_mode = "full";
std::cout << "\nProcessing with window_capacity=" << opts.window_capacity
<< ", min_points=" << opts.min_points << '\n';
fastloess::OnlineLoess model(opts);
std::cout << "\nProcessing data point-by-point...\n";
size_t total_emitted = 0;
for (size_t point_index = 0; point_index < point_count; ++point_index) {
auto res =
model.add_point(x_values[point_index], y_values[point_index]).value();
if (res.has_value()) {
total_emitted++;
if (point_index > 0 && point_index % k_progress_interval == 0) {
std::cout << " t=" << point_index
<< " original=" << y_values[point_index]
<< " smoothed=" << res.smoothed() << '\n';
}
}
}
std::cout << "\nOnline processing completed:\n";
std::cout << " Total points emitted: " << total_emitted << '\n';
// Incremental update mode
std::cout << "\n--- Update Mode: incremental ---\n";
{
fastloess::OnlineOptions inc_opts;
inc_opts.fraction = k_fraction;
inc_opts.window_capacity = k_window_capacity;
inc_opts.min_points = k_min_points;
inc_opts.update_mode = "incremental";
fastloess::OnlineLoess inc_model(inc_opts);
size_t inc_emitted = 0;
for (size_t point_index = 0; point_index < point_count; ++point_index) {
auto res_i =
inc_model.add_point(x_values[point_index], y_values[point_index])
.value();
if (res_i.has_value()) {
inc_emitted++;
}
}
std::cout << " incremental mode total emitted: " << inc_emitted << '\n';
}
// Advanced inherited options: degree, scaling_method, distance_metric,
// zero_weight_fallback
std::cout << "\n--- Advanced Online Options ---\n";
{
fastloess::OnlineOptions adv_opts;
adv_opts.fraction = k_fraction;
adv_opts.window_capacity = k_window_capacity;
adv_opts.min_points = k_min_points;
adv_opts.degree = "quadratic";
adv_opts.scaling_method = "mar";
adv_opts.distance_metric = "chebyshev";
adv_opts.zero_weight_fallback = "return_original";
fastloess::OnlineLoess adv_model(adv_opts);
size_t adv_emitted = 0;
for (size_t point_index = 0; point_index < point_count; ++point_index) {
auto res_a =
adv_model.add_point(x_values[point_index], y_values[point_index])
.value();
if (res_a.has_value()) {
adv_emitted++;
}
}
std::cout << " advanced online total emitted: " << adv_emitted << '\n';
}
std::cout << "\n=== Example completed successfully ===\n";
} catch (const std::exception &exception) {
std::fputs("Error: ", stderr);
std::fputs(exception.what(), stderr);
std::fputc('\n', stderr);
return 1;
}
return 0;
}
Building the Examples¶
# Build the C++ bindings
make cpp
# The examples are built as part of the bindings
# Or compile manually:
g++ -std=c++20 -I bindings/cpp/include \
examples/cpp/batch_smoothing.cpp \
-L target/release -lfastloess_cpp \
-o batch_smoothing
Quick Start¶
#include <fastloess.hpp>
#include <iostream>
#include <vector>
int main() {
// Generate sample data
std::vector<double> x(100), y(100);
for (size_t i = 0; i < 100; ++i) {
x[i] = i * 0.1;
y[i] = std::sin(x[i]) + 0.1;
}
// Configure options
fastloess::LoessOptions options;
options.fraction = 0.3;
options.iterations = 3;
options.confidence_intervals = 0.95;
options.return_diagnostics = true;
// Smooth
fastloess::Loess model(options);
auto expected = model.fit(x, y);
if (!expected.has_value()) {
std::cerr << "Error: " << expected.error() << std::endl;
return 1;
}
auto& result = expected.value();
std::cout << "R²: " << result.diagnostics().r_squared() << std::endl;
// Access smoothed values
auto smoothed = result.y_vector();
return 0;
}
Features¶
The C++ bindings provide:
- RAII memory management - Resources automatically freed
- STL container support -
std::vector<double>for all arrays - Exception-based errors -
fastloess::LoessErrorfor error handling - Modern C++ idioms - Designated initializers, move semantics