sciBASIC# knot logo sciBASIC#

03 Tutorial — Regression

Fit a line, cross-check a quadratic, and write the predictions back onto the table.

Module Bootstrapping · Fittings Dataset synthetic · 100 × 1 Fit R² 0.9988 · RMSE 0.201 Output PNG + CSV

The regression convention in sciBASIC# is deliberately boring: X is the feature columns of a NumericTable, y is the label column you name. A synthetic dataset with deterministic sawtooth noise keeps the run reproducible — LinearFit lands on y = 2.0018x + 2.9862 with R² = 0.9988, PolyFit(2) confirms nothing better is hiding in the noise, and SetPrediction writes predictions and residuals back as new label columns for export.

02 Pipeline

From synthetic data to a fitted line

Step 1

Synthesize

100 points of y = 2x + 3 + noise, where the noise is a deterministic sawtooth so every run produces exactly the same fit. NumericTable.FromRows wraps it in the unified 2-D table and table.SetLabel("y", y) attaches the observed values as the label column.

Step 2

Fit

table.LinearFit(y := "y") returns a FitResult model carrying slope, intercept, R², adjusted R², RMSE and residuals — y = 2.0018x + 2.9862 with R² = 0.9988.

Step 3

Cross-check

table.PolyFit(poly_n := 2) fits a quadratic for comparison: R² = 0.9988 — the quadratic buys nothing, which is the point of a linear ground truth.

Step 4

Write back

table.SetPrediction(model, withResidual := True) leaves the source table untouched and returns a new table whose label matrix carries y, prediction and residual columns.

Step 5

Plot & export

A ScatterPlot (800 × 600, Nature theme) draws the 100 observed points plus a 50-point model.GetY line series; result.WriteCsv exports the annotated table.

01 The Script

Full demo source

The complete script exactly as executed by the sciBASIC# script engine (vbs.exe) — nothing elided.

linear_regression.vb · 122 linesDownload linear_regression.vb
#include "Microsoft.VisualBasic.Data.Bootstrapping.Fittings.dll"
#include "Microsoft.VisualBasic.Data.Framework.dll"
#include "Microsoft.VisualBasic.Data.DataPlot.dll"
#include "Microsoft.VisualBasic.Drawing.dll"

imports Microsoft.VisualBasic.Data
imports Microsoft.VisualBasic.Data.Bootstrapping
imports Microsoft.VisualBasic.Data.Framework
imports microsoft.visualbasic.data.plots
imports microsoft.visualbasic.drawing

' ---------------------------------------------------------------------------
' Linear regression demo
'
'   dataset -> unified 2D table (NumericTable) -> LinearFit
'    -> prediction / residual written back to label columns
'    -> plot -> export csv
'
' The regression input convention is: X = all feature columns of the table,
' y = the label column named by the y parameter
' ---------------------------------------------------------------------------

' ---------------------------------------------------------------------------
' 1. Build a noisy linear dataset: y = 2x + 3 + noise
'
'    The noise is a deterministic sawtooth function so that every run produces
'    exactly the same fitting result
' ---------------------------------------------------------------------------
dim n = 100
dim features As Double()() = New Double(n - 1)() {}
dim y(n - 1) as double

for i = 0 to n - 1
    dim x = i * 0.1
    dim noise = ((i mod 7) - 3) * 0.1

    features(i) = New Double() {x}
    y(i) = 2.0 * x + 3.0 + noise
next

dim table = NumericTable.FromRows(Nothing, features, New String() {"x"})

call table.SetLabel("y", y)

call console.WriteLine($"dataset: {table.nsamples} samples x {table.nfeatures} feature")

' ---------------------------------------------------------------------------
' 2. Build the linear regression model
'
'    LinearFit returns a FitResult model object (slope/intercept/R2/RMSE/
'    residuals and so on are all carried on the model object)
' ---------------------------------------------------------------------------
dim model = table.LinearFit(y := "y")

call console.WriteLine($"linear fit  : y = {model.Slope} * x + {model.Intercept}")
call console.WriteLine($"R2 = {model.R_square}, adjust R2 = {model.AdjustR_square}, RMSE = {model.RMSE}")

' ---------------------------------------------------------------------------
' 3. Use a quadratic polynomial regression for comparison
' ---------------------------------------------------------------------------
dim quad = table.PolyFit(poly_n := 2)

call console.WriteLine($"poly fit(2) : R2 = {quad.R_square}, RMSE = {quad.RMSE}")

' ---------------------------------------------------------------------------
' 4. Write the prediction and the residual back into the label matrix of the table
'
'    SetPrediction does not modify the source table; it returns a new table with
'    the prediction columns written in
' ---------------------------------------------------------------------------
dim result = table.SetPrediction(model, withResidual := True)

call console.WriteLine($"prediction labels: {String.Join(", ", result.labelNames)}")
call console.WriteLine($"first row: y = {result.GetLabel("y")(0)}, prediction = {result.GetLabel("prediction")(0)}, residual = {result.GetLabel("residual")(0)}")

' ---------------------------------------------------------------------------
' 5. Draw the observed scatter points together with the fitted line
' ---------------------------------------------------------------------------
dim lineSize = 50
dim lineX(lineSize - 1) as double
dim lineY(lineSize - 1) as double
dim total = table.nsamples + lineSize
dim xs(total - 1) as double
dim ys(total - 1) as double
dim class_id(total - 1) as string

for i = 0 to lineSize - 1
    lineX(i) = i * 0.2
    lineY(i) = model.GetY(lineX(i))
next

for i = 0 to table.nsamples - 1
    xs(i) = table.Feature("x")(i)
    ys(i) = y(i)
    class_id(i) = "observed"
next

for i = 0 to lineSize - 1
    xs(table.nsamples + i) = lineX(i)
    ys(table.nsamples + i) = lineY(i)
    class_id(table.nsamples + i) = "fitted"
next

call SkiaDriver.Register()

Using plt As New ScatterPlot(800, 600, PlotTheme.Nature())
    plt.Title = "Linear regression of a synthetic dataset"
    plt.SubTitle = $"y = {model.Slope} * x + {model.Intercept}, R2 = {model.R_square}"
    plt.XLabel = "x"
    plt.YLabel = "y"
    plt.Plot(DataSerials(xs, ys, class_id).tolist())
    plt.SavePng(here("linear-regression.png"), 300)
End Using

' ---------------------------------------------------------------------------
' 6. Export the result table
'    (row names + x feature column + label:y / label:prediction / label:residual)
' ---------------------------------------------------------------------------
call result.WriteCsv(here("linear-regression.csv"))

call console.WriteLine("done: linear-regression.png")
call console.WriteLine("done: linear-regression.csv")

03 Results

The fitted line

Scatter of the synthetic dataset with the fitted regression line
Fig. 1 — The synthetic dataset (100 observed points) with the fitted line overlaid. The subtitle prints the model itself: slope 2.0018, intercept 2.9862, R² = 0.9988 — the sawtooth noise keeps every point honest but barely dents the fit.

Table preview — linear-regression.csv

The exported table pairs the observed label:y with the model's label:prediction and the signed label:residual for every sample. First rows and the last rows are shown; the full file holds all 100 data rows.

xlabel:ylabel:predictionlabel:residual
102.72.9862376237623910.2862376237623909
20.133.1864146414641610.18641464146416098
30.23.33.3865916591659310.08659165916593103
40.300000000000000043.63.5867686768677007-0.013231323132299355
···
989.70000000000000122.70000000000000322.403408340834073-0.2965916591659301
999.822.322.603585358535840.303585358535841
1009.922.622.803762376237610.2037623762376093
linear_regression.vb — console output
dataset: 100 samples x 1 feature
linear fit  : y = 2.0017701770176988 * x + 2.986237623762391
R2 = 0.9987900231789764, adjust R2 = 0.998777676476721, RMSE = 0.20111909379516651
poly fit(2) : R2 = 0.9987939843937537, RMSE = 0.20078961267610587
prediction labels: y, prediction, residual
first row: y = 2.7, prediction = 2.986237623762391, residual = 0.2862376237623909
done: linear-regression.png
done: linear-regression.csv
First row, straight from the console: y = 2.7, predicted 2.9862376, residual 0.2862376. Because the noise is deterministic, the whole run — and every digit on this page — reproduces exactly on your machine.