Hello, nSTAT
This is the minimum workflow for fitting a point-process GLM to a spike train in nSTAT: 8 classes, one synthetic dataset, finishing with a time-rescaling KS goodness-of-fit plot. Total runtime ~3 seconds. Open it in the Live Editor or run cell-by-cell.
What you'll learn:
- The 8 nSTAT classes you need to know (in dependency order). - How they compose into a Trial. - How Analysis.RunAnalysisForNeuron fits a GLM and returns a FitResult. - How to read the KS plot for goodness-of-fit.
What you won't learn here:
- The math behind the model -- see Chapter 4 of Decoding the Brain (bci-curriculum/chapters/chapter-04-point-processes.md). - Decoding (PPAF / PPHF / PPLFP) -- see helpfiles/DecodingExample.m. - State-space GLM for trial-drifting coefficients -- see helpfiles/SSGLMExample.m.
Contents
Step 1 -- simulate a spike train
In real use, replace this with your loaded spike times. We simulate a homogeneous Poisson process at 12 Hz for 10 seconds (a reasonable cortical-neuron firing rate).
rng(0, 'twister'); % reproducibility T = 10.0; % seconds sampleRate = 1000; % Hz (1 ms bins) delta = 1/sampleRate; trueRateHz = 12.0; t = (0:delta:T-delta)'; % nBins x 1 time vector y = double(rand(numel(t),1) < trueRateHz*delta); spikeTimes = t(y==1); fprintf('Simulated %d spikes (target rate %.1f Hz)\n', ... numel(spikeTimes), trueRateHz);
Step 2 -- wrap as nspikeTrain
nspikeTrain(spikeTimes, name, binwidth, minTime, maxTime) is the basic point-process container. The binwidth discretizes the process for filter and likelihood evaluation; we use 1 ms.
nst = nspikeTrain(spikeTimes, 'unit1', delta, 0, T);
Step 3 -- gather into nstColl
nstColl holds a collection of spike trains (one or more). nSTAT's Analysis methods iterate over collections; even a single-neuron workflow uses this wrapper.
spikeColl = nstColl(nst);
Step 4 -- build covariates
Covariates are time-aligned predictors. The intercept-only baseline is mandatory: nSTAT's GLM does NOT add an implicit intercept; you must provide one.
baseline = Covariate(t, ones(numel(t),1), 'Baseline', ... 'time','s','',{'const'}); covColl = CovColl({baseline});
Step 5 -- assemble the Trial
A Trial is the central experimental container: one spike-train collection + one covariate collection + (optional) events + (optional) history basis. Here we leave events and history empty for the minimal example.
trial = Trial(spikeColl, covColl);
Step 6 -- define a model configuration
A TrialConfig names which covariates the GLM uses. Multiple configurations can be wrapped in a ConfigColl for nested-model comparison via AIC/BIC; we use one here.
cfg = TrialConfig({{'Baseline','const'}}, sampleRate, []);
configColl = ConfigColl(cfg);
Step 7 -- fit the GLM
Analysis.RunAnalysisForNeuron(trial, neuronNumber, configColl, makePlot, algorithm, DTCorrection) fits a point-process GLM with the chosen covariates and returns a FitResult with the fitted intensity, coefficients, deviance, AIC/BIC, and time-rescaled spike times for goodness-of-fit.
fitResults = Analysis.RunAnalysisForNeuron( ... trial, 1, configColl, ... 0, ... % makePlot = 0 (we'll plot separately) 'GLM', ... % algorithm 1); % DTCorrection = 1 (Haslinger 2010) fprintf('\nFit complete:\n'); fprintf(' logLL = %.2f\n', fitResults.logLL); fprintf(' AIC = %.2f\n', fitResults.AIC); fprintf(' BIC = %.2f\n', fitResults.BIC); fprintf(' Fitted intercept coefficient: %.4f (true: log(%.3f) = %.4f)\n', ... fitResults.b{1}(1), trueRateHz*delta, log(trueRateHz*delta));
Step 8 -- KS plot for goodness-of-fit
The time-rescaling theorem (Brown et al. 2002) transforms the fitted intensity into a sequence of "rescaled" inter-spike intervals that should be Uniform(0,1) under the null hypothesis that the fitted intensity matches the true intensity. The KS plot compares the empirical CDF to the diagonal; deviations diagnose misspecification.
For our true-rate baseline-only model, we expect the KS plot to sit comfortably inside the 95% confidence band (the constant rate model IS correctly specified for homogeneous Poisson data).
figure('Position', [100 100 600 500]); fitResults.KSPlot; title('Hello, nSTAT -- KS plot for baseline-only GLM');
That's it
You now have:
- A fitted point-process GLM (fitResults). - A goodness-of-fit plot (the KS curve inside the 95% band). - The infrastructure to extend to multi-covariate models (add covariates to CovColl, name them in TrialConfig), history models (hist = History(windowTimes), pass to Trial), and multi-neuron ensemble fits (Analysis.RunAnalysisForAllNeurons).
Next reads:
- helpfiles/DecodingExample.m -- point-process adaptive filter (PPAF) decoding stimulus from spike trains. - helpfiles/PPSimExample.m -- simulating point processes with CIF. - examples/paper/example01_mepsc_poisson.m -- full reproduction of the 2012 paper's first analysis.
References:
- Cajigas, Malik, Brown 2012. J Neurosci Methods 211:245-264 (the nSTAT toolbox paper). - Cajigas Lab Curriculum, chapter-04 sec 4.A.1 onwards (the textbook treatment of the math). - Brown, Barbieri, Ventura, Kass, Frank 2002. Neural Comput 14:325 (time-rescaling theorem).