Repository navigation
Guides
This guide explains what dtfit does, how it works, and why -- starting from zero. You do not need to know advanced math or algorithms to follow it. Where a formula appears, it is introduced in words first, and you can skip the symbols and still understand the point. When you want the fully rigorous derivations, each section links to the matching file in ../methods/.
- This page -- the one core idea behind everything, with pictures in words.
- methods-explained.md -- each fitting method (LSI, EAC, DSB, the streaming filters): intuition -> how it works -> the proof -> the tuning knobs and adaptations.
- lineage-and-variants.md -- the complete atlas: where each method came from, how it was improved (its versions), and every variant and adaptation (#1-#6 and the rest) in one map.
- choosing-a-method.md -- a decision guide: which method, which model, which knob, for your data.
Suppose you measured something over time and it looks like a curve -- bacteria growing, a currency falling, a sensor settling, a chemical reaction saturating. You believe it follows a known shape with a few unknown numbers in it. For example "exponential growth," written
where
The catch is twofold:
- The relationship is nonlinear in the parameters. Doubling
$b$ does not double$y$ ; it reshapes the whole curve. You cannot solve for$a, b$ with ordinary (linear) least squares the way you can fit a straight line. - Your data is noisy. Every measurement is the true value plus some random jitter, and some points may be wild outliers.
The standard tool for this is nonlinear least squares (e.g. SciPy's
curve_fit): guess the parameters, see how far the curve is from each data
point, nudge the parameters to reduce the total squared distance, repeat. It
works well, but it compares the model to the data point by point, so noise
in individual points pushes it around, and it needs a decent starting guess or
it gets lost.
dtfit takes a different route that is gentler on noise. That route is the one
idea this whole library is built on.
Here is the analogy. Imagine two songs and you want to know if they are the same tune. You could line them up and compare them note-by-note at every instant -- but if one recording is hissy, you'll see differences everywhere even when the tune is identical. A better way: compute each song's frequency fingerprint (which pitches, how loud) and compare those. The fingerprint averages over the whole song, so the hiss mostly cancels out, and two recordings of the same tune have matching fingerprints even when the raw waveforms look noisy.
dtfit does exactly this for curves. Every smooth function has a fingerprint
-- a list of numbers that captures its shape. dtfit computes the fingerprint of
your data and the fingerprint of your model (which depends on the unknown
parameters), and then tunes the parameters until the two fingerprints match.
The mathematical name for this fingerprint is the differential spectrum (also
called the differential transform or non-Taylor transform). It is the central
object in the author's dissertation, and the source of the package name:
differential-transform fit. In the code it has a shorter name, the
image: Original(x, y).image(basis, order) builds it once from the samples.
See methods/image.md.
For a smooth function, the fingerprint is the sequence of numbers
where each
Two facts make this fingerprint the perfect thing to match:
- It is a faithful ID. Two smooth functions are identical if and only if their fingerprints agree number-for-number. So if you make the model's fingerprint equal the data's fingerprint, you have made the model equal the data -- and therefore found the right parameters. (This "if and only if" is the property every method leans on.)
-
The parameters appear in it cleanly. For the standard building-block
functions -- powers, exponentials, sines, cosines -- the fingerprint has a known
closed form, so the unknown parameters
$a, b, w, \dots$ show up as plain algebra inside the fingerprint, ready to be solved for or fitted.
That second fact is the "non-Taylor" part. Classically you would worry that a
function like
The magic is integration. To get the data's fingerprint, dtfit integrates the data (adds it up over intervals) rather than differentiating it (looking at point-to-point differences).
This matters enormously:
- Differentiating amplifies noise. Subtracting two nearby noisy points and dividing by a tiny gap blows the noise up. Methods that need derivatives of raw data are fragile.
- Integrating averages noise away. Random jitter is equally likely to be up or down, so when you sum many samples the ups and downs cancel and the noise shrinks toward zero. The signal's shape survives; the noise washes out.
So by phrasing the fit as "match integral fingerprints," dtfit gets noise robustness essentially for free. This single choice -- integrate, don't differentiate -- is why the methods degrade gracefully as noise rises, and it is the thread connecting all of them.
All the methods match fingerprints; they differ in which fingerprint and how strictly they match it. From strictest/most-fragile to most-relaxed/robust:
the differential spectrum (fingerprint) of a signal
|
+------------------+----------------------+---------------------+
| | | |
DSB LSI EAC ImageFilter(basis="block") / ImageFilter(basis="legendre")
match the match the match integrated match fingerprints
fingerprint fingerprints in a AREAS over one sample at a
EXACTLY least-squares sense windows time (streaming)
(symbolic solve) (accurate batch) (robust batch) (real-time tracking)
-
DSB -- Differential Spectra Balance. Demands the two fingerprints be exactly equal and solves the resulting equations symbolically. Beautiful and exact on clean, ideal data -- but brittle once there is noise. Kept as the reference method used to derive and check the others, not for production. -> methods-explained.md#dsb
-
LSI -- Least-Squares Integral. Relaxes "exactly equal" to "as close as possible in a least-squares sense." This is the accurate, general-purpose batch fitter -- your default. It also uses a smarter fingerprint basis (Legendre polynomials) that is numerically stable. -> methods-explained.md#lsi
-
EAC -- Equal-Areas Criterion. Matches the simplest possible integral fingerprint: the area under the curve over a handful of windows. Each equation is local: it sums the data of one window and nothing else. That makes it the basis for records whose level jumps at known epochs and for data that arrives as per-window totals; on a smooth record LSI spends the same number of coefficients better. -> methods-explained.md#eac
-
ImageFilter(basis="block") / ImageFilter(basis="legendre") -- the streaming versions. Run EAC's (or LSI's) matching recursively, updating the estimate with each new sample at fixed cost, like a Kalman filter. They track parameters that change over time and detect regime changes (a sudden break in the data). This is the real-time path for control loops and live data streams. -> methods-explained.md#streaming
You can call the methods directly, but dtfit also gives you friendlier layers:
-
A model catalog -- instead of writing
"L/(1 + exp(-k*(x - x0)))"you ask formodels.logistic(), and it even reads good starting guesses off your data.suggest_models(x, y)will try a shortlist and rank them for you. -> api/models.md -
One-call entry points --
fit(model, data, basis="auto")fits the candidate bases and keeps the one with the lowest residual, so you never pick the basis yourself;auto_forecast(x, y, horizon)does a structured fit-then-extrapolate forecast with safety guards. -> api/auto.md -
A scikit-learn estimator --
NonlineRegressorplugs intoPipeline,GridSearchCV,cross_val_score. -> api/estimator.md - Scaling backends -- fit thousands of channels in one matrix multiply, or stream a dataset too big for memory in one pass. -> api/scaling.md
-
Diagnostics --
fit_reportgives R^2/RMSE/AIC/BIC and residual tests so you can tell whether the fit is trustworthy. -> api/diagnostics.md
Good documentation says where a method doesn't win. dtfit's are:
- It needs a modest dynamic range. The fingerprint is taken around a point, so a signal that spans many orders of magnitude should be normalized first (the docs show how). This is a real constraint, not a bug.
- On a near-random-walk series (like daily exchange rates), no parametric model beats "tomorrow = today" one step ahead -- and dtfit doesn't pretend to. Its streaming value there is tracking and regime detection, not beating a random walk.
-
It matches, rather than beats, a well-initialized
curve_fiton clean, simple shapes. Its edge shows up under noise, on outliers, in streaming, and at scale -- where it is more robust, faster, or cheaper, while still returning interpretable parameters.
These trade-offs, with real numbers, are documented per method in ../methods/ and validated across application domains in ../experimental/.
Next: methods-explained.md walks through each method in the same intuition-first style, including the proofs.
Published automatically from the repo's wiki/ directory by the wiki workflow. All pages are maintained directly in wiki/.
Start here
Guides
Examples
- Overview
- 01 Quickstart
- 02 Fitting methods
- 03 Models & auto
- 04 sklearn estimator
- 05 Streaming
- 06 Scaling
- 07 Diagnostics
- 08 Stochastic
API reference
Methods (math)
Experimental
Validation